Saturday, July 4, 2009

Zend Framework building complete application

Well, I have written lots of separate article on Zend Framework. Hopefully most of folk out there searching for valuable help may have gotten some from my articles.
This time my objective is to share everything with you, from basic configuration till to building complete Zend Framework application.
This article will cover
1. How to make initial configuration.
2. How to use Zend_Auth for authentication
3. Using Zend_Session for handling sessions
4. What are models and how to create them
5. Using Zend_Db to handle backend database for storing valuable information.
And much more.
So let’s get started.
1. Creating directory structure and making initial configuration
Zend Framework is based on MVC design patterns. MVC stands for Model, View, Controller. A simple definition of MVC design pattern is to separate business logic from presentation logic.
In order to work with Zend Framework MVC design pattern, you’ll need to make necessary directory structure. There are various ways of making directory structure, I, however will use the most easier to create and understand structure.

The structure I created is.
html_root
/application
/controllers
IndexController.php
/models
Users.php
/forms
LoginForm.php
/views
/scripts
/dojo
index.phtml
/libaray
/Zend
/js
/css
/images
/index.phtm


On the top level we have html_root directory, containing application directory, library, js, css and images. It also contain index.phtml called our bootstrap file. This file contain all our configuration code. Further more application directory contain controller, models forms and views directories. Library directory contains Zend components. Js contain all our js files, css contains css files and images contains all images used in our application.
On the hard drive our directory structure looks like

Keep in mind that controllers directory contain our entire controllers, form directory contain all forms used in our application. Model contains php files contain our business logic. Views/scripts contain template files against each controllers.

Now as we have made necessary directory structure, next step is to make necessary configuration in our bootstrap file.
2. Making necessary configuration in our bootstrap- index.php file.
The most important file, that will get all request and route them to specific controllers. It contain code for setting include path, initializing front controller, set controllers path and call dispatch method.
<?php
define('ROOT_DIR', dirname(__FILE__));

set_include_path('.'
. PATH_SEPARATOR . ROOT_DIR . '/library'
. PATH_SEPARATOR . ROOT_DIR . '/application/models'
. PATH_SEPARATOR . ROOT_DIR . '/application/forms'
. PATH_SEPARATOR . get_include_path()
);

require_once "Zend/Loader/Autoloader.php";
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->setFallbackAutoloader(true);

$frontController = Zend_Controller_Front::getInstance();

$frontController->throwExceptions(true);

$frontController->setControllerDirectory(ROOT_DIR.'/application/controllers');
$frontController->dispatch();
?>


The code is very simple to explain. The first line define path to our root directory.
The next lines set include path to /library, /application/models and /application/forms.

Next lines are very important
require_once "Zend/Loader.php";
Zend_Loader::registerAutoload();


Help in loading all php files we need in our application. If we don’t use this code, we will need to include require_once statement to load php files needed.

In next lines we get instance of the front controller, set controllers directory and call dispatch method.
Front controller is responsible for handling the user request. It takes the request and on specific criteria take appropriate action.
That’s it. Its our configuration file. Simple and easy.

3. Creating controllers and views
In html_root/application/controllers, create IndexController.php and put the following code in it.
<?php
class IndexController extends Zend_Controller_Action
{
public function indexAction()
{
}
}


Every controller must be extended from Zend_Controller_Action and can contain as many methods as possible. Method defined in controllers is called action. Once you create action in the controller, next step is to create view template. For this purpose, in html_root/application/views/scripts/, create index/index.phtml and put the following code in it.
Hello world


That’s it.
Now if you browse
http://localhost/html_root/

You will see
Hello World
If you don’t see “Hello world” printed, read the above guide lines again.
As we have now successfully created directory structure and bootstrap file, its time to make other necessary configuration for database.

4. Configuration for working with database
How a web application can be completed without usage of database. Different web application uses different database. Some uses Mysql, other SQLite, SQL server and Oracle. One of the nice thing about Zend Framework is that it support multiple database servers.
Here we are going to making configuration for Mysql server.
For database configuration, first create config.ini in html_root/application/ and write the following code in it.
[general]
db.adapter = PDO_MYSQL
db.params.host = localhost
db.params.username = root
db.params.password =
db.params.dbname = zend


Now in your bootstrap file make the following changes.
<?php
define('ROOT_DIR', dirname(__FILE__));

set_include_path('.'
. PATH_SEPARATOR . ROOT_DIR . '/library'
. PATH_SEPARATOR . ROOT_DIR . '/application/models'
. PATH_SEPARATOR . ROOT_DIR . '/application/forms'
. PATH_SEPARATOR . get_include_path()
);

require_once "Zend/Loader.php";
Zend_Loader::registerAutoload();

$config = new Zend_Config_Ini(ROOT_DIR.'/application/config.ini', 'general');
$db = Zend_Db::factory($config->db);
Zend_Db_Table::setDefaultAdapter($db);


$frontController = Zend_Controller_Front::getInstance();
$frontController->throwExceptions(true);
$frontController->setControllerDirectory(ROOT_DIR.'/application/controllers');
$frontController->dispatch();
?>


First we load our config.ini file that contain our database configuration. Next we call static factory method of Zend_Db giving it $config->db for database configuration and at the end we set default adapter for our database tables.
That’s it. We have now done all necessary configurations.
Next step is to store and retrieve data from database
5. Working with database data
Now we are going to make an application that will save, display and edit data in the database.
First execute the following query.
CREATE TABLE `users` (
`id` INT( 11 ) NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`firstName` VARCHAR( 50 ) NOT NULL ,
`lastName` VARCHAR( 50 ) NOT NULL ,
`username` VARCHAR( 50 ) NOT NULL ,
`email` VARCHAR( 100 ) NOT NULL ,
`password` VARCHAR( 50 ) NOT NULL
)


This query create a table called users in the database.
Next step is to create model against this table. In your html_root/application/models/ create Users.php and write the following code in it.
<?php
class Users extends Zend_Db_Table
{
protected $_name = "users";
}


The only thing we have done is extend our Zend_Db_Table and define name of the model. This name must be same as the database table name.
Now in your html_root/application/controllers/IndexController.php, write
<?php
class IndexController extends Zend_Controller_Action
{
public function indexAction()
{
$users = new Users();
$data = $users->fetchAll($users->select());
$this->view->data = $data;
}
}


After making changes in IndexController.php, next step is to make changes in html_root/application/views/scripts/index/index.phtml. write the following code in it.
<h4>List of users</h4>
<h5><a href="add" style="color:blue">Add new user</a></h5>
<table border="1">
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Username</th>
<th>Email</th>
<th>Action</th>
</tr>
</thead>
<tboody>
<?php foreach ($this->data as $d) {?>
<tr>
<td><?=$d['firstName']?></td>
<td><?=$d['lastName']?></td>
<td><?=$d['username']?></td>
<td><?=$d['email']?></td>
<td><a href="edit/id/<?=$d['id']?>" style="color:blue">Edit</a></td>
</tr>
<?php }?>
</tbody>
</table>


This will show the list of users.
Next we are going to create a form for adding data to users table.
In your html_root/application/forms/ create CustomForm.php and write the following code in it.
<?php
class CustomForm extends Zend_Form
{
public function init()
{
$this->setMethod('post');
//$this->setAction('add');

$id = $this->createElement('hidden','id');
$firstname = $this->createElement('text','firstname');
$firstname->setLabel('First Name:')
->setAttrib('size',50);
$lastname = $this->createElement('text','lastname');
$lastname->setLabel('Last Name:')
->setAttrib('size',50);
$username = $this->createElement('text','username');
$username->setLabel('Username:')
->setAttrib('size',50);
$email = $this->createElement('text','email');
$email->setLabel('Email:')
->setAttrib('size',50);
$password = $this->createElement('password','password');
$password->setLabel('Password:')
->setAttrib('size',50);

$password2 = $this->createElement('password','password2');
$password2->setLabel('Confirm Password::')
->setAttrib('size',50);
$register = $this->createElement('submit','register');
$register->setLabel("Register")
->setIgnore(true);

$this->addElements(array(
$firstname,
$lastname,
$username,
$email,
$password,
$password2,
$id,
$register
));
}
}


We have successfully created our form, next we are going to write necessary code in our IndexController.php
Write the following code in it
<?php
class IndexController extends Zend_Controller_Action
{
public function indexAction()
{
$users = new Users();
$data = $users->fetchAll($users->select());
$this->view->data = $data->toArray();
}

public function addAction()
{
$users = new Users();
$form = new CustomForm();
$this->view->form = $form;


if ($this->getRequest()->isPost()) {
$formData = $this->_request->getPost();
if ($form->isValid($formData)) {
if ($formData['password'] != $formData['password2']) {
$this->view->errorMsg = "Password and Confirm Password must match.";
$this->render('add');
return;
}
unset($formData['password2']);
unset($formData['register']);
$users->insert($formData);
}
}
}
}


The code in addAction create form, get posted data, check whether password and confirm password match and then insert data in the users table.

Now create add.phtml in html_root/application/views/scripts/index/ and write the following code in it.
<h3>Add User</h3>
<?php
if ($this->errorMsg) {
echo $this->errorMsg;
}
?>
<?php
// for displaying form
echo $this->form;
?>


Next we are going to create action for editing the users table data. Write the following code in the your IndexController.php
<?php
class IndexController extends Zend_Controller_Action
{
public function indexAction()
{
//$this->_helper->layout->disableLayout();
$users = new Users();
$data = $users->fetchAll($users->select());
$this->view->data = $data->toArray();
}

public function addAction()
{
$users = new Users();
$form = new CustomForm();
$this->view->form = $form;


if ($this->getRequest()->isPost()) {
$formData = $this->_request->getPost();
if ($form->isValid($formData)) {
if ($formData['password'] != $formData['password2']) {
$this->view->errorMsg = "Password and Confirm Password must match.";
$this->render('add');
return;
}
unset($formData['password2']);
unset($formData['register']);
$users->insert($formData);
}
}
}

public function editAction()
{
$users = new Users();
$form = new CustomForm();

$id = $this->_getParam("id",1);
$select = $users->select()
->where("id = ?",$id);
$data = $users->fetchRow($select);
$form->populate($data->toArray());


if ($this->getRequest()->isPost()) {
$formData = $this->_request->getPost();
if ($form->isValid($formData)) {
if ($formData['password'] != $formData['password2']) {
$this->view->errorMsg = "Password and Confirm Password must match.";
$this->render('add');
return;
}
unset($formData['password2']);
unset($formData['register']);
$users->update($formData,"id = $id");
}
}
$this->view->form = $form;
}

}


The above code fetch the data and populate the form, and then get the posted data and update the users table based on the id.

Create edit.phtml in html_root/application/views/scripts/index/ and write the following code in it.
<h3>Edit User</h3>
<?php
if ($this->errorMsg) {
echo $this->errorMsg;
}
?>
<?php
// for displaying form
echo $this->form;
?>


6. Create authentication application
As now we have created database application successfully, we are going to create authentication application.

(i). Creating login form
in your html_root/application/forms/ create LoginForm.php and write the following code in it.
<?php
class LoginForm extends Zend_Form
{
public function init()
{
$this->setName('login');
$this->setMethod('post');

$userName = $this->createElement('text', 'userName',array('label' => 'username' ));
$userName->addFilters(array('StringTrim'))
->addValidator('StringLength', false,array(5,50))
->setValue('')
->setRequired(true);

$password = $this->createElement('password','password',array('label' => 'password' ));
$password ->setRequired(true)
->addValidator('StringLength', false,array(5,50))
->setValue('');

$submit = $this->createElement('submit','save',array('label' => 'login'));
$submit->setRequired(false)
->setIgnore(true);

$this->addElements(array(
$userName,
$password,
$submit,
));
}
}


In the above form we add two elements, a text box for entering username and password field for entering user password. We also add submit input box.
(ii).Next we create AuthController.php in html_root/application/controllers/ and write the following code
<?php
class AuthController extends Zend_Controller_Action
{
public function loginAction()
{
$form = new LoginForm();
if ($this->getRequest()->isPost()) {
$values = $this->_request->getPost();
if ($form->isValid($values)) {
$users = new Users();
$auth = Zend_Auth::getInstance();
$authAdapter = new Zend_Auth_Adapter_DbTable($users->getAdapter());
$authAdapter->setTableName('users');
$authAdapter->setIdentityColumn('userName');
$authAdapter->setCredentialColumn('password');

$authAdapter->setIdentity($values['userName']);
$authAdapter->setCredential($values['password']);
try {
$result = $auth->authenticate($authAdapter);
} catch (Zend_Exception $e) {
$this->view->errorMsg = $e->getMessage() . "<br>";
}

if ($result->isValid()) {
$data = $authAdapter->getResultRowObject();
$sess = new Zend_Session_Namespace();
$sess->username = $data->useruame;
echo "Welcome <b>".$data->username.'</b><br>You successfully logged in.';
} else {
echo "invalid username or password try again.";
}
}
}
$this->view->form = $form;
}
}


In login action we initialize our login form, check for submitted data. We then instantiate our user model as well as Zend_Auth. Setting table name, identity column and credential column. We then pass values submitted through form to identity column and credential column. After setting these things we call authenticate() method.
This authenticate column perform authentication for us.
We then call isValid() method that return true or false based on the authentication.
If isValid() return true, we get the user specific information by calling getResultRowObject() method.
We then create session and assign username to it.
The last thing we will need to create is view template against login action. In html_root/application/views/scripts create auth/login.phtml and write the following code in it.
<fieldset style="width:400px">
<legend>Login</legend>
<?php
echo $this->form;
?>
<div><a href="../index/add" style="color:blue">New user Sign up here</a></div>
</fieldset>


That’s it. Our login authentication. For registration, you can use add form I have previously created.
I think its enough for now. I will discuss other things later on hopefully.

32 comments:

  1. sorry sir by mistake save the file createuser.php

    instead of user,php


    thank u sir

    ReplyDelete
  2. Hey, you have a great blog here! I'm definitely going to bookmark you! Increasing your web traffic and page views Add, add your website in www.directory.itsolusenz.com/ site, it's pretty awesome too!

    ReplyDelete
  3. Hello! Thanks for this tutorial. But it seems like it does not work for me. Browser shows

    Fatal error : class 'Users' not found in .../IndexController on line 7

    I cheched the Users.php file and it seems ok...

    ReplyDelete
    Replies
    1. Hi raf.. same problem here..
      have any solution?
      -Thanks

      Delete
  4. I try your tutorial with zend framework 1.8.3
    But it is giving me notice like this :
    Zend_Loader::registerloadAutoload is deprecated as of 1.8.0

    ReplyDelete
  5. Hi Kalepsh, you can look at this adress for your issue: http://akrabat.com/2009/04/30/zend_loaders-autoloader_depreciated-in-zend-framawork-18/

    ReplyDelete
  6. sir, how to insert the data in table using zend

    ReplyDelete
  7. sir,add new user and edit options are not working how can solve this problem plz help

    ReplyDelete
  8. Hello Mr. Abbas,

    I have the same problem.
    Alache Log tells:
    [Fri Jul 17 15:17:58 2009] [error] [client 127.0.0.1] File does not exist: C:/xampp/htdocs/ZendTestWork/add, referer: http://localhost/ZendTestWork/

    As ApacheServer - using Xampp 1.7.0.
    ZendFramework 1.8.3.
    Only changes to the org.code were the Loader changes shown by akrabat.

    ReplyDelete
  9. Hello Mr. Abbas,
    @haney

    anonymous again... (sorry no account in one of your posibilities)

    I found out part of the problem about the add/edit not working.
    For the ZF you NEED mod_rewrite active in your apache. Also, follow the ZF-Doc abput the rewriterules inside the documents: http://framework.zend.com/manual/en/zend.controller.html
    After I created my .htaccess with the rewriterules as in the documents from Zend said, it worked partly.

    I now have the following errors:

    Fatal error: Uncaught exception 'Zend_Controller_Dispatcher_Exception' with message 'Invalid controller specified (add)' in ..\library\Zend\Controller\Dispatcher\Standard.php:241 Stack trace: #0 ..\library\Zend\Controller\Front.php(945): Zend_Controller_Dispatcher_Standard->dispatch(Object(Zend_Controller_Request_Http), Object(Zend_Controller_Response_Http)) #1 ..\index.php(39): Zend_Controller_Front->dispatch() #2 {main} thrown in ..\library\Zend\Controller\Dispatcher\Standard.php on line 241

    ReplyDelete
  10. Thanks for this great tutorial, but i have the same issue with raf.

    Class 'Users' not found in... IndexController on line 7.

    Don't know why i take this error or how to fix.

    ReplyDelete
  11. I am not able to get the add new user and edit new user i think there is some probolem with the path please check with this issue previusly posted by haney
    it throws /add request not found

    ReplyDelete
  12. I could not add/edit new users
    I tried changing httpd.conf but could not
    Please explain how a href="add" will take to add form.
    Being a novice to Zend framework, I could not find help anywhere.

    ReplyDelete
  13. Thank you this is exactly what I was looking for, so useful!

    I am going to try and port this tutorial for ZF 1.9 - I will report back if sucessful!

    ReplyDelete
  14. Just to add, I belive that a lot of the problems which are being faced here, e.g. setting up new actions, would be solved if ZF 1.9 is used - specifically by running zf.sh tool to setup new actions and controllers; (although clearly you should also be able to do this via normal coding)!

    ReplyDelete
  15. Yes Chris.
    you will need to make some change while using latest version, but those changes will not be hard enough.

    ReplyDelete
  16. Hi, thanks for this great tutorial it's explained alot to me!

    However I have a question still. How do you verify that the user is logged in through indexController?

    Right now, i have this script there:

    if (null === Zend_Auth::getInstance()->getIdentity()) {
    $this->_forward('newaccount');
    }

    however, newaccount can only be an action name in indexController -- i want to point it to authController/index.phtml or login.phtml

    How does the index page and all other pages make sure that the user is authenticated and if not redirect them to the login form?

    I appreciate the help!

    ReplyDelete
  17. Never mind! I figured it out :)

    $this->_redirect('auth/login');

    ReplyDelete
  18. Faheem bhai.... You are doing a very good job..... I found this tutorial very informative... I have visited many sites on net about zend tutorial but not find such a website like this..... Thanks....

    ReplyDelete
  19. I got stuck up with the following error

    Iam using Zend 1.10

    Fatal error: Class 'Users' not found in D:\xampp\htdocs\zendapps\application\controllers\IndexController.php on line 6

    ReplyDelete
  20. Hi
    I am new to zend.
    I am using zend 1.10.
    I have created actions add and edit using zf tool.
    But then also there is something with the url path.
    In view section I wrote following:

    Registration

    I have defined controller "index" and I gave action "add". But It never called. Because Path was wrong.

    Anybody has any Idea ??
    Please help ?

    ReplyDelete
  21. Hi
    I am new to zend.
    I am using zend 1.10.
    I have created actions add and edit using zf tool.
    But then also there is something with the url path.
    In view section I wrote following:


    a href="url(
    array('controller'=>'index','action'=>'login'),
    'default',
    true) ?>" style="color:blue">Add new user
    I have defined controller "index" and I gave action "add". But It never called. Because Path was wrong.

    Anybody has any Idea ??
    Please help !!

    ReplyDelete
  22. Developers having "User" not found error should write the following code for reloading the models.


    require_once "Zend/Loader/Autoloader.php";
    $autoloader = Zend_Loader_Autoloader::getInstance();
    $autoloader->setFallbackAutoloader(true);

    ReplyDelete
  23. how i can make a logoutaction, where both namespace and session will be destroyed?
    public function logoutAction()
    {
    //$session = new Zend_Session_Namespace('session');
    //unset($session->username);
    //unset($session->prova);
    //Zend_Session::namespaceUnset('session');
    Zend_Session::destroy();
    Zend_Auth::getInstance()->clearIdentity();
    $this->_helper->redirector('index'); // back to login page
    }

    this how i try to make a logout action. if i add the commentary part the logout action produce error like this: PHP Warning: session_destroy() [function.session-destroy]: Trying to destroy uninitialized session in C:\\ZendFramework-1.11.11\\library\\Zend\\Session.php on line 717

    ReplyDelete
  24. Beauty Salon Logo Design
    I don’t know what proportions of designers apply sketching in their process vs. departing right to the computer, but I sense that those designers who sketch first are becoming fewer and beyond between. I know that’s quite unreliable, so I would love to see Speak Up or Design with designers on whether they sketch before they set off to digital.

    ReplyDelete
  25. very informative article.And very well explained about different protocols.keep posting like good content posts.
    For more details please visit our website.
    Oracle Fusion Cloud HCM Online Training

    ReplyDelete