Zend Framework: Models, Mappers; Default Fields in Mappers & Field Operations in Models? - zend-framework

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.
Mapper_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?
Model_User
<?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.
UPDATE:
You said:
[...] 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.
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.

Related

Post-Initialization in extbase domain model

In one of my extbase models, I want to initialize some properties derived from the properties that are saved in the database. The computation of these virtual properties is time consuming, so I'd like to cache them. Thus my program flow should look somehow like this:
Load the domain object as usual from the database
Check if the virtual property is available in cache. If so, fetch it from there, otherwise compute and cache it.
I first thought the method "initializeObject" is what I need, but it is not: It is called before any property is initialized from the database. So I came up with two other ways:
I can call an initialization-method manually from the repository after fetching the object, but that seems weird and would break if someone adds another find* method to the general repository.
Another idea is to add a boolean "virtualPropertiesInitialized" to the model, query it whenever one of the virtual properties is accessed and initialize the virtual properties if needed. Also seems weird, but would not break if someone adds another "find"-method to the generic repository.
My question is:
Is there a default/best-practice how to do what I want to do?
If reading the final value from disk or database is less computationally intensive, then store the value using the TYPO3 caching framework or by your own caching method of a static class and restore it in the getter of the virtual property. Doing it in the getter method public mixed getYourPropery() will give you the feature that the value is only get from the cache when you call it.
On the second call, just return the value you stored previously:
private $myValue = NULL;
public function getMyValue() {
if($this->myValue != NULL) return $this->myValue;
$this->myValue = "test";
return $this->myValue;
}

Zend Model access in singleton class - best approach

I'm looking for best pattern/approach to access one table data in singleton class (in ZF 1.x). In details:
I have one singleton class (just like Zend_Date for example) that make for me some basic abstract stuff very detached from application reality.
In this class, in two points, I need to access to one db table and I need to make some basic operation on it.
It's not a problem to use my regular ZF models class inside functions of this singleton. It works fine. Now it look like:
class My_ZF_Singleton
{
...
public function someFunctionInMySingleton()
{
...
$oModel = new Model_My_Model_Form_ZF_Application();
$oModel->letsDoSomeStuffWithDb();
...
}
...
}
But I feel in my bones that it's not a very good solution, not so glamour as I would like to be. It make my singleton class more attached to application then it should be. I would like to use some other pattern to access this db data then application model class. I would be very thankfull for any clue or better solution - it's not a "hey I'm stuck probem" or "hey I've got an error" - I'm just looking for better solution.
Not sure I quite understand your question or want the point might be, but I'll try.
In ZF1 the database adapter is typically a singleton already. Multiple databases maybe connected to but each will require a unique identification. Typical access to the default adapter setup in the application.ini or Bootstrap.php:
$adapter = Zend_Db_Table::getDefaultAdapter();
a common way to provide access to a single database table and give access to the Zend_Db_Table api is to build a DbTable model:
class Application_Model_DbTable_TableName extends Zend_DbTable_Abstract
{
protected $_name = 'Table_Name' //required if classname does not match table name
protected $_primary = 'primary_key_column_name'//optional, use if primary key is not 'id'
}
You can treat this class as an instance of the default database adapter for a single table (works really well in a mapper). You can also add functions to this class to override or add to the default Zend_Db_Table api.
I hope this at least comes close.

Zend Framework Models and Doctrine 2 Entities

I've recently integrated Doctrine 2 into my ZF app, using the method introduced here:
http://www.zendcasts.com/unit-testing-doctrine-2-entities/2011/02/
I really like the way it works, however I'm confused a little bot on how this will affect the way I used to have my Models and Mappers.
Let me explain the confusion by an example,
Let's say we have User entities and Purchases as in the example given in ZendCast
Right now I have these entities that Doctrine uses
/library/ZC/Entity
User.php
Purchase.php
Before I used to have
application/models/
User.php (Application_Model_User)
Purchase.php (Application_Model_Purchase)
In classes in application/models/ I used to write functions to act on entities, (Fat model thin controller principle), for example if I wanted to send an email to a user, I would create a method named sendMail in Application_Model_User.
Now I'm not sure if I can add methods to files in /library/ZC/Entity, or if it's a good idea at all since Doctrine uses those files to manage database.
I rather have a separate model file, I also used to have mapper files which worked on more than one Model, for example if I wanted to email all inactive users I would create a method emailInactiveUsers to Application_Model_UserMapper.
How would I do that now?
I also googled a little bit and I found this:
http://net.tutsplus.com/tutorials/php/zend-framework-from-scratch-models-and-integrating-doctrine-orm/
It says
A scaffolding tool, called Doctrine_Cli that creates models from the database very quickly
However the command "generate-models-db" does not exist in my scripts/doctrine.php file. I'm not sure if this is something that Doctrine has stopped supporting in version 2 or what.
Adding methods and properties to your models which are not managed by Doctrine should be no problem. When it comes to mappers, you do not need them with Doctrine. Doctrine already takes care of mappings (e.g. via Annotations in your Entity-class) and for (complex) queries you have the EntityManager/Repositories.
I would place emailInactiveUsers() in a Service, which has access to the EntityManager, e.g.:
class UserMailService
{
private $em;
// Inject EntityManager, e.g. via setEntityManager() or __construct()
public function emailInactiveUsers()
{
$mail = new \Zend_Mail();
$users = $this->em->getRepository('User')->findBy(array('isActive' => false));
foreach ($users as $user) {
$mail->addTo($user->getEmail());
}
// And so on...
}
}
Something like sendMail() in my opinion belongs into a Service as it acts on a User-entity and requires a dependency to a Mailer, which should not be coupled with the model.
If a User does something it belongs in the model. If something acts on the User - in your case a Mailer, which takes the email-address from the user and sends out an email - it does not.

Zend - Design Pattern DataMapper & Table Gateway

This is directly out of the Zend Quick Start guide. My question is: why would you need the setDbTable() method when the getDbTable() method assigns a default Zend_Db_Table object? If you know this mapper uses a particular table, why even offer the possibility of potentially using the "wrong" table via setDbTable()? What flexibility do you gain by being able to set the table if the rest of the code (find(), fetchAll() etc.) is specific to Guestbook?
class Application_Model_GuestbookMapper
{
protected $_dbTable;
public function setDbTable($dbTable)
{
if (is_string($dbTable)) {
$dbTable = new $dbTable();
}
if (!$dbTable instanceof Zend_Db_Table_Abstract) {
throw new Exception('Invalid table data gateway provided');
}
$this->_dbTable = $dbTable;
return $this;
}
public function getDbTable()
{
if (null === $this->_dbTable) {
$this->setDbTable('Application_Model_DbTable_Guestbook');
}
return $this->_dbTable;
}
... GUESTBOOK SPECIFIC CODE ...
}
class Application_Model_DbTable_Guestbook extends Zend_Db_Table_Abstract
{
protected $_name = 'guestbook_table';
}
Phil is correct, this is known as lazy-loading design pattern. I just implemented this pattern in a recent project, because of these benefits:
When I call on getMember() method, I will get a return value, regardless if it has been set before or not. This is great for method chaining: $this->getCar()->getTires()->getSize();
This pattern offers flexibility in that outside calling code is still able to set member values: $myClass->setCar(new Car());
-- EDIT --
Use caution when implementing the lazy-loading design pattern. If your objects are not properly hydrated, a query will be issued for every piece of data which is NOT available. The best thing to do is tail your db query log, during the dev phase, to ensure the number and type of queries are what you expect. A project I was working on was issuing over 27 queries for a "detail" page, and I had no idea until I saw the queries.
This method is called lazy-loading. It allows a property to remain null until requested unless it is set earlier.
One use for setDbTable() would be testing. This way you could set a mock DB table or something like that.
One addition: if setDbTable() is solely for lazy-loading, wouldn't it make more sense to make it private? That way it will avoid accidental assignment and to wrong table as originally mentioned by Sam.
Should we be compromising the design for the sake of testability?

Spring List Binding in Form

I'm trying to bind a list/arraylist/hashmap/etc of custom objects to my form in JSP using Spring. Right now, the controller creates a Map of the two lists (Boolean list and custom object list) in referenceData(), and provides it to the form which uses those values to populate the fields. The values are initialized from a MySQL database using Hibernate, and all that works fine. The list is a known length before the form is initialized, so that part is easier. Now what I'd like to do is correctly bind those objects in the form, so that when there are changes made, I can detect that in onSubmit() (or wherever is appropriate), and update the database accordingly. I can't seem to bind them correctly in the form so that I can see changes made. I tried just using a list of the form fields as the model, but even that wasn't working correctly. Do I just need to inject the list in a particular way? Any ideas or examples here? Any help would be greatly appreciated.
UPDATE: At Ralph's request here is the solution I used:
In my data object class, I lazy loaded a map using MapUtils.lazyMap(), with a String key and other custom object value. The other custom object is just a class that contains List<String> and getters/setters. In the corresponding .jsp file, I just nest several loops to loop through the keys first using loop.current.key and then loop2.current.value.paramsList to loop through the values for that key. This was not really what I asked for in my original post, as I was looking for a more general solution, and the lazy loading pointed me in the right direction.
In Spring 2 you need a special List in your Command object, that is able to grow if one add the x-th element event if the list has not this size yet.
One way to do that is to use LayzList decorator from commons-collections.
#Override
protected Object formBackingObject(final HttpServletRequest request)
throws Exception {
List<PosterSelectionRow> posterSelectionRowList = LazyList.decorate(
new ArrayList<PosterSelectionRow>(),
new PosterSelectionRowListFactory());
return new PosterSelectionCommand(posterSelectionRowList);
//PosterSelectionCommand contains a list of selected poster rows
}
private static class PosterSelectionRowListFactory
implements org.apache.commons.collections.Factory {
/** Invoked if the list need a new element */
public Object create() {
return = new PosterSelectionRow();
}
}
When I remember right, there is a way without that Factory stuff, but I am not sure.