Welcome to Abdul Malik Ikhsan's Blog

Zend Framework 2 : Register Event Listeners in Configuration File

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on May 18, 2013

zf2-zendframework2Zend Framework 2.2 released. It comes with ton of new features. Now, I will show you how to set event listeners in Configuration File. Event Listeners can be registered in config/application.config.php, config/autoload/(.*?).php, and each module config.

For example, I have a sample listener like this :

namespace YourModule\Event;

use Zend\EventManager\ListenerAggregateInterface;
use Zend\EventManager\EventManagerInterface;
use Zend\EventManager\EventInterface;

class MySampleListener implements ListenerAggregateInterface
{
    protected $listeners = array();

    public function attach(EventManagerInterface $events)
    {
        $this->listeners[] = $events->attach('eventName', array($this, 'doEvent'));
    }

    public function detach(EventManagerInterface $events)
    {
        foreach ($this->listeners as $index => $listener) {
            if ($events->detach($listener)) {
                unset($this->listeners[$index]);
            }
        }
    }

    public function doEvent(EventInterface $event)
    {
        echo 'param id  = '.$event->getParam('id');
    }
}

Let’s register :

return array(    
    'listeners' => array(
        'MySampleListener'
    ),
    'service_manager' => array(
        'invokables' => array(
            'MySampleListener' => 'YourModule\Event\MySampleListener',
        ),
    ),    
);

Try calling in Controller :

namespace YourModule\Controller;

use Zend\Mvc\Controller\AbstractActionController;

class IndexController extends AbstractActionController
{  
    public function indexAction()
    {   
       $mysampleListener = $this->getServiceLocator()->get('MySampleListener');
       $this->getEventManager()->attachAggregate($mysampleListener);
       
       $parameter = array('id' => 1); 
       $this->getEventManager()->trigger('eventName', $this, $parameter);
    }
}

Um…, let’s make it separate, attach via Module::onBootstrap :

namespace YourModule;

use Zend\Mvc\MvcEvent;

class Module
{
    public function onBootstrap(MvcEvent $e)
    {
        $eventManager        = $e->getApplication()->getEventManager();
        $sharedManager = $eventManager->getSharedManager();
        $sm = $e->getApplication()->getServiceManager();

        $sharedManager->attach('Zend\Mvc\Controller\AbstractActionController',  'dispatch', function($e)
                           use ($sm) {
           $controller = $e->getTarget();
           $controller->getEventManager()->attachAggregate($sm->get('MySampleListener'));
        }, 2);
    }

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

And you just call in controller by :

namespace Application\Controller;

use Zend\Mvc\Controller\AbstractActionController;

class IndexController extends AbstractActionController
{
    public function indexAction()
    {
       $parameter = array('id' => 1); 
       $this->getEventManager()->trigger('eventName', $this, $parameter);
    }
}

References :
1. https://github.com/zendframework/zf2/pull/3931
2. http://samminds.com/2013/04/understanding-zf2-configuration/
3. http://framework.zend.com/blog/zend-framework-2-2-0-stable-released.html

Zend Framework 2 : Paginator – Using TableGateway Object

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on May 6, 2013

zf2-zendframework2Zend Framework 2.2 is coming, more feature, more improvement.One of features that i like is DbTableGateway adapter for Paginator that can be used at your Table Class to make our life easier. The current ZF2 doc is using DbSelect Adapter, so now i will post an example how to use DbTableGateway Adapter.

I will follow the AlbumTable tutorial in the doc, so AlbumTable should be like the following :

namespace Album\Model;

use Zend\Db\TableGateway\TableGateway;
use Zend\Paginator\Adapter\DbTableGateway;
use Zend\Paginator\Paginator;

class AlbumTable
{
    //...
    public function fetchAll($paginated = false)
    {
        if ($paginated) {
            $dbTableGatewayAdapter = new DbTableGateway($this->tableGateway);
            $paginator = new Paginator($dbTableGatewayAdapter);
            
            return $paginator;
        }
        
        return $this->tableGateway->select();
    }
    //...
}

Very easy :D

Note : currently, you can pass $where and $order to DbTableGateway adapter after tableGateway parameter.

References :
http://zf2.readthedocs.org/en/latest/tutorials/tutorial.pagination.html

Zend Framework 2 : Generate Doctrine Entities from Existing Database using DoctrineModule and DoctrineORMModule

Posted in orm, Teknologi, Tutorial PHP, Zend Framework 2 by samsonasik on April 10, 2013

zf2-zendframework2If we are working with Doctrine , we usually create entities first, and generate the database tables. How if the situation is the database tables and data is already ready, and we have to create an application based on doctrine and Zend Framework 2? We need generate entities! Don’t create them manually!
For example, i have two tables : album and track (i’m using PostgreSQL ) like the following :

-- Table: album
CREATE TABLE album
(
  id bigserial NOT NULL,
  artist character varying(255),
  title character varying(255),
  CONSTRAINT pk_album PRIMARY KEY (id )
)
WITH (
  OIDS=FALSE
);
ALTER TABLE album
  OWNER TO developer;

-- Table: track
CREATE TABLE track
(
  track_id bigserial NOT NULL,
  track_title character varying(255),
  album_id bigint,
  CONSTRAINT track_pkey PRIMARY KEY (track_id ),
  CONSTRAINT fk_track_album FOREIGN KEY (album_id)
      REFERENCES album (id) MATCH SIMPLE
      ON UPDATE CASCADE ON DELETE CASCADE
)
WITH (
  OIDS=FALSE
);
ALTER TABLE track
  OWNER TO developer;

Ok, let’s create an application based on ZF2 step by step :
1. install ZF2 SkeletonApplication

git clone git://github.com/zendframework/ZendSkeletonApplication.git zftutordoctrine

2. add “doctrine/doctrine-orm-module” to your zftutordoctrine/composer.json

{
    "name": "zendframework/skeleton-application",
    "description": "Skeleton Application for ZF2",
    "license": "BSD-3-Clause",
    "keywords": [
        "framework",
        "zf2"
    ],
    "homepage": "http://framework.zend.com/",
    "require": {
        "php": ">=5.3.3",
        "zendframework/zendframework": ">2.1.3",
        "doctrine/doctrine-orm-module": "0.7.*"
    }
}

3. Install it

php composer.phar self-update && php composer.phar install

4. Create an Album module with structure entity like the following :
5-albumomodule-structure
5. Configure doctrine connection
You can define it at one file, but separate it with two file(local and global) can make security happy :D
a. config/autoload/doctrine.global.php

//config/autoload/doctrine.global.php
return array(
    'doctrine' => array(
        'connection' => array(
            'orm_default' => array(
                'driverClass' => 'Doctrine\DBAL\Driver\PDOMySql\Driver',
                    'params' => array(
                        'host' => 'localhost',
                        'port' => '5432',
                        'dbname' => 'zftutordoctrine',
                ),
            ),
        )
));

b. config/autoload/doctrine.local.php

//config/autoload/doctrine.local.php
return array(
    'doctrine' => array(
        'connection' => array(
            'orm_default' => array(
                'driverClass' => 'Doctrine\DBAL\Driver\PDOPgSql\Driver',
                    'params' => array(
                        'user' => 'developer',
                        'password' => '123456',
                ),
            ),
        )
));

6. register Album\Entity into doctrine driver in module/Album/config/module.config.php

//module/Album/config/module.config.php
return array(
    'doctrine' => array(
        'driver' => array(
            'Album_driver' => array(
                'class' => 'Doctrine\ORM\Mapping\Driver\AnnotationDriver',
                'cache' => 'array',
                'paths' => array(__DIR__ . '/../src/Album/Entity')
            ),
            'orm_default' => array(
                'drivers' => array(
                     'Album\Entity' =>  'Album_driver'
                ),
            ),
        ),
    ),                 
);

7. Register modules into config/application.config.php

//config/application.config.php
return array(
    'modules' => array(
        'Application',
        'DoctrineModule',
        'DoctrineORMModule',
        'Album'
    ),
    
    // These are various options for the listeners attached to the ModuleManager
    'module_listener_options' => array(
        'module_paths' => array(
            './module',
            './vendor',
        ),
        'config_glob_paths' => array(
            'config/autoload/{,*.}{global,local}.php',
        ),
    ),
);

8. Generate Time !
a. convert-mapping

./vendor/doctrine/doctrine-module/bin/doctrine-module orm:convert-mapping --namespace="Album\\Entity\\" --force  --from-database annotation ./module/Album/src/

it will export “annotation” mapping information into ./module/Album/src/
b. generate-entities

 ./vendor/doctrine/doctrine-module/bin/doctrine-module orm:generate-entities ./module/Album/src/ --generate-annotations=true

it will add setter/getter into entities.
and you will get the following entities AUTOMATICALLY :
Album\Entity\Album

<?php

namespace Album\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * Album
 *
 * @ORM\Table(name="album")
 * @ORM\Entity
 */
class Album
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="bigint", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="SEQUENCE")
     * @ORM\SequenceGenerator(sequenceName="album_id_seq", allocationSize=1, initialValue=1)
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="artist", type="string", length=255, nullable=true)
     */
    private $artist;

    /**
     * @var string
     *
     * @ORM\Column(name="title", type="string", length=255, nullable=true)
     */
    private $title;



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

    /**
     * Set artist
     *
     * @param string $artist
     * @return Album
     */
    public function setArtist($artist)
    {
        $this->artist = $artist;
    
        return $this;
    }

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

    /**
     * Set title
     *
     * @param string $title
     * @return Album
     */
    public function setTitle($title)
    {
        $this->title = $title;
    
        return $this;
    }

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

Album\Entity\Track

<?php

namespace Album\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * Track
 *
 * @ORM\Table(name="track")
 * @ORM\Entity
 */
class Track
{
    /**
     * @var integer
     *
     * @ORM\Column(name="track_id", type="bigint", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="SEQUENCE")
     * @ORM\SequenceGenerator(sequenceName="track_track_id_seq", allocationSize=1, initialValue=1)
     */
    private $trackId;

    /**
     * @var string
     *
     * @ORM\Column(name="track_title", type="string", length=255, nullable=true)
     */
    private $trackTitle;

    /**
     * @var \Album\Entity\Album
     *
     * @ORM\ManyToOne(targetEntity="Album\Entity\Album")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="album_id", referencedColumnName="id")
     * })
     */
    private $album;



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

    /**
     * Set trackTitle
     *
     * @param string $trackTitle
     * @return Track
     */
    public function setTrackTitle($trackTitle)
    {
        $this->trackTitle = $trackTitle;
    
        return $this;
    }

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

    /**
     * Set album
     *
     * @param \Album\Entity\Album $album
     * @return Track
     */
    public function setAlbum(\Album\Entity\Album $album = null)
    {
        $this->album = $album;
    
        return $this;
    }

    /**
     * Get album
     *
     * @return \Album\Entity\Album 
     */
    public function getAlbum()
    {
        return $this->album;
    }
}

JUST IMAGINE that YOU SHOULD WRITE THEM MANUALLY :p
9. Fill database tables rows, and Let’s call from whatever controller for testing :

    public function indexAction()
    {
        $em = $this->getServiceLocator()
                ->get('doctrine.entitymanager.orm_default');
        $data = $em->getRepository('Album\Entity\Track')->findAll();
        foreach($data as $key=>$row)
        {
            echo $row->getAlbum()->getArtist().' :: '.$row->getTrackTitle();
            echo '<br />';
        }
    }

References :
1. Conversation with Anass Ans
1. http://docs.doctrine-project.org/en/2.0.x/reference/tools.html
2. http://wildlyinaccurate.com/useful-doctrine-2-console-commands

Zend Framework 2 : Getting Closer with EventManager

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on March 30, 2013

zf2-zendframework2I have posted a dozen of posts that utilize EventManager, but i think, it’s time to post detail about EventManager itself. Zend Framework 2 has component named EventManager. An EventManager is an object that aggregates listeners for one or more named events, and which triggers events, which a Listener is a callback ( closure, static method, typical array callback ) that can react to an event, and An Event is an action that is triggered by an EventManager.
This is a simple example :

use Zend\EventManager\EventManager;

$events = new EventManager;
$events->attach('do', function($e) {
    $event  = $e->getName();
    $params = $e->getParams();
    
    printf(
        'Handled event "%s" with parameter "%s"',
        $event,
        json_encode($params)
    );
});

$params = array('foo' => 'bar','baz' => 'bat');
$events->trigger('do', null, $params); //event, target, parameter
//print : Handled event "do" with parameter "{"foo":"bar","baz":"bat"}"

Features

  1. Wildcard Attachment
  2. Shared Manager
  3. Short-Circuiting
  4. Listener response Aggregation

Ok, let’s learn one by one.
1.Wildcard Attachment
We can attach to many events at once.
for example :

$events = new EventManager();
$events->attach(array('these', 'are', 'event', 'names'), $callback);

or using ‘*’ to make $callback available in all events.

$events = new EventManager();
$events->attach('*', $callback);

2.Shared Manager
This is to allow attaching to events when you don’t have access to the object. You should implements Zend\EventManager\EventManagerAwareInterface to make it run.
For example, we create a class like the following :

use Zend\EventManager\EventManager;
use Zend\EventManager\EventManagerAwareInterface;
use Zend\EventManager\EventManagerInterface;

class Foo implements EventManagerAwareInterface 
{
    protected $events;
    
    public function setEventManager(EventManagerInterface $events) 
    {
        $this->events = $events;
        return $this;
    }
    
    public function getEventManager()
    {
        if (!$this->events) { 
             $this->setEventManager(new EventManager(__CLASS__)); 
        }
        return $this->events;
    }
    
    public function bar($baz, $bat = null) {
        $params = compact('baz', 'bat');
        $this->getEventManager()->trigger(__FUNCTION__, $this, $params);
    }
}

and you can attach to it by :

use Zend\EventManager\SharedEventManager;

$sharedEvent = new SharedEventManager;
$sharedEvent->attach('Foo', 'bar', function($e) {
    $event  = $e->getName();
    $target = get_class($e->getTarget());
    $params = json_encode($e->getParams());

    printf(
        '%s called on %s, using params %s',
        $event,
        $target,
        $params
    );
});

$foo = new Foo();
$foo->getEventManager()->setSharedManager($sharedEvent);
$foo->bar('bazvalue', 'batvalue');
//print : bar called on Foo, using params {"baz":"bazvalue","bat":"batvalue"} 

We can call other class too, let’s assume we have two class that had virtually no knowledge of each other.
Let’s create class 1 named Class1 :

use Zend\EventManager\EventManager;
use Zend\EventManager\EventManagerAwareInterface;
use Zend\EventManager\EventManagerInterface;

class Class1 implements EventManagerAwareInterface {
    
    protected $events;
    
    public function setEventManager(EventManagerInterface $events) {
        $this->events = $events;
        return $this;
    }
    
    public function getEventManager() {
        if (!$this->events) { $this->setEventManager(new EventManager(__CLASS__)); }
        return $this->events;
    }
    
    public function run() {
        $this->getEventManager()->trigger('cls',$this);
    }
}

and class 2 named Class2 :

use Zend\EventManager\Event;
                    
class Class2 {
    public function listen(Event $e) {
        echo get_class($this) . ' has been called by ' . get_class($e->getTarget());
    }
}

Check it :

use Zend\EventManager\StaticEventManager;
                     
StaticEventManager::getInstance()->attach('Class1', 'cls', array(new Class2, 'listen'));
$cls = new Class1();
$cls->run();
 //print : Class2 has been called by Class1 

3.Short-Circuiting
This feature utilize if a particular result is obtained, or if a listener determines that something is wrong, or that it can return something quicker than the target.
for example, we have a class FooShortCircuit :

use Zend\EventManager\EventManager;
use Zend\EventManager\EventManagerAwareInterface;
use Zend\EventManager\EventManagerInterface;

class FooShortCircuit implements EventManagerAwareInterface
{
    protected $events;
    
    public function setEventManager(EventManagerInterface $events) {
        $this->events = $events;
        return $this;
    }
    
    public function getEventManager() {
        if (!$this->events) { $this->setEventManager(new EventManager(__CLASS__)); }
        return $this->events;
    }
    
    public function execute($obj)
    {
        $argv = array();
        $results = $this->getEventManager()->triggerUntil(__FUNCTION__, $this, $argv,
                function($v) use ($obj) {
            return ($obj instanceof Foo); 
        });
         
        // if $obj instanceof foo, so stopped 
        if ($results->stopped()) {
            return $results->last();
        }
        // continue...
        echo 'hei, i\'m continue :p';
    }
}

if $obj in execute function instance of Foo class, it will stopped.
Let’s create a default execution :

$sharedEvent = new SharedEventManager;
$sharedEvent->attach('FooShortCircuit', 'execute', function($e) {
    echo 'standard execution...';
});

if we pass new Foo() to execute() function, it should execute ‘standard execution…’ only, and stopped. Let’s check it :

$fooShortCircuit = new FooShortCircuit;
$fooShortCircuit->getEventManager()->setSharedManager($sharedEvent);
$fooShortCircuit->execute(new Foo()) ;
//print : standard execution...

4.Listener response Aggregation
Single class can listen to multiple events.
For example, we have a class :

use Zend\EventManager\EventManager;
use Zend\EventManager\EventManagerAwareInterface;
use Zend\EventManager\EventManagerInterface;

class Baz  implements EventManagerAwareInterface
{
     protected $events;
    
    public function setEventManager(EventManagerInterface $events) {
        $this->events = $events;
        return $this;
    }
    
    public function getEventManager() {
        if (!$this->events) { $this->setEventManager(new EventManager(__CLASS__)); }
        return $this->events;
    }
   
    public function get($id)
    {
        $params = compact('id');
        $results = $this->getEventManager()->trigger('Bar.pre', $this, $params);

        // If an event stopped propagation, return the value
        if ($results->stopped()) {
            return $results->last();
        }

        // do some work...

        $this->getEventManager()->trigger('Bar.post', $this, $params);
    }
}

and make a class that listen to multiple events.

use Zend\EventManager\ListenerAggregateInterface;
use Zend\EventManager\EventInterface;

class Bar implements ListenerAggregateInterface
{
    protected $listeners = array();
    
    public function attach(EventManagerInterface $e)
    {  
        $this->listeners[] = $e->attach('Bar.pre', array($this, 'load'));
        $this->listeners[] = $e->attach('Bar.post', array($this, 'save'));
    }
    
    public function detach(EventManagerInterface $e) 
    {
        foreach ($this->listeners as $index => $listener) {
            if ($e->detach($listener)) {
                unset($this->listeners[$index]);
            }
        }
    }
    
    public function load(EventInterface $e) { echo 'load...'; }
    public function save(EventInterface $e) { echo 'save...'; }
}

Let’s aggregate it :

$baz = new Baz;
$barListeners = new Bar;
$baz->getEventManager()->attachAggregate($barListeners);

$baz->get(1);

EventManager in ZF2 MVC Architecture
So, how EventManager works in ZF2 MVC Architecture ? Everything is an event !

  • A bootstrap is an Event
  • A Routing is an Event
  • A Dispatch is an Event
  • A Rendering View is an Event
  • etc

To make event triggered early, place it to init() function in Module.php

class Module
{ 
    public function init(ModuleManager $m)
    {
        $event = $m->getEventManager()->getSharedManager();
        $event->attach('Zend\Mvc\Application', 'bootstrap', function($e) { 
            echo 'executed on bootstrap app process <br />'; 
        });
    }
}

And if init() is too early, ModuleManager automatically registers the onBootstrap method if found in the module.

class Module
{
    public function onBootstrap(MvcEvent $e)
    {
        $event = $e->getApplication()->getEventManager();
       
        $event->attach('route', function($e) {
            echo 'executed on route process'; 
        });

         $event->attach('dispatch', function($e) {
            echo 'executed on dispatch process'; 
         });

        $event->attach('dispatch.error', function($e) {
            echo $e->getParam('exception');
            echo 'executed only if found an error <br />,
            for ex : sm not found <br />'; 
        });

        $event->attach('render', function($e) {
            $e->getViewModel()->setVariable('test', 'test');
            echo 'executed on render process <br />';
        });

         $event->attach('render.error', function($e) {
            echo 'executed on render  error found'; 
         });

         $event->attach('finish', function($e) {
            echo 'executed on finish process'; 
         });
    }
}

A sample Use Case
We want to check if logged in, setting up the userid variable of controllers.

class Module
{
    public function onBootstrap(MvcEvent $e)
    {
        $event->getSharedManager()
          ->attach('Zend\Mvc\Controller\AbstractActionController',
                    'dispatch', 
         function($e) {
              $controller = $e->getTarget();
    
             //check if logged in, setting up the userid variable of controllers
            if ($e->getApplication()->getServiceManager()->get('AuthService')
                                            ->hasIdentity()) {
                   $users = $e->getApplication()->getServiceManager()
                            ->get('SanAuth\Model\AuthStorage')->read();

                   $controller->userid = $users['id'];
             }
        }, 100);
    }
}

So Full ? make it separate…

class Module
{
    public function onBootstrap(MvcEvent $e)
    {
        $event = $e->getApplication()->getEventManager();
        $event->getSharedManager()
          ->attach('Zend\Mvc\Controller\AbstractActionController',
                    'dispatch',
                array($this, 'settingUpControllerVariables'), 100
        );
    }

    public function settingUpControllerVariables(MvcEvent $e)
    {
        $controller = $e->getTarget();
    
        //check if logged in, setting up the userid variable of controllers
        if ($e->getApplication()->getServiceManager()
                            ->get('AuthService')
                            ->hasIdentity()) {
            $users = $e->getApplication()->getServiceManager()
                            ->get('SanAuth\Model\AuthStorage')->read();

            $controller->userid = $users['id'];
        }
    }
}

Hm…, i think it’s enough :D

References :

Zend Framework 2 : Cache Module Config and Module Classmap

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on March 13, 2013

zf2-zendframework2We already know, that all module configs and class maps over modules are merged. But merging in every request is not a good idea because it will pay the time. If our application is run on production environment, our ZF2 application should use cache to cache config and the classmap too (Whether or not you create a class map for your modules classes, class map cache will be created automatically.). So, it can reduce time access, and of course, reduce memory to process it in the next time refresh.
I will show you how to use it.
1. Create a cache directory like the following
Screen Shot 2013-03-13 at 2.50.25 AM
2. Make the cache directory writable

sudo chmod -R 777 /path/to/zf21_learn/data/cache/modulecache

3. Configure config/application.config.php

return array(
    'modules' => array(
        'Application',
        'ZFTool',
        'Album'
        'ZendDeveloperTools'
    ),
    'module_listener_options' => array(
        'module_paths' => array(
            './module',
            './vendor'
            ),
        'config_glob_paths' => array('config/autoload/{,*.}{global,local}.php'),
        
        // Whether or not to enable a configuration cache.
        // If enabled, the merged configuration will be cached and used in
        // subsequent requests.
        'config_cache_enabled' => true,
        // The key used to create the configuration cache file name.
        'config_cache_key' => "2245023265ae4cf87d02c8b6ba991139",

        // Whether or not to enable a module class map cache.
        // If enabled, creates a module class map cache which will be used
        // by in future requests, to reduce the autoloading process.
        'module_map_cache_enabled' => true,
        // The key used to create the class map cache file name.
        'module_map_cache_key' => "496fe9daf9baed5ab03314f04518b928",
        
        
        // The path in which to cache merged configuration.
        'cache_dir' => "./data/cache/modulecache",
    )
);

4. Test run your ZF2 App

Once you’ve run, your cache files will be created like the following :
Screen Shot 2013-03-13 at 2.57.51 AM

References :
1. https://github.com/zendframework/ZendSkeletonApplication/blob/master/config/application.config.php

Zend Framework 2 : Working with SQL Server

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on March 8, 2013

zf2-zendframework2 In some situation, work on windows environment is not as easy as you’ve imagine. SQL Server is a powerfull database platform with complex features and it means complex requirements too. In this post, I will show you step by step to work with.

1. Install Your SQL Server
2. Download SqlSrv extension for PHP, you can download from http://www.microsoft.com/en-us/download/details.aspx?id=20098 , I’m prefer using *V30.EXE
3. Run the executable file, and you will find an input which show message “Please type the location where you want to place the extacted files. : “, fill in with your php ext directory.
4. Download Microsoft® SQL Server® 2012 Native Client here : http://www.microsoft.com/en-us/download/details.aspx?id=29065 and install the lib. ( choose between X64 Package (sqlncli.msi) or X86 Package (sqlncli.msi) ).
5. Register SqlSrv extension into php extension in php.ini

// or 54 based on your php version
extension=php_sqlsrv_53_ts.dll

6. Restart your web server
7. Configure your db adapter

'db' => array(
    'driver' => 'Sqlsrv',
    'server' => 'ACER-PC',
    'Database' => 'YourDB',
    'USER' => 'sa', //if you're using sa
    'password' => 'yourpassword'
);

Done !, let’s run.

Note : It’s work for SQL Server 2008.

Symfony 2 : Using Symfony2 Bundle in Zend Framework 2 Project

Posted in Symfony2 Framework, Tutorial PHP, Zend Framework 2 by samsonasik on March 2, 2013

symfony2-distributionsSymfony2 comes with Bundle, Zend Framework 2 comes with Module, The reusability code is coming. We can use a piece of them in other framework if we want. I think, besides of write about using ZF2 Module in SF2 project, it worth to write about it ( SF2 Bundle in ZF2 Project ).

Ok, let’s start, for example, I have an SF2 Bundle like the following :
SF2BUNDLE
Which has classes like the following :
a. ExampleDependentInterface

namespace San\MySFBundle\Classes;

interface ExampleDependentInterface
{
    public function run($parameter);
}

b. ExampleDependent class that implement ExampleDependentInterface

namespace San\MySFBundle\Classes;

class ExampleDependent implements ExampleDependentInterface
{
    public function run($environment)
    {
        echo 'ExampleDependent run in '.$environment;
    }
}

c. ExampleClass class that pass class implemented ExampleDependentInterface

namespace San\MySFBundle\Classes;

class ExampleClass
{
    protected $dependent;

    public function __construct(ExampleDependentInterface $dependent)
    {
        $this->dependent = $dependent;
    }

    public function run($env = 'Symfony2')
    {
       $this->dependent->run($env);
    }
}

Now, register ExampleClass in services.php ( resources/config/services.php)

use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

$container->setDefinition(
    'my_sample_service', new Definition('San\MySFBundle\Classes\ExampleDependent')
);

$container->setDefinition(
    'san_my_sf.example',
    new Definition(
        'San\MySFBundle\Classes\ExampleClass',
        array(
            new Reference('my_sample_service')
        )
    )
);

Bundle ‘definitions’ done!, let’s copy to ZF2 Project like the following :
ZF2withSF2Bundle

When Bundle copied, let’s configure Our ZF2 App.
1. create file named SF2AppKernel.php in ZF2 App ( config/SF2AppKernel.php )

use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Config\Loader\LoaderInterface;

class SF2AppKernel extends Kernel
{
    public function registerBundles()
    {
        $bundles = array(
            new San\MySFBundle\SanMySFBundle(),
        );

        return $bundles;
    }

    public function registerContainerConfiguration(LoaderInterface $loader){}
}

2. add require symfony/symfony to your composer.json

........
    "require": {
        "php": ">=5.3.3",
        "zendframework/zendframework": "2.*",
        "zendframework/zftool": "dev-master",
        "symfony/symfony": "2.1.*"
    }
........

4. Type

composer update

3. modify init_autoloader.php

if (file_exists('vendor/autoload.php')) {
    $loader = include 'vendor/autoload.php';
    $loader->add('San\\', __DIR__.'/module/SF2Bundles');
}
..........

4. Modify public/index.php to initialize SF2 Bundles and SF2 Container, and register the Container into ZF2 ServiceManager

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

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

// Setup autoloading
require 'init_autoloader.php';

//load SF2 App Kernel here...
require 'config/SF2AppKernel.php';
$kernel = new SF2AppKernel('prod', true);
$kernel->loadClassCache();

//initialize Bundles and Container
$kernel->boot();

$SF2Container = $kernel->getContainer();

/* extract
    Zend\Mvc\Application::init(require 'config/application.config.php')->run();
    to the following to register SF2Container into ZF2 ServiceManager
*/
$configuration = require 'config/application.config.php';
$smConfig = isset($configuration['service_manager']) ? $configuration['service_manager'] : array();
$serviceManager = new ServiceManager(new Service\ServiceManagerConfig($smConfig));
$serviceManager->setService('ApplicationConfig', $configuration);
$serviceManager->get('ModuleManager')->loadModules();

$serviceManager->setService('SF2Container', $SF2Container);

//run...
$serviceManager->get('Application')->bootstrap()->run();

5. Ok, configuration done!, let’s call the SF2 Bundle Service via Controller in Zend Framework 2 App :

$sl = $this->getServiceLocator()
$sl->get('SF2Container')->get('san_my_sf.example')
                        ->run('ZF2');

References :
1. https://speakerdeck.com/skoop/zend-framework-2-and-symfony2-the-perfect-team-zendcon

Zend Framework 2 : Using Zend Framework 2 Module in Symfony 2 Project

Posted in Symfony2 Framework, Tutorial PHP, Zend Framework 2 by samsonasik on March 1, 2013

zf2-zendframework2In ZF2, Module completely become re-usable code, It can be called even without Zend Framework MVC stack. I will give you extreme example, I will call my ZF2 module that contains service called by Symfony 2 Framework. It’s very rare, but sometime, you don’t need to Reinvent Your created module, even it created in other environment.

For example, I have a ZF2 Module like the following :

zfmodule
Which has Service like the following :
a. Service Class ( ZFModule\Service\MySampleService.php )

namespace ZFModule\Service;

class MySampleService
{
    protected $config;
    
    public function getConfig()
    {
        return $this->config;
    }
    
    public function setConfig(array $config)
    {
        $this->config = $config;
    }
}

b. The Service Factory ( ZFModule\Service\MySampleServiceFactory )

namespace ZFModule\Service;

use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class MySampleServiceFactory implements FactoryInterface
{
    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        $config = $serviceLocator->get('Config');
        
        $service  = new MySampleService;
        $service->setConfig($config);

        return $service;
    }
}

c. Register MySampleService into ZF2 Service Manager

namespace ZFModule;

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

    public function getServiceConfig()
    {
        return array(
            'factories' => array(
                'MySampleService' => 'ZFModule\Service\MySampleServiceFactory'  
            ),
        );
    }
}

Ok, let’s go step by step :
1. Copy the module to Your Symfony Project.
symfonyprjzfmodule
2. add require zendframework/zendframework to your composer.json

.....
require {
"php": ">=5.3.3",
"symfony/symfony": "2.1.*",
"zendframework/zendframework" : "2.1.3"
}
.....

3. Type :

composer update

4. Create a class to get Service From Your ZF2Module (SymfonyPrj/src/Acme\DemoBundle\Service\MyZFModuleService.php )

namespace Acme\DemoBundle\Service;

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

class MyZFModuleService 
{
    protected $sampleZFService;
    
    public function __construct()
    {
        $serviceManager = new ServiceManager(new ServiceManagerConfig(array()));
        $serviceManager->setService('ApplicationConfig', array(
               'modules' => array(
                   'ZFModule'
               ),
               'module_listener_options' => array()
        ));
        $serviceManager->get('ModuleManager')->loadModules();
        
        //MySampleService is a Service from Your ZF2 Module 
        $this->sampleZFService = $serviceManager->get('MySampleService');
    }
    
    public function dump()
    {
        echo '<pre>';
        print_r($this->sampleZFService->getConfig());
        echo '</pre>';
    }
}

5. Register to Symfony Services :

...
       <service id="MyZFModuleService" class="Acme\DemoBundle\Service\MyZFModuleService">
       </service>
...

6. Ok, check it in your Symfony2 Controller Action :

$serviceZF = $this->get('MyZFModuleService');
$serviceZF->dump();

References :
1. http://zend-framework-community.634137.n4.nabble.com/Doctrine-2-module-is-tight-to-MVC-td4659346.html

Practical GIT (3) : Remove Specific commits

Posted in GIT, Teknologi by samsonasik on February 24, 2013

git-logo If you’are working with pilot project that many feature are experiment conditions, you need this feature, You can remove commits that you don’t want to exist in your revisions log. This feature named rebase. You can find the whole commits lists on range by typing :

git rebase -i HEAD~5

that command will show you last 5 commit and show them in the editor like the following :

pick 5c22314 third commit
pick 8de60b5 fourth commit
pick 9d3556f fifth commit
pick 434ddc3 sixth commit
pick d9b917g seventh commit

# Rebase d9b917f..8de60b5 onto d9b917f
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#
~

~

If you want to remove fifth and sixth commit, just remove these two lines and save the editor ( if you’re working with vim, type ‘dd’ in the line you want to delete, and type ‘:wq!’. After do that, check by typing command :

git log

and you will get the result like the following ( fifth and sixth commit deleted ! )

commit d9b917g9d3556cfb0d0bbcd8ecd952cf4f358eea
Author: Abdul Malik Ikhsan <samsonasik@gmail.com>
Date:   Wed Feb 20 15:47:13 2013 +0700

    seventh commit

commit 8de60b59d3556cd8eeafb0d0bbcd8ec952cf4f35
Author: Abdul Malik Ikhsan <samsonasik@gmail.com>
Date:   Wed Feb 20 15:47:13 2013 +0700

    fourth commit

commit 5c223149d3556cd952cf4f358eeafb0d0bbcd8ec
Author: Abdul Malik Ikhsan <samsonasik@gmail.com>
Date:   Wed Feb 20 15:47:13 2013 +0700

    third commit

commit 5c2231434ddc3bac911ab38a22d2b47f5736f7d4
Author: Abdul Malik Ikhsan <samsonasik@gmail.com>
Date:   Wed Feb 20 15:46:50 2013 +0700

    second commit

commit d9b917f6a48eb9c0c415772f70b7c66adf3de0b5
Author: Abdul Malik Ikhsan <samsonasik@gmail.com>
Date:   Wed Feb 20 15:45:26 2013 +0700

    first commit

With rebase, you can combine commits to/from other branch or squash commits with commands list while you get the list of commit via rebase -i command.

references :
1. http://www.kernel.org/pub/software/scm/git/docs/git-rebase.html

Zend Framework 2 : Check Module Dependency

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

zf2-zendframework2 Zend Framework 2.0.7 and 2.1 were released! They come with new features and more improvements. One of the improvements is ability to check other module already loaded before. It provide Zend\ModuleManager\Feature\DependencyIndicatorInterface.
The sample code is like the following :

namespace Mod2;

use Zend\ModuleManager\Feature\DependencyIndicatorInterface;

class Module implements DependencyIndicatorInterface
{   
    public function getModuleDependencies()
    {
        return array('Mod1');   
    }
    
    public function getConfig() { /* common code here */ }
    public function getAutoloaderConfig() { /* common code here */ }
}

If Mod1 is not loaded before Mod2, it will show the exception : Module “Mod2″ depends on module “Mod1″

References :
1. https://github.com/zendframework/zf2/issues/3427
2. https://github.com/zendframework/zf2/pull/3443

Rsync : Synchronize local with shared hosting

Posted in Teknologi, tips and tricks by samsonasik on January 26, 2013

rsyncAs web developer, We already realize that we are lazy. Our client is often only have shared hosting, not dedicated server that we can install version control which simplify push and pull. We often forgot to upload some file we changed/added in local to shared hosting, and it make web is not run properly in shared hosting.  The solution is to use rsync in local PC.  Rsync is a file transfer program for Unix systems. rsync uses the ‘rsync algorithm’ which provides a very fast method for bringing remote files into sync. An important feature of rsync not found in most similar programs/protocols is that the mirroring takes place with only one transmission in each direction. Rsync can copy or display directory contents and copy files, optionally using compression and recursion.
So, you need to know the step to do this :
1. Go to your Cpanel
2. Click Ftp Account
Screen Shot 2013-01-26 at 4.36.40 PM
3. Click Configure FTP Client in your desired user
Screen Shot 2013-01-26 at 4.39.14 PM

And see the SFTP Port. Use the port to in rsync port target transfer.
4. Start syncing…

cd ~/www && rsync -timeout=50000 -av --exclude ".git" --exclude ".gitignore" --exclude "wp-config.php"  wordpress_local_dev/ -e "ssh -p 2223" yourremoteserveruser@yourdomain.com:/home/yourremoteserveruser/public_html/

The option –exclude will exclude unneeded to sync.
The option -timeout is what second to be connection to timeout.
And you will see the following ( just updated / added file will be uploaded ) :

5. Done !

References :
Image : http://www.haykranen.nl/wp-content/uploads/2008/05/timemachine.jpg
Source :
1. http://mike-hostetler.com/blog/2007/12/08/rsync-non-standard-ssh-port/
2. http://en.wikipedia.org/wiki/Rsync

Zend Framework 2 : ZFTool – Zend Framework 2 Module to Ease Development

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on January 16, 2013

zf2-zendframework2ZFTool is an utility module for maintaining modular Zend Framework 2 applications. It runs from the command line and can be installed as ZF2 module or as PHAR.
It currently can show installed modules, create a new module, or create a project.
1. Installation
Open your composer.json in your skeleton application :

{
    "name": "zendframework/skeleton-application",
    "description": "Skeleton Application for ZF2",
    "license": "BSD-3-Clause",
    "keywords": [
        "framework",
        "zf2"
    ],
    "homepage": "http://framework.zend.com/",
    "require": {
        "php": ">=5.3.3",
        "zendframework/zendframework": "2.*",
        "zendframework/zftool": "dev-master"
    }
}

and add “zendframework/zftool”: “dev-master” in require, and run :

php composer.phar self-update && php composer.phar update 

OR, you can remove above step by run the following :

composer require zendframework/zftool:dev-master

and You will see process like this :
zftool_img1
and your downloaded ZFTool show in your vendor folder.
Last step for installation, edit your config/application.config.php and add ZFTool to modules array.

2. Using ZFTool
a. Getting started : Show list commands
Type :

php public/index.php

and you will see the following list :
zftool_img2

b. show modules
Type :

php public/index.php modules

and you will see like the following :
zftool_img3

c. create module
Type :

//testmodule is name of module we want to create
php public/index.php create module testmodule

and if success, you will see like the following :
zftool_img4

d. create a project
Type :

//new_prj is name of project we want to create
php public/index.php create project ../new_prj

and if success, you will see like the following :
zftool_img5

References :
https://github.com/zendframework/ZFTool

Zend Framework 2 ‘Cheat Sheet’ : Zend\Db

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on January 15, 2013

zf2-zendframework2Zend Framework is a Full stack and component based framework. It can be used by standalone without full of components. At this time, I will post about Zend\Db. I hope this post will help You to try this component with or without full framework.
Continue reading

Zend Framework 2 ‘Cheat Sheet’ : Service Manager

Posted in Teknologi, Tutorial PHP, Zend Framework 2 by samsonasik on January 2, 2013

zf2-zendframework2The Service Locator design pattern is implemented by the ServiceManager. The Service Locator is a service/object locator, tasked with retrieving other objects.

Get the SM :
1. Inside Controller

$serviceLocator = $this->getServiceLocator();

2. Inside Module.php

namespace YourModule\Service;

use Zend\Mvc\MvcEvent;

class Module
{
    public function onBootstrap(MvcEvent $e)
    {
        $sm = $e->getApplication()->getServiceManager();
    }
}

3. Inside Controller Plugin

$serviceLocator = $this->getController()->getServiceLocator();

Types of Services
The registered name of service is not case sensitive. There are the type of services :
a. invokables : an array of service name/class name pairs. The class name should be class that may be directly instantiated without any constructor arguments
for ex :

//YourModule/config/module.config.php
return array(
    'controllers'=>array(
        'invokables' => array(
            'SanUser\Controller\User' => 'SanUser\Controller\UserController'
        ),
    ),
);

b. abstract_factories : Unknown Services ( The “Limbo” if ServiceManager failed to search in registered services)

//YourModule/src/YourModule/Service/CommonControlAppAbstractFactory.php
namespace YourModule\Service;

use Zend\ServiceManager\AbstractFactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class CommonControlAppAbstractFactory implements AbstractFactoryInterface
{
    public function canCreateServiceWithName(ServiceLocatorInterface $locator, $name, $requestedName)
    {
        if (class_exists($requestedName.'Controller')){
            return true;
        }

        return false;
    }

    public function createServiceWithName(ServiceLocatorInterface $locator, $name, $requestedName)
    {
        $class = $requestedName.'Controller';
        return new $class;
    }
}

Then, register in SM :

//YourModule/config/module.config.php
return array(
    'controllers'=> array(
	    'abstract_factories' => array(
		'YourModule\Service\CommonControlAppAbstractFactory',
	    ),
	);
    ),
);

In this case, if SM could not find controllers in invokables, the SM will turn to it whenever canCreateServiceWithName return true; ( controllers is service that called automatically by mvc stack )
What if you want other service ? This is it :

namespace YourModule\Service;

use Zend\ServiceManager\AbstractFactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class CommonModelTableAbstractFactory implements AbstractFactoryInterface
{
    public function canCreateServiceWithName(ServiceLocatorInterface $locator, $name, $requestedName)
    {
        return (substr($requestedName, -5) === 'Table');
    }

    public function createServiceWithName(ServiceLocatorInterface $locator, $name, $requestedName)
    {
        $db = $locator->get('Zend\Db\Adapter\Adapter');
        $tablemodel = new $requestedName;
        $tablemodel->setDbAdapter($db);
        
        return $tablemodel;
    }
}

You want if you call un-registered table model, you automatically create service with this abstract factory whenever last 5 chars of the service called = ‘Table’. Register this abstract factory under service_manager key :

//YourModule/config/module.config.php
return array(
    'service_manager'=> array(
	    'abstract_factories' => array(
		'YourModule\Service\CommonModelTableAbstractFactory',
	    ),
	);
    ),
);

Note for abstract_factories : Being explicit is more secure and reliable. You should not forgot to register service you write into ServiceManager.

c. factories : an array of service name/factory class name pairs.
c.1. If you are using PHP configuration files, you may provide any PHP callable as the factory.

//YourModule/config/module.config.php
return array(
    'service_manager'=>array(
        'factories' => array(
            'MyTable' => function ($sm) {
                $db = $sm->get('Zend\Db\Adapter\DbAdapter');
                $table = new \YourModule\Model\MyTableModel();
                $table->setDbAdapter($db);
            },
        ),
    ),
);

c.2. by implementing Zend\ServiceManager\FactoryInterface by create a factory first :

namespace YourModule\Service;

use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class MyTableFactory implements FactoryInterface
{
    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        $db = $serviceLocator->get('Zend\Db\Adapter\DbAdapter');
        $table = new \YourModule\Model\MyTableModel();
        $table->setDbAdapter($db);

        return $table;
    }
}

And the factories registered just like the following :

//YourModule/config/module.config.php
return array(
    'service_manager'=>array(
        'factories' => array(
            'MyTable' => 'YourModule\Service\MyTableFactory'
        ),
    ),
);

d. aliases : which should be an associative array of alias name/target name pairs (where the target name may also be an alias).

//YourModule/config/module.config.php
return array(
    'service_manager'=>array(
        'factories' => array(
            'MyTable' => 'YourModule\Service\MyTableFactory'
        ),
        'aliases' => array(
            'YourModule\Model\MyTable' => 'MyTable',
        ),
    ),
);

e. shared :an array of service name/boolean pairs, indicating whether or not a service should be shared. By default, the ServiceManager assumes all services are shared, but you may specify a boolean false value here to indicate a new instance should be returned.

//YourModule/config/module.config.php
return array(
    'service_manager'=>array(
        'factories' => array(
            'MyTable' => 'YourModule\Service\MyTableFactory'
        ),
        'shared' => array(
            // Usually, you'll only indicate services that should _NOT_ be
            // shared -- i.e., ones where you want a different instance
            // every time.
            'MyTable' => false,
        ),
    ),
);

f. services : an array of service name/object pairs. Clearly, this will only work with PHP configuration.

//YourModule/config/module.config.php
return array(
    'service_manager'=>array(
        'services' => array(
            // Keys are the service names
            // Values are objects
            'Auth' => new YourModule\Authentication\AuthenticationService(),
        ),
    ),
);

g. initializers
It initialize the service whenever service created. It can reduce the redundance the injections to services.

//YourModule/config/module.config.php
return array(
    'service_manager'=>array(
        'initializers' => array(
	    function ($instance, $sm) {
		if ($instance instanceof \Zend\Db\Adapter\AdapterAwareInterface) {
		    $instance->setDbAdapter($sm->get('Zend\Db\Adapter\Adapter'));
		}
	    }
	),
    ),
);

And you should not to inject Adapter manually in the Table Class :

namespace YourModule\Model;

use Zend\Db\TableGateway\AbstractTableGateway;
use Zend\Db\Adapter\AdapterAwareInterface;
use Zend\Db\Adapter\Adapter;

class UserTable extends AbstractTableGateway
    implements AdapterAwareInterface
{
    protected $table = 'zf2_users';

    public function setDbAdapter(Adapter $adapter)
    {
        $this->adapter = $adapter;
        $this->initialize();
    }
}

Just invoke at service_manager :

//YourModule/config/module.config.php
return array(
    'service_manager'=>array(
        'initializers' => array(
	    function ($instance, $sm) {
		if ($instance instanceof \Zend\Db\Adapter\AdapterAwareInterface) {
		    $instance->setDbAdapter($sm->get('Zend\Db\Adapter\Adapter'));
		}
	    }
	),
        'invokables' => array(
            'YourModule\Model\UserTable' => 'YourModule\Model\UserTable'
        )
    ),
);

h. allow_override
Override your existing Services.

//YourModule/config/module.config.php
return array(
    'service_manager'=>array(
        'factories' => array(
            'MyService' => 'YourModule\Service\MyServiceFactory'
        ),
        'allow_override' => array(
            'MyService' => true,
        ),
    ),
);

The top level configuration keys

Manager Key name in configuration array Method name in Module.php
ServiceManager service_manager getServiceConfig()
ViewHelperManager view_helpers getViewHelperConfig()
ControllerPluginManager controller_plugins getControllerPluginConfig()
ControllerLoader controllers getControllerConfig()

Sample Code in Module.php :

class Module
{
    //for 'service_manager'
    public function getServiceConfig()
    {
        return array(
            'invokables' => array( /* see Types of Services */  ),
            'factories' => array( /* see Types of Services */ ),
            'abstract_factories' => array( /* see Types of Services */ ),
            'aliases' => array( /* see Types of Services */ ),
            'services' => array( /* see Types of Services */ ),
            'initializers' => array( /* see Types of Services */ ),
            'shared' => array(/* see Types of Services */),
        );
    }

    //for 'controllers' -> it automatically composed by mvc stack
    //no need to call by your hand ( get('...') );
    public function getControllerConfig()
    {
        return array(
            /* looks like above code */
        );
    }

    //for 'controller_plugins'
    public function getControllerPluginConfig()
    {
        return array(
            /* looks like above code */
        );
    }

    //for 'view_helpers' call in view by $this->nameViewHelperRegistered()->dosomething()
    public function getViewHelperConfig()
    {
        return array(
            /* looks like above code */
        );
    }
}

For ZF 2.1 ( still in dev branch, the keys added with FormElementManager )

Manager Key name in configuration array Method name in Module.php
FormElementManager form_elements getFormElementConfig()

For what have to do to create a Controller pLugin , see : empirio’s post about creating Controller pLugin in ZF2. For what have to do to create a View Helper , see : my post about creating view helper or EvanDotPro’s post.

Hope this post helpful ;) . Happy new Year!

References :
1. http://zf2.readthedocs.org/en/latest/modules/zend.service-manager.intro.html
2. http://akrabat.com/zend-framework-2/zendservicemanager-configuration-keys/
3. http://juriansluiman.nl/en/article/120/using-zend-framework-service-managers-in-your-application
4. http://blog.evan.pro/creating-a-simple-view-helper-in-zend-framework-2
5. http://lab.empirio.no/custom-controller-plugin-in-zf2.html

Zend Framework 2 : Automatic Controller Invokables via Abstract Factories

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on December 23, 2012

zf2-zendframework2 Before you continue reading, you should know that this is just to handle if you forgot to mention/register your controller in invokables/factories. Being explicit is more secure and reliable.

Ok then, let’s start. One of the Zend Framework Service Managers keys is abstract_factories. An abstract factory can be considered a “fallback” – if the service does not exist in the manager, it will then pass it to any abstract factories attached to it until one of them is able to return an object. For example, we want to automatic register class of our controllers of our application if we forgot to register in invokables.
1. Create an abstract factories

//module/SanCommons/src/SanCommons/Services/CommonControlAppAbstractFactory.php
namespace SanCommons\Services;

use Zend\ServiceManager\AbstractFactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class CommonControlAppAbstractFactory implements AbstractFactoryInterface
{
    public function canCreateServiceWithName(ServiceLocatorInterface $locator, $name, $requestedName)
    {
        if (class_exists($requestedName.'Controller')){
            return true;
        }

        return false;
    }

    public function createServiceWithName(ServiceLocatorInterface $locator, $name, $requestedName)
    {
        $class = $requestedName.'Controller';
        return new $class;
    }
}

2. Register to getControllerConfig()

//module/SanCommons/Module.php
namespace SanCommons;

class Module
{
    public function getControllerConfig()
    {
	return array(
	    'abstract_factories' => array(
		'SanCommons\Services\CommonControlAppAbstractFactory'
	    ),
	);
    }
    public function getAutoloaderConfig(){ /*common code*/}
    public function getConfig(){ /*common code*/}
}

Remember, ‘controllers’ and ‘ModuleManager’ are the services that composed automatically by mvc stack. So, if route found a match ‘/:controller’ segment, ServiceManager will find a Service that named the Name of The Controller that pass in url in ‘controllers’ => ‘invokables’ key.

//module/SanCommons/config/module.config.php
return array(
    'controllers' => array(
       'invokables' => array(
         'YourModule\Controller\A' => 'YourModule\Controller\AController',
         'YourModule\Controller\B' => 'YourModule\Controller\BController',
         /*.. etc ...*/
       ),
     ),
),

The controllers services named ‘YourModule\Controller\A’, etc. Whenever you forgot to mention/register that in ServiceManager, ServiceManager will find it in the ‘Limbo’ (abstract_factories). If the abstract factory returns true to the canCreateServiceWithName method the service manager will create a service via createServiceWithName method.

For example : If we just created a controller with end with ‘Controller’, for example, TestAutoController, and forgot to register it into invokables, The Controller will automatically getted because service automatically created.

What if we want other service that not in Mvc Stack, and need to be called by ServiceLocator with ‘hand’ :D ? create abstract factory and register into abstract_factories under getServiceConfig().

Done !

References :
1. http://akrabat.com/zend-framework-2/zendservicemanager-configuration-keys/
2. http://zf2.readthedocs.org/en/latest/modules/zend.service-manager.intro.html
3. http://www.stephenrhoades.com/?p=513
4. http://www.framework.zend.com/manual/2.0/en/user-guide/routing-and-controllers.html
5. http://framework.zend.com/manual/2.0/en/modules/zend.mvc.quick-start.html#create-a-route

Follow

Get every new post delivered to your Inbox.

Join 103 other followers