Welcome to Abdul Malik Ikhsan's Blog

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

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

Follow

Get every new post delivered to your Inbox.

Join 296 other followers