2

I am struggling with performing a validation problem, based on a field entity called published I must apply validation rules:

  • Validate entity when it is set to true
  • Do nothing when it is set to false (draft feature)

I managed to add a callback validator to canonical fields which check my rule and it works fine:

Validator definition
    $emptyValidatorOnPublish = array(
        'name' => 'Callback',
        'options' => array(
            'messages' => array(
                \Zend\Validator\Callback::INVALID_VALUE => 'This field cannot be empty',
            ),
            'callback' => function($value, $context=array()) {
                if($context['published']){//Here I know the POST value
                    return !empty($value); //it must not be empty
                }
                return true;
            },
        ),
    );

Usage:

 array(
    'name' => 'title',
    'description' => 'Title of the interview',
    'required' => true,
    'filters' => array(
        0 => array(
            'name' => 'Zend\\Filter\\StringTrim',
            'options' => array()
        ),
        1 => array(
            'name' => 'Zend\\Filter\\StripTags',
            'options' => array()
        )
    ),
    'validators' => array($emptyValidatorOnPublish),
    'error_message' => 'title is required',
    'allow_empty' => true,
    'continue_if_empty' => true
)

Now I have on some fields which are validated with a ObjectExists validator:

array(
    'name' => 'person',
    'description' => 'Person link',
    'required' => true,
    'filters' => array(),
    'validators' => array(
        0 => array(
            'name' => 'MyApp\\Validators\\ObjectExists',
            'options' => array(
                'fields' => 'ref',
                'message' => 'This person does not exist',
                'object_manager' => 'doctrine.entitymanager.orm_default',
                'object_repository' => 'MyApp\\Person'
            )
        )
    ),
    'allow_empty' => true,
    'continue_if_empty' => true
),

The ObjectExists validator extends DoctrineModule\Validator\ObjectExists.

class ObjectExists extends BaseObjectExists
{

    /**
     * {@inheritDoc}
     */
    public function isValid($value)
    {
        if (! is_array($value)) {
            return parent::isValid($value);
        }

        $isValid = null;

        foreach ($value as $oneValue) {
            $isValid = false === $isValid ? $isValid : parent::isValid($oneValue);
        }

        return $isValid;
    }
}

Problem:

  • The parent class validator doesn't know the value of published

  • Even if I change the implementation to check the published value I will get the value from the DB not from the actual POST request

Is there a way to check the existence of an entity depending on the published value from the request?

Is there a way to pass a sibling value when validating another field (in my case pass 'published when validating a person)?

ka_lin
  • 9,329
  • 6
  • 35
  • 56
  • I am not sure if I understand you correctly, but is the problem that you don't have `$context` inside the `isValid` method of your `ObjectExists` validator? – Wilt Jan 26 '17 at 10:56
  • Hello Wilt, when a user sets the entity to published I need to validate fields (not by the data retrieved in the db but from the request). Found a solution though, don't know how ok it is :D – ka_lin Jan 26 '17 at 11:07

2 Answers2

3

Your input-filter has something called a validation group. You could remove the input that you don't want to validate from that validator group when the value for your published field is false and then those input elements don't get validated when the input-filter is running.

You don't have to solve this in the validator class itself. It would be better to move this logic to a higher level (so example by setting the validation group inside the init method of your input-filter class). I don't think the validator class itself should be handling this responsibility.

//pass the input names you want to validate here when published is false
if($published === false){
    $inputFilter->setValidationGroup();
}

In such a solution you only have to inject this published field inside your input-filter class and not inside all the individual validators.

Wilt
  • 41,477
  • 12
  • 152
  • 203
  • Thanks for the suggestion! Unfortunately I am using config based validations and apigility asks what validations I want for GET,POST,PATCH and DELETE (or global) – ka_lin Jan 26 '17 at 11:53
  • You could still implement this also when using Apigility. – Wilt Jan 26 '17 at 12:28
0

I have found a solution:

  1. Create a factory for the validator in which you inject the request parameters:
class ObjectExistsIfPublishedFactory implements FactoryInterface,MutableCreationOptionsInterface 
{
    /**
     * @var array
     */
    protected $options = array();

  public function setCreationOptions(array $options)
  {
      $this->options = $options;
  }

  public function createService(ServiceLocatorInterface $serviceLocator)
  {
      $services = $serviceLocator->getServiceLocator();
      $application = $services->get('Application');
      $mvcEvent  = $application->getMvcEvent();
      $dataContainer = $mvcEvent->getParam('ZFContentNegotiationParameterData', false);
      $data = $dataContainer->getBodyParams(); //Get request parameters
      $this->options['isPublished'] = $data['published']?true:false; //Add to the options of the validator
      $objectManager = $serviceLocator->getServiceLocator()->get($this->options['object_manager']);

      $this->options['object_repository'] = $objectManager->getRepository($this->options['object_repository']);
      return new ObjectExistsIfPublished($this->options);
  }
}
  1. Use the injected request variable when validationg:
use DoctrineModule\Validator\ObjectExists as BaseObjectExists;
class ObjectExistsIfPublished extends BaseObjectExists
{

    /**
     * {@inheritDoc}
     */
    public function isValid($value)
    {
        $isValid = true;
        if($this->getOption('isPublished')){
            if (! is_array($value)) {
                return parent::isValid($value);
            }
            $isValid = null;
            foreach ($value as $oneValue) {
                $isValid = false === $isValid ? $isValid : parent::isValid($oneValue);
            }
        }
        return $isValid;
    }
}
ka_lin
  • 9,329
  • 6
  • 35
  • 56