I'm currently struggling with some form validation. I'm working with the class below, which is intended to be a fluent interface.
class Validator implements ValidatorInterface {
protected $_count_validators = 0;
protected $_validators;
protected $errorMsg;
public function __construct($errorMsg = '')
{
$this->errorMsg = $errorMsg;
}
public function addValidator(ValidatorInterface $validator)
{
$this->_count_validators++;
$this->_validators[] = $validator;
return $this;
}
public function validate($value)
{
foreach($this->_validators as $validator) {
if ($validator->validate($value) === false) {
return false;
}
}
return true;
}
public function getError()
{
return $this->errorMsg;
}
}
It actually works 75 % - and I can add validators like this:
$postalcodeValidator = new \Framework\Formular\Validator\Validator();
$validatePostalcode= $postalcodeValidator->addValidator(new \Framework\Formular\Validator\NotEmpty)
->addValidator(new \Framework\Formular\IsNumeric);
$cityValidator = new \Framework\Formular\Validator\Validator();
$validateCity = $lastnameValidator->addValidator(new \Framework\Formular\Validator\NotEmpty);
Now I can just write:
$result = $postalcodeValidator->validate('00000');
- or -
$result = $cityValidator->validate('London');
And I will have a boolean.
My problem is, that I need to make it easy to set some errors. In the above example - if I just added a getErrors()-function in the class - I had to get the errors for every new instantiation of the class. I want to make a function for getting all errors.
Can you help me on a solution for that?
Thanks in advance,
denlau
A simple way is to implement a static member in an abstract validator class. All your concrete Validator extends this, and will add automaticly errors to this member. Finally you can get this static member with one call. But this is an anti-pattern and you have to reset this member after getting errors.
A better practice is to use the composite pattern. A class where you can add one or more elements with assigned validators. This composite class will execute all validators on your assigned elements and collect all error messages from each validator. Then you can retrieve all collected error messages from your composite, with one call.
For more information about composite pattern see here on wiki
Here an example..
$elementA = new ElementA; // implements Validable
$elementA->addValidator(new ValidatorA)->addValidator(new ValidatorB);
$elementB = new ElementB; // implements Validable
$elementB->addValidator(new ValidatorC);
$elementA->setValue('any_posted_value_to_validate');
$elementB->setValue('another_any_posted_value_to_validate');
$composite = new Composite; // implements Validable
$composite->addElement($elementA)->addElement($elementB);
if (!composite->isValid()) { // will execute all validators on all elements
$errorMessages = $composite->getErrors();
}
Within your composite..
public function isValid()
{
$isValid = true;
foreach ($this->elements as $element) {
if (!$element->isValid()) { // will execute all assigned validators to this element
$this->addErrors($element->getErrors());
$isValid = false;
}
}
return $isValid;
}
The Validable Interface
interface Validable
{
public function isValid();
public function getErrors();
}
Related
I am trying to write a unit test for a function that immediately loads an object from a different class that uses the input to the function as a parameter. I am new to php unit testing and couldn't find anything that address my particular problem. A few leads that I had that led to no avail was using an injector, and trying to us a reflection.
The code I am trying to write a unit test for is:
public static function isUseful($item) {
$objPromo = MyPromoCodes::Load($item->SavedSku);
if (!is_null($objPromo)
&& ($objPromo->PromoType == MyPromoCodes::Interesting_Promo_Type)) {
return true;
}
return false;
}
My attempt at mocking this out:
public function testIsUseful() {
$injector = $this->getMockBuilder('MyPromoCodes')
->setMethods(array('Load'))
->getMock();
$objPromo = $this->getMock('MyPromoCodes');
$objPromo->PromoType = 'very interesting promo type';
$injector->set($objPromo, 'MyPromoCodes');
$lineItem1 = $this->getDBMock('LineItem');
$this->assertTrue(MyClass::isUseful($lineItem1));
}
however this doesn't work because there is no set method for this object....
Not sure what else to try, any help would be appreciated.
I made the library that makes static classes mocking possible:
class MyClass {
public static $myPromoCodes = 'myPromoCodes';
public static function isUseful($item) {
$objPromo = self::$MyPromoCodes::Load($item->SavedSku);
if (!is_null($objPromo)
&& ($objPromo->PromoType == MyPromoCodes::Interesting_Promo_Type)) {
return true;
}
return false;
}
}
class MyClassTest extends \PHPUnit_Framework_TestCase
{
public function testSomething()
{
$myClass = Moka::stubClass('MyClass');
$myClass::$myPromoCodes = Moka::stubClass(null, ['::Load' => (object)[
'PromoType' => MyPromoCodes::Interesting_Promo_Type
]]);
$this->assertTrue($myClass::isUseful((object)['SavedSku' => 'SKU']);
$this->assertEquals([['SKU']], $myClass::$myPromoCodes->moka->report('::Load'));
}
}
To start with you cannot mock static method with PHPUnit. At least not with 4.x and 5.x.
I would suggest a DI approach like this:
class MyClass
{
private $promoCodesRepository;
public function __construct(MyPromoCodesRepository $promoCodesRepository)
{
$this->promoCodesRepository = $promoCodesRepository;
}
public function isUseful(MyItem $item)
{
$objPromo = $this->promoCodesRepository->Load($item->SavedSku);
// ...
}
}
Here you can easily mock the Load method.
Unfortunately the "static" approach creates a lot of issues during tests so it is better to avoid it whenever possible.
I have the following code:
<?php
class X
{
public function do($url)
{
$httpRequest = new \HttpRequest\Curl($url, $this->getOptions());
$httpRequest->fire();
// etc.
}
// ...
}
In order to be able to unit test this class, I'd like to inject a mocked HttpRequest class. One way to do this would be as follows:
<?php
class X
{
private $httpRequestClass;
public function __construct($httpRequestClass = '\HttpRequest\Curl')
{
$this->httpRequestClass = $httpRequestClass;
}
public function do($url)
{
$httpRequest = new $this->httpRequestClass($url, $this->getOptions());
$httpRequest->fire();
// etc.
}
// ...
}
But this doesn't seem right. Any other ideas?
public function __construct($url, $httpRequestClass = null)
{
$this->url = $url;
if ($httpRequestClass == null) //> Default
$this->httpRequestClass = new HttpRequest\Curl($this->url);
else
$this->httpRequestClass = $httpRequestClass;
}
so when you are using this class normally just call it with one param
yourClass('your url');
Otherwise pass the istance in the second argument
yourClass('url', new MockedObj);
Of course you should always Inject your dependencies without providing a default object
The class needs to generate objects of type HttpRequest, but we don't necessarily want it to initialize an object: we may want it to use the prototype pattern, for example. Therefore, the class calls for the factory pattern. I chose a factory callback, as opposed to a factory class, for brevity.
<?php
class X
{
private $factoryCallback;
public function __construct($factoryCallback = null)
{
$this->factoryCallback = $factoryCallback;
}
public function do($url)
{
$httpRequest = $this->createHttpRequest($url);
$httpRequest->fire();
// etc.
}
private function createHttpRequest($url)
{
$callback = $this->factoryCallback;
if (is_callable($callback)) {
return $callback($url, $this->getOptions());
}
return new \HttpRequest\Curl($url, $this->getOptions());
}
// ...
}
The helper method, createHttpRequest(), is a bit redundant in this example, but would be used for error handling in production code.
Using PHP 5.3, I am currently writing an MVC application and need to extract the data that was passed in to a model for validation, to send back to the view for repopuating a form on error.
The fields in the model are marked private and can only be accessed if they appear in a list of fieldNames (so controllers can't attempt to change the contents of other 'business' fields in the class.
In the controller area marked 'BUG' below, attempting to access the private field just results in the array item being created but set to null, not the value. While in my debugger, if I inspect the field of the source ($templateM->$field) it shows the correct value.
What's going on?
In the model base class:
class model {
protected $fieldNames;
public function __get($name)
{
if ($name === 'fieldNames')
return $this->fieldNames; // Special exception for getting the list of fields
if ($name === 'errorList')
return $this->errorList; // special exception for getting the current errors
if (in_array($name, (array)$this->fieldNames))
return $this->$name;
else
throw new Exception('Invalid access to private model field');
}
}
In the model:
class template extends model
{
function __construct()
{
parent::__construct();
$this->fieldNames = new immutableArray(array('id', 'etc'));
}
private $id = 0;
private $etc = 1;
}
In the controller:
class templateManager extends controller
{
function create()
{
// Validate form data
$templateM = getModel('template');
$templates = array();
$bodyData = array();
$bodyData['showSuccess'] = false;
$result = $templateM->validate();
if ($result)
{
if ($templateM->save())
{
$bodyData['showSuccess'] = true;
}
}
// Load present data (post insert)
$templates = $templateM->getAllAsArray();
$bodyData['errorMessages'] = (array)$templateM->errorList;
$formData = array();
if (count($bodyData['errorMessages']) > 0)
{
foreach($templateM->fieldNames as $field)
{
$formData[$field] = $templateM->$field; // <- BUG
}
}
$bodyData['formData'] = $formData;
$bodyData['templateData'] = $templates;
$this->_drawPage($bodyData);
}
Actually i would recommend for you to stop abusing __get(). You already have too many if's in it, and that list will get only longer and longer. Better to make proper getter and setter methods.
AS for the cause of your problems: the Model::__get() cannot access the private variables. It will work if you define them as protected.
Additionally, you might find this rant useful. At least the "side notes" part of it.
im am making a php validation class
with sub classes that extend it, eg, mobile, suburb, credit_card, ect
so, the idea is you can call
$validation = new Validation('mobile');
$valid = $validation->validate($number);
$validation->type('suburb');
$valid2 = $validation->validate($suburb);
now my idea for doing this is having
class Validation() {
private $v_type = null;
function __construct($type) {
$this->type($type);
}
public function type($type) {
$this->v_type = new $type();
}
public function validate($info) {
return $this->v_type->validate($info);
}
}
as a very basic example
but is there a better way of doing this?
You could do it this way, but it could be improved. Having the actual validators capsule their own validation logic is good. Extending them from a base class isn't. Let's implement an interface instead. This way, any class can be a Validator.
interface IValidate
{
public function validate($value);
}
Your validators would look like this then:
class IsNumeric implements IValidate
{
public function validate($value)
{
return is_numeric($value);
}
}
and
class GreaterThan implements IValidate
{
protected $_value;
public function __construct($value)
{
$this->_value = $value;
}
public function validate($value)
{
return $value > $this->_value;
}
}
You'd still have a main Validator class. Unlike in your example, the Validator below accepts multiple Validators, which will allow you to create a Filter Chain.
class Validator implements IValidate
{
protected $_validators;
public function addValidator(IValidate $validator)
{
$this->_validators[] = $validator;
return $this;
}
public function validate($value)
{
foreach($this->_validators as $validator) {
if ($validator->validate($value) === FALSE) {
return FALSE;
}
}
return TRUE;
}
}
And this could be used like:
$validator = new Validator;
$validator->addValidator(new IsNumeric)
->addValidator(new GreaterThan(5));
var_dump( $validator->validate('ten') ); // FALSE
var_dump( $validator->validate('10') ); // TRUE
var_dump( $validator->validate('1') ); // FALSE
The above is pretty much a Command pattern. And due to the Validator implementing IValidate as well, it is also a Composite. You could take the Validator chain from above and stack it into another Validator Chain, e.g.
$numericGreaterThanFive = new Validator;
$numericGreaterThanFive->addValidator(new IsNumeric)
->addValidator(new GreaterThan(5));
$otherValidator = new Validator;
$otherValidator->addValidator(new Foo)
->addValidator(new Bar)
->addValidator($numericGreatherThanFive);
For convenience, you could add a static factory method for creating Validators with the actual Validation Command objects (as shown elsewhere).
On a sidenote: the Zend Framework already has an extensive number of Validators you can build on. Since ZF is a component library, you can use them without having to migrate your entire application to ZF.
Usually you do these kind of things using the Factory pattern, something like this:
class ValidatorFactory {
public static function get($type) {
$validator = "Validator_$type";
return new $validator();
}
}
$valid = ValidatorFactory::get('mobile')->validate($number);
Would of course need some error checking and such, but you should get the idea
...
public function type($type) {
return new self($type);
}
...
Note: This one every time returns a new instance of your Validator class so it would be a better idea to use the Factory pattern as Dennis suggested or not to tie the new Validator to the type() method.
I had:
in a class implementing Validator:
an $errormessages property
an isCorrect() method
In the isCorrect method, I had:
switch ($type):
case 'email':
isEmailCorrect();
case 'password':
isPasswordCorrect();
case 'x':
isXCorrect();
isEmailCorrect(), isPasswordCorrect() and isXCorrect() had access to the same property with all error messages
Now, I have:
in Validator:
an $errormessages property
in an EmailValidator class extending Validator:
an isCorrect() method
in a PasswordValidator class extending Validator:
an isCorrect() method
in a XValidator class extending Validator:
an isCorrect() method
Now, in a file calling the isCorrect() methods, I have:
$EmailValidator = new EmailValidator();
$PasswordValidator = new PasswordValidator();
$XValidator = new XValidator();
$EmailValidator->isCorrect(), $PasswordValidator->isCorrect() and $XValidator->isCorrect() don't have access to the same property with all error messages
$errormessages are in different instances of different classes. They should be one, but are three.
What now?
I think you should develop another class: a ValidatorChain, which takes an arbitrary amount of validators, and that aggregates the errormessages of all validators that it has tested
For reference see the docs on Zend Framework's Validator Chain
EDIT
Now that I re-evaluate your question (thanks to Bryan M's comment); why do you want each individual Validator to have access to other Validators' error messages? I would say that collecting each individual Validators' error messages is the responsibility of an object higher in the hierarchy.
If, however, you want individual Validators to be able to act based on context, in other words, based on what the results of other Validators are, then I suppose you could add a $context parameter to the isCorrect method. This could for instance accept an arbitrary amount of Validators or something similar.
Something like:
interface ValidatorInterface
{
public function isCorrect( array $context );
public function getMessages();
}
abstract class ValidatorContextOptions
{
const SHOULD_BE_PRESENT = 'shouldBePresent';
const SHOULD_NOT_BE_PRESENT = 'shouldNotBePresent';
const SHOULD_BE_VALID = 'shouldBeValid';
}
class EmailValidator implements ValidatorInterface
{
protected $_field;
protected $_contextOptions = array();
protected $_messages = array();
public function __construct( $field, array $contextOptions )
{
$this->_field = $field;
$this->_contextOptions = $contextOptions;
}
public function isCorrect( array $context = null )
{
foreach( $this->_contextOptions as $field => $options )
{
foreach( $options as $option )
{
switch( $option )
{
case ValidatorContextOptions::SHOULD_NOT_BE_PRESENT:
if( isset( $context[ $field ] )
&& $context[ $field ] instanceof ValidatorInterface )
{
$this->_messages[] = $field . ' should not be present';
return false;
}
break;
case ValidatorContextOptions::SHOULD_BE_PRESENT:
if( !isset( $context[ $field ] )
|| !$context[ $field ] instanceof ValidatorInterface )
{
$this->_messages[] = $field . ' should be present';
return false;
}
break;
case ValidatorContextOptions::SHOULD_BE_VALID:
if( !isset( $context[ $field ] )
|| !$context[ $field ] instanceof ValidatorInterface
|| !$context[ $field ]->isCorrect() )
{
$this->_messages[] = $field . ' should be valid';
return false;
}
break;
}
}
}
// some dummy function which you should replace with real validation
return isAnEmailAddress( $this->_field );
}
public function getMessages()
{
return $this->_messages;
}
}
Usage:
$emailValidatorContextOptions = array
(
'phone' => array(
ValidatorContextOptions::SHOULD_BE_PRESENT,
ValidatorContextOptions::SHOULD_BE_VALID
)
);
$phoneValidator = new PhoneValidator( $phoneString );
$emailValidator = new EmailValidator( $emailString, $emailValidatorContextOptions );
if( !$emailValidator->isCorrect( array( 'phone' => $phoneValidator ) ) )
{
print_r( $emailValidator->getMessages() );
}
What I've shown here, needs a lot more thinking (and I really mean A LOT), is buggy as hell and definately not bulletproof. But I hope you catch my drift of where I'm going with this.
Moreover, where do you insert the values in your validator that need to be validated anyway?
Well you could make an external properties factory to control access to your property, assuming you are talking about properties files that is the approach I usually take.
If you are referring to a shared field then you can place it in your base class and access it that way.
I'll often use Zend_Validate classes to perform the validation, and aggregate any error message to a property on the object that's being validated (as well as a flag that control valid status).
My setup would be similar to this:
class User {
public $email;
protected $_errorMessages = array();
public function validate()
{
$valid = true;
$emailValidator = new EmailValidator();
if (!$emailValidator->isCorrect($this->email)) {
$valid = false;
// validation message are added to the $errormessages property in
// the validator class upon failure of isCorrect()
$this->_errorMessages[] = $emailValidator->getMessages();
}
// repeat this for all your validators
return $valid
}
public function getErrorMessages()
{
return $this->_errorMessages();
}
}
// in your page....
if (!$user->validate()) {
$messages = $user->getErrorMessages();
}
If I read you right, you want multiple instances to share the same error messages property, such that you can instantiate several validators and have them all contribute to a single array.
If this is the case, there are a few ways to do it. One would be to create a validator manager class which has responsibility for instantiating and registering validators. Then once validation is complete you could call $validator_manager->getErrors() which would aggregate the errors present in all the validators registered with it.
Another way you could do it would be to use a singleton error store class, which you acquire in the constructor of each validator. Each validator's addError() method would then delegate the job to the singleton.
There are other methods still, but basically you're going to have to use another object, either for managing the validators or storing the errors.
Someone below mentioned using a singleton for this.
I am not convinced that it's a great use of that design pattern, especially since it's commonly held that singletons are the "anti-pattern" and often over/mis-used.
Nonetheless, keeping that in mind, here's an example along those lines:
<?php
//Error Class implemented as a Singleton
class ErrorClass
{
static private $instance = false;
static private $errorMessages;
function getInstance() {
if (!self::$instance) {
self::$instance = new ErrorClass();
self::$errorMessages = "No errors;";
}
return self::$instance;
}
public function setError($errorMessage){
self::$instance->errorMessages .= $errorMessage;
}
public function getError(){
return self::$instance->errorMessages;
}
}
abstract class AbstractClass
{
// Force Extending class to define this method
abstract protected function isCorrect($b);
// Common Method for setting error
public function setError($errorMessage) {
ErrorClass::getInstance()->setError($errorMessage);
}
// Common Method for getting error
public function getError() {
return ErrorClass::getInstance()->getError();
}
}
class EmailValidator extends AbstractClass
{
public function isCorrect($b) {
if(!$b) {
$this->setError('EmailValidator->isCorrect();');
}
}
}
class PasswordValidator extends AbstractClass
{
public function isCorrect($b) {
if(!$b) {
$this->setError('PasswordValidator->isCorrect();');
}
}
}
// Then in your code
$errorState = 1; // used for testing purposes
$EmailValidator = new EmailValidator();
$EmailValidator->isCorrect($errorState);
$PasswordValidator = new PasswordValidator();
$PasswordValidator->isCorrect($errorState);
echo $EmailValidator->getError();
echo $PasswordValidator->getError();