A Zend Framework tutorial, part two
by Naneau
In part one of my tutorial for a zend framework application I talked about creating a general design for your application. If you’re following my approach, you should by now have in your possession some kind of document with requirements for your application. You should also have a diagram with the basic layout of your underlying data structure.
In part one I described to you three classes that formed the basis of the application. There were classes for users, posts and reactions. Those are the models we will be working with. Let’s take a closer look.
1 2 3 4 5 6 | <?php require_once 'Zend/Db/Table.php'; class User extends Zend_Db_Table { protected $_dependentTables = array('Post', 'Reaction'); } |
This tiny class describes our users. The only line of actual code tells Zend_Db_Table that there are other models (classes that extend Zend_Db_Table themselves) that depend on this one. In your diagram you should see this as some kind of connection between the entities, called a relation. For our application this means that users can have both posts and reactions.
1 2 | Zend_Loader::loadClass('User'); $uM = new User(); |
Will create an object of the User class. The best way to look at this is as a gateway to your data. You can ask the object for specific rows, without having to write any SQL yourself. Say for instance that you want to find a user with the id 1. You could simply do the following:
1 2 3 4 | $rowset = $uM->find(1); if ($rowset->valid()) { $user = $rowset->current(); } |
Note that the find() method returns an object of the class Zend_Db_Table_Rowset, even if you ask it to find a single id. If you have found a user row, you can do interesting things with it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | $rowset = $uM->find(3); //find the user(s) with id=3, will always return a rowset if ($rowset->valid()) { //there is a user with id=3 $user = $rowset->current(); $user->username = 'Maurice'; $user->save(); //update the username $posts = $user->findDependentRowset('Post'); //find all posts by this user foreach ($posts as $post) { //note that Zend_Db_Table_Rowset implements SPL Iterator //so you can use foreach() echo $post->title . ' '; } } |
Now that you can modify data, there’s two parts of your application left. The controllers and the views. Remember that requirements document from part one? Look at it again. You should see a list of things your application should be able to do. Controllers can do things. In our application you may want posts to be created, so your blog can get content. You may also want posts to be updated, deleted and read. These are all actions a controller can do.
If your applications are anything like mine, you’ll often come across a CRUD pattern. A simple controller for our posts would look something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <?php require_once 'Zend/Controller/Action.php'; class PostController extends Zend_Controller_Action { public function createAction() { } public function readAction() { } public function updateAction() { } public function deleteAction() { } } |
As you can see, there’s an action function for each letter in CRUD. Because three of the actions work on a specific post, it could be handy to create a helper function:
1 2 3 4 5 6 7 8 9 10 11 12 13 | private function getPost() { $id = (int) $this->getRequest()->getParam('id'); //get the id out of the request, cast it to integer Zend_Loader::loadClass('Post'); $mP = new Post(); $rowset = $mP->find($id); if ($rowset->valid()) { return $rowset->current(); } return false; //the post can't be found } |
Now you can access the requested post like:
1 2 3 4 5 6 | if ($post = $this->getPost()) { //do something with it } else { //don't do something with it } |
I strongly believe in keeping my controller’s actions very simple. In my opinion they should only have a single function. I create multiple actions for handling a single form, one for displaying the form and checking whether received input is valid or not, and another to do whatever it is you needed a user’s input for. I also try to move most domain logic out of the controllers and into my models. In short, I try to make my controller files easy to understand and free from repetition.
You should be able to find all your functional requirements in a controller somewhere. Everything your application will be able to do is in there. In part three of this tutorial we’ll be talking about how we can access controllers with an http request.
Comments
I hate to be such a nag, but I assume that most of the codesnippets above are controllers?
If so, do I have to put them in a new file and does it have to have a specific filename, or give it a random (yet descriptive) name?
You are right. All but the first snippet are to be put in a controller file. Don’t be afraid to ask questions though, I enjoy helping out others and you may point me to things that aren’t clear in my tutorial.
The naming process for controllers is as follows:
If you request http://yourdomain.com/somename/anothername it would map to a SomenameController.php file in your controllers directory, in which you would have to create a SomenameController class that extends Zend_Controller_Action and a anothernameAction() method.
In the examples given here, you would create a PostController.php file. And request the actions with http://yourdomain.com/post/action
If you don’t specify an action, it defaults to indexAction(). If also don’t specify a controller, it defaults to IndexController.php.
I have written a little explanation about the routing process, view it at: http://naneau.nl/2007/05/11/the-zend-framework-routing-process/
I’m not sure if I’m just stupid or that is is more complex than I initially thought, but where do I have to put codesnippets 2,3 and 4? Does it belong in the “PostController.php” file under one of the functions or do I have to create a new Controller file?
It may be a bit confusing. The first code snippet is a class that extends Zend_Db_Table. The following 3 snippets are examples of what you could do with objects of that class.
You would put those examples in a controller. They do not do anything useful in particular, though. They are merely there to illustrate how it works. The 3rd snippet updates a username and echo’s all posts belonging to that user.
You can create as many controller files as you want. Just structure them logically. Put everything that has to do with posts under a PostController.php file, and things that have to do with users under a UserController.php file. If you want to experiment, create something like TestController.php. You are completely free in naming them. The front controller will map your requests to the right controller.
The last 3 snippets give you an idea of how you would go about creating a controller for working with posts. There’s an action for reading/viewing a post, one for updating it, one for creating it and finally one for deleting it. The getPost() method is just a helper function to avoid having to write the same code in 3 functions.
I put this code:
Zend_Loader::loadClass(‘User’);
$uM = new User();
into the IndexController to the indexAction and server returned this page :
Fatal error: Uncaught exception ‘Zend_Db_Table_Exception’ with message ‘No object of type Zend_Db_Adapter_Abstract has been specified’ in D:localserverserverxampphtdocszfapplicationlibraryZendDbTableAbstract.php:490 Stack trace: #0 D:localserverserverxampphtdocszfapplicationlibraryZendDbTableAbstract.php(469): Zend_Db_Table_Abstract->_setupDatabaseAdapter() #1 D:localserverserverxampphtdocszfapplicationlibraryZendDbTableAbstract.php(258): Zend_Db_Table_Abstract->_setup() #2 D:localserverserverxampphtdocszfapplicationcontrollersIndexController.php(8): Zend_Db_Table_Abstract->__construct() #3 D:localserverserverxampphtdocszfapplicationlibraryZendControllerAction.php(438): IndexController->indexAction() #4 D:localserverserverxampphtdocszfapplicationlibraryZendControllerDispatcherStandard.php(214): Zend_Controller_Action->dispatch(‘indexAction’) #5 D:localserverserverxampphtdocszfapplicationlibraryZendControllerFront.php(754): Zend_Controller_Dispatcher_S in D:localserverserverxampphtdocszfapplicationlibraryZendDbTableAbstract.php on line 490
When I comment the line :
//$uM = new User();
there no errors returned. What happened?
nice tut on using relative tables
i have a problem though:
i’ll adapt my problem to suit your example’s tables (User and Post).
if i want to get all posts by one specific user, ur code works perfectly.
but i have another situation: i need all users that have posts. all ideas i came up to are pretty ugly, one of them being:
$users = $this->fetchAll();
if ($users->valid())
{
foreach ($users as $user)
{
$posts = $user->findDependentRowset(‘Post’);
if ($posts->valid())
{
echo $user->name . ‘ ‘;
}
}
}
if i add where clauses in both cases (another if’s statements), it gets even uglier.
any easier ideas ? might be to obvious to see it
thanks, cosmin
Yeah, if you limit yourself to Zend_Db_Table and it’s methods, this is probably as good as it gets. That is because what you want to do requires a bit more logic than it offers. You can use the database object directly (getAdapter()) and use select(), but I think tinkering with find() and fetchAll() will be most succesful in the end, as it will yield Zend_Db_Table_Rows as a result.
I’m using $this->_db->select() ($this->getAdapter()) and it’s working nicely. But this relative logic it’s a lot more clear and intuitive… too bad i can’t use it
Maybe in future releases of the framework.
Thanks for your answer.
But, after all that declaring dependentTables and References, is there a simple way to get the value of a lookup table? I’ve read so many pages of the manual, and so many times, but I can’t figure out how…
Say that you want to display a rowset of all the posts, with the post-author name: how could you?
I love the way you describe what u’re thinking, but I still can’t understand the detail such as where should I put the part of program you’ve explained, how your database structure (fields of each table) and others..
I think it’s better to explain what you think by giving a complete source, so it be easier to understand..
but, afterall I really love your article… thanks
In this article you have given detail about DependentRowset. How could we use ParentRow in ZF.
e.g. I have one table photoalbum and one category. I’m creating photoalbum listing page and want to show category name with photoalbum name.
public function indexAction()
{
//logic of the function
$this->view->title = “My Projects”;
$projects = new Projects();
$where = “1=1″;
$this->view->projects = $projects->fetchAll($where);
$projectRow = $this->view->projects->current();
$categoryArray = $projectRow->findParentRow(‘categorys’);
}
In above indexAction I have fetch all records from project which is photoalbum. To find parent row we have take current records and after that we can use findParentRow. Is there any ways to fetch all category name of the projects (photoalbums) using single function or process?
Amit Shah, there is a way. Take a look:
Assuming that you have correctly setup model (i.e. $_referenceMap ).
CONTROLLER:
===============================================
public function indexAction()
{
//logic of the function
$this->view->title = “My Projects”;
$projects = new Projects();
$where = “1=1″;
$this->view->projects = $projects->fetchAll($where);
(…)
VIEW:
===============================================
(…)
Foo
projects as $project) { ?>
escape($project->findParentRow(‘category’)->name); ?>
(…)
Shit, this striped HTML tags.
But anyway I hope this can help.
sdfsd
Hello I am nerd w in zend programming can I contact with you to get answered my question?
another else if I have table with self dependence like this:
table page (page_id,page_name,page_parent_id)
it means one page can have more page branch
what can I do in my code to have correct response .
thanks