I need to figure out a best practice for utilizing models efficiently in Zend Framework.
Currently, I have classes extending Zend_Db_Table_Abstract which handle my queries to each class' respective table.
When I need to access say 5 of those tables from a controller, I find myself creating 5 new instances of each specific Zend_Db_Table object. This is really ineffective.
I've thought about implementing a Factory pattern to create new instances (or provide existing static copy) but am not sure. Is this the best way to go about it?
What is the correct way to handle models ensuring speed without consuming excessive resources? Should lazy loading come into play here?
[EDIT]
As an example, I have a class I use to handle getting details about a location from a raw search query and need these objects in order to parse the query:
// Initialize database object
$this->dbLocations = new Model_Locations;
$this->dbStates = new Model_States;
$this->dbZipcodes = new Model_Zipcodes;
$this->dbLookup = new Model_Lookup;
In another class, I may need to access those models again so I repeat the above code. Essentially reinitializing objects that could be static/singleton.
I tend to work at the DbTable like you do. I've found it effective when I need to query multiple tables in a single action to create another layer of model above the dbTable. Similar to a service or domain layer. This way I only have to call a single model but I still have the functionality I need.
Here is a simple example that may eventually interact with 5 DbTable classes and most likely a couple of Row classes as well:
<?php
class Application_Model_TrackInfo
{
protected $_track;
protected $_bidLocation;
protected $_weekend;
protected $_shift;
protected $_station;
public function __construct() {
//assign DbTable models to properties for convience
$this->_track = new Application_Model_DbTable_Track();
}
/**
*
* #param type $trackId
* #return type object
*/
public function getByTrackId($trackId) {
$trackData = $this->_track->fetchRow($trackId);
//getAllInfo() Application_Model_Row_TRack
$result = $trackData->getAllInfo();
//returns std object reflecting data from 3 DbTable classes
return $result;
}
/**
*Get Station from trackid through bidlocationid
*
* #param type $trackId
* #return type object
*/
public function getStation($trackId){
$data = $this->_track->fetchRow($trackId);
//This a Application_Model_Row_Track method
$result= $data->getStationFromBidLocation();
return $result;
}
}
I hope this helps.
[EDIT]
Since I wrote this answer I have learned the benefits of Domain Models and Data Mappers. Wow what a difference in my app. Not a magic bullet, but a huge improvement.
Thanks to
Alejandro Gervasio over at PHPMaster.com
Rob Allen at Akrabat.com
and
Pádraic Brady at Surviving The Deepend
for all their help in understanding this pattern.
You seem to be in a possition where you require efficient data management with features that the current Zend framework does not come with. Zend does not have a built in engine for working with databases of any sort, it simply has a wrapper classes which help you write your queries.
What you need is an object relational model (ORM) which is a must-have in a professional framework. As I understand it, ORM is a framework by itself, it has patterns and strongly defined ways of "doing things", supports lazy loading (it takes the most of it) and optimizes your queries to the fullest. When you use ORM you don't even write SQL, instead you need to change your interpretation of data storage, you need to forget about tables and focus on Objects. In Doctrine for example each type (table) is specified by a Class and each record (row) as a class instance where you have access to different methods and properties. It supports event listeners and crazy cascading rellations.
No more need to extract rows from related tables when you delete records (it is automatic), no more need to write complex and chaotic scripts to ensure filesystem synchronisation, you can migrate to almost any db engine at any time (mysql, postgresql, simplesql..) and more..
I've been using Doctrine 2 in conjunction with Symfony 2 framework and I have to say I wouldn't go back to Zend for anything. Yes, it is complex and heavy, but really the ultimate solution. When you come to a moment when you need to manage hundreds of tables with millions of records total - then you will see the difference.
So, final summation:
ORM is what you need, there are many solutions, I know of two really good: Doctrine 1 or 2 and Propel.
P.S.: ORM is an independant part of your system, so you don't really need to use a specific framework, Zend can be configured to work with Doctrine wonderfully :)
Related
Looking through several tutorials and books regarding data access in Zend Framework, it seems as if most people do data access within their models (Active Record Pattern) or even controllers. I strongly disagree with that. Therefore I want to have a Data Access Layer (DAL) so that my domain layer remains portable by not having any "ZF stuff" in it. I have searched around but have not really found exactly what I wanted. Heads up: I am new to ZF.
DAL structure
So, the first problem is where to place the Data Access Layer. While it could certainly be placed within the library folder and adding a namespace to the autoloader, that does not seem logical as it is specific to my application (so the applications folder is suitable). I am using a modular structure. I am thinking of using the below structure:
/application/modules/default/dal/
However, I am not sure how include this folder so that I can access the classes within the controllers (without using includes/requires). If anyone knows how to accomplish this, that would be super! Any other ideas are of course also welcome.
The idea is to have my controllers interact with the Data Access Objects (DAO). Then the DAOs use models that can then be returned to the controllers. By doing this, I can leave my models intact.
Implementation
In other languages, I have previously implemented DAOs per model, e.g. DAL_User. This resulted in an awful lot of DAO classes. Is there a smarter way to do this (using a single class does not seem easy with foreign keys)?
I would also appreciate suggestions on how to implement my DAO classes in ZF. I have not spent an awful lot of time reading about all of the components available for database interaction, so any ideas are very welcome. I suspect that there is something smarter than standard PDO available (which probably uses PDO internally, though). Name drops would be sufficient.
Sorry for the many questions. I just need a push in the right direction.
Well, the first thing you have to take into account when dealing with the Data Access Layer, is that this layer also have sub-layers, it's unusual to find folders called "dal" in modern frameworks (I'm taking as basis both Zend Framework and Symfony).
Second, about ActiveRecord, you must be aware that by default Zend Frameworks doesn't implement it. Most of the tutorials take the easiest path to teach new concepts. With simple examples, the amount of business logic is minimal, so instead of adding another layer of complexity (mapping between database and model's objects) they compose the domain layer (model) with two basic patterns: Table Data Gateway and Row Data Gateway. Which is enough information for a beginner to start.
After analyzing it, you will see some similarity between ActiveRecord
and Row Data Gateway patterns. The main difference is that
ActiveRecord objects (persistable entities) carries business logic and
Row Data Gateway only represents a row in the database. If you add
business logic on a object representing a database row, then it will
become an ActiveRecord object.
Additionally, following the Zend Framework Quick Start, on the domain model section, you will realize that there's a third component, which uses the Data Mapper Pattern.
So, if the main purpose of your DAL is to map data between business objects (model) and your storage, the responsibility of this task is delegated to the Data Mappers as follows:
class Application_Model_GuestbookMapper
{
public function save(Application_Model_Guestbook $guestbook);
public function find($id);
public function fetchAll();
}
Those methods will interact with the Database Abstraction Layer and populate the domain objects with the data. Something along this lines:
public function find($id, Application_Model_Guestbook $guestbook)
{
$result = $this->getDbTable()->find($id);
if (0 == count($result)) {
return;
}
$row = $result->current();
$guestbook->setId($row->id)
->setEmail($row->email)
->setComment($row->comment)
->setCreated($row->created);
}
As you can see, the Data Mappers interacts with a Zend_Db_Table instance, which uses the Table Data Gateway Pattern. On the other hand, the $this->getDbTable->find() returns instances of the Zend_Db_Table_Row, which implements the Row Data Gateway Pattern (it's an object representing a database row).
Tip: The domain object itself, the guestbook
entity, was not created by the find() method on the DataMapper,
instead, the idea is that object creation is a task of factories
and you must inject the dependency in order to achieve the so called
Dependency Inversion Principle (DIP) (part of the SOLID principles). But that's
another subject, out of the scope of the question. I suggest you
to access the following link http://youtu.be/RlfLCWKxHJ0
The mapping stuff begins here:
$guestbook->setId($row->id)
->setEmail($row->email)
->setComment($row->comment)
->setCreated($row->created);
So far, I think I have answered your main question, your structure will be as following:
application/models/DbTable/Guestbook.php
application/models/Guestbook.php
application/models/GuestbookMapper.php
So, as in the ZF Quick Start:
class GuestbookController extends Zend_Controller_Action
{
public function indexAction()
{
$guestbook = new Application_Model_GuestbookMapper();
$this->view->entries = $guestbook->fetchAll();
}
}
Maybe you want to have a separated folder for the data mappers. Just change:
application/models/GuestbookMapper.php
to
application/models/DataMapper/GuestbookMapper.php
The class name will be
class Application_Model_DataMapper_GuestbookMapper
I've seen that you want to separate your domain model objects into modules. It's possible too, all you need is to follow the ZF's directory and namespace guidelines for modules.
Final tip: I've spent a lot of time coding my own data mappers for
finally realize that it's nightmare to maintain the object mapping when
your application grows with a lot of correlated entities. (i.e Account
objects that contain references to users objects, users that contain
roles, and so on) It's not so easy to write the mapping stuff at this
point. So I strongly recommend you, if you really want a true
object-relational mapper, to first study how legacy frameworks perform
such tasks and perhaps use it.
So, take some spare time with Doctrine 2, which is the
one of the best so far (IMO) using the DataMapper pattern.
That's it. You still can use your /dal directory for storing the DataMappers, just register the namespace, so that the auto loader can find it.
In my opinion you should have a gateway abstraction (not just Database access) per model. A DAO is not enough. What if you need to get the data from the cloud at some point? This is quickly coming a reality. If you abstract your gateway logic into something generic and then implement it using a database you can have the best of both worlds.
The implementation of a specific gateway interface could use a generic data mapper if you so chose. I work for a small company and have always just created my implementation using PDO. This lets me be close enough to the database to deal with any interesting bits of SQL I might need but am able to support a very abstracted interface.
I have not used the Zend Framework at all. I do not know if they have data-mapper tools that could help you implement the gateway interfaces.
The last few days, I have extensively read books and web pages about OOP and MVC in PHP, so that I can become a better programmer. I've come upon a little problem in my understanding of MVC:
Where do I put a mysql_query?
Should I put it in the controller and call a method on a model that returns data based on the provided query? Or should I put it in the model itself? Are both of the options I'm providing total garbage?
Materials on the subject of MVC
You could have listed the books you were reading, because most (if not all) php books, which touch on MVC, are wrong.
If you want to become a better developer, i would recommend for you to start with article by Marting Fowler - GUI Architectures. Followed by book from same author - "Patterns of Enterprise Application Architecture". Then the next step would be for you to research SOLID principles and understand how to write code which follows Law of Demeter. This should cover the basics =]
Can I use MVC with PHP ?
Not really. At least not the classical MVC as it was defined for Smalltalk.
Instead in PHP you have 4 other patterns which aim for the same goal: MVC Model2, MVP, MVVM and HMVC. Again, I am too lazy to write about differences one more time, so I'll just link to an old comment of mine.
What is Model ?
First thing you must understand is that Model in MVC is not a class or an object. It is a layer which contains multitude of classes. Basically model layer is all of the layers combined (though, the second layer there should be called "Domain Object Layer", because it contains "Domain Model Objects"). If you care to read quick summary on what is contained in each part of Model layer, you can try reading this old comment (skip to "side note" section).
The image is taken from Service Layer article on Fowler's site.
What does the Controllers do ?
Controller has one major responsibilities in MVC (I'm gonna talk about Model2 implementation here):
Execute commands on structures from model layer (services or domain objects), which change the state of said structures.
It usually have a secondary responsibility: to bind (or otherwise pass) structures from Model layer to the View, but it becomes a questionable practice, if you follow SRP
Where do I put SQL related code ?
The storage and retrieval of information is handled at the Data Source Layer, and is usually implemented as DataMapper (do not confuse with ORMs, which abuse that name).
Here is how a simplified use of it would look like:
$mapper = $this->mapperFactory->build(Model\Mappers\User::class);
$user = $this->entityFactory->build(Model\Entities\User::class);
$user->setId(42);
$mapper->fetch($user);
if ($user->isBanned() && $user->hasBannExpired()){
$user->setStatus(Model\Mappers\User::STATUS_ACTIVE);
}
$mapper->store($user);
As you see, at no point the Domain Object is even aware, that the information from it was stored. And neither it cases about where you put the data. It could be stored in MySQL or PostgreSQL or some noSQL database. Or maybe pushed to remote REST API. Or maybe the mapper was a mock for testing. All you would need to do, to replace the mapper, is provide this method with different factory.
Also, please see these related posts:
understanding MVC Views in PHP
testable Controllers with dependencies
how should services communicate between each other?
MVC for advanced PHP developers
Model and Entity Classes represents the data and the logic of an application, what many calls business logic. Usually, it’s responsible for:
Storing, deleting, updating the application data. Generally it includes the database operations, but implementing the same operations invoking external web services or APIs is not an unusual at all.
encapsulating the application logic. This is the layer that
should implement all the logic of the application
Here is the MVC Sequence Diagram which shows the flow during a http request:
In this case Model is the best place to implement the code realted to access database.
The model contains the domain objects or data structures that represent the application's state. [wikipedia]. So the model would be the place to make the database call.
In the 'classic' (lack of a better word atm) MVC pattern the view would get the current state from the model.
Don't make the mistake by saying that the model is for accessing the database. It's more than just accessing the database.
For one, don't use mysql_query() and family; they're being deprecated, so consider also learning about PDO and/or mysqli.
The model takes care of data handling; it provides an interface to the controller by which it retrieves and/or stores information. So this would be a primary place where database actions take place.
Update
To answer a question asked by the OP in the comments: "one generic model for the whole db or a model for each table/action?"
Models are meant to abstract away individual tables (although there are models that exclusively handle a single table); for instance, instead of asking for all articles and then query the usernames for the authors you would have one function like this:
function getArticles()
{
// query article table and join with user table to get username
}
How many models you will create largely depends on how big the project is and how inter-related the data is. If you can identify independent groups of data, it's likely that you'd create a model for each group; but this is no hard & fast rule.
Data manipulation can be part of the same model, unless you want a clear separation between read-only and write-only models (I wouldn't know of a situation that warrants this, but who knows).
To go even further, your model should not contain the database access code. This belongs to another layer outside the Model/View/Controller: this is called the persistence layer, which can be implemented using an Object-Relational Mapper such as the popular Doctrine 2 for PHP.
This way, you never touch any (my)SQL code. The persistence layer takes care of this for you.
I really advise you to have a look at a Doctrine tutorial, this is a really professional way to create your applications.
Instead of working with raw data loaded from the database, you create objects that hold your data, and the behavior associated with it.
For example, you might have a User class, such as:
class User
{
protected $id;
protected $name;
protected $privileges;
public function setName($name) { ... }
public function getName() { ... }
public function addPrivilege(Privilege $privilege) { ... }
public function getPrivileges() { ... }
}
You controller will only interact with objects:
class UserController
{
public function testAction()
{
// ...
$user = $em->getRepository('User')->find(123); // load User with id 123
$user->setName('John'); // work with your objects,
echo $user->getName(); // and don't worry about the db!
$em->flush(); // persist your changes
}
}
Behind the scenes, the ORM takes care of all the low-level work of issuing a SELECT query, instantiating your object, detecting modifications to your object, and issuing the necessary UPDATE statement!
I'm comparing Doctrine 2 and Propel 1.5/1.6, and I'm looking in to some of the patterns they use. Doctrine uses the DataMapper pattern, while Propel uses the ActiveRecord pattern. While I can see that DataMapper is considerably more complicated, I'd assume some design flexibility comes from this complication. So far, the only legitimate reason I've found to use DataMapper over ActiveRecord is that DataMapper is better in terms of the single responsibility principle -- because the database rows are not the actual objects being persisted, but with Propel that doesn't really concern me because it's generated code anyway.
So -- what makes DataMapper more flexible?
I've worked with both, new Propel and Doctrine2. What DataMapper (and I mean Doctrine2) makes adorable is that your domain objects are clean and simple, they don't extend irrelevant classes which add many irrelevant methods to your classes (violating SRP as you said). They're just simple entities with a few properties and a few methods that are a part of your business layer. And that, of course, lets you write unit tests for them and reuse them in the future.
I wouldn't say DataMapper considerably more complicated. It is complicated if you write your own implementation of DataMapper, but Doctrine2 is way easier to use than propel (maybe except for setting it up, we do that only once anyway). It has an entity manager that manipulates any entities. You may have entity repositories for complex queries. And that's it.
And an entity is as simple as:
/**
* Question
*
* #Entity
*/
class Question
{
/**
* #Column(type="string")
*/
private $title;
public function getTitle() { return $this->title; }
public function setTitle($title) { $this->title = $title; }
}
In Propel we would have 6 classes for that entity which would contain a lot of generated and often unused code.
What makes DataMapper more flexible? Simplicity that it provides.
ActiveRecord makes a data type do two things -- own responsibility for persisting data to a database, and own responsibility for storing the data for general calculations. This means that if you want to change the data store to which data is persisted in the future, code which generally doesn't have to care about database behavior is exposed to the change.
DataMapper completely decouples a given database implementation from a set of in-memory working objects, meaning the same internal working data type can be persisted to multiple databases, without modifying code that otherwise uses the working type.
For example, with DataMapper patterns, if tomorrow you want to add XML export for your data, you implement one new data mapper, the "MyDataToXmlDataMapper" or similar, that adds the new persistence format, and no other code has to change. With ActiveRecord adding such functionality requires changing the interface of the working data type.
It used to be that within a CodeIgniter model you couldn't access another model.
$this->load->model('bar');
$this->bar->something();
Is this still valid, or have they changed it?
Those are some VERY long answers for a simple question.
Short answer: This is now fully supported. Cross-load away if you feel like it!
I strongly disagree with the notion that a "model" should only encapsulate a database table with simple CRUD operations. As noted in the Wikipedia article:
http://en.wikipedia.org/wiki/Model-view-controller#As_a_design_pattern
...that application layer is intended to do more than simply act as a single database table abstraction. Think about the meaning of the word "controller" -- it should act more as a director rather than being the entire application in and of itself. A "model" is a place for business logic. Most large scale applications in fact hold much of their business logic in the database itself (in the form of triggers, stored procedures, foreign keys, etc.).
I think the misunderstanding of what a "model" is is partly caused by the same (over-)hype of "MVC", without carrying with it much understanding of the concepts themselves. Kinda like how empty "AJAX" is, or even more easy, "Web 2.0". For better or worse, plenty of script kiddies have jumped on the MVC wagon, and since the simple howtos and example scenarios don't do much more than tell you to put your database code in the "model", the misuse of that layer as only a database abstraction has become commonplace. Now you read posts all over the Internet calling it "unpure", "dirty", "hackish" to put any business logic in a model. That's WRONG. Misinformed.
The easy example is to think about foreign keys: even if you only want your "model" to be a database model, if you want to be "pure", "correct" or what have you, you really should be enforcing referential integrity therein. Thanks to the lack of real foreign key support in MySQL for many years, web applications grew up without anyone worrying about referential integrity at all. Fits the script kiddie lifestyle I guess. Anyhow, for even this simplified view of a model to be able to maintain foreign key validity, a model then has to work with others (or, especially if a framework like CodeIgniter does not let you do so, you have to write queries to other tables, sometimes duplicating queries elsewhere - THAT is bad style).
Therefore, I believe this to be a shortcoming of CodeIgniter. I understand that it might not be an easy fix, but it's certainly a disappointing oversight.
So what I did was take the example code above and abstract it into a helper so that I now have a function that works almost identically to the normal $this->load->model() functionality. Here it is (put it into a helper that is auto-loaded and you can use it in any model):
/**
*
* Allow models to use other models
*
* This is a substitute for the inability to load models
* inside of other models in CodeIgniter. Call it like
* this:
*
* $salaries = model_load_model('salary');
* ...
* $salary = $salaries->get_salary($employee_id);
*
* #param string $model_name The name of the model that is to be loaded
*
* #return object The requested model object
*
*/
function model_load_model($model_name)
{
$CI =& get_instance();
$CI->load->model($model_name);
return $CI->$model_name;
}
It's possible, but not ideal and considered bad and more for "quick fix" than ideal or pure implementation.
class Location extends Model{
public function get($ID){
// Get main CI object handle and load model
$CI =& get_instance();
$CI->load->model('LocationType');
// Call new model functions using handle to main CI object
$CI->LocationType->setID($result->LocationTypeID);
$CI->LocationType->setTitle($result->TypeTitle);
$this->_locationType = $CI->LocationType;
//Other Stuff
}
}
Anytime you're using the main CI object like this is probably a bad idea. Try to re-think your layout and just pass data to/from your controller to the models.
http://codeigniter.com/forums/viewthread/69833/
You can load models from models as Phil Sturgeon says, but you have to be careful of dependencies if you load the models in the model constructor: if model A uses model B and model B uses model A, when you try to load one or the other, you'll go into an infinite loop.
In situations like this in Code Igniter I prefer one of two posiibilities:
1) Have model's attribute and setter like this:
class X extends Model {
var $Y_model;
public function setY($Y) {
$this->Y_model = $Y;
}
public function doItRightNow($a,$b) {
$list = $this->Y_model->getSomeList($a,$b);
// ...
}
// ...
}
And then use this setter before other methods to give an instance of other model so it can be used by methods.
$this->load->model('X');
$this->load->model('Y');
$this->X->setY($this->Y);
$this->X->doItRightNow($something,$somethingElse);
2) To have a parameter in method by which I will give an other model instance from controller.
class X extends Model {
public function doItRightNow($a,$b,$Y_model) {
$list = $Y_model->getSomeList($a,$b);
// ...
}
// ...
}
And use it like this:
$this->load->model('X');
$this->load->model('Y');
$this->X->doItRightNow($something,$somethingElse,$this->Y);
I think that these are more clean possibilities.
Which way to use depends on how many methods need to access other model. If there are one or two it might be better to give it as a method parameter. If more - I think it's better to have a class attribute and setter.
And in elegant way you can give one model or another depending on some condition - if they both partially implement same interface with the same kind of data returned (it's rarely useful, but it can be sometimes).
I think it's generally better the write libraries that access the models and then include the libraries in your model if need be.
For instance, if you needed to check if someone is authorized to go through with a certain CRUD action, you might want to include whatever authentication library you are using (its probably auto-included in most cases). You wouldn't necessarily want to access the model directly--it just seems dirty and improper.
I think the preferred way is to do what you need to do in your controller and pass the results from one model's method(s), if need be, to your other model's method(s).
Regardless, I can't see why it wouldn't be possible to include one model into another, per se. I don't think you can do it with the syntax you are showing, though. You would have to do it some other convoluted way. In any case, IMO, it's bad practice to include a model directly into another model.
In CI 2.0 you can just call one model directly from another.
Better to create a helper function instead of calling the function from another model so that it can be used in 2 models at a time and code can be reused.
What are some of the ways you have implemented models in the Zend Framework?
I have seen the basic class User extends Zend_Db_Table_Abstract and then putting calls to that in your controllers:
$foo = new User;
$foo->fetchAll()
but what about more sophisticated uses? The Quickstart section of the documentation offers such an example but I still feel like I'm not getting a "best use" example for models in Zend Framework. Any interesting implementations out there?
EDIT: I should clarify (in response to CMS's comment)... I know about doing more complicated selects. I was interested in overall approaches to the Model concept and concrete examples of how others have implemented them (basically, the stuff the manual leaves out and the stuff that basic how-to's gloss over)
I worked for Zend and did quite a bit of work on the Zend_Db_Table component.
Zend Framework doesn't give a lot of guidance on the concept of a "Model" with respect to the Domain Model pattern. There's no base class for a Model because the Model encapsulates some part of business logic specific to your application. I wrote a blog about this subject in more detail.
Persistence to a database should be an internal implementation detail of a Model. The Model typically uses one or more Table. It's a common but improper object-oriented design to consider a Model as an extension of a Table. In other words, we should say Model HAS-A Table -- not Model IS-A Table.
This is an example of IS-A:
class MyModel extends Zend_Db_Table_Abstract
{
}
This is an example of HAS-A:
class MyModel // extends nothing
{
protected $some_table;
}
In a real domain model, you would use $some_table in the methods of MyModel.
You can also read Martin Fowler's take on the Domain Model design pattern, and his description of the Anemic Domain Model antipattern, which is how many developers unfortunately approach OO programming.
I personally subclass both Zend_Db_Table_Abstract and Zend_Db_Table_Row_Abstract. The main difference between my code and yours is that explicitly treat the subclass of Zend_Db_Table_Abstract as a "table" and Zend_Db_Table_Row_Abstract as "row". Very rarely do I see direct calls to select objects, SQL, or the built in ZF database methods in my controllers. I try to hide the logic of requesting specific records to calls for behind Zend_Db_Table_Abstract like so:
class Users extends Zend_Db_Table_Abstract {
protected $_name = 'users';
protected $_rowClass = 'User'; // <== THIS IS REALLY HELPFUL
public function getById($id) {
// RETURNS ONE INSTANCE OF 'User'
}
public function getActiveUsers() {
// RETURNS MULTIPLE 'User' OBJECTS
}
}
class User extends Zend_Db_Table_Row_Abstract {
public function setPassword() {
// SET THE PASSWORD FOR A SINGLE ROW
}
}
/* CONTROLLER */
public function setPasswordAction() {
/* GET YOUR PARAMS */
$users = new Users();
$user = $users->getById($id);
$user->setPassword($password);
$user->save();
}
There are numerous ways to approach this. Don't think this is the only one, but I try to follow the intent of the ZF's design. (Here are more of my thoughts and links on the subject.) This approach does get a little class heavy, but I feel it keeps the controllers focused on handling input and coordinating with the view; leaving the model to do the application specific work.
Don't ever use Zend_Db_Table as your model. It just gets you into trouble. Either you write your own model classes which use Zend_Db_Table to talk to your database or you can read my blog post here for a hack that allows you to somewhat combine the "Model" class and Zend_Db_Table.
The main thing to not is that when you use Zend_Db_Table directly in your controllers you end up doing the same things in multiple places. If you have to make a change to some of that logic, you have to make a change in multiple places. Not good. My first professional project was done like this because I was the one in the company who had to learn how to use ZF and it's a total mess now.
I also tend to write helper functions into my classes for sophisticated fetches. Somthing like $table->doNameFetchAll() or $table->doOrderFetchAll().
I've been doing some research on Models for ZF and came across an interesting series of articles by Matthew Weier O'Phinney which are well worth checking out:
Using Zend_Form in your Models
Applying ACLs to Models
Model Infrastructure
It's not "production code" and a lot is left to the imagination, but it's a good read and has helped me quite a bit.
A model has nothing to do with the database. What if I am fetching data from an RSS feed or a SOAP service or reading files from the FS?
I put all these kinds of things in models. In that case, my model class might not extend anything. I'm about to write a model that uses methods of other models.
Skip ZF for the models part, there are much better solutions. The "M" in ZF's "MVC" is pretty much absent. Reading their docs they don't really mention models at all -- which is a good thing, it means you can use just about anything you want without writing lots of adapter code.
Take a look at Doctrine for models instead. It is quickly becoming the de-facto ORM for PHP.
You can do more complicated queries, check the Advanced usage section in the Zend_Db_Table manual page.
$select = $table->select();
$select->from($table,
array('COUNT(reported_by) as `count`', 'reported_by'))
->where('bug_status = ?', 'NEW')
->group('reported_by');
you can extend the Zend_Db_Table_Abstract class and add some useful methods to it. for example you can add a changePassword() method to your user class and manipulate it's data. or you can change the default __toString() method of your class, so you'll have a customized __toString() method that, let's say returns the whole contact information of the user (name, address, phone number) in a well formatted string. in your constructor you could populate your data into properties of your object. then use them like:
public function __toString() {
$data = $this->_name . ', ' . $this->_adderss . ', call: ' . $this->_phone;
return $data;
}
your model extends the Zend_Db_Table_Abstract just to ease the process of accessing its data, but the functionality you could have on that data is all up on your creativity and need.
I recommend you the book "php|architect's guide to programming with zend framework" by Cal Evans. the book is very informative and easy to read. chapters 4 and 6 are going to be useful for this matter.
A database entity is not the only kind of model component. As such, it doesn't really make sense to speak of models (in plural) - Your application has one model, which contains a multitude of components. Some of these components could be table gateways (And thus extend from Zend_Db), while others would not.
I recommend that you get hold of the book Domain Driven Design by Eric Evans, which does an excellent job of explaining how to construct an object model.
I use Propel 1.3 instead of Zend_Db_Table.
It's tricky to setup, but awesome.
It can examine your database and auto-generate all your models.
It actually generates 2 levels and 2 types of model.
Examples for 'user' table:
Level 1: BaseModel & BasePeer: these get overwritten every time you regenerate your ORM. i.e. BaseUser.php & BaseUserPeer.php
Level 2: StubModel & StubPeer: these don't get overwritten. They're the ones you customize. i.e. User.php & UserPeer.php
Type 1: Model - for basic CRUD operations, not queries i.e. User.php
Type 2: Peer -- for queries. These are static objects. i.e. UserPeer.php
So to create a user:
$derek = new User();
$derek->setFirstName('Derek');
$derek->save();
To find all dereks:
$c = new Criteria();
$c->add(UserPeer::FIRST_NAME, 'Derek');
$dereks = UserPeer::doSelect($c);
http://zfsite.andreinikolov.com/2008/08/zend_db_table-time-overhead-about-25-percents/
Bit of a catch 22, Zend_Table is nice in principle, but generates some performance overheads (without caching)...