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 testing, 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/