Welcome to Abdul Malik Ikhsan's Blog

Testing Zend Framework 2 factory using Prophecy

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on August 29, 2015

Simulate every detail what classes methods doing with phpunit sometime make you little crazy and make you do too many effort ? There is a library to reduce that! It named Prophecy, a “Highly opinionated mocking framework for PHP 5.3+”.

In this post, I want to show you in Zend Framework 2 application case, which if we are using ZF2 ~2.4.0, we already have zendframework/zend-test which require phpunit/phpunit:~4.0. The Prophecy is included in phpunit start from 4.7, so the requirement already met. If we use old ZF2 version, we can add "phpspec/prophecy-phpunit": "~1.5" under require-dev in our composer requirement.

For example, we have a factory class for controller creation like the following:

namespace MyModule\Factory\Controller;

use MyModule\Controller\MyController;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\ServiceManager\FactoryInterface;

class MyControllerFactory implements FactoryInterface
{
    /**
     * @{inheritDoc}
     */
    public function createService(ServiceLocatorInterface $sl)
    {
        $services           = $sl->getServiceLocator();
        $formElementManager = $services->get('FormElementManager');
        $myForm             = $formElementManager->get('MyModule\Form\MyForm');
        
        $controller = new MyController($myForm);

        return $controller;
    }
}

Without Prophecy, we need write the tests like the following:

namespace MyModule\Factory\Controller;

use PHPUnit_Framework_TestCase;
use MyModule\Factory\Controller\MyControllerFactory;

/**
 * @author Abdul Malik Ikhsan <samsonasik@gmail.com>
 */
class MyControllerFactoryTest extends PHPUnit_Framework_TestCase
{
    /** @var MyControllerFactory */
    protected $factory;

    /** @var ServiceLocatorInterface */
    protected $serviceLocator;

    /** @var \Zend\Mvc\Controller\ControllerManager */
    protected $controllerManager;

    public function setUp()
    {
        $this->controllerManager = $this->getMockBuilder('Zend\Mvc\Controller\ControllerManager') 
                                        ->disableOriginalConstructor()
                                        ->getMock();
        $this->serviceLocator    = $this->getMock('Zend\ServiceManager\ServiceLocatorInterface');

        $factory = new MyControllerFactory();
        $this->factory = $factory;
    }

    public function testCreateService()
    {
        $formElementManager = $this->getMockBuilder('Zend\Form\FormElementManager')
                                   ->disableOriginalConstructor()
                                   ->getMock();
        $myForm  = $this->getMockBuilder('MyModule\Form\MyForm')
                                   ->disableOriginalConstructor()
                                   ->getMock();
        $formElementManager->expects($this->once())
                           ->method('get')
                           ->willReturn($myForm);
        $this->serviceLocator->expects($this->once())
                             ->method('get')
                             ->with('FormElementManager')
                             ->willReturn($FormElementManager);
        
        $this->controllerManager->expects($this->once())
                                ->method('getServiceLocator')
                                ->willReturn($this->serviceLocator);

        $result = $this->factory->createService($this->controllerManager);
        $this->assertInstanceOf('MyModule\Controller\MyControllerFactory', $result);
    }
}

This is the example for 1 dependency, we can to call expects($this->once()), what if we have many dependencies which use many services?, we can end up doing something like this:

$myService1 = $this->getMockBuilder('MyModule\Service\MyService1')
                   ->disableOriginalConstructor()
                   ->getMock();
$this->serviceLocator->expects($this->at(0))
                     ->method('get')
                     ->with('MyService1')
                     ->willReturn($myService1);
$myService2 = $this->getMockBuilder('MyModule\Service\MyService2')
                   ->disableOriginalConstructor()
                   ->getMock();
$this->serviceLocator->expects($this->at(1))
                  ->method('get')
                  ->with('MyService2')
                  ->willReturn($myService2);

And if we added a service dependency in the middle, we need to reset the increment.

With Prophecy, it now simplified, we can eliminate them, and use the mock just like the object do, like the following:

$formElementManager = $this->prophesize('Zend\Form\FormElementManager');
$myForm             = $this->prophesize('MyModule\Form\MyForm');
$formElementManager->get('MyModule\Form\MyForm')->willReturn($myForm);

Let’s see the full code for testing factory above if we are using Prophecy:

namespace MyModule\Factory\Controller;

use PHPUnit_Framework_TestCase;
use MyModule\Factory\Controller\MyControllerFactory;

/**
 * @author Abdul Malik Ikhsan <samsonasik@gmail.com>
 */
class MyControllerFactoryTest extends PHPUnit_Framework_TestCase
{
    /** @var MyControllerFactory */
    protected $factory;

    /** @var ServiceLocatorInterface */
    protected $serviceLocator;

    /** @var \Zend\Mvc\Controller\ControllerManager */
    protected $controllerManager;

    public function setUp()
    {
        $this->controllerManager = $this->prophesize('Zend\Mvc\Controller\ControllerManager');
        $this->serviceLocator    = $this->prophesize('Zend\ServiceManager\ServiceLocatorInterface');

        $factory = new MyControllerFactory();
        $this->factory = $factory;
    }

    public function testCreateService()
    {
        $formElementManager = $this->prophesize('Zend\Form\FormElementManager');
        $myForm             = $this->prophesize('MyModule\Form\MyForm');
        $formElementManager->get('MyModule\Form\MyForm')->willReturn($myForm);
        $this->serviceLocator->get('FormElementManager')->willReturn($formElementManager);
        $this->controllerManager->getServiceLocator()->willReturn($this->serviceLocator);

        $result = $this->factory->createService($this->controllerManager->reveal());
        $this->assertInstanceOf('MyModule\Controller\MyControllerFactory', $result);
    }
}

That’s it ;). It now simplified.

Tagged with: ,

Zend Framework 2 : Using __invoke(PluginManager $manager) in Service’s Factory

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on March 31, 2015

zf2-zendframework2I assume you already knew about registering service with factories type via closure or factory class that implements Zend\ServiceManager\FactoryInterface which is quite complex to do, and if we use pluginManager, for example, on Controller creation, we pushed to use :

namespace Application\Factory\Controller;

use Application\Controller\IndexController;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManger\ServiceLocatorInterface;

class IndexControllerFactory implements FactoryInterface
{
    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        $services  = $serviceLocator->getServiceLocator();    
        $myService = $services->get('MyService');
        
        return new IndexController($myService);
    }
}

Although above way will work, if you check above code with scrutinizer, you will get error :
interface-error-implementation

This is because the getServiceLocator() only exists in the concrete implementation(the ControllerManager class), not in the ServiceLocatorInterface interface.

__invoke(PluginManager $manager) for the rescue

If you read the documentation, you can get quote :

The factories should be either classes implementing Zend\ServiceManager\FactoryInterface 
or invokable classes.

That mean, you can use class that has __invoke() method, so you can pass the PluginManager instead of ServiceLocatorInterface.

namespace Application\Factory\Controller;

use Application\Controller\IndexController;
use Zend\Mvc\Controller\ControllerManager;

class IndexControllerFactory
{
    public function __invoke(ControllerManager $controllerManager)
    {
        $services  = $controllerManager->getServiceLocator();
        $myService = $services->get('MyService');
        
        return new IndexController($myService);
    }
}

Now, your scrutinizer check will be happy ;).

Note :
As Lucas Suggestion, if you still want to use createService(), you can check it with whenever $serviceLocator instanceof Zend\ServiceManager\ServiceLocatorAwareInterface, then we call getServiceLocator() :

// ...
function createService(ServiceLocatorInterface $serviceLocator)
{
   if ($serviceLocator instanceof ServiceLocatorAwareInterface) {
       $serviceLocator = $serviceLocator->getServiceLocator();
   }
   // ...
}
// ...

References :
1. http://blog.alejandrocelaya.com/2014/10/09/advanced-usage-of-service-manager-in-zend-framework-2/#comment-1627763990
2. https://samsonasik.wordpress.com/2013/01/02/zend-framework-2-cheat-sheet-service-manager/
3. http://framework.zend.com/manual/current/en/modules/zend.service-manager.quick-start.html#using-configuration

Using Doctrine Data Fixture for Testing QueryBuilder inside Repository

Posted in Tutorial PHP by samsonasik on March 24, 2015

Doctrine This is an immediate post to incorporate my latest post about Mocking Doctrine\ORM\AbstractQuery to test querybuilder. Well, while that works, Ocramius – A Doctrine core team – said that we shouldn’t do that way, as SQL and DQL are actual code. What we can do is using data fixture. There is a sample that he gave with ZF2 environment. In this post, I will try to write a demo with step by step about repository class that consume a QueryBuilder, so we can test it.

Let’s start with the Entity

Like my post before, I will going to use News entity :

namespace DoctrineFixtureDemo\Entity;

use Doctrine\ORM\Mapping as ORM;

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

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

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

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

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

        return $this;
    }

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

    /**
     * Set content.
     *
     * @param string $content
     *
     * @return self
     */
    public function setContent($content)
    {
        $this->content = $content;

        return $this;
    }

    /**
     * Get content.
     *
     * @return string
     */
    public function getContent()
    {
        return $this->content;
    }
}

The Repository

I have a repository to get latest news with limit parameter, like the following :

namespace DoctrineFixtureDemo\Repository;

use Doctrine\ORM\EntityRepository;

class NewsRepository extends EntityRepository
{
    /**
     * Get Latest News.
     *
     * @param int $limit
     *
     * @return array
     */
    public function getLatestNews($limit)
    {
        $result = $this->createQueryBuilder('n')
                       ->setFirstResult(0)
                       ->setMaxResults($limit)
                       ->getQuery()->getResult();

        return $result;
    }
}

Fixture Dependency

We need "doctrine/data-fixtures" dependency in our vendor/, we can require by composer command :

$ composer require "doctrine/data-fixtures 1.0.*"

Fixture Class

We can create a fixture class to insert some sample data to be tested.

namespace DoctrineFixtureDemo\DataFixture;

use DoctrineFixtureDemo\Entity\News;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\Common\DataFixtures\FixtureInterface;

class NewsLoad implements FixtureInterface
{
    public function load(ObjectManager $manager)
    {
        $news = new News();
        $news->setTitle('bar');
        $news->setContent('BarBazBat');

        $news2 = new News();
        $news2->setTitle('bar2');
        $news2->setContent('BarBazBat2');

        $manager->persist($news);
        $manager->persist($news2);
        $manager->flush();
    }
}

We are going to use Doctrine\Common\DataFixtures\Executor\ORMExecutor to execute loaded fixture class(es). We can setup fixture test by use database that only for test / not for production ( – note : this is just sample, you can rely on your framework for get the db setting, EntityManager, SchemaTool, etc – ) by creating class like the following :

namespace DoctrineFixtureDemotest;

use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Annotations\AnnotationRegistry;
use Doctrine\Common\DataFixtures\Purger\ORMPurger;
use Doctrine\Common\DataFixtures\Executor\ORMExecutor;
use Doctrine\ORM\Tools\Setup;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
use Doctrine\ORM\Tools\SchemaTool;

final class FixtureManager
{
    /**
     * Get EntityManager
     */
    public static function getEntityManager()
    {
        $paths = [dirname(__DIR__).'/src/DoctrineFixtureDemo/Entity'];
        $isDevMode = true;

        // the TEST DB connection configuration
        $connectionParams = [
            'driver'   => 'pdo_mysql',
            'user'     => 'root',
            'password' => '',
            'dbname'   => 'foobartest',
        ];

        $config = Setup::createConfiguration($isDevMode);
        $driver = new AnnotationDriver(new AnnotationReader(), $paths);

        AnnotationRegistry::registerLoader('class_exists');
        $config->setMetadataDriverImpl($driver);

        $entityManager = EntityManager::create($connectionParams, $config);

        return $entityManager;
    }
    
    /**
     * Make sure drop and create tables
     */
    public static function start()
    {
        $schemaTool = new SchemaTool(static::getEntityManager());
        $metadatas  = static::getEntityManager()
                            ->getMetadataFactory()
                            ->getAllMetadata();

        $schemaTool->dropSchema($metadatas);
        $schemaTool->createSchema($metadatas);
     }

    /**
     * @return ORMExecutor
     */
    public static function getFixtureExecutor()
    {
        return new ORMExecutor(
            static::getEntityManager(),
            new ORMPurger(static::getEntityManager())
        );
    }
}

In favor of above class, we can always call \DoctrineFixtureDemotest\FixtureManager::start(); at our phpunit’s bootstrap :

chdir(__DIR__);

$loader = null;
if (file_exists('../vendor/autoload.php')) {
    $loader = include '../vendor/autoload.php';
} else {
    throw new RuntimeException('vendor/autoload.php could not be found. Did you run `php composer.phar install`?');
}

$loader->add('DoctrineFixtureDemotest', __DIR__);
\DoctrineFixtureDemotest\FixtureManager::start();

So, all tables will be removed in the every beginning test.

Repository Test

Time for test, Our repository class test can be like the following :

namespace DoctrineFixtureDemoTest\Repository;

use DoctrineFixtureDemo\DataFixture\NewsLoad;
use DoctrineFixtureDemotest\FixtureManager;
use DoctrineFixtureDemo\Repository\NewsRepository;
use Doctrine\ORM\Mapping\ClassMetadata;
use PHPUnit_Framework_TestCase;

class NewsRepositoryTest extends PHPUnit_Framework_TestCase
{
    protected function setUp()
    {
        $this->repository      = new NewsRepository(
            FixtureManager::getEntityManager(),
            new ClassMetadata('DoctrineFixtureDemo\Entity\News')
        );

        $this->fixtureExecutor = FixtureManager::getFixtureExecutor();
    }

    public function testGetLatestNews()
    {
        $this->fixtureExecutor->execute([new NewsLoad()]);
        $this->assertCount(1, $this->repository->getLatestNews(1));
        $this->assertInstanceOf('DoctrineFixtureDemo\Entity\News', $this->repository->getLatestNews(1)[0]); 
    }
}

If everything goes well, then your repository class will be tested.

Want to grab the sourcecode?, you can take a look https://github.com/samsonasik/DoctrineFixtureDemo

Another approach

You can already have prepared tables before test executed, and call truncate in every tearDown() at your unit test class as Manuel Stosic suggestion.

References :
1. https://twitter.com/Ocramius/status/577979551281729536
2. https://github.com/doctrine/data-fixtures
3. https://gist.github.com/Ocramius/3994325

Testing Doctrine 2 Query Result from QueryBuilder with PHPUnit

Posted in Tutorial PHP by samsonasik on March 18, 2015

DoctrineYes, to get the result of QueryBuilder, we need to get Query first(which is final class :p). So, how to mock it? Impossible, huh? Don’t worry, we have the Query Abstract class! I got the very smart solution from this post by Julius Beckmann. Let me create a use case and paraphrase for that sample. The way we an do is we can mock the Abstract class and registers methods that we want to use, for example, on this case, the getResult method.

Use Case

We need to get latest data of news with limit and order, so we use QueryBuilder and its Query Result like the following :

namespace OurApp\Service;

use OurApp\Entity\News;
use Doctrine\ORM\EntityManager;

class NewsService
{
    /**
     * @var EntityManager
     */
    private $manager;
    
    /**
     * Construct
     * @param EntityManager $manager
     */
    public function __construct(EntityManager $manager)
    {
        $this->manager = $manager;    
    }
    
    /**
     * Get Last Recent News
     *
     * @param int $limit
     * @return array
     */
    public function getLastRecentNews($limit)
    {
        $result = $this->manager->getRepository(News::class)->createQueryBuilder('n')
                       ->setFirstResult(0)
                       ->setMaxResults($limit)
                       ->orderBy('n.id', 'DESC')
                       ->getQuery()
                       ->getResult();
        return $result;
    }
}

Of course, you can create instance of above class by factory with constructor injection.

The Test

This is it, we can mock the EntityManager, expects EntityRepository, expects QueryBuilder, use QueryBuilder until getResult() ready to be called, and use AbstractQuery with define only ‘getResult’ for expecting Result.

namespace OurAppTest\Service;

use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\AbstractQuery;
use OurApp\Entity\News;
use OurApp\Service\NewsService;
use PHPUnit_Framework_TestCase;

class NewsServiceTest extends PHPUnit_Framework_TestCase
{
    /**
     * @var EntityManager
     */
    private $manager;
    
    /**
     * @var NewsService
     */
    private $service; 
    
    public function setUp()
    {
        $this->manager  = $this->getMockBuilder(EntityManager::class)
                               ->disableOriginalConstructor()
                               ->getMock();

        $$this->service = new NewsService($this->entityManager);
    }
    
    public function testLastRecentNews()
    {
        $repository = $this->getMockBuilder(EntityRepository::class)
                           ->disableOriginalConstructor()
                           ->getMock();
        $this->manager->expects($this->once())
                            ->method('getRepository')
                            ->with(News::class)
                            ->will($this->returnValue($repository));
        
        $queryBuilder = $this->getMockBuilder(QueryBuilder::class)
                           ->disableOriginalConstructor()
                           ->getMock();
        $repository->expects($this->once())
                   ->method('createQueryBuilder')
                   ->with('n')
                   ->will($this->returnValue($queryBuilder));
        
        // We use QueryBuilder as Fluent Interface
        // several times, so we need to make it sequence
        $queryBuilder->expects($this->at(0))
                     ->method('setFirstResult')
                     ->with(0)
                     ->will($this->returnValue($queryBuilder));
        $queryBuilder->expects($this->at(1))
                     ->method('setMaxResults')
                     ->with(2)
                     ->will($this->returnValue($queryBuilder));
        $queryBuilder->expects($this->at(2))
                     ->method('orderBy')
                     ->with('n.id', 'DESC')
                     ->will($this->returnValue($queryBuilder));

        // We use AbstractQuery
        $getQuery = $this->getMockBuilder(AbstractQuery::class)
                         ->setMethods(array('getResult'))
                         ->disableOriginalConstructor()
                         ->getMockForAbstractClass();
        $queryBuilder->expects($this->at(3))
                     ->method('getQuery')
                     ->will($this->returnValue($getQuery));
        
        $entity1 = new News();
        $entity1->setHeadLine('Hello world from PHP');
        
        $entity2 = new News();
        $entity2->setHeadLine('Hello world from PHPUnit');
        
        $result = [
            0 => $entity1,
            1 => $entity2,
        ];             
        $getQuery->expects($this->once())
                 ->method('getResult')
                 ->will($this->returnValue($result));
        
        $this->assertEquals($result, $this->service->getLastRecentNews(2));
    }
}

I hope this sample make easier to understand for you who find a way to solve it.

Note
Ocramius suggest to not do it. He suggest to use Fixture instead like his gist. I created a new post in favor of it.

References :
1. http://h4cc.tumblr.com/post/61502458780/phpunit-mock-for-doctrine-orm-query
2. https://gist.github.com/gnutix/7746893

Image :
https://avatars2.githubusercontent.com/u/209254?v=3&s=200

Conditional Redirect on ZfcUser when login success

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on March 15, 2015

I have been digging with ZfcUser in this week, and I realize, I need to do conditional redirection when authentication successful. With current version ( 1.2.2 ), the ‘login_redirect_route’ only support ‘string’ value,  which is very strict. We currently can’t pass callable to it like the following code ( hopefully, in next version or  2.* we can do ) :

'login_redirect_route' => function(\ZfcUser\Entity\UserInterface $user) {
    if ($user->getRole()->getName() === 'admin') {
        return 'zfcadmin';
    }
    return 'zfcuser';
}

We need another approach right now!

Clues

1. ZfcUser\Options\ModuleOptions

The ZfcUser configuration is consumed by ZfcUser\Options\ModuleOptions, registered as ‘zfcuser_module_options’ service, so, we can use it to re-set the ‘loginRedirectRoute’ property value, and re-register as service.

2. ‘authenticate.success’ event

‘authenticate.success’ is an event that will be triggered when we catch that user has Identity on Authentication process, in ZfcUser\Authentication\Adapter\AdapterChain::prepareForAuthentication.

Execution

With 2 clues above, What we can do now is, using listener that re-set ‘zfcuser_module_options’ service which bring new ‘loginRedirectRoute’, that react to ‘authenticate.success’. We can do in our Module class like the following :

use Application\Entity\User;
class Module
{
    // ...
    public function onBootstrap($e)
    {
        $servicemaManager = $e->getTarget()->getServiceManager();
        $objectManager    = $serviceManager->get('objectManager');

        $zfcAuthEvents    =  $serviceManager->get('ZfcUser\Authentication\Adapter\AdapterChain')->getEventManager();
        $zfcAuthEvents->attach('authenticate.success', function($authEvent) use ($serviceManager, $objectManager) {
            $userId = $authEvent->getIdentity();
            $user   = $objectManager->find(User::class, $userId);

            if ($user->getRole()->getName() === 'admin') {
                // we are going to re-set service,
                // we need to set allow_override of serviceManager= true
                $serviceManager->setAllowOverride(true);

                $zfcuserModuleOptions = $serviceManager->get('zfcuser_module_options');
                $zfcuserModuleOptions->setLoginRedirectRoute('zfcadmin');
                $serviceManager->setService('zfcuser_module_options', $zfcuserModuleOptions);

                // set 'allow_override' back to false
                $serviceManager->setAllowOverride(false);
            }
        });
    }
}

Now, whenever we got role name = ‘admin’ during authentication process, we will be redirected to ‘zfcadmin’ route.

References :
1. http://stackoverflow.com/questions/16053479/zfcuser-redirect-roles-to-different-pages-after-login
2. Conversation with Mr. Daniel Strøm
3. http://circlical.com/blog/2013/7/5/capturing-auth-events-with-zfcuser

Using Ember.js in Zend Framework 2 Application

Posted in Javascript, Tutorial PHP, Zend Framework 2 by samsonasik on January 7, 2015

Ember.js is one of the javascript frameworks that adopt Single-Page Application principles. When working with Zend Framework 2 application, we can terminate 2 step view process to just render the view (not the layout) when the request that comes is XmlHttpRequest. We can do it in our Module.php like the following code :

use Zend\View\Model\ViewModel;

class Module
{
    public function onBootstrap($e)
    {
        $eventManager = $e->getApplication()->getEventManager();
        $sharedEvents = $eventManager->getSharedManager();
        $sharedEvents->attach('Zend\Mvc\Controller\AbstractActionController',
            'dispatch', function($e)
        {
            $result = $e->getResult();
            if ($result instanceof ViewModel) {
                $result->setTerminal($e->getRequest()->isXmlHttpRequest());
            }
        });

        // ...
    }
    
    // ...
}

At this post, I will try to make a 3 static page : ‘Home’, ‘About’, and ‘Contact’ in Application module. Let’s first create the navigation :

// config/autoload/navigation.global.php
return [
    'navigation' => [
        'default' => [
            [
                'label' => 'Home',
                'route' => 'home'
            ],
            [
                'label'  => 'About',
                'route'  => 'about',
            ],
            [
                'label' => 'Contact',
                'route' => 'contact',
            ],
        ],
    ],
];

We can then register the navigation service :

// module/Application/config/module.config.php
// ...
   'service_manager' => [
      'factories' => [
            'Navigation' => 'Zend\Navigation\Service\DefaultNavigationFactory',
       ],
    ]
// ...

From the navigation registered above, we can create 3 controller like this :
1. IndexController

// module/Application/src/Application/Controller/IndexController.php
namespace Application\Controller;

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

class IndexController extends AbstractActionController
{
    public function indexAction()
    {
        return new ViewModel();
    }
}

2. AboutController

// module/Application/src/Application/Controller/AboutController.php
namespace Application\Controller;

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

class AboutController extends AbstractActionController
{
    public function indexAction()
    {
        return new ViewModel();
    }
}

3. ContactController

// module/Application/src/Application/Controller/ContactController.php
namespace Application\Controller;

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

class ContactController extends AbstractActionController
{
    public function indexAction()
    {
        return new ViewModel();
    }
}

Controllers created, we can register it into module.config.php under ‘router’ and ‘controllers’ :

// module/Application/config/module.config.php
// ...
   'router' => [
        'routes' => [
            'about' => [
                'type'    => 'Literal',
                'options' => [
                    'route'    => '/about',
                    'defaults' => [
                        'controller' => 'Application\Controller\About',
                        'action'        => 'index',
                    ],
                ],
            ],

            'contact' => [
                'type'    => 'Literal',
                'options' => [
                    'route'    => '/contact',
                    'defaults' => [
                        'controller' => 'Application\Controller\Contact',
                        'action'        => 'index',
                    ],
                ],
            ],
         ],
    ],

    'controllers' => [
        'invokables' => [
            'Application\Controller\Index' => 'Application\Controller\IndexController',
            'Application\Controller\Contact' => 'Application\Controller\ContactController',
            'Application\Controller\About' => 'Application\Controller\AboutController',
        ],
    ],
// ...

I assume you have using default ZendSkeletonApplication so the IndexController route already defined. Ok, now we need to fill the view.
1 index view ( same as the skeleton app view/application/index/index.phtml )
2 about view

// module/Application/view/application/about/index.phtml
<h1>About Me</h1>
<p>
    I'm a web developer.
</p>

3 contact view

// module/Application/view/application/contact/index.phtml
<h1>Contact Me</h1>
<p>
    You can contact me via <a href="mailto: foo@bar.baz.com">foo@bar.baz.com</a>
</p>

Yup, Let’s go to javascript side.

First, require Ember.js in bower.json and install it :

// bower.json
{
    "name":"ZF2 App with Ember Demo",
    "dependencies": {
        "ember": "1.*"
    }
}

Configure it to be installed in public/js folder in .bowerrc.

// .bowerrc
{
    "directory": "public/js",
    "json": "bower.json"
}

Run bower install :

bower install

For Ember 1.10, As we will need template compilation, We need to require ember-template-compiler like shown in here. To make it included, we need to require it in layout.phtml in headScript() view helper :

echo $this->headScript()
            ->prependFile($this->basePath() . '/js/bootstrap.min.js')
            ->prependFile($this->basePath() . '/js/jquery.min.js')
            ->prependFile($this->basePath() . '/js/respond.min.js', 'text/javascript', array('conditional' => 'lt IE 9',))
            ->prependFile($this->basePath() . '/js/html5shiv.js',   'text/javascript', array('conditional' => 'lt IE 9',))

            // ember js dependencies
            ->appendFile($this->basePath() . '/js/ember/ember-template-compiler.js')
            ->appendFile($this->basePath() . '/js/ember/ember.min.js');
        ; 

Now, we need to create a new javascript file for its application specific requirement, I name it app.js :

// public/js/app.js
App = Ember.Application.create();

App.Router.map(function() {
    this.resource('home', {
        path: '/'
    });
    this.resource('about');
    this.resource('contact');
});

Layout

We need to replace :

<?php echo $this->content; ?>

with

{{outlet}}

And wrap it in <script type="text/x-handlebars"> :

// module/Application/view/layout/layout.phtml
// ...
<script type="text/x-handlebars">
        <nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
            <div class="container">
                <div class="navbar-header">
                    <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                    </button>
                    <a class="navbar-brand" href="<?php echo $this->url('home') ?>"><img src="<?php echo $this->basePath('img/zf2-logo.png') ?>" alt="Zend Framework 2"/> <?php echo $this->translate('Skeleton Application') ?></a>
                </div>
                
                    <div class="collapse navbar-collapse">
                        <ul class="nav navbar-nav">
                            <?php $navigationContainer = $this->navigation('navigation')->getContainer();
                                  foreach($navigationContainer as $page) {
                            ?>
                                <li>
                                    {{#link-to '<?php echo $page->get('route'); ?>'}}
                                        <?php echo $page->get('label'); ?>
                                    {{/link-to}}
                                </li>  
                            <?php   }   ?>
                        </ul>
                        
                    </div><!--/.nav-collapse -->
            </div>
        </nav>
        
        <div class="container">
            {{outlet}}
        </div>
</script>

We need to require our public/js/app.js in footer :

<?php
$script =  $this->inlineScript();
$script->appendFile($this->basePath() . '/js/app.js');

echo $script;
?>
</body> // means in the footer!

Yup, time to make ajax works, modify public/js/app.js by adding the following codes :

// public/js/app.js
// ...
App.HomeRoute = Ember.Route.extend({
   beforeModel: function() {
     return $.ajax({
        url: '/'
     })
     .then(function(response) {
        Ember.TEMPLATES.home = Ember.Handlebars.compile(response);
    });
   }
});

App.AboutRoute = Ember.Route.extend({
   beforeModel: function() {
     return $.ajax({
        url: '/about'
     })
     .then(function(response) {
        Ember.TEMPLATES.about = Ember.Handlebars.compile(response);
    });
   }
});

App.ContactRoute = Ember.Route.extend({
   beforeModel: function() {
     return $.ajax({
        url: '/contact'
     })
     .then(function(response) {
        Ember.TEMPLATES.contact = Ember.Handlebars.compile(response);
    });
   }
});

// to remove # ( hash ) in url
if (window.history && window.history.pushState) {
    App.Router.reopen({
      location: 'history'
    });
}

Great! If everything ok, then your ZF2 app with Ember.js should work like a magic! The requested page loaded without refreshing the page!. about-png

Bonus

You can make ‘li’ under ‘ul’ for navigation class setted to active when it in the page, with create new ‘li’ component, we can add it in public/js/app.js

// public/js/app.js
// ...
App.LinkLiComponent = Ember.Component.extend({
  tagName: 'li',
  classNameBindings: ['active'],
  active: function() {
    return this.get('childViews').anyBy('active');
  }.property('childViews.@each.active')
});

Ember.Handlebars.helper('link-li', App.LinkLiComponent);

And then, we can modify the looks like :

// module/Application/view/layout/layout.phtml
// ...
<?php $navigationContainer = $this->navigation('navigation')->getContainer();
      foreach($navigationContainer as $page) { ?>

     {{#link-li}}
        {{#link-to '<?php echo $page->get('route'); ?>'}}
           <?php echo $page->get('label'); ?>
        {{/link-to}}
     {{/link-li}}  

<?php   }   ?>

Ok, I hope it useful for you ;). Want to grab the sourcecode ? Clone from my repository https://github.com/samsonasik/zfember ;)

Images :
1. http://www.gravatar.com/avatar/0cf15665a9146ba852bf042b0652780a?s=200
References :
1. http://emberjs.com/
2. http://code.tutsplus.com/tutorials/getting-into-ember-js-part-2–net-31132
3. http://code.tutsplus.com/tutorials/getting-into-emberjs-part-3–net-31394
4. http://stackoverflow.com/questions/17792280/ember-live-uploading-templates
5. http://stackoverflow.com/questions/19871265/ember-js-with-external-handlebars-template
6. http://stackoverflow.com/questions/14328295/how-do-i-bind-to-the-active-class-of-a-link-using-the-new-ember-router
7. http://en.wikipedia.org/wiki/Single-page_application
8. http://emberjs.com/blog/2015/02/07/ember-1-10-0-released.html

Testing Lazy Load with ReflectionClass

Posted in Tutorial PHP by samsonasik on January 2, 2015

One of the benefits by using ReflectionClass is when dealing with testing Lazy load. The flow is get the property, use its instance as ReflectionProperty to make it accessible, and set the property value via setValue().
Ok, let’s give a try.
I’ve a “Bar” class that looks like the following :

namespace Samsonasik\TutorialLazyLoading;

/**
 * @author Abdul Malik Ikhsan <samsonasik@gmail.com>
 */
class Bar
{
    /**
     * @var Foo
     */
    private $foo;
    
    /**
     * Get foo property
     * @return Foo
     */
    public function getFoo()
    {
        if (!$this->foo) {
            $this->foo = new Foo;
            return $this->foo;    
        }
        
        return $this->foo;
    }
}

When you call getFoo() in unit test with :

use Samsonasik\TutorialLazyLoading\Bar;
use Samsonasik\TutorialLazyLoading\Foo;

// ...
    protected function setUp()
    {
        $this->bar = new Bar;   
    }
    
    public function testGetFooNotInitializedYet()
    {
        $this->assertInstanceOf(
            Foo::class, /** use 'Samsonasik\TutorialLazyLoading\Foo' for PHP <=5.4 **/
            $this->bar->getFoo()
        );
    }
// ...

, you will get coverage only if (!$this->foo) { block like this :
half-phpunit-test-coverage-on-lazyload

Time to do ReflectionClass in action!, add new test for it’s needed.

use ReflectionClass;
use Samsonasik\TutorialLazyLoading\Bar;
use Samsonasik\TutorialLazyLoading\Foo;

// ...
    protected function setUp()
    {
        $this->bar = new Bar;   
    }
    public function testGetFooNotInitializedYet() { // ...  }
    
    public function testGetFooWithAlreadyInialized()
    {
        $class = new ReflectionClass(
            Bar::class /** use 'Samsonasik\TutorialLazyLoading\Bar' for PHP <=5.4 **/
        );
        $property = $class->getProperty('foo');
        $property->setAccessible(true);
        $property->setValue($this->bar, new Foo);
        
        $this->assertInstanceOf(
            Foo::class, /** use 'Samsonasik\TutorialLazyLoading\Foo' for PHP <=5.4 **/
            $this->bar->getFoo()
        );
    }
// ...   

And yay! You now get fully tested :
full-phpunit-test-coverage-on-lazyload

Note :
As @ocramius suggestion, there is an easier way to do it by calling the method twice ;).

Want to grab it ? You can grab from my repository : https://github.com/samsonasik/TutorialLazyLoading .

References :
1. http://www.mikeyd.com.au/2011/01/20/how-to-use-phps-reflectionclass-to-test-private-methods-and-properties-with-phpunit/

Using igorw/retry to handle collision of Uuid

Posted in Tutorial PHP by samsonasik on December 31, 2014

While the Uuid collide probability is almost zero, and only happen when you have a very very very bad luck, there is always a chance for it. If you’re using PHP, I encourage to use igorw/retry! You can retry your process as many as your wish!
Let’s grab it by adding into composer require :

composer require "igorw/retry:dev-master"

And as demo, I’m going to use “rhumsaa/uuid”, we can add again in composer require :

composer require "rhumsaa/uuid:~2.8"

Great!, let's use it :

require 'vendor/autoload.php';

use function igorw\retry;
use Rhumsaa\Uuid\Uuid;

$uuid    = Uuid::uuid4();
$uuidStr = $uuid->toString();

Now, the Uuid instance and its string random generation created, so we can use it in retry.

$i = 0;
// retry maximum : 5 times until succeed
retry(5, function () use ($uuid, $uuidStr, &$i) {

    $i++;

    if ($i > 1) {
        $uuidStr = $uuid->toString();
    }

    // this is pseudo code
    $this->db('user')->insert([
        'id' => $uuidStr,
        'name' => 'foo'
    ]);
});

When your insertion failed and got exception, it will silently ( doesn’t show error message ) and do re-try again with new string generation instead and stop when it succeded. You can change how many retry by changing 1st parameter in retry function.

References :

  1. http://en.wikipedia.org/wiki/Universally_unique_identifier
  2. https://github.com/igorw/retry
  3. https://github.com/ramsey/uuid

Zend Framework 2 : Check request URI matched against registered Router

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

zf2-zendframework2 When you develop web application using Zend Framework 2, there is sometime you get the situation that you need to check requested URI matched against registered ‘Router’, for example : you have uri : http://zf2app/register?redirect=/user  which the application will redirect to passed ‘redirect’ parameter. What if user make bypassed and inject unexpected redirect param ? For example, you have code like this in your controller action :

$redirect = $this->params()->fromQuery('redirect','');
return $this->redirect()->toUrl($redirect);

We can inject unwanted redirect into browser, and BOOM, the site will be redirected to unwanted uri. So, we need to handle it! we need to check whenever the ‘redirect’ value can be matched with registered Router.
First, check if the request URI can be matched against registered Router.

$redirect = $this->params()->fromQuery('redirect','');

$request  = $this->getRequest();
$request->setUri($redirect);

// assignment, not Comparison, that's why use single '='
if ($routeToBeMatched = $this->getServiceLocator()->get('Router')->match($request)) {
    // ... process redirection based on 'redirect' param...
}

When request URI matched against registered Router, then we assign it to $routeToBeMatched variable, we can procees it. What if the “redirect route equals current route” ? We can check it under if.

if ($routeToBeMatched = $this->getServiceLocator()->get('Router')->match($request)) {
   // handle if redirect route = current route
   $currentRouteMatchName = $this->getEvent()->getRouteMatch()->getMatchedRouteName();
   if ($routeToBeMatched->getMatchedRouteName() != $currentRouteMatchName) {
       return $this->redirect()->toUrl($redirect);
   }
}

Great! now, handle default redirection when doesnt’ match :

if ($routeToBeMatched = $this->getServiceLocator()->get('Router')->match($request)) {
    // handle if redirect route = current route
    $currentRouteMatchName = $this->getEvent()->getRouteMatch()->getMatchedRouteName();
    if ($routeToBeMatched->getMatchedRouteName() != $currentRouteMatchName) {
        return $this->redirect()->toUrl($redirect);
    }
}
return $this->redirect()->toUrl('/user');

Ok, this is the complete code sample:

$redirect = $this->params()->fromQuery('redirect','');

$request  = $this->getRequest();
$request->setUri($redirect);

if ($routeToBeMatched = $this->getServiceLocator()->get('Router')->match($request)) {
    // handle if redirect route = current route
    $currentRouteMatchName = $this->getEvent()->getRouteMatch()->getMatchedRouteName();
    if ($routeToBeMatched->getMatchedRouteName() != $currentRouteMatchName) {
        return $this->redirect()->toRoute($redirect);
    }
}
return $this->redirect()->toUrl('/user');

Symfony 2.6.1 : Using class_alias of Debug’s FlattenException to work with Error page previews

Posted in Symfony2 Framework, Tutorial PHP by samsonasik on December 19, 2014

Error page previews is one of the new features that land in Symfony 2.6. You should be able to access /_error/{statusCode}.{format}. Unfortunatelly, when I try in Symfony 2.6.1, It doesn’t work. The reason of issue is that in TwigBundle\Controller\ExceptionController::showAction() require HttpKernel’s FlattenException that should be require Debug’s FlattenException.
symfony2-exception-error-preview
The HttpKernel’s FlattenException is deprecated from Symfony 2.3 and should be removed in 3.0.
If you see the Symfony\Bundle\TwigBundle\Controller\ExceptionController, you will see :

use Symfony\Component\HttpKernel\Exception\FlattenException;
// ...
// ...
    public function showAction(
       Request $request,
       FlattenException $exception,
       DebugLoggerInterface $logger = null)
    {

    }
// ...
// ...

So, You just cannot make custom exception that extends the ExceptionController because of the typehint requirement. What we can do ? The issue already reported in github so we just can wait to make it work in next maintenance release – OR – do tricky in the skeleton! Don’t do it in vendor libraries directly. You shouldn’t touch it anymore. We can edit our symfony-standard skeleton application in web/app_dev.php by apply class_alias after loader loaded :

// web/app_dev.php
// ...
$loader = require_once __DIR__.'/../app/bootstrap.php.cache';
class_alias(
    'Symfony\Component\Debug\Exception\FlattenException',
    'Symfony\Component\HttpKernel\Exception\FlattenException'
);
// ...

That’s it, so when you try access : http://127.0.0.1:8000/_error/404.html or whatever your Symfony url skeleton application with /_error/404.html, you will get something like this :
symfony2-404-error-preview

Done ;)

Using PHP Phantomjs with Codeception

Posted in Javascript by samsonasik on December 18, 2014

phantomjs logoWhen you do a web acceptance test for web with javascript support, and you want to use phantomjs but doesn’t have root access to install new application, you probably need this, the “jonnyw/php-phantomjs” lib. You can install via composer parallel with “codeception/codeception” by configuring composer.json like the following :
 

{
    "require": {
        "codeception/codeception": "2.*",
        "jonnyw/php-phantomjs": "3.*"
    },
    "scripts": {
        "post-install-cmd": [
            "PhantomInstaller\\Installer::installPhantomJS"
        ],
        "post-update-cmd": [
            "PhantomInstaller\\Installer::installPhantomJS"
        ]
    }
}

and then run :

composer install

Ok, when everything installed, you already have : vendor/bin/phantomjs executable file.

If you add :

   "config": {
        "bin-dir": "bin"
    }

Inside your composer.json, you should have individual bin folder outside the vendor folder so you can run from it like described at http://jonnnnyw.github.io/php-phantomjs/. I personally like it should be inside the vendor instead. You can choose what you prefer.

Next step is start the phantomjs service by run command :

vendor/bin/phantomjs --webdriver=4444

Let’s continue to the sample page, try create a sample html page :

<html>
<body>

   <h1>Hello World!</h1>
 
  <button name="test-btn" id="test-btn">test-btn</button>
  <script type="text/javascript" src="jquery.js"></script>
  <script type="text/javascript">
    $(document).ready(function() {
       $("#test-btn").click(function() {
            $("h1").html("abcdef");
       });
    });
  </script> 

</body>
</html>

At this sample page, we want to :

if user go to /index.html, he will see “Hello World!” and don’t see the “abcdef” inside “h1” and then if user click “#test-btn” then he can see “Hello World!” replaced with “abcdef” and see the “abcdef” inside “h1”.

Now, let’s initialize the codecept by open separate console window and run :

vendor/bin/codecept bootstrap

Then, call command :

vendor/bin/codecept generate:cept acceptance Index

We then will get pre-filled configuration and make tests with folder named “tests”. What we need to do, is configure the ‘tests/acceptance.suite.yml’ file like the following :

# Codeception Test Suite Configuration
# filename tests/acceptance.suite.yml
class_name: AcceptanceTester
modules:
    enabled: [WebDriver]
    config:
        WebDriver:
            url: 'http://localhost/codeceptiontest/' # our url base
            browser: firefox
            capabilities:
                unexpectedAlertBehaviour: 'accept'

Great! then we can create a test in ‘tests/acceptance/IndexCept.php’ :

// filename : tests/acceptance/IndexCept.php
$I = new AcceptanceTester($scenario);
$I->wantTo('see home page');
$I->amOnPage('/index.html');
$I->see('Hello World!');
$I->dontSee("abcdef", '//body/h1');
$I->click('#test-btn');
// $I->wait(1); // you can set delay
$I->See('abcdef','//body/h1');

And finally! Run the test!

vendor/bin/codecept run acceptance

run-test-codeception-php-phantomjs

Done ;)

References :
1. http://jonnnnyw.github.io/php-phantomjs/
2. http://codeception.com/docs/modules/WebDriver

Tagged with:

Symfony 2.6 : The good momentum to implement “official” Symfony best practices

Posted in Symfony2 Framework, Tutorial PHP by samsonasik on November 30, 2014

Symfony 2.6 released yesterday. There are ton of features/bug fixes added. There is another thing that awesome! We can implements the “AppBundle way” of the official best practices. Although the AppBundle that mentioned in the official documentation brought by Symfony 2.6, is not in Symfony-standard yet ( at least until now), we can generate it by new updated SensioGeneratorBundle that already shipped by new symfony-standard.


So, after you generate :

php app/console generate:bundle --namespace=AppBundle \
--dir=src --format=annotation --no-interaction

Now, you can have bundle without “vendor” namespace.

Then, if you do what mentioned in the best practices, that template location in app/Resources instead (for templates that the bundle only used in your application). The project structure will like the following :
template-loc
There are complete list of “official” recommended ways to build application using Symfony application including configuration, controllers, templates, forms, internationalization, security, web assets, and tests.

One more thing, now, we can use Symfony Installer to create a new Symfony application that you can download from https://github.com/symfony/symfony-installer . The console interface of installer is pretty good :
symfony-installer1
symfony-installer2

References :
1. http://symfony.com/doc/current/best_practices/index.html
2. https://twitter.com/weaverryan/status/538610340020649984
3. https://github.com/symfony/symfony-installer

Zend Framework 2 : Using AbstractConsoleController and ConsoleModel

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

zf2-zendframework2 When using console in ZF2 application, we can extends AbstractConsoleController instead of AbstractActionController. There is overridden dispatch() function in AbstractConsoleController which handle access that doesn’t come from console which is tricked like this :

// ...
    /**
     * {@inheritdoc}
     */
    public function dispatch(RequestInterface $request, ResponseInterface $response = null)
    {
        if (! $request instanceof ConsoleRequest) {
            throw new InvalidArgumentException(sprintf(
                '%s can only dispatch requests in a console environment',
                get_called_class()
            ));
        }
        return parent::dispatch($request, $response);
    }
// ...

So, by extending it, we can reduce its checking. So, our controller will look like the following :

namespace TutorialConsoleModule\Controller;

use Zend\Mvc\Controller\AbstractConsoleController;

class TutorialConsoleController extends AbstractConsoleController
{
    public function showDataAction()
    {
        // ...
    }
}

Now, we need to show something in console, instead of using Response object, we can use ConsoleModel so we can return ConsoleModel that setted with the data like this :

namespace TutorialConsoleModule\Controller;

use Zend\Mvc\Controller\AbstractConsoleController;
use Zend\Text\Table;
use Zend\View\Model\ConsoleModel;

class TutorialConsoleController extends AbstractConsoleController
{
    public function showDataAction()
    {
        $table = new Table\Table([
            'columnWidths' => [20, 20] 
        ]);
        $table->setDecorator('ascii');
        $table->appendRow(['FirstName', 'LastName']);
        $table->appendRow(['Abdul Malik', 'Ikhsan']);
        $table->appendRow(['Sharty', 'Mushlihah']);
    
        $consoleModel = new ConsoleModel();
        $consoleModel->setResult($table);
        
        return $consoleModel;
    }
}

Now, you can just register your controller into module.config.php :

return [
    
    'controllers' => [
        'invokables' => [
            'TutorialConsoleModule\Controller\TutorialConsole'
                => 'TutorialConsoleModule\Controller\TutorialConsoleController',
        ],
    ],
    
    'console' => [
        'router' => [
            'routes' => [
                'show-data' => [
                    'options' => [
                        'route'    => 'show data',
                        'defaults' => [
                            'controller' => 'TutorialConsoleModule\Controller\TutorialConsole',
                            'action'     => 'show-data'
                        ]
                    ]
                ]
            ]
        ]
    ],
];

And you can call in console :

php public/index.php show data

and you will get :
console1

Done ;)

Re-fill selectize js value

Posted in Javascript, Teknologi by samsonasik on November 28, 2014

It’s been a while since I didn’t write a post about non-framework category. Ok, this time, I will show you how to use selectize js on re-fill functionality. Selectize js is a jQuery plugin that useful for tagging and autocomplete. I used it in several projects. Once it’s installed and selectize() called, your form can be like the following :
selectize-1
In images demo above, I want to re-set the “district” based on the “province” changes by javascript. To make selectize still applied to “district”, you need to do :

  • re-set Html option values
  • re-set selectize value options

Ok, let’s do a demo application for this.
1. Preparation
1.a make bower.json for dependency requirements definition

{
    "name":"Selectize Demo",
    "dependencies": {
        "jquery": "1.11.1",
        "selectize":"0.11.2"
    }
}

1.b make .bowerrc for specification

{
    "directory": "js",
    "json": "bower.json"
}

1.c install dependencies

bower install

2. Initialize selectize
We can initialize selectize js by include it in the header ( js and css ) like this :

    <link href="./js/selectize/dist/css/selectize.default.css" media="screen" rel="stylesheet" type="text/css">

    <script type="text/javascript" src="./js/jquery/dist/jquery.min.js"></script>
    <script type="text/javascript" src="./js/selectize/dist/js/standalone/selectize.min.js"></script>

and then, we create the elements which we want to selectize :

<form method="post">

     <select name="province_id" id="province_id">
            <option value="0">--Select Province--</option>
            <option value="1">Jawa Barat</option>
            <option value="2">Jawa Tengah</option>
      </select>

      <select name="district" id="district">
            <option value="0">--Select District--</option>
      </select>

</form>

Now, time to execute :


        $(document).ready(function() {
            //initialize selectize for both fields
            $("#province_id").selectize();
            $("#district").selectize();
        });  

3. Do the awesome
Ok, now what ? We need to re-fill the “district” data on change of “province”, In this case, I wrote a case when using Ajax request and catched by PHP script. So, create a “change-data.php” file :

<?php

if (isset($_POST['province_id'])) {

    $data = [];
    if ($_POST['province_id'] == 1) {
        $data = [
            0 => [
                'id' => 1,
                'name' => 'Bandung',
            ],
            1 => [
                'id' => 2,
                'name' => 'Cimahi',
            ]
        ];
    }

    if ($_POST['province_id'] == 2) {
        $data = [
            0 => [
                'id' => 3,
                'name' => 'Kudus',
            ],
            1 => [
                'id' => 4,
                'name' => 'Cirebon',
            ]
        ];
    }

    echo json_encode($data);
}

Basically, the selectize can be filled by json object that have “text” and “value” key, like the following :

[
    {text: "Bandung", value: 1 },
    {text: "Cimahi", value: 2 }
]

So, we need to get the data, and convert to json object, we can do with eval :

new_value_options = eval('(' + new_value_options + ')');

Ok, now, let’s do this :

$(document).ready(function() {
            //initialize selectize for both fields
            $("#province_id").selectize();
            $("#district").selectize();

            // onchange
            $("#province_id").change(function() {
                $.post('./change-data', { 'province_id' : $(this).val() } , function(jsondata) {
                    var htmldata = '';
                    var new_value_options   = '[';
                    for (var key in jsondata) {
                        htmldata += '<option value="'+jsondata[key].id+'">'+jsondata[key].name+'</option>';

                        var keyPlus = parseInt(key) + 1;
                        if (keyPlus == jsondata.length) {
                            new_value_options += '{text: "'+jsondata[key].name+'", value: '+jsondata[key].id+'}';
                        } else {
                            new_value_options += '{text: "'+jsondata[key].name+'", value: '+jsondata[key].id+'},';
                        }
                    }
                    new_value_options   += ']';

                    //convert to json object
                    new_value_options = eval('(' + new_value_options + ')');
                    if (new_value_options[0] != undefined) {
                        // re-fill html select option field 
                        $("#district").html(htmldata);
                        // re-fill/set the selectize values
                        var selectize = $("#district")[0].selectize;

                        selectize.clear();
                        selectize.clearOptions();
                        selectize.renderCache['option'] = {};
                        selectize.renderCache['item'] = {};

                        selectize.addOption(new_value_options);

                        selectize.setValue(new_value_options[0].value);
                    }

                }, 'json');
            });
        });

That’s it, hope it helpful. Want to grab the code ? grab it from https://github.com/samsonasik/selectize-demo

Tagged with: ,

Zend Framework 2 : Using Doctrine Extension with DoctrineModule and DoctrineORMModule

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

zf2-zendframework2 In ZF2, by using DoctrineModule, we can manage the extension that implements Doctrine\Common\EventSubscriber in configuration. For example, we have TablePrefix subcsriber to set table prefix query result like the following :

namespace Application\DoctrineExtension;

use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
use Doctrine\Common\EventSubscriber;

class TablePrefix implements EventSubscriber
{
    protected $prefix = '';

    public function __construct($prefix)
    {
        $this->prefix = (string) $prefix;
    }
    
    public function getSubscribedEvents()
    {
        return ['loadClassMetadata'];
    }

    public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs)
    {
        $classMetadata = $eventArgs->getClassMetadata();
        $classMetadata->setTableName($this->prefix . $classMetadata->getTableName());
        foreach ($classMetadata->getAssociationMappings() as $fieldName => $mapping) {
            if ($mapping['type'] == \Doctrine\ORM\Mapping\ClassMetadataInfo::MANY_TO_MANY) {
                $mappedTableName = $classMetadata->associationMappings[$fieldName]['joinTable']['name'];
                $classMetadata->associationMappings[$fieldName]['joinTable']['name'] = $this->prefix . $mappedTableName;
            }
        }
    }
}

Because above class need a dependency at __construct part, we need to build the instance via service manager factory :


use Application\DoctrineExtension\TablePrefix;

return [
   'service_manager' => [
        'factories' => [
            'tablePrefix' => function($sm) {
                // for cache-able config, use factory instead!
                return new TablePrefix('app.');                                      
            },
        ],
   ],
   //...
];

And now, we can register to the ‘subscribers’ key under ‘orm_default’ ( if we use ‘orm_default’ as connection ).

return [
   // ...
   'doctrine' => [
        // ... common doctrine driver config here

        'eventmanager' => [
            'orm_default' => [
                'subscribers' => [
                    'tablePrefixService',
                ],
            ],
        ],
        // ...
    ],       
   //...
];

And when we call it :

$albumQb = $this->getServiceLocator()
   ->get('Doctrine\ORM\EntityManager')->getRepository('Application\Entity\Album')
   ->createQueryBuilder('a');
echo $albumQb->getQuery()->getSql();

It will produce :

SELECT a0_.id AS id0, a0_.artist AS artist1, a0_.title AS title2 FROM app.album a0_

Now… What if the Extension doesn’t extends the Doctrine\Common\EventSubscriber ?

We need to register it in the EventManager of Doctrine Connection as new event listener with utilize DoctrineORMModule, so we have to override the DoctrineORMModule\Service\EntityManagerFactory :

namespace Application\Factory\Service;

use DoctrineORMModule\Service\EntityManagerFactory as BaseEntityManagerFactory;
use Doctrine\ORM\EntityManager;
use DoctrineModule\Service\AbstractFactory;
use Zend\ServiceManager\ServiceLocatorInterface;

class EntityManagerFactory extends BaseEntityManagerFactory
{
    /**
     * {@inheritDoc}
     * @return EntityManager
     */
    public function createService(ServiceLocatorInterface $sl)
    {
        /* @var $options \DoctrineORMModule\Options\EntityManager */
        $options    = $this->getOptions($sl, 'entitymanager');
        $connection = $sl->get($options->getConnection());
        $config     = $sl->get($options->getConfiguration());

        $sl->get($options->getEntityResolver());
        
        // add Table Prefix
        $evm = $connection->getEventManager();
        // assumed 'tablePrefixService' already registered in service_manager before... 
        $evm->addEventListener(\Doctrine\ORM\Events::loadClassMetadata, $sl->get('tablePrefixService'));

        return EntityManager::create($connection, $config, $evm);
    }
}

Now, we override the ‘doctrine_factories’ config in our config :

return [
    'doctrine' => [
        // ... common doctrine driver config here 
    ],
    
    'doctrine_factories' => [
        'entitymanager' => 'Application\Factory\Service\EntityManagerFactory',
    ],
];

To make it works, The "doctrine_factories" override config need to be placed after the doctrine config.

That’s it. I hope it useful ;)

References :
1. http://marco-pivetta.com/doctrine-orm-zf2-tutorial/
2. http://stackoverflow.com/questions/12841102/how-to-configure-doctrine-extensions-in-zend-framework-2
3. https://gist.github.com/samsonasik/90f041f049d509161d61
4. https://github.com/doctrine/DoctrineModule
5. https://github.com/doctrine/DoctrineORMModule

Follow

Get every new post delivered to your Inbox.

Join 323 other followers