Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.
OK, so I'd like some help with a small addition to the Zend Framework that will do Filter / Validation correctly. I mean, I don't plan on inclusion in the framework or anything, but their validation / filtering sucks, so I'm fixing it for my own purposes. If you like it, feel free to use it. I'm trying to come up with some common use cases. Here is what I have so far. Any suggestions?
<?php
$request = $this->getRequest();
$data = new MC2_Input($request->getPost());
$filter = new MC2_Input_Filter_Set();
$filter->addFilter(new Zend_Filter_StringTrim()); // applies to all
$filter->addFilter(new Zend_Filter_Digits(), array('age', 'bees_eaten', 'some_element'));
$data->addFilter(new Zend_Filter_StringTrim());
$data->addFilter($filter);
$ruleset1 = new MC2_Input_Validate_Set();
$data->addValidator(new Zend_Validate_StringLength(1, 3), 'username', 'This field must be 1 to 3 characters in length');
$data->addValidator(new Zend_Validate_Alnum(), array('username', 'amount_of_monkeys'), 'This field must be alphanumeric');
$ruleset2 = new MC2_Input_Validate_Set();
$data->addValidator(new MC2_Input_Validator_conditionalTermsConditions());
$data->addValidator($ruleset1);
$data->addValidator($ruleset2);
$data->addValidator(new Zend_Validate_StringLength(6, 35), 'password', 'Password must be between 6 and 35 characters');
$data->filter();
if ($data->isValid())
{
echo "Good job!";
}
else
{
$errors = $data->getErrors();
$username_errors = $data->getErrors('username');
$age_and_chinese_people_errors = $data->getErrors(array('age', 'amount_of_monkeys'));
}
Last edited by Luke on Wed Sep 12, 2007 11:40 am, edited 3 times in total.
The basic design is to have polymorphic Filters and Rules -- and then implement Validator and Filter chains to run those Filters and Rules on a container (which is really the Request). The goal of the standard interfaces is to make the the system easily infinitely extensible.
Traditionally the Rules have ab isValid() method as does the Validator (so it in turn can be used as a Rule). Filters have a doFilter() (or run() or execute()) methods.
The goals of the Rules and Filters are different. Filters modify the container passed through them. Rules provide both a pass/fail status (isValid()) and an error message that usually is displayed for the user.
I don't think FilterChain/Filters and Validator/Rules should be mixed. This is one weird god class thing about Zend_Filter_Input. They are separate systems and they are not usually needed in the same place in the code.
wei, forgive my ignorance. It is late and I am not at my best. I think I am seeing some of the benefits in what you are suggesting, but I could use a bit more elaboration. Please use as realistic of an example as you can think of (if you dont mind). Thank you very much.
arborint,
The basic design is to have polymorphic Filters and Rules -- and then implement Validator and Filter chains to run those Filters and Rules on a container (which is really the Request). The goal of the standard interfaces is to make the the system easily infinitely extensible.
I was planning on relying on the Zend_Validate_Abstract class to provide my interface for validators since this code is meant to be used with the Zend Framework and its built-in validators. The only part I planned on really replacing was Zend_Filter_Input
The goals of the Rules and Filters are different. Filters modify the container passed through them. Rules provide both a pass/fail status (isValid()) and an error message that usually is displayed for the user.
I don't think FilterChain/Filters and Validator/Rules should be mixed. This is one weird god class thing about Zend_Filter_Input. They are separate systems and they are not usually needed in the same place in the code.
$request = $this->getRequest();
$filter = new MC2_Input_Filter($request->getPost());
$filter->addFilter('__ALL__', new Zend_Filter_StringTrim());
$filter->addFilter(array('age', 'bees_eaten', 'some_element'), new Zend_Filter_Digits());
$filtered_post = $filter->filter();
$validate = new MC2_Input_Validate($filtered_post);
$validate->addValidator('username', new Zend_Validate_StringLength(1, 3), 'This field must be 1 to 3 characters in length');
$validate->addValidator(array('username', 'amount_of_monkeys'), new Zend_Validate_Alnum(), 'This field must be alphanumeric');
if ($validate->isValid())
{
echo "Good job!";
}
else
{
$errors = $processor->getErrors();
$username_errors = $processor->getErrors('username');
$age_and_chinese_people_errors = $processor->getErrors(array('age', 'amount_of_monkeys'));
}
I prefer to have a generic input class to which you can apply the filters and rules using a common method, and having it decide how to apply filters/rules to the objects. I.e. filters are only run after the validation has occured unless otherwise specified. I've included a snipplet above which demonstrates how to handle the ordering of how you want to apply your filter and rules, however I've left a couple things out as I don't have my codebase infront of me and simply cannot remember how I handled errors and such. Hope this helps in some way..
$data = new Zodiac_Input($this->_request->getPost());
$data->add('name', new Zodiac_Input_Filter_Trim(), Zodiac_Input::PreValidation); //before validation
$data->add('age', new Zodiac_Input_Filter_Digits()); //after validation (default behavior)
$data->add('name', new Zodiac_Input_Rule_Length(1, 3), 'This field must be 1 to 3 characters in length');
$data->add('age', new Zodiac_Input_Rule_Alnum(), 'This field must be alphanumeric');
$data->add('password', new Zodiac_Input_Rule_Equals('password2'), 'Passwords do not match..');
public function add($name, $object, $option)
{
/**
* Organize the input/filter objects so we can run them in the correct order
*/
if ($object instanceof Zodiac_Input_Filter) {
if ($option === true) {
$this->_dispatch['pre']['filter'][$name] = $object;
}
else {
$this->_dispatch['post']['filter'][$name] = $object;
}
}
elseif ($object instanceof Zodiac_Input_Rule) {
$this->_dispatch['pre']['rule'][$name] = $object;
}
}
You should search for some of my posts on chain validation, as Maugrim or Arbortint really had some good advise for me.. especially creating rule/filter sets using YAML which would then automatically be parsed into this validation library.
Thanks jcart! I am not sure I like the common method for adding filters and validators though. I think I would rather keep them seperate. What is the benefit here?
Also, does anybody else see the benefit of the composite structure wei is suggesting? I am starting to like the idea (look at my new use case on the first post). Let me know what you think of the new use case, jcart! Thanks.
The Ninja Space Goat wrote:Thanks jcart! I am not sure I like the common method for adding filters and validators though. I think I would rather keep them seperate. What is the benefit here?
The benefit is you do not have to specify whether it is a validator or a filter, and why should you really? .. thus avoiding
$filter = new Input_Filter();
$filter->add(.. );
$validator = new Input_Validator();
$validator->add(.. );
$input = new Input();
$input->add($filter, $validator);
You still have to pass your filter and validator objects to your input object so the benefit here is having things handled internally. By nature a chain library validator/filter involves a lot of keystroke, so I personally like to keep the interface as small as possible.
Also, does anybody else see the benefit of the composite structure wei is suggesting?
Minus the function he used it looks good, but arn't we already using the composite pattern?
Let me know what you think of the new use case, jcart! Thanks.
Take a look at my first post and let me know what you think of the interface I have there. Too complicated. I am now considering one add method... but I'm not 100% convinced. I'm going to play around with it. Thanks for your input jcart... I really appreciate it.
I need some help coming up with a term. I need a word that would describe both a Filter and a Validator. What would a term be that collectively describes filter and validator? Here is the context: