Custom Routing in Zend Framework 2

December 9th, 2012 by Cosmin Harangus 8 comments »

Routing in Zend Framework 2 is very simple to use and configure if your needs are limited to only the classes provided by the MVC component.

You can find some clear examples and a good tutorial on how to configure routes in the ZF2 documentation. There you will also find more information about some of the move advanced topics discussed in this article.

However, if you are looking to create a custom route that does a bit more than just matching a particular URL, you may find yourself in a pickle.

Recently I wanted to create a custom route that can tell me if the provided URL can be found in a database table.

I’ve used my old friend Google and searched the web and some of my favorite blogs for answers to how I could get an instance of the ServiceLocator in a custom route but without any success.

I did find a few links where the developers solution was to add a static variable on the route class and set the ServiceLocator class on bootstrap, but that was in my opinion a bad practice, especially since ZF2 is all about Dependency Injection and best practices.

OK… so how do you do it?

Before we answer that let’s talk a little about how we can create a custom route class and how we can use it in your application.

Create Custom Routes

In order to create a custom http route we have to either implement \Zend\Mvc\Router\Http\RouteInterface or extend one of of the \Zend\Mvc\Router\Http\* classes, depending on what we need to do.

For example to create the route described above we will be implementing RouteInterface and add our custom functionality:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<?php
namespace Application\Router;
use Zend\Mvc\Router\Http\RouteInterface;
use Zend\Stdlib\RequestInterface as Request;
use Zend\Stdlib\ArrayUtils;
use Zend\Mvc\Router\Exception\InvalidArgumentException;

class Page implements RouteInterface
{

    protected $defaults = array();

    /**
     * Create a new page route.
     */

    public function __construct(array $defaults = array())
    {
        $this->defaults = $defaults;
    }

    /**
     * Create a new route with given options.
     */

    public static function factory($options = array())
    {
        if ($options instanceof \Traversable) {
            $options = ArrayUtils::iteratorToArray($options);
        } elseif (!is_array($options)) {
            throw new InvalidArgumentException(__METHOD__ . ' expects an array or Traversable set of options');
        }

        if (!isset($options['defaults'])) {
            $options['defaults'] = array();
        }

        return new static($options['defaults']);
    }


    /**
     * Match a given request.
     */

    public function match(Request $request, $pathOffset = null)
    {
        //@todo test the Request object and return a \Zend\Mvc\Router\RouteMatch instance
        return null;
    }

    /**
     * Assemble the route.
     */

    public function assemble(array $params = array(), array $options = array())
    {
        //@todo assemple the route and return the URL as string
        return '';
    }

    /**
     * Get a list of parameters used while assembling.
     */

    public function getAssembledParams()
    {
        return array();
    }

}

 

The above class can be used as starting point for any custom route you need, though it doesn’t really do anything yet. We will need to modify the match function and return an instance of \Zend\Mvc\Router\RouteMatch class if want the router to pick this route for the current request.

Apart from the current Request object the match function may also receive a path offset variable if the route is a child of a Part route. The path offset will let you know what is the index in the URL from which you should start processing.

Accessing the service locator

The Router in ZF2 uses an implementation of the \Zend\ServiceManager\AbstractPluginManager named RoutePluginManager in order to load each route class. This is basically a service locator that contains an invokable entry for each route class. The class also implements the ServiceLocatorAwareInterface, but it doesn’t have a service locator instance set by default.

First thing we need to do to access the service locator in a route is to implement the \Zend\ServiceManager\ServiceLocatorAwareInterface in order to get an instance of the RoutePluginManager. This way when the route is loaded the setServiceLocator method will be called and the service locator (the RoutePluginManager in our case) that loads the class will be set in the route.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?php
namespace Application\Router;
use Zend\Mvc\Router\Http\RouteInterface;
// ...
use Zend\ServiceManager\ServiceLocatorAwareInterface;

class Page implements RouteInterface, ServiceLocatorAwareInterface
{

    protected $routePluginManager = null;

    /**
     * Set service locator
     *
     * @param ServiceLocatorInterface $routePluginManager
     */

    public function setServiceLocator(ServiceLocatorInterface $routePluginManager)
    {
        $this->routePluginManager = $routePluginManager;
    }

    /**
     * Get service locator
     *
     * @return ServiceLocatorInterface
     */

    public function getServiceLocator()
    {
        return $this->routePluginManager;
    }

}

Please note that the $routePluginManager is an instance of the ServiceLocatorInterface, however it can only return instances of the defined route classes.

In order to make it have access to the application service locator instance we will have to extend the RouterFactory and set the instance of the service locator for the RoutePluginManager manually.

Create the following class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
namespace Application\Service;
use Zend\Mvc\Service\RouterFactory as DefaultRouterFactory;
use Zend\ServiceManager\ServiceLocatorInterface;
/**
 *
 * @author Cosmin Harangus <cosmin@around25.com>
 */

class RouterFactory extends DefaultRouterFactory
{
    public function createService(ServiceLocatorInterface $serviceLocator, $cName = null, $rName = null)
    {
        $router = parent::createService($serviceLocator, $cName, $rName);

        //get instance of the RoutePluginManager
        $routePluginManager = $router->getRoutePluginManager();
        //set the ServiceLocator for the RoutePluginManager so we can use it in the route
        $routePluginManager->setServiceLocator( $serviceLocator );

        return $router;
    }
}

The above class extends the default router factory and sets the service locator on the RoutePluginManager instance used by the router.
Now all we have to do is make sure that the router uses this factory instead of the default one. To do this we will alter our module.config.php file and add the following:

1
2
3
4
5
6
7
8
9
10
11
<?php
return array(

    'service_manager' => array(
        'factories' => array(
            'Router'        => 'Application\Service\RouterFactory',
        ),
    ),

    // ...
);

Now we should be able to have access to the service locator in the custom route like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
    // ...

    public function match(Request $request, $pathOffset = null)
    {
        // get the service locator
        $serviceLocator = $this->routePluginManager->getServiceLocator();

        //@todo do something with the request and return an RouteMatch instance

        return false;
    }

    // ...

I hope this article helps you avoid some headaches in the future.
I am not sure if there is any better way of doing this in ZF2. If there is please send us a comment or better yet integrate it in the official ZF2 documentation.

Share

Zend Javascript and CSS caching

October 25th, 2012 by piter No comments »

How often, when developing/testing a website, did you ask your customer to clear his browser cache? Ok, your client can clear his cache… But what about a live application? How to tell users to clear their cache? So, if you’re using headLink and headScript view helpers I think this will help. The main idea is [...]

Share

Database module for Zend Framework 2

August 23rd, 2012 by Cosmin Harangus 3 comments »

In a previous post I talked about how we can improve the database layer from Zend Framework by extending the Zend_Db_Table_Abstract class and adding various magic functions.

Today I want to talk about how we can so something similar in Zend Framework 2.

The brand new ZF2 database layer allows you to write complex queries, select your own entity classes, quote variables for a specific platform and much much more.

The only downside is that in order to fetch something using the current implementation you need to write something similar to this:

1
2
3
$artistTable = new TableGateway('artist', $adapter);
$rowset = $artistTable->select(array('id' => 2));
$artistRow = $rowset->current();

Although you can certainly get used to writing your own methods and do this in each of them you would loose a lot of time creating the full implementation of your DBAL.

If you’ve used the proposed Ze_Model class or want to try a simpler approach you could check out the ZeDb Module.

Like Ze_Model the ZeDb module allows you to call a variety of magic functions for your Model classes, each executing specific queries on the database and returning your custom Entity classes.

ZeDb allows you to focus on the business side of your application by saving you time when working with the database. It provides you with a powerful set of magic functions that allow you to execute queries easily without the need to write sql queries.

ZeDb works out of the box with entity classes, an even though it provides a base Entity class, you also have the posibility to write your own custom Entities by implementing ZeDb\EntityInterface.

ZeDb works in a similar way with Doctrine, in the fact that it uses a Manager to retrieve model instances. These model instances contain magic functions for accessing the database in order to retrieve Entity objects, remove data or count records.

The Model class defined the following functions:

  • save(EntityInterface $entity): saves an entity record into the database and returns it’s id
  • persist($entities): stores one or more entities locally before saving them into the database
  • flush(): saves all persisted objects into the database
  • get($id): returns an Entity class based on the id of the record

Apart from the above function the model also defines a set of magic functions that can handle combination of table columns, order by or limit.

The pattern used by these methods is as follows:

1. Function name prefix, which can be one of the following:

  • removeBy: removes one or more records from the table
  • getAll: returns all the records from the table
  • getBy: returns a single Entity class based on the values in the specified fields
  • getAllBy: same as getBy only it returns more that one record, if found
  • getLike: returns a single Entity class based on the values in the specified fields using LIKE instead of =
  • getAllLike: same as getLike only it returns more that one record, if found
  • getByColumns: allows you to specify an array of keys and values that can be passed over to the where method of the Select instance before returning a single entity.
  • getAllByColumns: same as getByColumns only it returns more that one record

2. A list of field names in camelCase separated by the “And” word. Ex: $model->getByUsernameAndStatus('paul', 'active');. This is only needed for the following functions: removeBy, getBy, getAllBy, getLike, getAllLike.
3. The “OrderBy” text followed by a list of field names in camelCase separated by the “And” word where each field name can be suffixed by either “Asc” or “Desc”. This section is optional for all functions.
4. The “Limit” text followed by a number representing the maximum number of records that should be returned. If limit is defined then you may also specify an offest from which to start by adding the text “From” followed by the offset number.

The Entity class defined two methods that can help you work with the database faster:

  • save(): saves the current entity instance in the database
  • delete(): removes the entity from the database

Currently the fields for each Entity instance are kept in a data array for fast access and conversion between object and array, but you can always implement the EntityInterface and create your custom Entity classes.

A number of helper functions and optimizations are also scheduled to be added in future versions of the module.

In the meanwhile why not give it a try and see how productive you can be using this approach.

Feedback is always welcome to improve the quality of the next releases.

For any other details on how to install and use the module refer to the module documentation located on github.

Share

Composer – The PHP Dependency Manager

May 22nd, 2012 by Cosmin Harangus No comments »

Do you remember that when you start a new PHP project you make a list of all the libraries, frameworks or technologies that you want to use?

Then you go and search for their homepages, download the code into your project, setup autoloading for each one and only then start actual coding for your own project. Everything up until now was managing your project dependencies.

Following the examples set by node’s npm and ruby’s bundler there is now a dependency manager for PHP called Composer that lets you define your dependencies in a composer.json file and downloads them for you. It also handles autoloading for all dependencies and makes it easy to update your dependencies in the future.

To use Composer you just need to create a composer.json file in the root of your project in which you define the what packages you need to have in order for your project to work.

Here’s an example of such a file:

1
2
3
4
5
6
{
"require": {
"zendframework/zendframework": "2.0.x-dev",
"ZendExperts/ZeTwig": "v1.0.1"
}
}

 

Next you need to download Composer and install it. This is easy since it’s just one *.phar file that you can either download directly from http://getcomposer.org/download/ or type the following in your console:

1
$ curl -s http://getcomposer.org/installer | php

or use the –install-dir option if you want to install it in a specific directory

1
$ curl -s http://getcomposer.org/installer | php -- --install-dir=bin

 

The installer will check for a few PHP settings then download composer.phar in your working directory (or in the specified directory if the –install-dir option is used). If you put this file in your PATH you will be able to use it globally not just for your current project.

In order to install all dependencies you will need to run the following command:

1
$ php composer.phar install

This will check the composer.json file from the working directory and download the required libraries into the vendor/ folder.

It will also create a composer.lock file that will contain the list of installed packages and their associated versions and vendor/autoload.php file that can be used to autoload all the dependencies in your project.

Just include vendor/autoload.php in your index.php file and you’re done.

You should now be able to use any class from your installed packages.

One other advantage in using composer is that most libraries are already using composer and its package archive (see http://packagist.org). This includes Symfony, Twig, Zend Framework 2, Monolog, Doctrine, Propel, etc.

For a detailed list of that you can do with composer and how you can add your own repositories to packagist please visit: http://getcomposer.org and http://packagist.org .

Share

Twig in Zend Framework 2

April 8th, 2012 by Cosmin Harangus 5 comments »

The Zend Framework 2 library came a long way since it’s beta 1 version. In beta 3 you now have a full MVC stack, View Models, Database layer, a Mail component, a Cache component, Dependency Injection and an Event Manager.

In an attempt to extend the existing functionality of zend framework 2 and provide a powerful view layer for my applications I created a module which integrates the full functionality of the Twig library along with a new extension specific for zend.

The module allows you to use *.twig templates instead of the usual php *.phtml templates that are shipped by default in zend framework. This allows you to extend templates files, use concise syntax and extend the syntax with new tags or functionality.

It also supports aliases for your template names, rendering a particular action from within the template files (follows the save naming conventions as Symfony) and triggering events on an object with different parameters.

You can define an array for aliases within the configuration file for your modules and use those aliases throughout your code, instead of a specific file name. This way you can easily change the main layout of your pages from the configuration file and allow other modules to change them as well (this allows your code to be extensible and allows templates to have their own structure). This functionality comes directly with the new View Models from the framework.

The latest version also contains two new constructs:

1. A tag for rendering a controller action, which follows the Symfony naming conventions or the controller alias and can be used as :

{% render “Core:Index:index” with {‘param1′:1} %}

2. A tag for triggering an event on the renderer that is similar to the above syntax:

{% trigger “myRendererEvent” on myObject with {‘param1′:1} %}

Both the target object and parameters are optional. The result of each listener is converted to string and rendered intead of the definition.

For more Zend Framework 2 module please visit: https://github.com/ZendExperts

Update: This module was merged into ZfcTwig and is currently available at: https://github.com/ZF-Commons/ZfcTwig . This will ensure better maintenance for the project and continuous improvements.

Share

Zend Framework 2 Overview

December 15th, 2011 by Paul No comments »

Check out the upcoming features in Zend Framework 2 and seriously think about making the change after the release:

Share

jQuery Window Resize

July 13th, 2011 by Cosmin Harangus 1 comment »

For the past two months I’ve been working on a very complex web application using jQuery and Zend Framework with a strong client side orientation. Recently I noticed a very strange bug related to the resize event of the browser window. In my app in am basically displaying events on a calendar similar to google [...]

Share

How to install Zend Server CE & PEAR & PHPUnit

June 21st, 2011 by Paul 29 comments »

The steps below should allow you to install Zend Server CE & PEAR & PHPUnit so you have a stable development environment on your local PC. This installation has been tested on an Intel Core 2 Duo machine running Win 7 and an AMD Athlon X2 Dual machine running Vista. We installed everything in the [...]

Share

PNG Fix with Alpha Transparency for IE

March 3rd, 2011 by Cosmin Harangus No comments »

I’ve found this great method to properly display png files on IE which also works when using jQuery alpha effects on them.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
jQuery.fn.extend({
      fixPNG: function(sizingMethod, forceBG) {
              if (!(jQuery.browser.msie)) return this;
              var emptyimg = "images/dot_alpha.gif"; //Path to an empty 1x1px GIF goes here
              sizingMethod = sizingMethod || "scale"; //sizingMethod, defaults to scale (matches image dimensions)
              this.each(function() {
                      var isImg = (forceBG) ? false : jQuery.nodeName(this, "img"),
                              imgname = (isImg) ? this.src : this.currentStyle.backgroundImage,
                              src = (isImg) ? imgname : imgname.substring(5,imgname.length-2);
                      this.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + src + "', sizingMethod='" + sizingMethod + "')";
                      if (isImg) this.src = emptyimg;
                      else this.style.backgroundImage = "url(" + emptyimg + ")";
              });
              return this;
      }
  });
 jQuery('#slideshow img').fixPNG();

Don’t forget to specify the path to an empty gif image above in order for this script to work.

Share

Zend Model Improvements

March 22nd, 2010 by Cosmin Harangus 2 comments »

The class I will describe today is an extension of the Zend_Db_Table class that exposes a list of magic functions with the most commonly used operations over the database.

It uses the __call function to add a functions to retrieve records from the database by simply calling a function like: $this->getAllByProductIdAndStatus(1, 'Active'). When calling this function the following two field names are subtracted from the function name: “product_id” and “status”. Each of these fields are set to the parameters passed to the function. After that a query is built using these fields to restrict the result to only those records that have product_id set to 1 and status set to Active. [...]

Share