ka_lin
ka_lin

Reputation: 9442

Zend 2 conditional validator

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

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:

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)?

Upvotes: 2

Views: 526

Answers (2)

Wilt
Wilt

Reputation: 44422

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.

Upvotes: 3

ka_lin
ka_lin

Reputation: 9442

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;
    }
}

Upvotes: 0

Related Questions