Welcome to Abdul Malik Ikhsan's Blog

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

Zend Framework 2 : Build application Using PHP 5.4 – 5.6 Features

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

zf2-zendframework2Zend Framework 2 ( 2.3.3 ) requires PHP 5.3.23 or above. If we use PHP 5.4 or above, we can use their features when building module(s). There are many feature, I will only show you some of them that are useful.

1. PHP 5.4 features

a. Traits
ZF2 have several component that provide traits that ready to be used in our application. For example : Zend\EventManager\EventManagerAwareTrait. Instead of implements Zend\EventManager\EventManagerInterface, we can just use this traits.
This is the old way :

namespace Sample\Model;

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);
    }
}

This is the new way :

namespace Sample\Model;

use Zend\EventManager\EventManagerAwareInterface;
use Zend\EventManager\EventManagerAwareTrait;

class Foo implements EventManagerAwareInterface
{
    use EventManagerAwareTrait;
    
    public function bar($baz, $bat = null)
    {
        $params = compact('baz', 'bat');
        $this->getEventManager()->trigger(__FUNCTION__, $this, $params);
    }
}

Why not extends ? Because extends feature can already been used, and we need another things to be used, for example in the form class that already extends Zend\Form.
We can use many trait in one class.

b. Short array syntax
This is my favourite feature from PHP 5.4 that change array() to []. Lesser code that well be used.
This is the old way :

return array(    
    'router' => array(
        'routes' => array(
           'home' => array(
                'type' => 'Zend\Mvc\Router\Http\Literal',
                'options' => array(
                    'route'    => '/',
                    'defaults' => array(
                        'controller' => 'Application\Controller\Index',
                        'action'     => 'index',
                    ),
                ),
            ),
        ),
    ),
    // ... 
);

This is the new way :

return [
    'router' => [
        'routes' => [
            'home' => [
                'type' => 'Zend\Mvc\Router\Http\Literal',
                'options' => [
                    'route'    => '/',
                    'defaults' => [
                        'controller' => 'Application\Controller\Index',
                        'action'     => 'index',
                    ],
                ],
            ],
        ],
    ],
    // ...
];

You don’t want to manually change existing project ? You can use this tool to convert everything : https://github.com/thomasbachem/php-short-array-syntax-converter .

c. array dereferencing
In old way, when you want to access value of array from function that return array, you need to make it variable first.
For example, when you want to use one of config file.
This is the old way :

$config = $this->getServiceLocator()->get('Config');
echo $config['myconfig'];

This is the new way :

$myconfig = $this->getServiceLocator()->get('Config')['myconfig'];

d. Closures now support $this.
This is the old way : ( grabbed from https://mwop.net/blog/2012-07-30-the-new-init.html )

public function setEventManager(EventManagerInterface $events)
{
    parent::setEventManager($events);
    $controller =  $this;
    
    $events->attach('dispatch', function ($e) use ($controller) {
        $request = $e->getRequest();
        $method = $request->getMethod();
        
        if (!in_array($method, array('PUT', 'DELETE', 'PATCH'))) {
            // nothing to do
            return;
        }
        
        if ($controller->params()->fromRoute('id', false)) {
            // nothing to do
            return;
        }
        
        // Missing identifier! Redirect.
        return $controller->redirect()->toRoute(/* ... */);
    }, 100); // execute before executing action logic
}

This is the new way :
Now, we don’t need to pass $this that assigned to variable anymore, just use it inside the closure.

public function setEventManager(EventManagerInterface $events)
{
    parent::setEventManager($events);
    
    $events->attach('dispatch', function ($e)  {
        $request = $e->getRequest();
        $method = $request->getMethod();
        
        if (!in_array($method, ['PUT', 'DELETE', 'PATCH'])) {
            // nothing to do
            return;
        }
        
        if ($this->params()->fromRoute('id', false)) {
            // nothing to do
            return;
        }
        
        // Missing identifier! Redirect.
        return $this->redirect()->toRoute(/* ... */);
    }, 100); // execute before executing action logic
}

e. Class member access on instantiation has been added
This is the old way :

use Zend\Db\TableGateway\TableGateway;
$selectedtable = new TableGateway('tableneedtobeselected', $adapterSelect);
$select = $selectedtable->getSql()->select()->where(array('field' => 'value'));

This is the new way :

use Zend\Db\TableGateway\TableGateway;
$select = (new TableGateway('tableneedtobeselected', $adapterSelect))
    ->getSql()->select()->where(['field' => 'value']);

2. PHP 5.5 features

a. Class name resolution via ::class
It is very useful when we call service that has name same as its Namespace\ClassName.
This is the old way :

$serviceLocator->get('My\Very\Long\Service\Name');

This is the new way :

use My\Very\Long\Service\Name;
$serviceLocator->get(Name::class);

It will reduce of re-type long servicename when the service call many times in our file, for example : in tests file. It will be useful when we want to call full class name in our module.config.php like the following :

use Application\Controller\IndexController;

return[
    'router' => [
        'routes' => [
            'home' => [
                'type' => 'Zend\Mvc\Router\Http\Literal',
                'options' => [
                    'route'    => '/',
                    'defaults' => [
                        'controller' => IndexController::class,
                        'action'     => 'index',
                    ],
                ],
            ],
        ],
    ],
    
    'controllers' => [
        'invokables' => [
            IndexController::class => IndexController::class
        ],
    ],
    
    // ...
];

b. finally keyword
Code within the finally block will always be executed after the try and catch blocks, regardless of whether an exception has been thrown, and before normal execution resumes.

try{
    $adapter->getDriver()->getConnection()->beginTransaction();
    
    $adapter->query("INSERT INTO album(artist, title) values('Ray', 'Love')")->execute();
    $lastInsertedId = $this->getAdapter()->getDriver()->getConnection()->getLastGeneratedValue(); 
    $adapter->query("INSERT INTO track(title, album_id) values('New Title', 'Foo')")->execute();
    
    $adapter->getDriver()->getConnection()->commit();
} catch(\Exception $e) {
    $adapter->getDriver()->getConnection()->rollback();
    throw $e;
} finally {
    $adapter->getDriver()->getConnection()->disconnect();        
}

3. PHP 5.6 features

Managing function parameters is now easier with Variadic and Argument Unpacking.
This is the old way :

class Bar {}
class Foo
{
    public function __construct(Bar $bar)
    {
        var_dump($bar);
        
        //getting argument list 
        $numargs = func_num_args();
        if ($numargs > 1) {
            $args = func_get_args();
            
            $options = array();
            foreach($args as $key => $row) {
                if ($key > 0 ) {
                    $options[] = $row;
                }
            }
            
            var_dump($options);
        }
    }
}

$foo = new Foo(new Bar, 'a', 'b', 'c', 'd');

This is the new way :

class Bar {}
class Foo
{
    public function __construct(Bar $bar, ...$options)
    {
        var_dump($bar);
        var_dump($options);
    }
}

$foo = new Foo(new Bar, 'a', 'b', 'c', 'd');

We can pack the argument list by : … too.

$options = [
    'a',
    'b',
    'c',
    'd'
];
$foo = new Foo(new Bar, ...$options);

// OR collect all of them in one array

$options = [
    new Bar,
    'a',
    'b',
    'c',
    'd'
];
$foo = new Foo(...$options);

This feature can be useful when managing service dependencies, for example :

namespace MyModule\Factory\Service;

use MyModule\Service\Bar;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class FooServiceFactory implements FactoryInterface
{
    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        $options = [
            $serviceLocator->get('Bar'),
            'a',
            'b',
            'c',
            'd',
        ];
        
        $foo = new Foo(...$options);
        return $foo;
    }
}

Ok, I hope my post is useful. What PHP version you’re using ? :)

References :

1. http://php.net/manual/en/migration54.new-features.php
2. http://php.net/manual/en/migration55.new-features.php
3. http://php.net/manual/en/migration56.new-features.php
4. https://mwop.net/blog/2012-07-30-the-new-init.html
5. https://github.com/thomasbachem/php-short-array-syntax-converter

Zend Framework 2 : Translate ZF2 form label and value using translatorTextDomain

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

zf2-zendframework2When you work with ZF2 Form, you can translate the form element label and element value by specify translatorTextDomain. It can be applied globally on Module class so all form will be applied with its translation.
For example, We have structure of our translation like this :
zf2-form-label-translation

We can create a translation file then

<?php

return array(
    'Address' => 'Alamat', // for label "Address"
    'Create'  => 'Buat',   // for Submit value "Create" 
);

Now, let’s configure the translator in the module.config.php

return array(
    // .. other array config here

    'translator' => array(
        'locale' => 'id',
        'translation_file_patterns' => array(
            array(
                'type' => 'phpArray',
                'base_dir' => 'vendor/zendframework/zendframework/resources/languages',
                'pattern' => '%s/Zend_Captcha.php',
                'text_domain' => 'formvalidation',
            ),
            array(
                'type' => 'phpArray',
                'base_dir' => 'vendor/zendframework/zendframework/resources/languages',
                'pattern' => '%s/Zend_Validate.php',
                'text_domain' => 'formvalidation',
            ),
            
            // for label and value that we define ourself ...
            array(
                'type'     => 'phpArray',
                'base_dir' => __DIR__ . '/../translation',
                'pattern' => '%s/site.php',
                'text_domain' => 'myformlabelandvalue',
            ), 
        ),
    ),
    
    // ...
);

Ok, so, we then can create a form for that demo :

namespace TranslationSample\Form;

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

class SampleForm extends Form implements InputFilterProviderInterface
{
    public function __construct()
    {
        parent::__construct('sampleform');
    }
    
    public function init()
    {
        $this->add(array(
            'name' => 'address',
            'type' => 'Textarea',
            'options' => array(
                'label' => 'Address',
            ),
        ));
        
        $this->add(array(
            'type' => 'Submit',
            'name' => 'create',
            'attributes' => array(
                'value'    => 'Create',
                'class'    => 'btn btn-success',
            ),
        ));
    }
    
    public function getInputFilterSpecification()
    {
        return array(
            array(
                'name' => 'address',
                'required' => true,
                'validators' => array(

                    array(
                        'name'    => 'StringLength',
                        'options' => array(
                            'min'      => 6,
                            'max'      => 200,
                        ),
                    ),
                    
                ),
            ),
        );
    }
}

The above setup need to be fired at Module::onBootstrap :

namespace TranslationSample;

use Zend\Mvc\MvcEvent;
use Zend\Validator\AbstractValidator;

class Module
{
    public function onBootstrap(MvcEvent $event)
    {
        $application    = $event->getApplication();
        $serviceManager = $application->getServiceManager();
        $translator     = $serviceManager->get('translator');
        // for validation
        AbstractValidator::setDefaultTranslator($translator, 'formvalidation'); 
        // for form element label and value
        $serviceManager->get('ViewHelperManager')
                       ->get('formcollection')
                       ->setTranslatorTextDomain('myformlabelandvalue');
    }

    // ...
}

So, when $this->formCollection($form); is called :

$form = $this->form;
$form->prepare();

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

then the label and value will use the translatortextdomain registered like this :

formcollection-zf2-translate

If you use formLabel or formRow individually, you can apply the translatorTextDomain into it.

I uploaded the full code at my github account : https://github.com/samsonasik/TranslationSample .

NOTE : based on feedback by Manuel Stosic, we can do this for specific resource in view like :

$this->formLabel()->setTranslatorTextDomain('myformlabelandvalue');
$this->formInput()->setTranslatorTextDomain('myformlabelandvalue');
$this->formButton()->setTranslatorTextDomain('myformlabelandvalue');

References :
1. https://gist.github.com/stefanotorresi/13da4d2c8e486927a2f8
2. https://github.com/zendframework/zf2/issues/5826

Zend Framework 2 : Using $creationOptions in PluginManager

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

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

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

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

namespace ExamplePluginManagerCreationOptions\Service;

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

2. create a Foo class that implements PluginInterface

namespace ExamplePluginManagerCreationOptions\Service;

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

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

namespace ExamplePluginManagerCreationOptions\Service;

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

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

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

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

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

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

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

7. Ok, then now we can call it :

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

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

namespace ExamplePluginManagerCreationOptions\Factory\Service;

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

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

Now, we can register it at out PluginManager.

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

And now, we can call it with :

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

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

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

Done ;). I hope it is useful.

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

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

Zend Framework 2 : Create Custom Toolbar for ZendDeveloperTools

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

zf2-zendframework2

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

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

1. Let’s start with the Collector :

namespace SanSessionToolbar\Collector;

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

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

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

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

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

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

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

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

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

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

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

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

Zend Framework 2 : Disable some toolbar entries from ZendDeveloperTools

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

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

namespace ZendDeveloperTools;

use Zend\Stdlib\AbstractOptions;

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

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

namespace ZendDeveloperTools;

use Zend\Stdlib\AbstractOptions;

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

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

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

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

Zend Framework 2 : Using Custom Authentication condition with DoctrineModule

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

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

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

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

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

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

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

            return $this->createAuthenticationResult();
        }

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

        return $authResult;
    }
}

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

namespace MyDoctrineAuth\Factory\Authentication;

use DoctrineModule\Service\Authentication\AdapterFactory as BaseAdapterFactory;

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

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

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

        return new ObjectRepository($options);
    }
}

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

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

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

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

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

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

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

use Doctrine\ORM\Mapping as ORM;

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

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

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



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

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

        return $this;
    }

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

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

        return $this;
    }

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

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

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

use Doctrine\ORM\EntityRepository;

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

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

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

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

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

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

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

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

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

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

4. Create controller

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

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

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

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

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

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

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

        return $controller;
    }
}

5. Register services

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

6. Last but not least, build a view :

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

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

Ok, done ;)

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

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

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

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

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

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

$select->limit(1);

So, Before we return it with :

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

We can check it with :

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

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

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

Don’t push yourself

Posted in Psychology by samsonasik on May 8, 2014

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

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

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

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

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

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

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

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

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

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

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

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

Done ;)

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

Follow

Get every new post delivered to your Inbox.

Join 279 other followers