0

Can implement dynamic validation on element level? I used this example to implement validation of one element dependent on the value of the other element. But for this purpose I'll need to implement this for every single form where I use this element (comment) with this validation. I have many forms like that. Is there way to do the following:

to take this filter/validation logic to the element level using some kind of "data-comment-for" attribute and retrieving the value of the element on which it depends from the parent form.

This is my current code (but I need to have it for every form now. It does not look elegant at all) :

class CompetencyAdvanceHumanRightsAndJusticeFormFilter extends InputFilter
{
    public function isValid($context = null)
    {
        $figradeCommentName = 'applJusticeFIGrade'.'Comment';

        $forGrade = $this->get('applJusticeFIGrade');
        $gradeComment = $this->get($figradeCommentName);

        $applJusticeFIGradeRawValue = $forGrade->getRawValue('applJusticeFIGrade');

        if(is_numeric($applJusticeFIGradeRawValue)){
            $gradeValue = intval($applJusticeFIGradeRawValue);
        }else{
            $gradeValue = $applJusticeFIGradeRawValue;
        }

        if ($gradeValue != 'na' && $gradeValue > 0) {
            $gradeComment->setRequired(true);
            $validatorChain = new Validator\ValidatorChain();
            $validatorChain->attach(
                new Validator\NotEmpty(),
                true
            );
            $gradeComment->setValidatorChain($validatorChain);
        }

        return parent::isValid($context);
    }

    public function __construct(){
        $this->add(array(
            'name' => 'id',
            'required' => true,
            'filters' => array(
                array('name' => 'Int'),
            ),
        ));

        $this->add(array(
            'name' => 'studEvalId',
            'required' => true,
            'filters' => array(
                array('name' => 'Int'),
            ),
        ));
    }
}

EDIT:

I added code for the custom element to the question. There are some "leftovers" of my attempts to place this logic to the element level.

Comment Element

class Comment extends Element implements InputProviderInterface
{

    /**
     * @var ValidatorInterface
     */
    protected $validator;

    // set its type
    protected $attributes = array(
        'type' => 'comment'
    );


    public function init()
    {

        if (null === $this->validator) {

            $validator = new StringLength();
            $validator->setMax(10);

            $validator->setMessage('The comment should not exceed 1000 letters!', StringLength::INVALID);

            $this->validator = $validator;
        }


    }


    /**
     * Get a validator if none has been set.
     *
     * @return ValidatorInterface
     */
    public function getValidator()
    {
        return $this->validator;
    }


    /**
     * @param ValidatorInterface $validator
     * @return $this
     */
    public function setValidator(ValidatorInterface $validator)
    {
        $this->validator = $validator;
        return $this;
    }


    /**
     * remove require and validator defaults because we have none
     *
     * @return array
     */
    public function getInputSpecification()
    {
//        return array(
//            'name' => $this->getName(),
//            'required' => false,
//            'validators' => array(
//                $this->getValidator(),
//            ),
//            'filters' => array(
//              new FIGradeCommentDynamicBufferFilter()
//            ),
//        );


        return array(
            'name' => $this->getName(),
            'required' => false,
            'filters' => array(
                array('name' => 'Zend\Filter\StringTrim'),
            ),
            'validators' => array(
                $this->getValidator(),
            ),

        );
    }

    // tell it where to find its view helper, so formRow and the like work correctly
    public function getViewHelperConfig()
    {
        return array('type' => '\OnlineFieldEvaluation\View\Helper\FormComment');
    }

}
vlr
  • 780
  • 4
  • 16
  • 33

1 Answers1

1

You could make a base abstract input-filter class and an interface and make all your form filters extend the base class that implements the interface with the methods you expect inside your form classes to make the thing work correctly.

Make an interface with the methods:

interface GradeCommentFormFilterInterface()
{
    protected function getGradeInput();

    protected function getCommentInput();
}

Then you move the common code to your base class:

abstract class BaseGradeCommentFormFilter extends InputFilter implements GradeCommentFormFilterInterface
{   
    protected function getGradeInput()
    {
        return $this->get(static::GRADE_NAME);
    }

    protected function getCommentInput()
    {
        return $this->get(static::GRADE_NAME . 'Comment');
    }

    public function isValid($context = null)
    {
        $gradeInput = $this->getGradeInput();
        $commentInput = $this->getCommentInput();
        $rawValue = $this->getRawValue($gradeInput);

        if(is_numeric($rawValue))
        {
            $gradeValue = intval($rawValue);
        }
        else
            $gradeValue = $rawValue;

        if ($gradeValue != 'na' && $gradeValue > 0) {
            $commentInput->setRequired(true);
            $validatorChain = new Validator\ValidatorChain();
            $validatorChain->attach(
                new Validator\NotEmpty(),
                true
            );
            $commentInput->setValidatorChain($validatorChain);
        }
        return parent::isValid($context);
    }
}

Now you can use your abstract class like this:

class CompetencyAdvanceHumanRightsAndJusticeFormFilter extends BaseGradeCommentFormFilter
{   
    const GRADE_NAME = 'applJusticeFIGrade';

    //... other code
}

I quickly tried to make it work for your case, but this isn't tested, and probably there are ways to optimize this, but it gives you an idea of what you can do.

Wilt
  • 41,477
  • 12
  • 152
  • 203
  • Nice! So, but you do not think there is a way to place it inside Comment Element specification? – vlr Jun 02 '16 at 17:34
  • I added code for the custom element to the question. There are some "leftovers" of my attempts to place this logic to the element level. – vlr Jun 02 '16 at 17:40