Welcome to Abdul Malik Ikhsan's Blog

Zend Framework 2 : Using $creationOptions in PluginManager

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on August 14, 2014

zf2-zendframework2$creationOptions is an optional parameter that we can pass on 2nd parameter when we get service from PluginManager. It can ease coding process when we need to pass a different parameter or injection on the fly while service is created. We can apply it in $invokableClasses or $factories.

Let’s take a look more deep with example. Let’s say, we have a module with PluginManager that collect service in there like this :
invokables-and-creationoptions

We need to call ‘bar’ service with injected Foo class. Ok, let’s start.
1. create PluginInterface

namespace ExamplePluginManagerCreationOptions\Service;

interface PluginInterface
{
    /*
     * @return void
     */  
    public function getProperty();
}

2. create a Foo class that implements PluginInterface

namespace ExamplePluginManagerCreationOptions\Service;

class Foo implements PluginInterface
{
    /**
     * @var string
     */
    protected $fooProperty;
    
    /**
     * Construct with $fooProperty param
     * @param array $fooProperty
     */
    public function __construct(array $fooProperty)
    {
        $this->fooProperty = $fooProperty;    
    }
    
    /**
     * {@inheritdoc}
     */
    public function getProperty()
    {
        var_dump($this);
    }
}

3. create a Bar class that implements PluginInterface and use Foo in __construct.

namespace ExamplePluginManagerCreationOptions\Service;

class Bar implements PluginInterface
{
    /**
     * @var Foo
     */
    protected $foo;
    
    /**
     * Construct with $foo param
     * @param Foo $foo
     */
    public function __construct(Foo $foo)
    {
        $this->foo = $foo;    
    }
    
    /**
     * {@inheritdoc}
     */
    public function getProperty()
    {
        var_dump($this);
    }
}

4. When realizing the dependency above. We usually use factory to create service, but with $creationOptions, we can make it as invokables like the following :

namespace ExamplePluginManagerCreationOptions\Service;
 
use Zend\ServiceManager\AbstractPluginManager;
 
class PluginManager extends AbstractPluginManager
{
    protected $invokableClasses = array(
        'bar' => 'ExamplePluginManagerCreationOptions\Service\Bar',
    );
 
    /**
     * {@inheritdoc}
     */
    public function validatePlugin($plugin)
    {
        if ($plugin instanceof PluginInterface) {
            // we're okay
            return;
        }
 
        throw new \InvalidArgumentException(sprintf(
            'Plugin of type %s is invalid; must implement %s\PluginInterface',
            (is_object($plugin) ? get_class($plugin) : gettype($plugin)),
            __NAMESPACE__
        ));
    }
}

5. Then, we register the pluginmanager at PluginManagerFactory as PLUGIN_MANAGER_CLASS const as usual.

namespace ExamplePluginManagerCreationOptions\Factory;
 
use Zend\Mvc\Service\AbstractPluginManagerFactory;
 
class PluginManagerFactory extends AbstractPluginManagerFactory
{
    const PLUGIN_MANAGER_CLASS = 'ExamplePluginManagerCreationOptions\Service\PluginManager';
}

6. Now, we can register at service_manager key in module.config.php

return array(
    'service_manager' => array(
        'factories' => array(
            'testpluginmanager' => 'ExamplePluginManagerCreationOptions\Factory\PluginManagerFactory',  
        ),
    ),
);

7. Ok, then now we can call it :

$testpluginmanager = $this->getServiceLocator()->get('testpluginmanager');
        
$bar = $testpluginmanager->get(
    'bar',
    new \ExamplePluginManagerCreationOptions\Service\Foo(array('test'))
);
$bar->getProperty();

Hm.., do you feel pass everything of instance(s) on the fly when creating service is too much but still want “on the fly” freedom ? Then we can create a factory for it. We can then add “BarServiceFactory” like the following structure :
factories-and-creationoptions
The “BarServiceFactory” can be like the following :

namespace ExamplePluginManagerCreationOptions\Factory\Service;

use ExamplePluginManagerCreationOptions\Service\Bar;
use ExamplePluginManagerCreationOptions\Service\Foo;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\MutableCreationOptionsInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

/**
 * a factory to create ExamplePluginManagerCreationOptions\Service\Bar
 * service
 */
class BarServiceFactory implements
    FactoryInterface,
    MutableCreationOptionsInterface
{
    /**
     * {@inheritdoc}
     */
    public function setCreationOptions(array $creationOptions)
    {
        $this->creationOptions = $creationOptions;
    }
    
    /**
     * {@inheritdoc}
     */
    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        //you can call other service by $serviceLocator()->get('servicename')
        //as far as the service implements PluginInterface defined in
        //your pluginmanager
        //otherwise, you need to call top serviceManager
        //by $serviceLocator->getServiceLocator()->get('servicename')
        return new Bar(new Foo($this->creationOptions));
    }
}

Now, we can register it at out PluginManager.

namespace ExamplePluginManagerCreationOptions\Service;
 
use Zend\ServiceManager\AbstractPluginManager;
 
class PluginManager extends AbstractPluginManager
{
    protected $invokableClasses = array(
        'bar' => 'ExamplePluginManagerCreationOptions\Service\Bar',
    );
    
    protected $factories = array(
        'barservice' => 'ExamplePluginManagerCreationOptions\Factory\Service\BarServiceFactory',
    );
    
    /**
     * {@inheritdoc}
     */
    public function validatePlugin($plugin)
    {
        if ($plugin instanceof PluginInterface) {
            // we're okay
            return;
        }
 
        throw new \InvalidArgumentException(sprintf(
            'Plugin of type %s is invalid; must implement %s\PluginInterface',
            (is_object($plugin) ? get_class($plugin) : gettype($plugin)),
            __NAMESPACE__
        ));
    }
}

And now, we can call it with :

$testpluginmanager = $this->getServiceLocator()->get('testpluginmanager');
        
$bar = $testpluginmanager->get(
    'barservice', 
    array('test')
);
$bar->getProperty();

When everything is ok, then we can get the following data output :

object(ExamplePluginManagerCreationOptions\Service\Bar)[303]
  protected 'foo' => 
    object(ExamplePluginManagerCreationOptions\Service\Foo)[298]
      protected 'fooProperty' => 
        array (size=1)
          0 => string 'test' (length=4)

Done ;). I hope it is useful.

Note : We can make $this->creationOptions as collection of array of instances too.

References :
1. http://zend-framework-community.634137.n4.nabble.com/Abstract-Factory-for-custom-Validators-options-discarded-td4661990.html
2. http://zend-framework-community.634137.n4.nabble.com/AbstractPluginManager-amp-options-creationOptions-will-not-work-as-newInstanceArgs-td4656077.html#a4656083

Zend Framework 2 : Create Custom Toolbar for ZendDeveloperTools

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on June 27, 2014

zf2-zendframework2

ZendDeveloperTools toolbars are great to help us debug our application. However, when we need to have additional toolbar that not yet in it, we need to add it by creating custom toolbar for it. Ok, for example, we want to add a session toolbar that read our current session Container key -> value like this :

session-toolbar-rev
To make it easy to learn, let’s apply it into new module, I created a new module for it named SanSessionToolbar like the following :
san-session-toolbar

1. Let’s start with the Collector :

namespace SanSessionToolbar\Collector;

use ZendDeveloperTools\Collector\CollectorInterface;
use Zend\Mvc\MvcEvent;
use Zend\Session\Container;

/**
 * Session Data Collector.
 */
class SessionCollector implements CollectorInterface
{
    /**
     * @inheritdoc
     */
    public function getName()
    {
         // this name must same with *collectors* name in the configuration
        return 'session.toolbar';
    }

    /**
     * {@inheritDoc}
     */
    public function getPriority()
    {
        return 10;
    }

    /**
     * @inheritdoc
     */
    public function collect(MvcEvent $mvcEvent)
    {
    }
    
    public function getSessionData()
    {
        $container = new Container;
        $arraysession = $container->getManager()->getStorage()->toArray();
        
        $data = array();
        foreach($arraysession as $key => $row) {
            if ($row instanceof \Zend\Stdlib\ArrayObject) {
                $iterator = $row->getIterator();
                while($iterator->valid()) {
                    $data[$iterator->key()] =  $iterator->current() ;
                    $iterator->next();
                }
            }
        }
        
        return $data;
    }
}

2. Now, create a view to var_dump the SanSessionToolbar\Collector\SessionCollector::getSessionData().

<?php /* @var $collector \SanSessionToolbar\Collector\SessionCollector */ ?>
<div class="zdt-toolbar-entry">
    <div class="zdt-toolbar-preview">
        <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAATxJREFUeNpi/P//PwMhMJuRkRVItQNxKhBzAPFOIM5O/f//MbpaFgbiAMigYiS+LxCDXOKPrpCJSAP1sYiZY1NIrIGHsYhtw6aQWC8vA2IlIM4EYn6oYRXYFDISEylokQOKlG/ACPlLsguBBggAKRUglgbi70B8EWjQS3x6sLoQaFAikLIDYjcglkKSegHEGUBDN+IyEFekeAOxJRBfAeJPSOISQDwZaKEYSS5Ec20KiEITVgW68g65yeYHAwmAGAN90PgPgPgjWQZCw8oOTXgX0LuvyXWhBxBLIvFBaW8zJV52RePfA+LdZBkI9K44kHJAEz4G9O5Pcl3IA8QyaGJHKYllRixiylDXywCxFKkGvgPiG2hiJUCDQHn5PhBbkGQgMKxABsYC8UEg/grFH4D4BBDHA/EebPoAAgwA3RZUHjvT8+IAAAAASUVORK5CYII=" alt="SESSION Data">
        <span class="zdt-toolbar-info">
                SessionData     
        </span>
    </div>
    <div class="zdt-toolbar-detail">
        <span class="zdt-toolbar-info zdt-toolbar-info-redundant">
            <span class="zdt-detail-label">Session Data</span>
        </span>
        <span class="zdt-toolbar-info">
            <span class="zdt-detail-pre">
                <?php Zend\Debug\Debug::dump($collector->getSessionData()); ?>
            </span>
        </span>
    </div>
</div> 

3. Great!, Let’s configure the module configuration ( config/module.config.php ), Remember, that the toolbar entries name must same with our SessionCollector::getName().

return array(
    
    'service_manager' => array(
        'invokables' => array(
            'session.toolbar' => 
                'SanSessionToolbar\Collector\SessionCollector',
        ),
    ),
    
    'view_manager' => array(
        'template_map' => array(
            'zend-developer-tools/toolbar/session-data'
                => __DIR__ . '/../view/zend-developer-tools/toolbar/session-data.phtml',
        ),
    ),
        
    'zenddevelopertools' => array(
        'profiler' => array(
            'collectors' => array(
                'session.toolbar' => 'session.toolbar',
            ),
        ),
        'toolbar' => array(
            'entries' => array(
                'session.toolbar' => 'zend-developer-tools/toolbar/session-data',
            ),
        ),
    ),
    
);

The ‘session.toolbar’ must be registered into ServiceManager with an instance of SanSessionToolbar\Collector\SessionCollector, and registered into ‘zenddevelopertools’ config profiler and toolbar.
4. The Module.php is a usual Module class.
5. Register your new module into config/application.config.php
6. Now, let’s test it by creating session data in our controller :

    public function indexAction()
    {
        $container = new \Zend\Session\Container;
        $container->a   = 'b';
        $container->foo = 'bar';
        
        return new ViewModel();
    }

Done ;). You can grab this module from my github account : https://github.com/samsonasik/SanSessionToolbar

Reference :
1. http://stackoverflow.com/questions/20325842/how-to-log-something-to-zend-developer-tools-toolbar
2. Image session icon originally from : http://makemore.info.yorku.ca/files/2012/11/info.png, encoded with base64_encode.

Zend Framework 2 : Disable some toolbar entries from ZendDeveloperTools

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on June 26, 2014

zf2-zendframework2Sometime, when we are working with modules that provide a new toolbar entries, then we no need to keep the default ZendDeveloperTools toolbar entries because it replaced by new toolbar entries. For example, we are working with Doctrine and we use DoctrineORMModule,  we automatically get the toolbar like this :
zdt-toolbar-db-doctrine
What if we need to eliminate the Zend\Db toolbar entry from the toolbar ? Let’s take a look at ZendDeveloperTools Options for it one by one method to know what’s going on :
1. Take a look at ZendDeveloperTools\Options $toolbar property :

namespace ZendDeveloperTools;

use Zend\Stdlib\AbstractOptions;

class Options extends AbstractOptions
{
    /*** Other properties here ***/
    /**
     * @var array
     */
    protected $toolbar = array(
        'enabled'       => false,
        'auto_hide'     => false,
        'position'      => 'bottom',
        'version_check' => false,
        'entries'       => array(
            'request' => 'zend-developer-tools/toolbar/request',
            'time'    => 'zend-developer-tools/toolbar/time',
            'memory'  => 'zend-developer-tools/toolbar/memory',
            'config'  => 'zend-developer-tools/toolbar/config',
            'db'      => 'zend-developer-tools/toolbar/db',
        ),
    );
    
    /*** Options methods here ***/
}

Now, we know that the toolbar entry for Zend\Db is the entries with key ‘db’.
2. There is a method to setToolbar with its logic

namespace ZendDeveloperTools;

use Zend\Stdlib\AbstractOptions;

class Options extends AbstractOptions
{
    /*** Options properties here ***/
    
    /**
     * Sets Toolbar options.
     *
     * @param array $options
     */
    public function setToolbar(array $options)
    {
        /*** other logic here ***/ 
        if (isset($options['entries'])) {
            if (is_array($options['entries'])) {
                foreach ($options['entries'] as $collector => $template) {
                    if ($template === false || $template === null) {
                        unset($this->toolbar['entries'][$collector]);
                    } else {
                        $this->toolbar['entries'][$collector] = $template;
                    }
                }
            }
            /*** other logic here ***/
        }
    }
    
    /*** other Options methods here ***/
}

Now, we know, to unset the entries, we need to make the ‘value’ of its key false or null.
3. Last step, setting up the config/autoload/zenddevelopertools.local.php :

return array(
    'zenddevelopertools' => array(
         'profiler' => array( /** other config here **/ ),
         'events' => array( /** other config here **/ ),
         'toolbar' => array(
               /** other config here **/
               'entries' => array(
                   'db' => false,
               )
          ),
    ),
);

Done, now, our ZendDeveloperTools toolbar will look like this :
zdt-toolbar-doctrine-only-without-zend-db

Zend Framework 2 : Using Custom Authentication condition with DoctrineModule

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on June 21, 2014

zf2-zendframework2In DoctrineModule, there is a way to use authentication functionality that call specific entity and its properties with identity and credential. What if we need to use custom authentication conditional, like when user has is_active = 1 or is_enabled = 1. For example, we have a table structure like the following :

CREATE TABLE IF NOT EXISTS `User` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `email` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `password` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `is_active` smallint(6) NOT NULL DEFAULT '1',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1;

There is a is_active field that need to be checked when authentication process excecuted. What we need to do, is to create a custom Authentication adapter objectrepository that extends DoctrineModule\Authentication\Adapter\ObjectRepository :

namespace MyDoctrineAuth\Adapter;
 
use DoctrineModule\Authentication\Adapter\ObjectRepository as BaseObjectRepository;
use Zend\Authentication\Result as AuthenticationResult;

class ObjectRepository extends BaseObjectRepository
{
    /**
     * {@inheritDoc}
     */
    public function authenticate()
    {
        $this->setup();
        $options  = $this->options;
        $identity = $options
            ->getObjectRepository()
            ->findOneBy(array(
                $options->getIdentityProperty() => $this->identity,
                // with assumption, our entity use $isActive property
                'isActive' => 1,  
            ));

        if (!$identity) {
            $this->authenticationResultInfo['code'] = AuthenticationResult::FAILURE_IDENTITY_NOT_FOUND;
            $this->authenticationResultInfo['messages'][] = 'A record with the supplied identity could not be found.';

            return $this->createAuthenticationResult();
        }

        $authResult = $this->validateIdentity($identity);

        return $authResult;
    }
}

The custom ObjectRepository above override the authenticate() method with adding more condition into findOneBy method.
Now, we need to create a factory to instantiate its class :

namespace MyDoctrineAuth\Factory\Authentication;

use DoctrineModule\Service\Authentication\AdapterFactory as BaseAdapterFactory;

use MyDoctrineAuth\Adapter\ObjectRepository;
use Zend\ServiceManager\ServiceLocatorInterface;

class AdapterFactory extends BaseAdapterFactory
{
    /**
     * {@inheritDoc}
     *
     * @return \MyDoctrineAuth\Adapter\ObjectRepository
     */
    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        /* @var $options \DoctrineModule\Options\Authentication */
        $options = $this->getOptions($serviceLocator, 'authentication');

        if (is_string($objectManager = $options->getObjectManager())) {
            $options->setObjectManager($serviceLocator->get($objectManager));
        }

        return new ObjectRepository($options);
    }
}

Great!, time to register the AdapterFactory into module.config.php :

return array(
    'doctrine_factories' => array(
        'authenticationadapter' => 'MyDoctrineAuth\Factory\Authentication\AdapterFactory',
    ),
),

You can grab sample of above code here : https://github.com/samsonasik/MyDoctrineAuth .

Zend Framework 2 : Using DoctrineModule\Form\Element\ObjectSelect and custom repository

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on May 22, 2014

zf2-zendframework2When we are using Doctrine2 in Zend Framework 2 project, we can use existing module named DoctrineModule that can be used to easist our job. Now I will explain about how to use DoctrineModule\Form\Element\ObjectSelect in our Form to load data into select element with custom query instead of default one.
For example, we have data like this :
countries-continent
Now, we need to build a form element that collect continent data, that’s means, we need to ‘group by continent’ for query-ing the table data like this :
continent-grab
To make it work, we need to create custom repository. Ok, let’s start.
1. Prepare the Entity

//module/Tutorial/src/Tutorial/Entity/Countries.php
namespace Tutorial\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * Countries
 *
 * @ORM\Table(name="countries")
 * @ORM\Entity(repositoryClass="Tutorial\Repository\CountriesRepository")
 */
class Countries
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="bigint", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="country", type="string", length=30, nullable=false)
     */
    private $country;

    /**
     * @var string
     *
     * @ORM\Column(name="continent", type="string", length=30, nullable=false)
     */
    private $continent;



    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set country
     *
     * @param string $country
     * @return Countries
     */
    public function setCountry($country)
    {
        $this->country = $country;

        return $this;
    }

    /**
     * Get country
     *
     * @return string 
     */
    public function getCountry()
    {
        return $this->country;
    }

    /**
     * Set continent
     *
     * @param string $continent
     * @return Countries
     */
    public function setContinent($continent)
    {
        $this->continent = $continent;

        return $this;
    }

    /**
     * Get continent
     *
     * @return string 
     */
    public function getContinent()
    {
        return $this->continent;
    }
}

Above entity is pretty generic, but we add new “repositoryClass” attribute in the @ORM\Entity annotation to linked with our custom repository.
2. Create a custom repository

//module/Tutorial/src/Tutorial/Repository/CountriesRepository.php
namespace Tutorial\Repository;

use Doctrine\ORM\EntityRepository;

class CountriesRepository extends EntityRepository
{
    public function getContinent()
    {
        $querybuilder = $this->_em
                             ->getRepository($this->getEntityName())
                             ->createQueryBuilder('c');
        return $querybuilder->select('c')
                    ->groupBy('c.continent')
                    ->orderBy('c.id', 'ASC')
                    ->getQuery()->getResult();
    }
}

The getContinent grab the countries data group by continent.
3. Create form

//module/Tutorial/src/Tutorial/Form/CountriesForm.php
namespace Tutorial\Form;

use Zend\Form\Form;
use Zend\InputFilter\InputFilterProviderInterface;
use Doctrine\ORM\EntityManager;

class CountriesForm extends Form
    implements InputFilterProviderInterface
{
    protected $entityManager;
    
    public function __construct(EntityManager $entityManager)
    {
        parent::__construct();

        $this->entityManager = $entityManager;       
    }
    
    public function init()
    {
        $this->add(array(
           'name' => 'continent',
           'type' => 'DoctrineModule\Form\Element\ObjectSelect',
           'options' => array(
                'object_manager'     => $this->entityManager,
                'target_class'       => 'Tutorial\Entity\Countries',
                'property' => 'continent',
                'is_method' => true,
                'find_method'        => array(
                    'name'   => 'getContinent',
                ),
            ), 
        ));
    }
    
    public function getInputFilterSpecification()
    {
        return array(); // filter and validation here
    }
}

We need to inject form with Doctrine\ORM\EntityManager, and call the getContinent method that we already define at our CountriesRepository. So the Form need to be created via factory :

//module/Tutorial/src/Tutorial/Factory/Form/CountriesFormFactory.php
namespace Tutorial\Factory\Form;

use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Tutorial\Form\CountriesForm;

class CountriesFormFactory implements FactoryInterface
{
    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        $services         = $serviceLocator->getServiceLocator();
        $entityManager    = $services->get('Doctrine\ORM\EntityManager');
        
        $form = new CountriesForm($entityManager);
        return $form;
    }
}

4. Create controller

//module/Tutorial/src/Tutorial/Controller/CountriesController.php
namespace Tutorial\Controller;

use Zend\Mvc\Controller\AbstractActionController;
use Zend\Form\FormInterface;
use Zend\View\Model\ViewModel;

class CountriesController extends AbstractActionController
{
    protected $countriesForm;
    
    public function __construct(FormInterface $countriesForm)
    {
        $this->countriesForm = $countriesForm;
    }
    
    public function indexAction()
    {
        return new ViewModel(array(
            'form' => $this->countriesForm, 
        ));
    }
}

We need to inject controller with the CountriesForm, so the Controller need to be created via factory :

//module/Tutorial/src/Tutorial/Factory/Controller/CountriesControllerFactory.php
namespace Tutorial\Factory\Controller;

use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Tutorial\Controller\CountriesController;

class CountriesControllerFactory implements FactoryInterface
{
    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        $services       = $serviceLocator->getServiceLocator();
        $countryForm    = $services->get('FormElementManager')->get('Tutorial\Form\CountriesForm');
        $controller = new CountriesController($countryForm);

        return $controller;
    }
}

5. Register services

//module/Tutorial/config/module.config.php
return array(
    'doctrine' => array(
        'driver' => array(
            'Tutorial_Entities' => array(
                'class' =>'Doctrine\ORM\Mapping\Driver\AnnotationDriver',
                'cache' => 'array',
                'paths' => array(__DIR__ . '/../src/Tutorial/Entity')
            ),
            'orm_default' => array(
                'drivers' => array(
                    'Tutorial\Entity' => 'Tutorial_Entities'
                ),
            ),
        ),
    ),
    
    'controllers' => array(
        'factories' => array(
            'Tutorial\Controller\Countries' => 'Tutorial\Factory\Controller\CountriesControllerFactory',  
        ),
    ),
    
    'form_elements' => array(
        'factories' => array(
            'Tutorial\Form\CountriesForm' => 'Tutorial\Factory\Form\CountriesFormFactory',  
        ),
    ),
    
    'router' => array(
        'routes' => array(
            'countries' => array(
                'type'    => 'segment',
                'options' => array(
                    'route'    => '/countries[/:action]',
                    'constraints' => array(
                        'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
                    ),
                    'defaults' => array(
                        'controller' => 'Tutorial\Controller\Countries',
                        'action'     => 'index',
                    ),
                ),
            ),
        ),
    ),
    
    'view_manager' => array(
        'template_path_stack' => array(
            'tutorial' => __DIR__ . '/../view',
        ),
    ),
    
);

6. Last but not least, build a view :

// module/Tutorial/view/tutorial/countries/index.phtml
$form = $this->form;
$form->prepare();

echo $this->form()->openTag($form);
echo $this->formCollection($form);
echo $this->form()->closeTag();

Ok, done ;)

Zend Framework 2 : Getting real Sql String of Zend\Db

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on May 16, 2014

zf2-zendframework2 I know, this is maybe a simple post for you, but it useful for me :). Hope it useful for someone. When we build a query using Zend\Db, sometime, complex query need checking for debugging to make sure our query is right. For example, we need to make sure we build a query like this :

SELECT `album`.* FROM `album` WHERE `title` = 'abracadabra' AND (`id` = '1' OR `artist` = 'Tony') LIMIT 1

And we already have an \Zend\Db\Sql\Select instance. In case if you have Model class like zf2 docs provide

$sql = $this->tableGateway->getSql();
$select = $sql->select();
$select->where(array('title' => 'abracadabra'));
$select->where
  ->NEST->
        equalTo('id', 1)
            ->OR->
        equalTo('artist', 'Tony')
  ->UNNEST;

$select->limit(1);

So, Before we return it with :

return $this->tableGateway->selectWith($select);

We can check it with :

echo $sql->getSqlstringForSqlObject($select); die ; // ( die/exit to debugging purpose )
//it will print sql string :
// SELECT `album`.* FROM `album` WHERE `title` = 'abracadabra' AND (`id` = '1' OR `artist` = 'Tony') LIMIT 1

That will return the real sql build for our specific platform.

references :
https://github.com/zendframework/zf2/issues/3224

Don’t push yourself

Posted in Psychology by samsonasik on May 8, 2014

When I was a child, I wished I will be an army. And.. it is not granted. In sport, I’m not good at all. So, it will not be good if I push myself to make it happen. Albert Einstein said, “Everybody is a genius. But if you judge a fish by it’s ability to climb a tree, it will live its whole life believing that it is stupid.”. This realized me that I should do what I can and better at that part. From highschool, I like to write poems. That’s my ability. I’m not saying that I’m good on writing poems, but I can say that it’s better for me to write a poem rather than push myself to do sport that I can’t handle. Now, I’m a web developer, it is better to me to code rather than to do networking job.
It doesn’t means that you should surrender at your condition, but you should understand that you have things that if you do it well, it will be great for you. So, know your weakness, know your strong suit. Do what your best ability.

reference :
image : https://encrypted-tbn1.gstatic.com/images?q=tbn:ANd9GcQkwkta-0K4qeu9QkMNw6J1p_sHaIiq7lHiLMfpTHlLxFE9nZ5B
quote : http://www.goodreads.com/quotes/101458-everybody-is-a-genius-but-if-you-judge-a-fish

Zend Framework 2 : Using INSERT INTO … SELECT with Zend\Db

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on March 19, 2014

zf2-zendframework2Zend Framework 2.3.0 released with dozen of features. One of feature on Zend\Db is we can make insert using select instance. It can make life easier when you have a situation, for example : to ‘copy’ the content of some table to other table, for example : migration with some criteria need to be achieved. Here we go :

//this example is when we use component instead of full stack framework
include './vendor/autoload.php';

use Zend\Db\Adapter\Adapter;
use Zend\Db\Sql\Insert;
use Zend\Db\Sql\Sql;

$adapterSelect = new Zend\Db\Adapter\Adapter(array(
    'driver'   => 'pdo_mysql',
    'database' => 'DbSELECT',
    'username' => 'root',
    'password' => ''
));

$adapterInsert = new Zend\Db\Adapter\Adapter(array(
    'driver'   => 'pdo_mysql',
    'database' => 'DbINTO',
    'username' => 'root',
    'password' => ''
));

/**
 * php 5.4 code, for php 5.3 code, you can extract with
   $selectedtable = new Zend\Db\TableGateway\TableGateway('tableneedtobeselected', $adapterSelect);
   $select = $selectedtable->getSql()->select()->where(array('field' => 'value'));
 */
$select = (new Zend\Db\TableGateway\TableGateway('tableneedtobeselected', $adapterSelect))
                ->getSql()->select()
                ->where(array('field' => 'value'));

/**
 * table that need to be inserted
 */
$tableToBeInserted = new Zend\Db\TableGateway\TableGateway('tablenamewillinserted', $adapterInsert);

//insert with select
$tableToBeInserted->insert($select);

Done ;)

References :
1. http://framework.zend.com/blog/zend-framework-2-3-0-released.html

Zend Framework 2 : Utilize ValidatorManager to work with Custom Validator in Zend\Form

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on February 2, 2014

zf2-zendframework2ValidatorManager is a Plugin Manager that corresponding to Zend\Validator\ValidatorPluginManager, and used to manage validator instances. While we work with existing validators in Zend\Form, we can call validators in ‘validators’ key under inputFilter specification with the ‘invokables’ registered service at Zend\Validator\ValidatorPluginManager. In this post, I will propose detail way to work with custom validator.

For example, we have custom validator named Special to handle words with at least one special character in it, so the form when not valid will show like this :
form-validator-custom
So, we need to start with creating the validator like the following :

//filename : module/TutorialValidator/src/TutorialValidator/Validator/Special.php
namespace TutorialValidator\Validator;

use Zend\Validator\AbstractValidator;
                    
class Special extends AbstractValidator
{
    const NOTSPECIAL = 'NOTSPECIAL';

    protected $messageTemplates = array(
        self::NOTSPECIAL => 'Value should at least one special character',
    );
    
    public function __construct(array $options = array())
    {
       parent::__construct($options);
    }

    public function isValid($value)
    {
       $this->setValue($value);
       
       $special   = preg_match('#[\W]{1,}#', $value);

       if (!$special) {
           $this->error(self::NOTSPECIAL);
           return false;
       }

       return true;
    }
}

And we need to use it to validate the forms, so we need to register the validator in ‘validators’ key in our configuration:

//filename : module/TutorialValidator/config/module.config.php
'validators' => array(
    'invokables' => array(
        'Special' => 'TutorialValidator\Validator\Special'  
     ),
),

There are two ways to build form with custom validator.

A. We can use Zend\Form\FormAbstractServiceFactory that can be configured via ‘forms’ key in configuration files.
To do this, we need to :
1. register Zend\Form\FormAbstractServiceFactory into configuration

//filename : module/TutorialValidator/config/module.config.php
'service_manager' => array(
    //other service registration here...
    'abstract_factories' => array(
        'Zend\Form\FormAbstractServiceFactory',
    ),
    //other service registration here...
),

2. And then, configure the ‘forms’

//filename : module/TutorialValidator/config/module.config.php
'forms' => array(
    'SampleForm' => array(
        'hydrator' => 'ObjectProperty',
        'type'     => 'Zend\Form\Form',
        'elements' => array(
            array(
                'spec' => array(
                    'type' => 'Text',
                    'name' => 'sampleinput',
                    'options' => array(
                        'label' => 'Sample Input:',
                    )
                ),
            ),
            array(
                'spec' => array(
                    'type' => 'Submit',
                    'name' => 'submit',
                    'attributes' => array(
                        'value' => 'Go',
                    )
                ),
            ),
        ),
        'input_filter' => array(
            'sampleinput' => array(
                'required'   => true,
                'validators' => array(
                    array(
                        'name' => 'Special',
                    ),
                ),
            ),
        ),
    ),
),

SampleForm is a registered form service that can be called via ‘FormElementManager’ manager. Special is a custom validator that registered at ‘validators’ key or getValidatorConfig() under Module class.

B. Using form class
For form class, we need to implements Zend\InputFilter\InputFilterProviderInterface and implement method getInputFilterSpecification :
To do this, we need to :
1. create the form class

//filename : module/TutorialValidator/src/TutorialValidator/Form/SampleForm.php
namespace TutorialValidator\Form;

use Zend\Form\Form;
use Zend\InputFilter\InputFilterProviderInterface;

class SampleForm extends Form implements InputFilterProviderInterface
{
    public function init()
    {
        $this->add(array(
            'name' => 'sampleinput',
            'type' => 'Text',
            'options' => array(
                'label' => 'Sample Input: ',
            ),
        ));

        $this->add(array(
            'name' => 'submit',
            'attributes' => array(
                'type'  => 'submit',
                'value' => 'Go',
                'id' => 'submitbutton',
            ),
        ));
    }

    public function getInputFilterSpecification()
    {
        return array(
            array(
                'name' => 'sampleinput',
                'required' => true,
                'validators' => array(
                    array('name' => 'Special'),
                ),
            ),
        );
    }
}

2. Register form service in ‘form_elements’ key in config

//filename : module/TutorialValidator/config/module.config.php
'form_elements' => array(
    'invokables' => array(
        'SampleForm' => 'TutorialValidator\Form\SampleForm'
    ),                         
),

When everything is OK, we can call the form via ‘FormElementManager’ in controller ( you can inject controller with SampleForm service via ‘factories’ key of course :)) :

    public function indexAction()
    {
        $form = $this->getServiceLocator()
                     ->get('FormElementManager')
                     ->get('SampleForm');
        
        $request = $this->getRequest();
        if ($request->isPost()) {
            $form->setData($request->getPost());
            if ($form->isValid()) {
                echo 'Great!form is valid';
            }
        }
        
        $viewmodel = new ViewModel;
        $viewmodel->setVariable('form', $form);
        
        return $viewmodel;
    }

Done. I’ve uploaded the sample code to my github account : https://github.com/samsonasik/TutorialValidator . Hope it helpful ;)

Note : Thanks to Daniel Gimenes for the suggestion to this post and make a PR to my repository on github.
danizord-suggestion-on-getInputFilterSpecification

References :
1. http://zf2.readthedocs.org/en/latest/modules/zend.mvc.services.html
2. http://stackoverflow.com/questions/19642139/possible-to-create-a-factory-to-instantiate-custom-form-validators
3. http://stackoverflow.com/questions/13476164/zend-framework-2-custom-validators-for-forms
4. https://github.com/samsonasik/TutorialValidator/pull/1

Zend Framework 2 : Getting Closer with PluginManager

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on January 29, 2014

zf2-zendframework2In ZF2, there is PluginManager that we can use to collect services with same contract interfaces or ancestor class into single class properties. After that, we can validate whenever the plugins as services are instance of that interfaces or ancestor class ( usually an abstract class ). There are properties of PluginManager class that can collect the services :
1. $factories
2. $invokableClasses
3. $aliases(aliasing registered services at $factories or $invokableClasses)
The basic class structure of PluginManager is like this :

namespace YourModule;

use Zend\ServiceManager\AbstractPluginManager;

class PluginManager extends AbstractPluginManager
{
    protected $factories = array(
        //represent factories key
    );
    
    protected $invokableClasses = array(
        //represent invokables key
    );
    
    protected $aliases = array(
        //represent aliases key
    );
    
    public function validatePlugin($plugin)
    {
        if ($plugin instanceof Plugin\PluginInterface) {
            // we're okay
            return;
        }
        
        throw new \InvalidArgumentException(sprintf(
            'Plugin of type %s is invalid; must implement %s\Plugin\PluginInterface',
            (is_object($plugin) ? get_class($plugin) : gettype($plugin)),
            __NAMESPACE__
        ));
    }
}

For example, you need to create collection of services that can convert content into another format, for example, you want to create services that can convert to ‘xls’, ‘pdf’, or other. so you can create pluginmanager to collect that services, like the following :
pluginmanager_shot
Let’s code!
1. create the ConverterContentPluginManager PluginManager

//filename : module/Tutorial/src/Tutorial/ConverterContentPluginManager.php
namespace Tutorial;

use Zend\ServiceManager\AbstractPluginManager;

class ConverterContentPluginManager extends AbstractPluginManager
{
    protected $invokableClasses = array(
        //represent invokables key
        'xls' => 'Tutorial\Plugin\Xls',
        'pdf' => 'Tutorial\Plugin\Pdf'
    );

    public function validatePlugin($plugin)
    {
        if ($plugin instanceof Plugin\PluginInterface) {
            // we're okay
            return;
        }

        throw new \InvalidArgumentException(sprintf(
            'Plugin of type %s is invalid; must implement %s\Plugin\PluginInterface',
            (is_object($plugin) ? get_class($plugin) : gettype($plugin)),
            __NAMESPACE__
        ));
    }
}

2. Create the plugin interface to be implemented

//filename : module/Tutorial/src/Tutorial/Plugin/PluginInterface.php
namespace Tutorial\Plugin;

interface PluginInterface
{
    public function convert($content);
}

3. Create that services
a. the Xls service

//filename : module/Tutorial/src/Tutorial/Plugin/Xls.php
namespace Tutorial\Plugin;

class Xls implements PluginInterface
{   
    public function convert($content)
    {
        echo 'xls convert here';
        //implementation of convert $content to convert content into xls
    }
}

b. the Pdf service

//filename : module/Tutorial/src/Tutorial/Plugin/Pdf.php
namespace Tutorial\Plugin;

class Pdf implements PluginInterface
{   
    public function convert($content)
    {
        echo 'pdf convert here';
        //implementation of convert $content to convert content into pdf
    }
}

4. Create a factory class for the ConverterContentPluginManager

//filename : module/Tutorial/src/Tutorial/Service/ConverterContentPluginManagerFactory.php
namespace Tutorial\Service;

use Zend\Mvc\Service\AbstractPluginManagerFactory;

class ConverterContentPluginManagerFactory extends AbstractPluginManagerFactory
{
    const PLUGIN_MANAGER_CLASS = 'Tutorial\ConverterContentPluginManager';
}

5. Last step, register into service manager ( just one service, the ConverterContentPluginManagerFactory one )

//filename : module/Tutorial/config/module.config.php
return array(
    'service_manager' => array(
        'factories' => array(
            'convertercontent' => 'Tutorial\Service\ConverterContentPluginManagerFactory'  
        ),
    ),
);

Ok, now you can grab the plugins via :

$content = 'sample content you want to convert';
$converter = $this->getServiceLocator()->get('convertercontent')
$converter->get('xls')->convert($content);
$converter->get('pdf')->convert($content);

Done ;)

References :
1. http://raing3.gshi.org/2013/05/26/creating-custom-plugin-manager-in-zend-framework-2/
2. http://zf2.readthedocs.org/en/latest/modules/zend.mvc.services.html

Zend Framework 2 : Handle and Catch E_* PHP errors

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on January 21, 2014

zf2-zendframework2I made this post because real application is hard ( err…, not as easy as we imagine ) . One error exposed to user can be harmful for our site. As developer, there is a time that something can go wrong even it unit tested ( because we are human!). The ‘dispatch.error’ and ‘render.error’ are errors that handled by Framework to handle “framework specific” error, like  service not found, or view file not found. But what if the error is PHP Error Constant, like you forgot to handle empty array and just :

$array = array();
//many complex things here
//and you echoing..
 echo $array[1];
//that is empty...

You will got error like this : Notice: Undefined offset: 1 in /your/path/to/file.
It’s very dangerous because it exposed to your user. We need to fix it as soon as possible!, so when we need on site access, we need to automate logging ( for example, save error to file and send mail) with the error described and show user that something is go wrong and developer is working to fix it.

Ok, first, prepare the view file that will show user that something is go wrong :

<!-- //module/Application/view/error/e_handler.phtml -->
website is down right now :), we are working on it. please come back again...

Next, we create code to handle the PHP E_* errors :

// ./e_errorhandler.php in root of ZF2 app
//adapt from http://stackoverflow.com/questions/277224/how-do-i-catch-a-php-fatal-error
define('E_FATAL',  E_ERROR | E_USER_ERROR | E_PARSE | E_CORE_ERROR |
        E_COMPILE_ERROR | E_RECOVERABLE_ERROR);

define('DISPLAY_ERRORS', TRUE);
define('ERROR_REPORTING', E_ALL | E_STRICT);

register_shutdown_function('shut');
set_error_handler('handler');

//catch function
function shut()
{
    $error = error_get_last();
    if ($error && ($error['type'] & E_FATAL)) {
        handler($error['type'], $error['message'], $error['file'], $error['line']);
    }
}

function handler($errno, $errstr, $errfile, $errline)
{
    switch ($errno) {

        case E_ERROR: // 1 //
            $typestr = 'E_ERROR'; break;
        case E_WARNING: // 2 //
            $typestr = 'E_WARNING'; break;
        case E_PARSE: // 4 //
            $typestr = 'E_PARSE'; break;
        case E_NOTICE: // 8 //
            $typestr = 'E_NOTICE'; break;
        case E_CORE_ERROR: // 16 //
            $typestr = 'E_CORE_ERROR'; break;
        case E_CORE_WARNING: // 32 //
            $typestr = 'E_CORE_WARNING'; break;
        case E_COMPILE_ERROR: // 64 //
            $typestr = 'E_COMPILE_ERROR'; break;
        case E_CORE_WARNING: // 128 //
            $typestr = 'E_COMPILE_WARNING'; break;
        case E_USER_ERROR: // 256 //
            $typestr = 'E_USER_ERROR'; break;
        case E_USER_WARNING: // 512 //
            $typestr = 'E_USER_WARNING'; break;
        case E_USER_NOTICE: // 1024 //
            $typestr = 'E_USER_NOTICE'; break;
        case E_STRICT: // 2048 //
            $typestr = 'E_STRICT'; break;
        case E_RECOVERABLE_ERROR: // 4096 //
            $typestr = 'E_RECOVERABLE_ERROR'; break;
        case E_DEPRECATED: // 8192 //
            $typestr = 'E_DEPRECATED'; break;
        case E_USER_DEPRECATED: // 16384 //
            $typestr = 'E_USER_DEPRECATED'; break;
    }
    
    $message = " Error PHP in file : ".$errfile." at line : ".$errline."
    with type error : ".$typestr." : ".$errstr." in ".$_SERVER['REQUEST_URI'];

    if(!($errno & ERROR_REPORTING)) {
        return;
    }

    if (DISPLAY_ERRORS) {
        //logging...
        $logger = new Zend\Log\Logger;
		
        //stream writer			
        $writerStream = new Zend\Log\Writer\Stream(__DIR__.'/data/logs/'.date('Ymd').'-log.txt');
        //mail writer
        $mail = new Zend\Mail\Message();
        $mail->setFrom('system@yoursite.com', 'Sender\'s name');
        $mail->addTo('team@yoursite.com', 'Your Site Team');
        $transport = new Zend\Mail\Transport\Sendmail(); 
        $writerMail = new Zend\Log\Writer\mail($mail, $transport);
        $writerMail->setSubjectPrependText("PHP Error :  $typestr : $errstr ");
        
        $logger->addWriter($writerStream);
        $logger->addWriter($writerMail);
      
        //log it!
        $logger->crit($message);
        
        //show user that's the site is down right now
        include __DIR__.'/module/Application/view/error/e_handler.phtml';
        die;
    }
}

The code line 61-87 is show how to log it and show user custom error page immediately.
Ok, time to include the handler in public/index.php and keep silent ( @ for parse error ) for showing error :

/**
 * This makes our life easier when dealing with paths. Everything is relative
 * to the application root now.
 */
chdir(dirname(__DIR__));

// Decline static file requests back to the PHP built-in webserver
if (php_sapi_name() === 'cli-server' && is_file(__DIR__ . parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH))) {
    return false;
}

// Setup autoloading
require 'init_autoloader.php';
//Setup error handler
require_once 'e_errorhandler.php';

// Run the application!
@Zend\Mvc\Application::init(require 'config/application.config.php')->run();

Ok, done! Hope helpful ;)

References :
1. http://stackoverflow.com/questions/277224/how-do-i-catch-a-php-fatal-error
2. http://zf2.readthedocs.org/en/latest/modules/zend.mail.introduction.html
3. http://zf2.readthedocs.org/en/latest/modules/zend.log.writers.html

Zend Framework 2 : using PSR-4 autoloader in your Module

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on January 11, 2014

zf2-zendframework2 This post is inspired by Phil Sturgeon blog post about Autoloading Laravel application code with PSR-4. It can be applied when you’re using Zend Framework module. Instead of using default structure, you can define the structure like this.zf2-module-with-psr-4The Classes inside Controller folder will have namespace Foo\Controller, and in the Model folder will have namespace Foo\Model.
Ok, let’s make it happen!
1. Let’s the Module::getAutoloaderConfig() function empty

//module/Foo/Module.php
namespace Foo;

class Module
{
    public function getConfig()
    {
        return include __DIR__ . '/config/module.config.php';
    }

    public function getAutoloaderConfig()
    {
    }
}

2. Configure composer.json
Add psr-4 autolaod config into your composer.json.

{
    "autoload": {
        "psr-4":{
            "Foo\\" : "module/Foo/src/"
        }
    },
    "require": {
        "php": ">=5.3.3",
        "zendframework/zendframework": "2.2.5"
    }
}

3. update composer

$ php composer.phar self-update
Updating to version 69e77fbbb564e57a6c1a97eaa3c8b751bab70688.
    Downloading: 100% 

4. Run dump-autoload

$ php composer.phar dump-autoload
Generating autoload files

5. Now, You are getting new generated autoload file named vendor/composer/autoload_psr4.php automatically

// autoload_psr4.php @generated by Composer

$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);

return array(
    'Foo\\' => array($baseDir . '/module/Foo/src'),
);

6. Perfect!, Let’s creating file for samples
a. the model class

//module/Foo/src/Model/FooModel.php
namespace Foo\Model;

class FooModel
{
    function __construct()
    {
        echo "foo";
    }
}

b. the controller class

//module/Foo/src/Controller/FooController.php
namespace Foo\Controller;

use Zend\Mvc\Controller\AbstractActionController;

class FooController extends AbstractActionController
{
    public function indexAction()
    {
        new \Foo\Model\FooModel();
        die; //break for test
    }
}

c. the config/module.config.php

//module/Foo/config/module.config.php
return array(
    'router' => array(
        'routes' => array(
            'foo' => array(
                'type' => 'Zend\Mvc\Router\Http\Literal',
                'options' => array(
                    'route'    => '/foo',
                    'defaults' => array(
                        'controller' => 'Foo\Controller\Foo',
                        'action'     => 'index',
                    ),
                ),
            ),
        ),
    ),
    'controllers' => array(
        'invokables' => array(
            'Foo\Controller\Foo' => 'Foo\Controller\FooController'
        ),
    ),
);

7. Register your module into config/application.config.php as usual.

return array(
    'modules' => array(
        'Application',
        'Foo'
    ),
    'module_listener_options' => array(
        'module_paths' => array(
            './module',
            './vendor',
        ),
        'config_glob_paths' => array(
            'config/autoload/{,*.}{global,local}.php',
        ),
    ),
);

8. Let’s call in browser : http://yourapphost/foo , if it’s working as planned, it will show us :
foo-screen-call

Done! I hope this post helpful.

References :
1. http://philsturgeon.co.uk/blog/2014/01/autoloading-laravel-application-code-with-psr4
2. https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md
3. http://akrabat.com/zend-framework-2/thoughts-on-module-directory-structure/
4. http://zf2.readthedocs.org/en/latest/user-guide/modules.html

Zend Framework 2 : Move out your listeners from Module class

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on January 8, 2014

zf2-zendframework2 As we already knew. We can have ‘listeners’ with array callback that placed on Module class which can be called via onBootstrap() method. When our application is growing, to many methods on Module class will make project maintenance take harder, and make our application less-readable.
Here is a sample if we don’t have move out the method yet :

class Module
{
    public function onBootstrap(MvcEvent $e)
    {
        $app            = $e->getApplication();
        $eventManager   = $app->getEventManager();

        $eventManager->attach('dispatch.error',
                array($this, 'handleDispatchErrorWithLogger'), 100);
    }

    public function handleDispatchErrorWithLogger(MvcEvent $e)
    {
        $exception = $e->getParam('exception');

        //it is just a sample, you can create service for logger
        $writer = new \Zend\Log\Writer\Stream('./data/logs/'.date('Y-m-d').'-log.txt');
        $log      = new \Zend\Log\Logger();
        $log->addWriter($writer);

        $log->err($exception);
    }
    public function getConfig(){/*common code*/}
    public function getAutoloaderConfig(){/*common code*/}
}

And many more when application growing, so, this is how it can be moved out :
1. Create a class that has __invoke method that will be fired when event triggered

class DispatchErrorHandlerListener
{
    public function __invoke(MvcEvent $e)
    {
        $exception = $e->getParam('exception');

        //it is just a sample, you can create service for logger
        $writer = new \Zend\Log\Writer\Stream('./data/logs/'.date('Y-m-d').'-log.txt');
        $log      = new \Zend\Log\Logger();
        $log->addWriter($writer);

        $log->err($exception);
    }
}

2. Make the listener as object

class Module
{
    public function onBootstrap(MvcEvent $e)
    {
        $app            = $e->getApplication();
        $eventManager   = $app->getEventManager();

        $eventManager->attach('dispatch.error', new \Tutorial\Listener\DispatchErrorHandlerListener, 100);
    }

    public function getConfig(){/*common code*/}
    public function getAutoloaderConfig(){/*common code*/}
}

and if you like the listener as service, you can pass like this :

class Module
{
    public function onBootstrap(MvcEvent $e)
    {
        $app            = $e->getApplication();
        $eventManager   = $app->getEventManager();
        $service        = $app->getServiceManager();

        $eventManager->attach('dispatch.error', $sm->get('YourRegisteredErrorHandlerListener'), 100);
    }

    public function getConfig(){/*common code*/}
    public function getAutoloaderConfig(){/*common code*/}
}

Done, now your Module class is simplified ;)

Zend Framework 2 : Programmatically handle 404 page

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on January 1, 2014

zf2-zendframework2Sometime, in real life project, we need to handle 404 page more than only show the 404 page, but logging, redirecting, forwarding, change template/content, etc. We can handle it by programmatically at Module.php. In Zend Framework 2, there is 4 types of 404 ( page not found ), they are : ERROR_CONTROLLER_CANNOT_DISPATCH, ERROR_CONTROLLER_NOT_FOUND, ERROR_CONTROLLER_INVALID, and ERROR_ROUTER_NO_MATCH.  Let’s investigate one by one.

1. ERROR_CONTROLLER_CANNOT_DISPATCH
It means the controller is matched, but the action that passed to the url can’t be dispatched. We can handle it via onBootstrap like the following :

namespace YourModule;

use Zend\Mvc\MvcEvent;

class Module
{
    public function onBootstrap(MvcEvent $e)
    {
        $eventManager  = $e->getApplication()->getEventManager();
        $sharedManager = $eventManager->getSharedManager();
        //controller can't dispatch request action that passed to the url
        $sharedManager->attach('Zend\Mvc\Controller\AbstractActionController',
                  'dispatch', 
                  array($this, 'handleControllerCannotDispatchRequest' ), 101);
    }

    public function handleControllerCannotDispatchRequest(MvcEvent $e)
    {
        $action = $e->getRouteMatch()->getParam('action');
        $controller = get_class($e->getTarget());
        
        // error-controller-cannot-dispatch
        if (! method_exists($e->getTarget(), $action.'Action')) {
            $logText = 'The requested controller '.
                        $controller.' was unable to dispatch the request : '.$action.'Action';
            //you can do logging, redirect, etc here..
             echo $logText;
        }
    }   

    public function getConfig() { //common code here }
    public function getAutoloaderConfig() { //common code here }
}

2. ERROR_CONTROLLER_NOT_FOUND
It means controller class not found with requested [/:controller] route that defined already at module.config.php
3. ERROR_CONTROLLER_INVALID
It means the controller is not dispatchable, it usually because the controller is not extends Zend\Mvc\Controller\AbstractActionController
4. ERROR_ROUTER_NO_MATCH
It means The requested URL could not be matched by routing, for example, there is no route with prefix /foo that passed at the url.
For point 2, 3, and 4, we can handle by :

namespace YourModule;

use Zend\Mvc\MvcEvent;
use Zend\Mvc\Application;

class Module
{
    public function onBootstrap(MvcEvent $e)
    {
        $eventManager = $e->getApplication()->getEventManager();
        $eventManager->attach('dispatch.error',
             array($this,
             'handleControllerNotFoundAndControllerInvalidAndRouteNotFound'), 100);
    }
     
    public function handleControllerNotFoundAndControllerInvalidAndRouteNotFound(MvcEvent $e)
    {
        $error  = $e->getError();
        if ($error == Application::ERROR_CONTROLLER_NOT_FOUND) {
            //there is no controller named $e->getRouteMatch()->getParam('controller')
            $logText =  'The requested controller '
                        .$e->getRouteMatch()->getParam('controller'). '  could not be mapped to an existing controller class.';
            
            //you can do logging, redirect, etc here..
            echo $logText;
        }
        
        if ($error == Application::ERROR_CONTROLLER_INVALID) {
            //the controller doesn't extends AbstractActionController
            $logText =  'The requested controller '
                        .$e->getRouteMatch()->getParam('controller'). ' is not dispatchable';
            
            //you can do logging, redirect, etc here..
            echo $logText;
        }
        
        if ($error == Application::ERROR_ROUTER_NO_MATCH) {
            // the url doesn't match route, for example, there is no /foo literal of route
            $logText =  'The requested URL could not be matched by routing.';
            //you can do logging, redirect, etc here...
            echo $logText;
        }
    } 
    public function getConfig() { //common code here }
    public function getAutoloaderConfig() { //common code here }
}

Want to handle All ? Let’s do this :

namespace YourModule;
use Zend\Mvc\MvcEvent;
use Zend\Mvc\Application;

class Module
{
    public function onBootstrap(MvcEvent $e)
    {
        $eventManager = $e->getApplication()->getEventManager();
        $sharedManager = $eventManager->getSharedManager();
        //controller can't dispatch request action that passed to the url
        $sharedManager->attach('Zend\Mvc\Controller\AbstractActionController',
               'dispatch',
               array($this, 'handleControllerCannotDispatchRequest' ), 101);
        //controller not found, invalid, or route is not matched anymore
        $eventManager->attach('dispatch.error', 
               array($this,
              'handleControllerNotFoundAndControllerInvalidAndRouteNotFound' ), 100);
    }
    
    public function handleControllerCannotDispatchRequest(MvcEvent $e)
    {
        $action = $e->getRouteMatch()->getParam('action');
        $controller = get_class($e->getTarget());
        
        // error-controller-cannot-dispatch
        if (! method_exists($e->getTarget(), $action.'Action')) {
            $logText = 'The requested controller '.
                        $controller.' was unable to dispatch the request : '.$action.'Action';
            //you can do logging, redirect, etc here..
             echo $logText;
        }
    }
    
    public function handleControllerNotFoundAndControllerInvalidAndRouteNotFound(MvcEvent $e)
    {
        $error  = $e->getError();
        if ($error == Application::ERROR_CONTROLLER_NOT_FOUND) {
            //there is no controller named $e->getRouteMatch()->getParam('controller')
            $logText =  'The requested controller '
                        .$e->getRouteMatch()->getParam('controller'). '  could not be mapped to an existing controller class.';
            
            //you can do logging, redirect, etc here..
            echo $logText;
        }
        
        if ($error == Application::ERROR_CONTROLLER_INVALID) {
            //the controller doesn't extends AbstractActionController
            $logText =  'The requested controller '
                        .$e->getRouteMatch()->getParam('controller'). ' is not dispatchable';
            
            //you can do logging, redirect, etc here..
            echo $logText;
        }
        
        if ($error == Application::ERROR_ROUTER_NO_MATCH) {
            // the url doesn't match route, for example, there is no /foo literal of route
            $logText =  'The requested URL could not be matched by routing.';
            //you can do logging, redirect, etc here...
            echo $logText;
        }
    } 
    
    public function getConfig(){ // common code }
    public function getAutoloaderConfig(){ //common code }
}

Done ;)

Zend Framework 2 : Centralize phpunit test

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on November 19, 2013

zf2-zendframework2 Centralize phpunit test is good when we work at integration test to test all modules we have created. What we need is place autoloader at Bootstrap file, and make ‘ServiceManager Grabber’ to grab services over modules.

1. Preparation :
a. Create tests folder at our ZF2 Application.
tests-folder-prepare
At this phase, we added Bootstrap.php and phpunit.xml as configuration.
b. Write Bootstrap.php

//we create ServiceManagerGrabber class later...
use ModulesTests\ServiceManagerGrabber; 

error_reporting(E_ALL | E_STRICT);

$cwd = __DIR__;
chdir(dirname(__DIR__));

// Assume we use composer
$loader = require_once  './vendor/autoload.php';
$loader->add("ModulesTests\\", $cwd);
$loader->register();

ServiceManagerGrabber::setServiceConfig(require_once './config/application.config.php');
ob_start();

c. Write phpunit.xml
We register module that we add directory of module tests, at this case, named “ModulesTests”.

<?xml version="1.0" encoding="UTF-8"?>

<phpunit
         backupStaticAttributes="false"
         colors="true"
         convertErrorsToExceptions="true"
         convertNoticesToExceptions="true"
         convertWarningsToExceptions="true"
         processIsolation="false"
         stopOnFailure="false"
         syntaxCheck="false"

         bootstrap="Bootstrap.php">
    <testsuites>
        <testsuite name="sanzf2">
            <directory suffix=".php">./ModulesTests</directory>
        </testsuite>
    </testsuites>
</phpunit>

d. Based on the phpunit.xml, we then need to create directory named “ModulesTests” to collect test for modules.
moduletests-collect-rev

e. Create ServiceManager Grabber ( as you can see at “ModulesTests” folder )

//inspired from https://github.com/doctrine/DoctrineModule/blob/master/tests/DoctrineModuleTest/ServiceManagerTestCase.php
// thanks to Marco Pivetta

namespace ModulesTests;

use Zend\ServiceManager\ServiceManager;
use Zend\Mvc\Service\ServiceManagerConfig;

class ServiceManagerGrabber
{
    protected static $serviceConfig = null;
    
    public static function setServiceConfig($config)
    {
        static::$serviceConfig = $config;
    }
    
    public function getServiceManager()
    {
        $configuration = static::$serviceConfig ? : require_once './config/application.config.php';
        
        $smConfig = isset($configuration['service_manager']) ? $configuration['service_manager'] : array();
        $serviceManager = new ServiceManager(new ServiceManagerConfig($smConfig));
        $serviceManager->setService('ApplicationConfig', $configuration);
 
        $serviceManager->get('ModuleManager')->loadModules();
        
        return $serviceManager;
    }
}

This class will grab the our ZF2 application config, load Modules, that means the serviceManager of all modules will be returned.

f. create per-module test case
moduletests-collect-hasmodule-collect
At this case, I added directory named “SanDbModellingWithZendDbTest” to test my “SanDbModellingWithZendDb” module. It’s up to us to create other name, or collect or not collect module per directory, but for my perspective, it should be on separate folder per-module test.

2. Write a Test
The test can be formatted like the following :

namespace ModulesTests\SanDbModellingWithZendDbTest\Model;

use PHPUnit_Framework_TestCase;
use ModulesTests\ServiceManagerGrabber;

class AlbumTrackMapperTest extends PHPUnit_Framework_TestCase
{
    protected $serviceManager;
    
    public function setUp()
    {
        $serviceManagerGrabber   = new ServiceManagerGrabber();
        $this->serviceManager = $serviceManagerGrabber->getServiceManager();
    }
    
    public function testJoinLeft()
    {
        $count =  count($this->serviceManager->get('AlbumTrackMapper')->findAll());
        ($count > 0 ) ? $this->assertNotEmpty($count) : $this->assertEmpty($count);
    }
}

3. Run Test
We just need to go to tests folder with command line :

$ cd ~/yourzf2app/tests
$ phpunit

And you will get this :
phpunit-run-cli-zf2

Of course, we can add more folders under “ModulesTests” folder.

Thanks to Marco Pivetta that give me enlightenment to not share one ServiceManager accross different tests.
thanks-to-marco-pivetta-for-suggestion-to-not-share-one-service-accross-different-test

Done ;)

Follow

Get every new post delivered to your Inbox.

Join 247 other followers