官术网_书友最值得收藏!

The EventManager and Bootstrap classes

We will be showing off one of the most beautiful features of Zend Framework 2: The EventManager.

How to do it…

The EventManager and Bootstrap classes are an essential part of our application, this recipe is all about how to use those two tools:

Using the bootstrap

The bootstrap is in our case the start of a module, whenever a module is requested it will use the onBootstrap() method located in the Module.php file. Although the method is not required, we usually want this method in our module as it is an easy method of making sure that some instances already exist or are configured before venturing further in our client request.

Starting a session

Sessions are a wonderful way of saving information about a user on a temporary basis. Think about saving the information of a logged-in user, or history on the pages they have been. Once we begin creating an application we find ourselves saving a lot of things in the session.

The first thing we need to do is modify the /module/Application/config/module.config.php file, and add another section called session to it. Let's assume that we have a completely empty module configuration:

<?php
return array(
  'service_manager' => array(
     // These are the factories needed by the Service 
     // Locator to load in the session manager
    'factories' => array(
      'Zend\Session\Config\ConfigInterface' => 
  'Zend\Session\Service\SessionConfigFactory',
      'Zend\Session\Storage\StorageInterface' => 
  'Zend\Session\Service\SessionStorageFactory',
      'Zend\Session\ManagerInterface' => 
  'Zend\Session\Service\SessionManagerFactory',
    ),
    'abstract_factories' => array(
  'Zend\Session\Service\ContainerAbstractFactory',
    ),
  ),
  'session_config' => array(
    // How long can the session be idle for in seconds 
    // before it is being invalidated
    'remember_me_seconds' => 3600,

    // What is the name of the session (can be anything)
    'name' => 'some_name',
  ),
  // What kind of session storage do we want to use, 
  // only SessionArrayStorage is available at the minute
  'session_storage' => array(
    'type' => 'SessionArrayStorage',
    'options' => array(), 
  ),
  // These are session containers we can use to store 
  // our information in
  'session_containers' => array(
    'ContainerOne',
    'ContainerTwo',
  ),
);

And that is it. Sessions are now useable in our controllers and models. We have now created two session containers that we can use to store our information in. We can access these containers in any Controller or Model that has a service locator available by doing the following (file: /module/Application/src/Application/Controller/IndexController.php):

<?php

namespace Application;

use Zend\Mvc\Controller\AbstractActionController;

class IndexController extends AbstractController
{
  public function indexAction()
  {
    // Every session container we define receives a 
    // SessionContainer\ prefix before the name
    $containerOne = $this->getServiceLocator()
  ->get('SessionContainer\ContainerOne');
  }
}

Using the EventManager class

The EventManager class is possibly one of the nicest features in the framework. When used properly, it can make our code a lot more dynamic and maintainable without creating spaghetti code.

What it does is relatively simple, for example; a class might have a method called MethodA. This MethodA has a list of listeners, which are interested in the outcome of that class. When MethodA executes, it just runs through its normal procedures, and when finished it just notifies the EventManager a specific event has occurred. Now the EventManager will trigger all of the interested parties that this event has taken place, and the parties in their turn will execute their code.

Got it? Don't worry if you don't, because this example code might clear things up (file: /module/Application/src/Application/Model/SwagMachine.php):

<?php
// Don't forget to add the namespace
namespace Application\Model;

// We shouldn't forget to add these!
use Zend\EventManager\EventManager;
 
class SwagMachine
{
  // This will hold our EventManager
  private $em;

  public function getEventManager() 
  {
    // If there is no EventManager, make one!
    if (!$this->em) {
      $this->em = new EventManager(__CLASS__);
    }
 
    // Return the EventManager.
    return $this->em;
  }
 
  public function findSwag($id)
  {
    // Trigger our findSwag.begin event 
    // and push our $id variable with it.
    $response = $this->getEventManager()
                     ->trigger(
           'findSwag.begin', 
           $this,
           array(
             'id' => $id
           )
    );
  
    // Make our last response, the final 
    // ID if there is a response.
    if ($response->count() > 0)
      $id = $response->last();
 
    // ********************************
    // In the meantime important code 
    // is happening...
    // ********************************
  
    // ...And that ends up with the 
    // folowing return value:
    $returnValue = 'Original Value ('. $id. ')';
    
    // Now let's trigger our last 
    // event called findSwag.end and 
    // give the returnValue as a 
    // parameter.
    $this->getEventManager()
         ->trigger(
           'findSwag.end', 
           $this, 
           array(
             'returnValue' => $returnValue
           )
    );
 
    // Now return our value.
    return $returnValue;
  }
}

As we can see we created a little class with two event triggers, findSwag.begin and findSwag.end, respectively on the beginning of the method, and one on the end of the method. The findSwag.begin event will potentially modify the $id, and the findSwag.end event only parses the returnValue object, with no modification possible to the value.

Now let's see the code that implements the triggers (file: /module/Application/src/Application/Controller/IndexController.php):

<?php

namespace Application\Controller;

use Zend\Mvc\Controller\AbstractActionController;

class IndexController extends AbstractActionController
{
  public function indexAction() 
  {
    // Get our SwagMachine
    $machine = new SwagMachine();
    
    // Let's attach our first callback, 
    // which potentially will increase 
    // the $id with 10, which would 
    // make it result in 30!
    $machine->getEventManager()
            ->attach(
        'findSwag.begin',
        function(Event $e) 
        {
          // Get the ID from our findSwag() 
          // method, and add it up with 12.
          return $e->getParam('id') + 10;
        },
        200
    );
    
    // Now attach our second callback, 
    // which potentially will increase 
    // the value of $id to 60! We give 
    // this a *higher* priority then 
    // the previous attached event 
    // trigger.
    $machine->getEventManager()
            ->attach(
        'findSwag.begin',
        function(Event $e) 
        {
        // Get the ID from our findSwag() 
        // method, and add it up with 15.
        return $e->getParam('id') + 40;
      },
      100
    );
  
    // Now create a trigger callback 
    // for the end event called findSwag.end, 
    // which has no specific priority, 
    // and will just output to the screen.
    $machine->getEventManager()
            ->attach(
        'findSwag.end',
        function(Event $e) 
        {
          echo 'We are returning: '
             . $e->getParam('returnValue');
        }
    );

    // Now after defining the triggers, 
    // simply try and find our 'Swag'.      
    echo $machine->findSwag(20);
  }
}

As we can see attaching triggers to events is pretty straightforward. And – if the events are properly documented – can come in handy when we want to, say, modify parameters going into a method (like we did with the findSwag.begin), or just outputting the results to a log (like findSwag.end).

When we look at what is on our screen, it should be something like this:

We are returning: Original Value (60)
Original Value (60)

The result consists of the top line being the output from the findSwag.end trigger, while the value 60 comes from the highest priority trigger, the one with priority 100 (as that is considered a higher priority than 200).

Changing the View output

Sometimes it is necessary that we have different View outputs, for example when we need to build ourselves a REST service or a SOAP service. Although this can be arranged much simpler by a controller plugin, it is an example on how to hook into the dispatch event, and see what is going on there.

Without further ado, let us take a look at the following code snippet:

Module.php:
namespace Application;

// We are going to use events, and because we use a MVC, 
// we need to use the MvcEvent.
use Zend\Mvc\MvcEvent;

class Module
{
  public function onBootstrap(MvcEvent $e)
  {
    // Get our SharedEventManager from the MvcEvent $e 
    // that we got from the method
    $sharedEvents = $e->getApplication()
                      ->getEventManager()
                      ->getSharedManager();

    // Also retrieve the ServiceManager of the 
    // application.
    $sm = $e->getApplication()->getServiceManager();

    // Let's propose a new ViewStrategy to our 
    // EventManager.
    $sharedEvents->attach(

        // We are attaching the event to this namespace 
        // only.
        __NAMESPACE__, 

        // We want to attach to this very specific 
        // event, the Dispatch event of our controller.
        MvcEvent::EVENT_DISPATCH, 

        // The callback function of the event, used when 
        // the event we attached to happens. In our 
        // callback we also want our local variable $sm 
        // to be available for use.
        function($e) use ($sm) 
        {
          // Get our alternate view strategy from the 
          // ServiceManager and attach the EventManager     
          // to the strategy.
          $strategy = $sm->get('ViewJsonStrategy');
          $view = $sm->get('ViewManager')->getView();
          $strategy->attach($view->getEventManager());
        }, 

        // We want to give this a priority, so this will 
        // get more priority.
        100
    );
}

As we can see it is relatively simple to attach a callback function to the EventManager object. In this example we are using McvEvent::EVENT_DISPATCH as the event we want to hook in to. So what basically happens is that whenever a controller executes the onDispatch() method, this event will be triggered as well. This means that through events we can modify the outcome of a method without actually needing to modify the code.

How it works…

The EventManager class works through a couple of different methods, namely the Observer pattern, the Aspect-Oriented Programming technique (or AOP) and the Event-Driven architecture.

The Observer pattern explained

Simply said the Observer pattern means that there are several interested parties, called listeners that want to know when the application triggers a certain event. When a specific event is triggered, the listeners will be notified so that they can take their necessary actions.

Aspect-Oriented Programming (AOP) explained

If we want to explain what AOP is, we could say that in short it stands for writing clean code that have only function and are as isolated from the rest of the code as possible.

Event-driven architecture explained

The benefit of an Event-driven architecture is that instead of creating bulks of code that need to check every condition, we can easily hook ourselves to different events, which in essence will create a more responsive application.

There's more…

The EventManager object is queried through a PriorityQueue, which tells us that an important event will generally get a lower value, while an unimportant event a higher value. For example, the highest priority might get priority -1000 while a quite low priority might get 40. The EventManager class then gets the queue through a FIFO (First In, First Out) concept, meaning the higher the priority, the lower the number.

主站蜘蛛池模板: 辉南县| 盘锦市| 怀安县| 织金县| 冷水江市| 东兰县| 临澧县| 璧山县| 万安县| 巴彦淖尔市| 四会市| 施秉县| 桐城市| 涞源县| 张掖市| 宝兴县| 承德市| 荣成市| 维西| 新化县| 万荣县| 稻城县| 林西县| 紫金县| 乌拉特中旗| 金川县| 富锦市| 金堂县| 肥乡县| 六安市| 喜德县| 嘉祥县| 恩平市| 嘉义县| 宜兰县| 静安区| 汶上县| 江川县| 西乌珠穆沁旗| 报价| 新丰县|