I'm creating a simple ORM in Zend Framework, to roughly encapsulate a public library application, using the DbTable/Mapper/Model approach. I'm not sure if the way I'm doing my User-related classes is right, though, as I have some logic in Mapper_User
, and some in Model_User
.
<?php
class Mapper_Users {
/*
createModelObject would be called by a Controller handling a Form_Regsiter's
data, to create a new Model_User object. This object'd then be saved by the
same Controller by calling Mapper_Users->save();
*/
public function createModelObject(array $fields) {
if(!isset($fields['date_registered']))
$fields['date_registered'] = date('Y-m-d H:i:s');
if(!isset($fields['max_concurrent_rentals']))
$fields['max_concurrent_rentals'] = 3;
return new Model_User($fields);
}
}
?>
In the method which creates new Model_User
objects from scratch (as in, not pulling a record from the DB, but registering a new user), I instantiate a new Model_User
with the name/username/password provided from a Form, then set a few object properties such as the registration date, "max books allowed at one time" and such. This data, being stuffed inside the Model_User
by the Mapper_User
, then gets written to the DB when Mapper_User->save();
gets called. The Mapper feels like the right place for this to go - keeps the Model light.
Is this right, or should default fields like this be set inside Model_User
itself?
<?php
class Model_User {
public function setPassword($value) {
$this->password = md5($value);
}
}
?>
When setting a user object's password, I'm doing this in Model_User->setPassword($value);
, as you might expect, and doing $this->password = md5($value);
inside this method. Again, this feels right - trying to do the md5 step in Mapper_User->save();
method would cause issues if the Model_User
were one pulled from the DB, as the password field would clearly already be hashed.
And this is where my confusion's arising. To my mind, all the logic pertaining to "fields to do with a user" should either live in its Model, or its Mapper, but here I have some logic (default fields) in the Mapper, and some (field operations) in the Model. Is this right, or should I be trying to somehow get default fields in the Model, or field operations in the Mapper?
Thanks for taking the time to read this!
Edit for @RockyFord:
Mapper_User
actually extends an Abstract I've written, as I don't like writing the same basic code in 500 Mapper_*.php
files, so there's some bureaucracy due to that, but its effective __construct() is pretty simple:
<?php
class Mapper_Users {
public function __construct() {
$this->_db = new DbTable_Users();
if(!$this->_db instanceof Zend_Db_Table_Abstract)
throw new Exception('Invalid table data gateway provided');
}
}
?>
The DataMapper
is responsible for populating the object with its data, as well as persisting it. It seems like you're mixing things when you call $user->save()
because you're putting persistence logic within your domain object. This is a common approach when you're using the ActiveRecord
pattern instead of DataMappers
, which is a bad thing.
Your DataMapper
should be responsible for saving the object $mapper->save($user);
and it needs to update just the changed properties. So, the password will be updated only if you set the new hash.
You said:
[...] trying to do the md5 step in
Mapper_User->save();
method would cause issues if theModel_User
were one pulled from the DB, as the password field would clearly already be hashed.
Creates a method called setPasswordHash()
and use it when pulling from the database.
Remember: Don't look for things!
Instead of looking for the database inside your mappers, you should ask for it.
public __construct(Zend_Db_Table $dbTable) {
$this->dbTable = $dbTable;
}
It's all about Dependency Injection.
This may take awhile to answer completely but I'll start with the setPassword
question.
your current:
public function setPassword($value) {
$this->password = md5($value);
}
Now this has nothing to do with convention or best practice but practicality.
ask yourself:
What happens when you retrieve a database record for your user object and that database record contains a hashed password?
Answer: When you construct the user object and call $this->setPassword($password);
or equivalent, you will be applying the hash to a hash.
So you are almost obligated to hash the password in the mapper's save() method or the method used to update the password. Think of the hash value in the database table as the password and the value that's typed into the form field as a placeholder for that password.
Next Part:
To my mind, all the logic pertaining to "fields to do with a user" should either live in its Model, or its Mapper
This is mostly correct.
Everything that belongs to the object domain (Model_User) shall be addressed in the domain Model class (Model_User).
Mappers are only to translate (map) a data object (database row, json string, xml file, flat file, csv file ...) to a form that can instantiate a domain object (Model_User).
So you may end up with more then one mapper available for a given domain object or one mapper may map to more then one source of data.
It might help you if you stopped thinking of your data as "fields", which might tend to keep your head in the database, and instead think of your objects in terms of properties or characteristics.
Because when you get down to the most basic level a Model_User
object is just:
class Model_User {
protected $id;
protected $name;
protected $password;
//continue....
}
all of the getters, setters, constructors and other methods are pretty much so we can put values into those variables.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With