Zend Framework - Working Example



In this chapter, we will learn how to create a complete MVC based Employee Application in Zend Framework. Follow the steps given below.

Step 1: Module.php

First, we should create an Employee module inside the – myapp/module/Employee/src/ directory and then implement the ConfigProviderInterface interface.

The complete code for the Module class is as follows −

<?php  
namespace Employee;  
use Zend\ModuleManager\Feature\ConfigProviderInterface;  
class Module implements ConfigProviderInterface { 
   public function getConfig() {    
      return include __DIR__ . '/../config/module.config.php'; 
   }    
}

Step 2: composer.json

Configure the Tutorial module in composer.json under the autoload section by using the following code.

"autoload": { 
   "psr-4": { 
      "Application\\": "module/Application/src/", 
      "Tutorial\\": "module/Tutorial/src/", 
      "Employee\\": "module/Employee/src/" 
   } 
}

Now, update the application using a composer update command.

composer update

The Composer command will do the necessary changes to the application and show the logs as shown in the command prompt below.

Loading composer repositories with package information 
Updating dependencies (including require-dev) 
   - Removing zendframework/zend-component-installer (0.3.0) 
   - Installing zendframework/zend-component-installer (0.3.1) 
   Downloading: 100%           
    
   - Removing zendframework/zend-stdlib (3.0.1) 
   - Installing zendframework/zend-stdlib (3.1.0) 
   Loading from cache  
    
   - Removing zendframework/zend-eventmanager (3.0.1) 
   - Installing zendframework/zend-eventmanager (3.1.0) 
   Downloading: 100%           
    
   - Removing zendframework/zend-view (2.8.0) 
   - Installing zendframework/zend-view (2.8.1) 
   Loading from cache  
    
   - Removing zendframework/zend-servicemanager (3.1.0) 
   - Installing zendframework/zend-servicemanager (3.2.0) 
   Downloading: 100%           
    
   - Removing zendframework/zend-escaper (2.5.1) 
   - Installing zendframework/zend-escaper (2.5.2) 
   Loading from cache  
   
   - Removing zendframework/zend-http (2.5.4) 
   - Installing zendframework/zend-http (2.5.5) 
   Loading from cache  
    
   - Removing zendframework/zend-mvc (3.0.1)
   - Installing zendframework/zend-mvc (3.0.4)  
   Downloading: 100%           
   
   - Removing phpunit/phpunit (5.7.4) 
   - Installing phpunit/phpunit (5.7.5) 
   Downloading: 100%           
  
Writing lock file 
Generating autoload files     

Step 3: module.config.php for the Employee Module

Create the module configuration file, “module.config.php” under the myapp/module/Employee/config with the following code.

<?php  
namespace Employee;  
use Zend\ServiceManager\Factory\InvokableFactory; 
use Zend\Router\Http\Segment;  
return [ 
   'controllers' => [ 
      'factories' => [ 
         Controller\EmployeeController::class => InvokableFactory::class, 
      ], 
   ], 
   'view_manager' => [ 
      'template_path_stack' => ['employee' => __DIR__ . '/../view',], 
   ], 
];

Now, configure the Employee module in the application level configuration file – myapp/config/modules.config.php.

return ['Zend\Router', 'Zend\Validator', 'Application', 'Tutorial', 'Employee'];

Step 4: EmployeeController

Create a new PHP class, EmployeeController by extending the AbstractActionController and place it at the myapp/module/Employee/src/Controller directory.

The complete code listing is as follows −

<?php  
namespace Employee\Controller;  
use Zend\Mvc\Controller\AbstractActionController; 
use Zend\View\Model\ViewModel;  
class EmployeeController extends AbstractActionController { 
   public function indexAction() { 
      return new ViewModel(); 
   } 
}

Step 5: Router Configuration

Let us add a segment route in our Employee module. Update the employee module configuration file, module.config.php available at myapp/module/Employee/config.

<?php  
namespace Employee;
use Zend\ServiceManager\Factory\InvokableFactory; 
use Zend\Router\Http\Segment;  
return [ 
   'controllers' => [ 
      'factories' => [ 
         Controller\EmployeeController::class => InvokableFactory::class, 
      ], 
   ], 
   'router' => [ 
      'routes' => [ 
         'employee' => [ 
            'type' => Segment::class,
            'options' => [ 
               'route' => '/employee[/:action[/:id]]',
               'constraints' => [
                  'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
                  'id' => '[0-9]+', 
               ], 
               'defaults' => [ 
                  'controller' => Controller\EmployeeController::class,
                  'action' => 'index', 
               ], 
            ], 
         ], 
      ], 
   ], 
   'view_manager' => [ 
      'template_path_stack' => [ 
         'employee' => __DIR__ . '/../view', 
      ], 
   ], 
]; 

We have successfully added the routing for our Employee module. The next step is to create a view script for the Employee application.

Step 6: Create ViewModel

Create a file called as “index.phtml” under the myapp/module/Employee/view/employee/employee directory.

Add the following changes in the file −

<div class = "row content"> 
   <h3>This is my first Zend application</h3> 
</div> 
Move to “EmployeeController.php” file and edit the following changes, 

<?php 
namespace Employee\Controller;  
use Zend\Mvc\Controller\AbstractActionController; 
use Zend\View\Model\ViewModel;  
class EmployeeController extends AbstractActionController { 
   public function indexAction() { 
      return new ViewModel();  
   } 
}

Finally, we have successfully completed the Employee module. we can access it using the following url − http://localhost:8080/employee.

Result

Application Template

In the next step, we will perform add, edit and delete data operations in the employee application. To perform these operations, we should first create a database model. It is described in the next step.

Step 7: Create a Model

Let us create a model, Employee in our module src directory. Generally, models are grouped under the Model folder (myapp/module/Employee/src/Model/Employee.php)

<?php  
namespace Employee\Model;  
class Employee { 
   public $id; 
   public $emp_name; 
   public $emp_job; 
}

Step 8: MySQL Table

Create a database named as tutorials in the local MYSQL server using the following command −

create database tutorials;

Let us create a table named as employee in the database using following SQL command −

use tutorials;  
CREATE TABLE employee ( 
   id int(11) NOT NULL auto_increment, 
   emp_name varchar(100) NOT NULL, 
   emp_job varchar(100) NOT NULL, 
   PRIMARY KEY (id) 
);

Insert data into the employee table using the following query −

INSERT INTO employee (emp_name, emp_job) VALUES ('Adam',  'Tutor'); 
INSERT INTO employee (emp_name, emp_job) VALUES ('Bruce',  'Programmer'); 
INSERT INTO employee (emp_name, emp_job) VALUES ('David',  'Designer'); 

Step 9: Update the Database Configuration

Update the Global Configuration file, myapp/config/autoload/global.php with the necessary database drive information.

return [
   'db' => [
      'driver' => 'Pdo',
      'dsn' => 'mysql:dbname = tutorials;host=localhost',
      'driver_options' => [PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''],
   ],
];

Now, Update the database credentials in the local configuration file – myapp/config/autoload/local.php. In this way, we can separate local and live database connection credentials.

<?php 
return array( 
   'db' => array('username' => '<user_name>', 'password' => '<password>',), 
); 

Step 10: Implement exchangeArray

Implement exchangeArray function in Employee model.

<?php 
namespace Employee\Model; 
class Employee { 
   public $id; 
   public $emp_name; 
   public $emp_job;  
   public function exchangeArray($data) { 
      $this->id = (!empty($data['id'])) ? $data['id'] : null; 
      $this->emp_name = (!empty($data['emp_name'])) ? $data['emp_name'] : null; 
      $this->emp_job = (!empty($data['emp_job'])) ? $data['emp_job'] : null; 
   } 
}

Step 11: Use TableGateway to fetch the Employee Data

Create the class, EmployeeTable in the Model folder itself. It is defined in the following code block.

<?php  
namespace Employee\Model;  
use Zend\Db\TableGateway\TableGatewayInterface;  
class EmployeeTable { 
   protected $tableGateway; 
   public function __construct(TableGatewayInterface $tableGateway) { 
      $this->tableGateway = $tableGateway; 
   }
   public function fetchAll() { 
      $resultSet = $this->tableGateway->select();  
      return $resultSet; 
   } 
}    

Step 12: Configure EmployeeTable Class

Update employee service in Module.php using getServiceConfig() method

<?php
namespace Employee;
use Zend\Db\Adapter\AdapterInterface;
use Zend\Db\ResultSet\ResultSet;
use Zend\Db\TableGateway\TableGateway;
use Zend\ModuleManager\Feature\ConfigProviderInterface;

class Module implements ConfigProviderInterface {
   public function getConfig() {
      return include __DIR__ . '/../config/module.config.php';
   }
   public function getServiceConfig() {
      return [
         'factories' => [
            Model\EmployeeTable::class => function (    $container) {
               $tableGateway = $container>get( Model\EmployeeTableGateway::class);
               $table = new Model\EmployeeTable($tableGateway);
               return $table;
            },
            Model\EmployeeTableGateway::class => function ($container) {
               $dbAdapter = $container->get(AdapterInterface::class);
               $resultSetPrototype = new ResultSet();
               $resultSetPrototype->setArrayObjectPrototype(new Model\Employee());
               return new TableGateway('employee', $dbAdapter, null, $resultSetPrototype);
            },
         ],
      ];
   }
}

Step 13: Add Employee Service in Controller

Update the controller section of the Employee Module Configuration in − myapp/module/config/module.config.php as shown below.

'controllers' => [
   'factories' => [
      Controller\EmployeeController::class => function($container) {
         return new Controller\EmployeeController(
            $container->get(Model\EmployeeTable::class)
         ); 
      }, 
   ], 
]

Step 14: Add Constructor for EmployeeController

Add the constructor with EmployeeTable as the argument and edit the following changes.

<?php  
namespace Employee\Controller; 
use Zend\Mvc\Controller\AbstractActionController; 
use Zend\View\Model\ViewModel;
use Employee\Model\Employee; 
use Employee\Model\EmployeeTable;  

class EmployeeController extends AbstractActionController { 
   private $table;  
   public function __construct(EmployeeTable $table) { 
      $this->table = $table; 
   }  
   public function indexAction() { 
      $view = new ViewModel([ 
         'data' => $this->table->fetchAll(), 
      ]);  
      return $view; 
   } 
} 

Step 15: Display Employee Information in the view script “index.phtml”

Move to the file − index.phtml and make the following changes −

<?php 
$title = 'Employee application'; 
$this->headTitle($title); 
?>  

<table class="table"> 
   <tr> 
      <th>Employee Name</th> 
      <th>Employee Job</th> 
      <th>Edit/Delete operations</th>
   </tr> 
   <?php foreach ($data as $empdata) : ?> 
   <tr>  
      <td><?php echo $this->escapeHtml($empdata->emp_name);?></td> 
      <td><?php echo $this->escapeHtml($empdata->emp_job);?></td> 
      <td> 
         <a href="<?php echo $this->url('employee', 
            array('action'=>'edit', 'id' =>$empdata->id));?>">Edit</a> 
         <a href="<?php echo $this->url('employee', 
            array('action'=>'delete', 'id' => $empdata->id));?>">Delete</a> 
      </td> 
   </tr> 
   <?php endforeach; ?> 
</table> 

Now we have successfully created a database model and can fetch the records within the application.

Request the application using the url − http://localhost:8080/employee.

Result

Successful Database

The next step explains about the insert, edit and delete data operations in the employee module.

Step 16: Create an Employee Form

Create a file called EmployeeForm.php in myapp/module/Employee/src/Form directory. It is described in the code block below.

<?php  
namespace Employee\Form; 
use Zend\Form\Form;  

class EmployeeForm extends Form { 
   public function __construct($name = null) { 
      /
      / we want to ignore the name passed 
      parent::__construct('employee');  
      $this->add(array( 
         'name' => 'id', 
         'type' => 'Hidden', 
      )); 
      $this->add(array( 
         'name' => 'emp_name', 
         'type' => 'Text', 
         'options' => array( 
            'label' => 'Name', 
         ), 
      )); 
      $this->add(array( 
         'name' => 'emp_job', 
         'type' => 'Text', 
         'options' => array( 
            'label' => 'Job', 
         ), 
      )); 
      $this->add(array( 
         'name' => 'submit', 
         'type' => 'Submit', 
         'attributes' => array(
            'value' => 'Go', 
            'id' => 'submitbutton', 
         ), 
      )); 
   } 
}                    

Step 17: Update the Employee Model

Update the employee model and implement the InputFilterAwareInterface. Move to the directory myapp/module/Employee/src/Employee/Model and add the following changes in the Employee.phpfile.

<?php  
namespace Employee\Model;  

// Add these import statements 
use Zend\InputFilter\InputFilter; 
use Zend\InputFilter\InputFilterAwareInterface; 
use Zend\InputFilter\InputFilterInterface;  

class Employee implements InputFilterAwareInterface { 
   public $id; 
   public $emp_name; 
   public $emp_job; 
   protected $inputFilter;                         
   public function exchangeArray($data) { 
      $this->id = (isset($data['id'])) ? $data['id'] : null;         
      $this->emp_name = (isset($data['emp_name'])) ? $data['emp_name'] : null;         
      $this->emp_job = (isset($data['emp_job']))  ? $data['emp_job'] : null; 
   }  
    
   // Add content to these methods:
   public function setInputFilter(InputFilterInterface $inputFilter) { 
      throw new \Exception("Not used"); 
   }  
   public function getInputFilter() { 
      if (!$this->inputFilter) { 
         $inputFilter = new InputFilter();  
         $inputFilter->add(array( 
            'name' => 'id', 
            'required' => true, 
            'filters' => array( 
               array('name' => 'Int'), 
            ), 
         ));  
         $inputFilter->add(array( 
            'name' => 'emp_name', 
            'required' => true, 
            'filters' => array( 
               array('name' => 'StripTags'), 
               array('name' => 'StringTrim'), 
            ), 
            'validators' => array( 
               array('name' => 'StringLength', 
                        'options' => array( 
                           'encoding' => 'UTF-8', 
                           'min' => 1, 
                           'max' => 50, 
                        ), 
                    ), 
                ), 
            ));
         $inputFilter->add(array( 
            'name' => 'emp_job', 
            'required' => true, 
            'filters' => array( 
               array('name' => 'StripTags'),  
               array('name' => 'StringTrim'), 
            ), 
            'validators' => array( 
               array('name' => 'StringLength', 
                  'options' => array( 
                     'encoding' => 'UTF-8', 
                     'min' => 1, 
                     'max' => 50, 
                  ), 
                  ), 
               ), 
         ));  
         $this->inputFilter = $inputFilter; 
      } 
      return $this->inputFilter; 
   } 
}             

Step 18: Add addAction in the Employee Controller

Add the following changes in the EmployeeController class.

<?php  
use Zend\Mvc\Controller\AbstractActionController; 
use Zend\View\Model\ViewModel; 
use Employee\Model\Employee;       
use Employee\Model\EmployeeTable;    
use Employee\Form\EmployeeForm;

public function addAction() { 
   $form = new EmployeeForm();  
   $form->get('submit')->setValue('Add');  
   $request = $this->getRequest(); 
   
   if ($request->isPost()) { 
      $employee = new Employee(); 
      $form->setInputFilter($employee->getInputFilter()); 
      $form->setData($request->getPost());  
      
      if ($form->isValid()) { 
         $employee->exchangeArray($form->getData()); 
         $this->table->saveEmployee($employee);  
         
         // Redirect to list of employees 
         return $this->redirect()->toRoute('employee'); 
      } 
   } 
   return array('form' => $form); 
} 

Step 19: Add save functionality in the EmployeeTable class

Add the following two functions in the EmployeeTable class – myapp/module/Employee/src/Model/EmployeeTable.php

public function getEmployee($id) { 
   $id  = (int) $id; 
   $rowset = $this->tableGateway->select(array('id' => $id)); 
   $row = $rowset->current();  
   if (!$row) { 
      throw new \Exception("Could not find row $id"); 
   }
   return $row; 
}  
public function saveEmployee(Employee $employee) { 
   $data = array (  
      'emp_name' => $employee->emp_name, 
      'emp_job'  => $employee->emp_job, 
   );  
   $id = (int) $employee->id; 
   if ($id == 0) { 
      $this->tableGateway->insert($data); 
   } else { 
      if ($this->getEmployee($id)) { 
         $this->tableGateway->update($data, array('id' => $id)); 
      } else { 
         throw new \Exception('Employee id does not exist'); 
      } 
   } 
}

Step 20: Create View script for AddAction method, Add.phtml

Add the following changes in the “Add.phtml” file in the − myapp/module/view/employee/employee.

<?php 
   $title = 'Add new employee'; 
   $this->headTitle($title); 
?> 
<h1><?php echo $this->escapeHtml($title); ?></h1>  

<?php 
   $form->setAttribute('action', $this->url('employee', array('action' => 'add'))); 
   $form->prepare(); 
   echo $this->form()->openTag($form); 
   echo $this->formHidden($form->get('id')); 
   echo $this->formRow($form->get('emp_name'))."<br>"; 
   echo $this->formRow($form->get('emp_job'))."<br>";   
   echo $this->formSubmit($form->get('submit')); 
   echo $this->form()->closeTag(); 
Request the application using the url, http://localhost:8080/employee/add 

Result

New Employee

Once the data has been added, it will redirect to the home page.

Redirect Home Page

Step 21: Edit Employee Records

Let us perform the editing data operations in the Employee module. Update the following changes in the Employeecontroller.php.

public function editAction() { 
   $id = (int) $this->params()->fromRoute('id', 0); 
   if (!$id) { 
      return $this->redirect()->toRoute('employee', array( 
         'action' => 'add' 
      )); 
   }  
   try { 
      $employee = $this->table->getEmployee($id); 
   } catch (\Exception $ex) { 
      return $this->redirect()->toRoute('employee', array( 
         'action' => 'index' 
      )); 
   }  
   $form = new EmployeeForm(); 
   $form->bind($employee); 
   $form->get('submit')->setAttribute('value', 'Edit');  
   $request = $this->getRequest(); 
   
   if ($request->isPost()) { 
      $form->setInputFilter($employee->getInputFilter()); 
      $form->setData($request->getPost());  
      if ($form->isValid()) { 
         $this->table->saveEmployee($employee);  
         
         // Redirect to list of employees 
         return $this->redirect()->toRoute('employee'); 
      } 
   }  
   return array('id' => $id, 'form' => $form,); 
}

Here, we look for the id, which is in the matched route and then load the employee details for the editing operation.

Step 22: Employee.php

Now add the following changes in the “Employee.php” file, which resides in the − myapp/module/Employee/src/Employee/Model/ directory.

public function getArrayCopy() { 
   return get_object_vars($this); 
}

Here, the Zend\Stdlib\Hydrator\ArraySerializable expects to find two methods in the model: getArrayCopy() and exchangeArray().

In which, the exchangeArray() is used for iteration. This function is used for binding the data from the employee table.

Now, we need to create a view script for editAction().

Step 23: Create Edit.phtml

Create a view script file in the module/Employee/view/employee/employee/edit.phtml

<?php 
   $title = 'Edit employee records'; 
   $this->headTitle($title); 
?>  
<h1><?php echo $this->escapeHtml($title); ?></h1>  

<?php 
$form = $this->form;  
$form->setAttribute('action', $this->url( 
   'employee', 
   array('action' => 'edit', 'id' => $this->id,) 
)); 
$form->prepare();  
echo $this->form()->openTag($form); 
echo $this->formHidden($form->get('id')); 
echo $this->formRow($form->get('emp_name'))."<br>"; 
echo $this->formRow($form->get('emp_job'))."<br>"; 
echo $this->formSubmit($form->get('submit')); 
echo $this->form()->closeTag();

Editing the employee details is shown in the following screenshot.

Edit Record

Once the data has been edited, it will redirect to the home page.

Edited Data

Step 24: Add deleteEmployee method

Add the deleteEmployee method in the EmployeeTable class – myapp/module/Employee/src/Model/EmployeeTable.php

public function deleteEmployee($id) { 
   $this->tableGateway->delete(['id' => (int) $id]); 
}

Step 25: Delete the Employee Records

Let us now perform the deleting data operations in the Employee module. Add the following method, deleteAction in the EmployeeController class.

public function deleteAction() { 
   $id = (int) $this->params()->fromRoute('id', 0); 
   if (!$id) { 
      return $this->redirect()->toRoute('employee'); 
   }  
   $request = $this->getRequest(); 
   if ($request->isPost()) { 
      $del = $request->getPost('del', 'No');  
      if ($del == 'Yes') { 
         $id = (int) $request->getPost('id');
         $this->table->deleteEmployee($id); 
      } 
      return $this->redirect()->toRoute('employee'); 
   }  
   return array( 
      'id' => $id, 
      'employee' => $this->table->getEmployee($id) 
   ); 
}            

Here, the deleteEmployee() method deletes the employee by his id and redirects to the employees list page (home page).

Let us now create a corresponding view scripts for the deleteAction() method.

Step 26: Create a View Script

Create a file named delete.phtml in the − myapp/module/Employee/view/employee/employee/delete.phtml and add the following code in it.

<?php 
   $title = 'Delete an employee record'; 
   $this->headTitle($title);  
?> 
<h1><?php echo $this->escapeHtml($title); ?></h1>  

'<?php echo $this->escapeHtml($employee->emp_name); ?>' by 
'<?php echo $this->escapeHtml($employee->emp_job); ?&'?  
<?php 
   $url = $this->url('employee', array('action' => 'delete', 'id' => $this->id,)); 
?>  

<form action ="<?php echo $url; ?>" method = "post">
   <div> 
      <input type = "hidden" name = "id" value = "<?php echo (int) $employee->id; ?>" /> 
      <input type = "submit" name = "del" value = "Yes" /> 
      <input type = "submit" name = "del" value = "No" /> 
   </div> 
</form>  

Now, delete any employee using the edit link in the home page and the result will be as shown in the following screenshot.

Result

Deleted Record

We have successfully completed the Employee module by implementing all necessary features.

Conclusion

In the current competitive environment, Zend framework is placed at the top spot by the developer. It provides abstractions to any program or any type of an application in the PHP language. It is a matured framework and supports modern PHP language features. It is fun, professional, evolving and keeping pace with the current technology.

Advertisements