Book Image

Zend Framework 2 Cookbook

By : Josephus Callaars, [email protected]
Book Image

Zend Framework 2 Cookbook

By: Josephus Callaars, [email protected]

Overview of this book

Zend Framework 2 is the latest creation of World Wide Web infrastructure company Zend Technologies Ltd. This new PHP framework comes with tons of features and an attractive way of creating applications. Not only is the overall usability of the technology much better, but it also makes your applications more testable, something that is often overlooked. "Zend Framework 2 Cookbook" will show you how applications are set up in Zend Framework 2 and how you can develop successfully in this massive framework. You will master features like Modules, Views, Controllers, and Authentication. The book also discusses the Event Manager, unit testing, and how to optimize your application. The book begins with a discussion about setting up Zend Framework 2, and you will also look at how the framework itself works. By the end of this book, you will be able to create entire secure applications on your own and make sure they are tested and optimized for performance as well. You will learn about sending and receiving e-mails, translation and localization of the application, and how to set up the framework on a Linux web server. You will also learn how to display data from the application to the user by using different display strategies and renderings. The creation of modules will also be discussed. Then, you will move on to look at how to authenticate users and make sure the developer knows how to pick the best method available. Unit testing, debugging, and enhancing the performance will also be covered in this book. "Zend Framework 2 Cookbook" is a perfect book for anyone who wants to start developing with Zend Framework 2.
Table of Contents (17 chapters)
Zend Framework 2 Cookbook
Credits
About the Author
About the Reviewers
www.PacktPub.com
Preface
Index

Handling routines


An important aspect (if not the most important one) is the routing within Zend Framework 2. In its most basic form routing tells the framework how the user should get from page A to page B, and what needs to be done to arrive at that destination. That is why we generally think this is the most important part to understand if you are just starting out.

How to do it…

To define a route we can simply go into one of the configuration files and add the router configuration to there.

Setting up routing

Let's look at our simple (Segment) configuration as follows (file: /module/Application/config/module.config.php):

return array(
  // Here we define our route configuration
  'routes' => array( 

    // We give this route the name 'website'
    'website' => array( 

      // The route type is of the class:
      // Zend\Mvc\Router\Http\Segment
      'type' => 'segment', 

        // Lets set the options for this route
        'options' => array( 

          /*
            The route that we want to match is /website
            where we can optionally add a controller name
            and an action name. For example:
              /website/index/index
          */ 
          'route' => '/website[/:controller[/:action]]',

          /*
            We don't want to accept everything, but this
            regex makes sure we only accept alpha-
            numeric characters and a dash and underscore.

            In our instance we want to check this for the
            action and the controller.
          */
          'constraints' => array( 
            'controller' => '[a-zA-Z][a-zA-Z0-9_-]*',
            'action' => '[a-zA-Z][a-zA-Z0-9_-]*'
          ),

          /*
           We want to make sure that if the user only 
           types /website in the URL bar it will actually
           go somewhere. We defined that here.
          */
          'defaults' => array( 
            'controller' => 'Website\Controller\Index', 
            'action' => 'index'
          ),
        ),
      ),
    ),
  ),
);

With this basic configuration we can easily define routes in our application, and in this instance we have configured a route that responds to the /website URL. When we would go to the /website URL, we would be routed to the Website\Controller\Index::indexAction by default. If we however use the route /website/another/route, we would be routed to the Website\Controller\Another::routeAction, as we have defined that the controller and action can be parsed behind that. If we omit the route path and put in /website/another, we would be redirected to the Website\Controller\Another::indexAction, as that is used by default by the framework.

The preceding example has only one really major drawback, which is, when we decide to use anonymous function in the configuration to create more dynamic routes, we would not be able to cache the route as closures are not serializeable by the cache.

However, there is another method of declaring the route, and that is in the code. The need to create the route functionality in the code could (obviously everyone has their own reasons and requirements) arise because we want to cache the configuration in a later stage (as we cannot cache anonymous function, for example) or when we want to load up a route dynamically from a database.

Let's take a look at the /module/Application/Module.php example:

<?php

// We are working in the Application module
namespace Application;

// Our main imports that we want to use
use Zend\Mvc\ModuleRouteListener;
use Zend\Mvc\MvcEvent;

// Define our module class, this is always 'Module', but 
// needs to be specifically created by the developer.
class Module
{
  public function onBootstrap(MvcEvent $e)
  {
    // First we want to get the ServiceManager
    $sm = $e->getApplication()->getServiceManager();
    
    /*
      Say our logged in user is 'gdog' and we want
      him to be able to go to /gdog to see his profile.
    */
    $user = 'gdog';

    // Now get the router
    $router = $sm->get('router');

    // Lets add a route called 'member' to our router
    $router->addRoute('member', array(

      /*
        We want to make /$user the main end point, with 
        an optional controller and action.
      */
     'route' => '/'. $user. '[/:controller[/:action]]',

      /*
        We want a default end point (if no controller
        and action is given) to go to the index action
        of the index controller.
      */
      'defaults' => array( 
        'controller' => 'Member\Controller\Index', 
        'action' => 'index' 
      ), 

      /*
        We only want to allow alphanumeric characters
        with an exception to the dash and underscore.
      */
      'constraints' => array( 
          'controller' => '[a-zA-Z][a-zA-Z0-9_-]*', 
          'action' => '[a-zA-Z][a-zA-Z0-9_-]*' 
      ), 
    ));
  }
}

Naturally there are more ways of adding a route, but the method mentioned in the preceding code for adding a route displays a canny way of dynamically adding a route. What we created there is that whenever Gdog goes to his profile, he can simply type in http://example.ext/gdog and end up on his profile.

Even more wonderful is that if our friend Gdog wants to see his friends, he is able to do that by just typing in for example, http://example.ext/gdog/my/friends, which will resolve to the Member module and then go to the My controller, lastly executing the Friends action.

Using SimpleRouteStack

This route stack is—as the name implies—the simplest router around and is basically a list with routes that is being parsed to see which route matches, by default this type of router is not used in Zend Framework 2. The general rule of thumb is that if we want to add a route with a high priority, we give it a high index number for example, 100, or 200. If we want to give the route a very low priority, we would give it an index number of, for example, 5 or 10.

Giving priorities to routes comes in handy when we have very specific routes (which usually have a high priority) and less specific routes (low priority). If we, for example, want to make /website/url redirect to a completely different module, controller, and action, but not affect the other website routes, we need to give the /website/url route a higher priority so that when it is found, it will not search for the lower priority routes.

If we, by accident, turn the priorities around, we would find our /website/url always redirect to the route that contains all the /website routes.

SimpleRouteStack uses a Zend\Mvc\Router\PriorityList class to manage its routes priorities.

We need to consider routing before we want to start creating our application, as when the application grows we might get into trouble with our routing if we haven't considered 'how to route' beforehand. It would therefore be wise for us to 'sitemap' the application before coding the routes to make sure we have a correct route list and are not creating any conflicting routes.

The SimpleRouteStack class has a number of methods defined that are very useful for us:

  • getRoute($name) / getRoutes($name): This will retrieve the current route—if a name is provided—or routes that are defined in our SimpleRouteStack. If we are unsure about the routes we have defined, this would be a good place to check first.

addRoute($name, $route, $priority) / addRoutes($routes): We can use this to add a new route or an array of routes to our route type by simply adding it through this method. A route requires a name, route (which can be a string or an instance of RouteInterface) and if we fancy a priority, we can give that as the third parameter.

hasRoute($name): If we would want to check whether a specific route already exists, we can search using its name and find out if it does or doesn't.

  • removeRoute($name): When we are tired of a route we can simply give its name and remove it from the list. This can be particularly handy if we want for example to have a module override a certain /login when the user has logged in to route to/user.

  • SimpleRouteStack: Does not have a functionality to have multiple routes with the same priority. If there is a route with a priority already defined, it will prioritize the last route added as the route with the highest priority.

Using TreeRouteStack

Routers are not restricted to using the URI path to find out how to route a request. They can also use other information such as the query parameters, headers, methods, or hostnames to find a match.

How it works…

In Zend Framework 2, we will generally use routing that is based on a request URI, which contains path segments that should be queried. Routes are matched by a router, which utilizes RouteStack to find the match to the query made by the router. We use RouteStack because we want a decent way of managing our different routes. With Zend Framework 2 there are loads of route types provided, but only two flavorless routers namely SimpleRouteStack and TreeRouteStack.

When we are defining a router, we need to make sure we understand how it works. Although creating lists with different paths is simple enough, it is wise to remember that the Zend Framework 2 router generally works with the Last In First Out (LIFO) concept, which means that a route that would be used often would be registered last, and a route that is less common would be registered earlier in the router stack.

There's more…

Besides the two standard route types, Zend Framework 2 comes with a whole scale of route types that are more specialized to the Internet navigation or even through the console.

Namespace – Zend\Mvc\Router\Http

A wonderful set of HTTP routers can be found in the Zend\Mvc\Router\Http namespace and we will take a quick look at the different classes that reside within this namespace.

The Hostname class explained

The Zend\Mvc\Router\Http\Hostname namespace will try to match its routing against the hostname defined in the configuration. For example, if we define the route to be something.example.ext, our router will make its routing decision based on the full URL. But, if we add a single colon at the beginning of that same route, for example: :something.example.ext, the router would base its route on the something variable, which could be anything from aardvark.example.ext to zyxt.example.ext.

The Literal class explained

The Zend\Mvc\Router\Http\Literal class will literally match the path we give in. For example, if we put a route in there, which is /grouphug, the route will only resolve to that URL, and nothing else.

Methods explained

The Zend\Mvc\Router\Http\Method class is used when we want to match against an HTTP method instead of a segment or path. This could be, for example, a POST, DELETE and so on. The method is also called verb by Zend Framework 2, which means that instead of a route parameter, it requests a verb parameter when adding the route, which is an excellent way to create RESTful APIs.

The Part class explained

The Zend\Mvc\Router\Http\Part class is used to describe child_routes in our routing configuration. This means that—although never used directly—we can define that /user/profile is being redirected to use the UserController, with the profile action.

Let's consider the following configuration:

return array(
  // We begin our router configuration
  'router' => array(

    // Define our routes 
    'routes' => array(

      // We are defining a route named 'Example'
      'Example' => array(
        'type' => 'Literal',
        'options' => array(
  
           /*
            This route will resolve to /recipe 
            which will resolve to the Example 
            module's IndexController and execute 
            the IndexAction.
           */
           'route' => 'recipe',
           'defaults' => array(
             '__NAMESPACE__' => 'Example\Controller',
             'controller' => 'Index',
           ),
         ),
                
         'may_terminate' => true,

          /*
            Here we begin to define our Part route, 
            which always begins with the 
            'child_routes' configuration.
          */
          'child_routes' => array(
            'client' => array(
              'type' => 'Literal',
              'options' => array(
           
              /*
                This child route (or Part) 
                will resolve to /recipe/foo       
                and will call the fooAction in  
                the IndexController.
              */
              'route' => '/foo',
              'defaults' => array(
               'action' => 'fooAction'
              ),
            ),
          ),
        ),
      ),
    ),
  ),
);

Regex explained

The Zend\Mvc\Router\Http\Regex class would be used when we have a complex routing structure that requires us to dynamically create the route. This would, for example, come in handy when we look at News sites, where posts are built up like /archive/some-subject-2013.html. This fairly complex route (as some-subject-2013.html is dynamic in our case) would require a Regex router that can resolve the Controller, Action, and in our case also the output format.

Let's consider the following example:

// We begin our router configuration
'router' => array(

  // Define our routes 
  'routes' => array(

    // We are defining a route named 'Archive'
    'Archive' => array(
      'type' => 'Literal',
      'options' => array(

        /*
          This route will resolve to /archive 
          which will resolve to the Archive 
          module's IndexController and execute 
          the IndexAction.
        */
        'regex' => '/archive/(?<id>[a-zA-Z0-9_-
  ]+)(\.(?<format>(html|xml)))?',
        'defaults' => array(
            '__NAMESPACE__' => 'Archive\Controller',
            'controller' => 'Index',
            'action' => 'indexAction',
            'format' => 'html',
        ), 
        'spec' => '/archive/%id%.%format%',
      ),
    ),
  ),
),

In the preceding example, it is important to note that /archive/%id%.%format% tells us that we will receive two parameters in our method called indexAction that is, id and format.

The Scheme class explained

The Zend\Mvc\Router\Http\Scheme class is always using the defaults parameter and will accept only one other parameter, which is called scheme and can only contain one of the following options, that is, http, https, and mailto.

The Segment class explained

The Zend\Mvc\Router\Http\Segment class is probably one of the most-used routers that we would use, as you can dynamically define the route and controller for any module by using, for example, /:controller/:action, which is easily recognizable by the colon separation. We can define any constraints to the segment by configuring only the use of alphanumeric characters or another definition that we would like to use.

An example of Segment is given in the first example in the How to do it... section.