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

Understanding dependency injection


When we talk about the dependency injection, or in short DI, we talk about the simple task of, for example, injecting data in object or methods at initialization when needed by one or other higher up classes, which either modify or dispose off the object after use. The DI is probably the most complex feature in Zend Framework 2 to understand. Unfortunately because DI's over complexity in debugging and performance and the Service Locator (explained in Chapter 6, Modules, Models and Services). However, although it is not the best tool in the shed, we must try to learn it, because when mastered it could prove to be a very powerful tool to create a very maintainable piece of code.

If we come across a situation where it is necessary for us to input a lot of parameters in classes because of objects deeper in the code are dependent on them is probably the most annoying and un-maintainable piece of code that we can find in even the most professional environment. We need to think mainly about objects that are used more than once in an application, and always required to instantiate again.

How to do it…

Let us take a look at the following example and assume that FirstClass is the only class that we will actually need further in the code:

namespace OneNamespace
{
  class FirstClass 
  {
    private $secondClass;
    public function __construct(SecondClass $secondClass)
    {
      $this->secondClass = $secondClass;  
    } 
  }

  class SecondClass 
  {
    private $thirdClass;
    private $vehicle;
    public function __construct(ThirdClass $thirdClass, $vehicle)
    {
      $this->thirdClass = $thirdClass;
      $this->vehicle = $vehicle;
    }
  }
}

namespace AnotherNamespace 
{
  class ThirdClass 
  {
    private $first_name;
    private $last_name;

    public function __construct($first_name, $last_name)
    {
      $this->first_name = $first_name;
      $this->last_name = $last_name;
    }
  }
}

// Let us now create the example through the classic 
// method.
$thirdClass = new AnotherNamespace\ThirdClass("John", "Doe");
$secondClass = new OneNamespace\SecondClass($thirdClass, 
  'Motorcycle');
$firstClass = new OneNamespace\FirstClass($secondClass);

Both the preceding examples give either variables that are only used to instantiate another class and/or add complexity in reading the code. Although they both are correct, the use of DI can, in this case, make the configuration of both the classes much easier.

Initializing the DI at call-time

Let's take a look at this DI example, considering that we have the same classes as the preceding example:

namespace OneNamespace
{
  class FirstClass 
  {
    [..] 
  }

  class SecondClass 
  {
    [..]  
  }
}

namespace AnotherNamespace 
{
  class ThirdClass 
  {
    [..]
  }
}

// Instead of configuring all the classes, we will now 
// simply configure the Di, and only instantiate the 
// class that we want to use.
$di = new \Zend\Di\Di();
$lister = $di->get(
    'OneNamespace\FirstClass',
    array(
        'first_name' => 'Jane',
        'last_name' => 'Doe',
        'vehicle' => 'Car',
    )
);

In the preceding example, we simply say to the DI that AnotherNamespace\ThirdClass has two parameters in its __construct method. The DI will then utilize Reflection to find out what parameters are present there, and will then give any class that has a first_name, vehicle, or last_name parameter in its constructor that parameter.

Of course we will see a potential flaw here, as you might need to utilize multiple instantiations, one can presume that at some point the same parameter name will be used. In our example, it would cause a problem if another class also has a $first_name parameter but requires a different input, as the DI will simply give the one that is in its list.

Tip

If we use DI to instantiate our classes and all we need the constructor for is to set our variables, we can easily remove the constructor altogether as the DI doesn't use the constructor to initialize the variables. Instead the DI will just set the properties of the values.

One good thing about this is that this can flaw only happens when we use the DI at a call-time level, and not in a global configuration level as we will see now. That is why it isn't recommended to use the DI at call-time level at all.

Initializing the DI through a Configuration object

What we also can do to create a more specific (or accurate) initialization of our object – and to make sure classes with the same property names don't conflict – is initializing the DI with a configuration object.

The idea behind this is that we first create a configuration object (or array) that defines which classes need which properties set, and then use that to initialize the DI, which in its turn finds out when it needs to initiate what.

Take a look at the following example, which shows you the exact thing we just explained:

<?php
// We are assuming that we are using the same classes as 
// in the previously shown examples.
namespace OneNamespace 
{
  class FirstClass 
  {
    [..] 
  }

  class SecondClass 
  {
    [..]  
  }
}

namespace AnotherNamespace 
{
  class ThirdClass 
  {
    [..]
  }
}

// After defining our classes we now begin to create our 
// configuration array which we will use to initialize 
// the DI.
$configuration = array(

  // We want to use this specific configuration at 
  // initialization of our class.
  'instance' => array(
    
    // We specify the class name to use here
    'SecondClass' => array(
       
      // We want to use this as a parameter
      'parameters' => array(

        // The property name to fill is vehicle.
        'vehicle' => 'Airplane'
      ),
    ),

    'FirstClass' => array(
      // Again we want to use this as a parameter
      'parameters' => array(

        // The property name to fill is first name and 
        //last name.
        'first_name' => 'Neil',
        'last_name' => 'deGrasse Tyson',
      ),
    ),
  ),
);

// We want to instantiate the Di\Configuration now.
use \Zend\Di\Configuration; 

$diConfiguration = new Configuration($configuration);

// Now instantiate the Di itself, with the configuration 
// attached.
$di = new \Zend\Di\Di($configuration);

// And to get the object we want to use, we just do the 
//same as before.
$firstClass = $di->get('OneNamespace\FirstClass');

To make everything even nicer, we would just put the Zend\Di\Configuration of the DI in the bootstrap of our module, so that we can use it easily throughout the namespace. This way we can simply put the configuration of the DI in our module.config.php and let the framework take care of it.

How it works…

The DI or dependency injector is an important, and most of the time overlooked feature of Zend Framework 2. The DI makes our lives a lot easier by automatically finding the classes we need in our application.

With all its complexity however, comes a couple of features we should be wary of.

The DI only gives out one instance of an object

This means that every get() call will result in the same instantiation over and over again. If we would like a new instance, we would need to call newInstance() as the DI implements the singleton pattern, which means that all the data persists every time we call the get() method unless we force a new instance of the DI.

Defining either all properties, or using a Fully Qualified (FQ) setter parameter

When our class has more properties than we define, we will find out that the DI will use the last value for every other property in the class. Of course this is unwanted, and if we wrote the class ourselves we should consider refactoring the configuration and/or class.

However, when there is no other way we can define the right properties only by using a Fully Qualified (FQ) setter parameter.

In our configuration we would then define a very specific property name, for example, class::method:paramPos. If we take our ThirdClass example from earlier on, this would then be ThirdClass::setFirstName:0 and ThirdClass::setLastName:0 respectively.

There's more…

There is loads more we can learn about the DI in Zend Framework 2. The following list provides a very short and compact description of other interesting DI components:

  • RuntimeDefinition (default), CompilerDefinition and ClassDefinition: These definitions are used to determine how to configure our objects. Although the default one usually does the job, it can't hurt to see what the other two Definitions do, because they all have their pros and cons.

  • InstanceManager: Used to define the configuration, specifically the Aliases, Parameters and Preferences.