I'm fairly new to object oriented programming here and am trying to get my head around the best way to go about creating Users and Validating them in the system.
My first attempt is below and my main questions apart from does it all seem correct is in regards passing a validator class to the save function and pros/cons to this.
A lot of the functionality has been stripped back to enable you to read it a bit easier, including removing any getters and setters but please assume they are there:
User Model
class User implements Validateable {
protected $name;
protected $email;
protected $validator;
// getters and setters for name, email, validator here
public function save( Validator $validator ) {
$this->validator = $validator;
if( $this->validator->validate( $this ) )
return true;
return false;
}
public function getRules() {
return [
'name' => new NameValidator( $this->name ),
'email' => new EmailValidator( $this->email )
];
}
}
Abstract Validator
abstract class ValidatorAbstract {
protected $errors;
public function getErrors() {
return $this->errors;
}
}
Name Validator
class NameValidator extends ValidatorAbstract {
protected $name;
protected $errors;
public function __construct( $name ) {
$this->name = $name;
}
public function validate() {
if( empty( $this->name ) )
$this->errors[] = 'Invalid Name';
if( $this->errors )
return false;
return true;
}
}
Email Validator
class EmailValidator extends ValidatorAbstract {
protected $email;
protected $errors;
public function __construct( $email ) {
$this->email = $email;
}
public function validate() {
if( !filter_var( $this->email, FILTER_VALIDATE_EMAIL ) ) :
$this->errors[] = 'Invalid Email';
endif;
if( $this->errors )
return false;
return true;
}
}
User Validator
class UserValidater implements Validator {
protected $data;
protected $rules;
protected $errors;
public function validate( Validateable $obj ) {
$this->rules = $obj->getRules();
foreach( $this->rules as $rule ) :
if( !$rule->validate() )
$this->errors[] = $rule->getErrors();
endforeach;
if( $this->errors )
return false;
return true;
}
public function getErrors() {
return $this->errors;
}
}
Validator Interface
interface Validator {
public function validate( Validateable $data );
public function getErrors();
}
Validateable Interface
interface Validateable {
public function getRules();
}
And finally some example code using all of the above:
$user = new User;
$user->setName( 'Peter' );
$user->setEmail( '[email protected]' );
if( $user->save( new UserValidater ) ) :
// do some stuff
else :
$errors = $user->getValidator()->getErrors();
// do some stuff with the errors
endif;
-
\$\begingroup\$ Welcome to Code Review! Does the code work as is presented, with the removed functionality? \$\endgroup\$Phrancis– Phrancis2015年04月01日 15:56:12 +00:00Commented Apr 1, 2015 at 15:56
-
\$\begingroup\$ Hi @sᴉɔuɐɹɥԀ It won't without the getters and setters, I will remember that in future! \$\endgroup\$Peter Featherstone– Peter Featherstone2015年04月01日 16:34:45 +00:00Commented Apr 1, 2015 at 16:34
-
\$\begingroup\$ It's better to post the whole code, some reviewers like to actually run the code so they can find ways to improve it without breaking it. See the Help Center for more information. \$\endgroup\$Phrancis– Phrancis2015年04月01日 17:04:46 +00:00Commented Apr 1, 2015 at 17:04
1 Answer 1
It's better to separate the validation from the save operation and to use a constraint object to setup the rules which be applied to the entity. So you get a better separation of concern, which is one of the keys to good OOP architecture. I propose something like this:
$user = new User;
$user->setName( 'Peter' );
$user->setEmail( '[email protected]' );
$validator = new Validator(new UserConstrainte));
if( $validator->validate($user)){
$databaseStuff->save($user)
} else {
$error = $validator->getErrors()
}
I advise you to check the validator component from symfony2: https://github.com/symfony/Validator which is really well designed.
-
\$\begingroup\$ Hey liobase, feels like you are following me around here! My own personal guru, thanks for all your help so far... I like the idea of keeping the constraints in a seperate User Contstraints class and having one master Validator class. \$\endgroup\$Peter Featherstone– Peter Featherstone2015年04月02日 09:29:02 +00:00Commented Apr 2, 2015 at 9:29
-
\$\begingroup\$ You are welcome, your questions are interesting and focused on software architecture, which is what I do :). And I can say you are in the right way! \$\endgroup\$lilobase– lilobase2015年04月02日 10:14:39 +00:00Commented Apr 2, 2015 at 10:14
-
\$\begingroup\$ Thanks lilo, I have started to become incredibly interested in software architecture recently after getting asked to build some quite substantial projects so I really need to get this stuff right... Can you give me any tips on where to go for advice/books/websites in this regard or how you learnt? \$\endgroup\$Peter Featherstone– Peter Featherstone2015年04月02日 12:49:16 +00:00Commented Apr 2, 2015 at 12:49
-
\$\begingroup\$ I give you my twitter account, follow me so I can send you some info through pm: @lilobase \$\endgroup\$lilobase– lilobase2015年04月02日 13:05:12 +00:00Commented Apr 2, 2015 at 13:05
-
\$\begingroup\$ Thanks, I reallly appreciate all your help and taking the time... I just followed you, mine is @plumapiedra \$\endgroup\$Peter Featherstone– Peter Featherstone2015年04月02日 13:13:10 +00:00Commented Apr 2, 2015 at 13:13