Tuesday, August 27, 2013

TIP: How to Immitate init() inside Controllers in ZF2

For ZF1 users there is the convenient init() method that you can use on every controller but in ZF2 the init() does not exist anymore for design purposes.

There are sometimes advantages though of having an init() type functionality inside the constructor. For example, checking the ACL.

A little background. One of the problems of working directly inside the constructor in ZF2 is because the dispatch event has not occured yet, you cannot grab important objects like controller plugins so doing the bellow wont work.
// assume we are inside a controller
public function __construct()
{
    $this->redirect()->toRoute('someroute'); // I will not work!
}
Solution:
There are tons of ways to do this but I find this a little easier:
class IndexController extends AbstractActionController
{
    public function __construct()
    {
        $resource = $this->getResourceId();
        $this->getEventManager()->attach('dispatch', function ($e) use ($resource) {
            $controller = $e->getTarget(); // this will return your controller instance

            // there is no ACL plugin ok! I created that!
            if (!$controller->acl()->isAllowed('me', $resource)) {
                // now i can use controller plugins!
                return $controller->redirect()->toRoute('authenticate');
            }     
        });
    }

    public function getResourceId()
    {
        return 'some_resource';
    }
}
Reference:
http://mwop.net/blog/2012-07-30-the-new-init.html

TIP: How to Retrieve and Register Custom Controller Plugins

You can think of controller plugins as action helpers in ZF1.
In order to retrieve the list of plugins including custom plugins you created.

Create a custom plugin:
//module.php
class Module
{
    public function getControllerPluginConfig()
    {
        return array(
            'factories' => array(
                'MyCustomAclPlugin' => function ($sm) {
                    $supahService = $sm->getServiceLocator()->get('superService');
                    return new MyCustomAclService($supahService);
                },
            )
        );
    }
}
Now to use it:
// pretend we are in a controller that extands AbstractActionController
public function indexAction() {
    var_dump($this->getPluginManager()->getRegisteredServices());
    // array('mycustomaclplugin', ...);

    // using our plugin
    $this->mycustomaclplugin()->someMethod();
}

Routing URI With Forward Appended Slashes in ZF2

You may sometimes want to route forward appended URIs to its last corresponding action.

For example:

http://www.sample.com/mycontroller/login/ (WILL NOT route to login action)
http://www.sample.com/mycontroller/login (WILL ROUTE to login action)
http://www.sample.com/mycontroller/ (WILL NOT ROUTE to index action)
http://www.sample.com/mycontroller (WILL ROUTE to index action)

And assume this Route setting for above:

array(
    'mycontroller' => array(
        'type'    => 'Segment',
        'options' => array(
            'route'    => '/mycontroller',
            'defaults' => array(
                'controller' => 'SomeModule\Controller\MyController',
                'action'     => 'index',
            ),
        ),
        'may_terminate' => true,
        'child_routes' => array(
            'login' => array(
                'type'    => 'Segment',
                'options' => array(
                    'route'    => '/login',
                    'defaults' => array(
                        'controller' => 'SomeModule\Controller\MyController',
                        'action'     => 'login',
                    ),
                ),
                'may_terminate' => true,
            ),
        ),
    ),
);

To make the forward appended slashes default to the last action in the URI:
array(
    'mycontroller' => array(
        'type'    => 'Segment',
        'options' => array(
            'route'    => '/mycontroller',
            'defaults' => array(
                'controller' => 'SomeModule\Controller\MyController',
                'action'     => 'index',
            ),
        ),
        'may_terminate' => true,
        'child_routes' => array(
            'default' => array(
                'type'    => 'Segment',
                'options' => array(
                    'route'    => '/[:action][/]',
                    'constraints' => array(
                        'action'     => '[a-zA-Z][a-zA-Z0-9_-]+',
                    ),
                    'defaults' => array(
                        'controller'    => 'SomeModule\Controller\MyController',
                        'action'        => 'index',
                    ),
                ),
                'may_terminate' => true,
            ),
            'login' => array(
                'type'    => 'Segment',
                'options' => array(
                    'route'    => '/login[/]',
                    'defaults' => array(
                        'controller' => 'SomeModule\Controller\MyController',
                        'action'     => 'login',
                    ),
                ),
                'may_terminate' => true,
            ),
        ),
    ),
);

Notice that I added a "default" child route so that I will not have to worry if a I added a new action and forget to create a route for it. Also notice the "[/]" which means that forward slashes are optional now and that's basically it. You just have to add "[/]" in each route of a Segment type.

http://www.sample.com/mycontroller/login/ (WILL ROUTE to login action)
http://www.sample.com/mycontroller/login (WILL ROUTE to login action)
http://www.sample.com/mycontroller/ (WILL ROUTE to index action)
http://www.sample.com/mycontroller (WILL ROUTE to index action)

If you dont want a default fallback route, you can do just so.
array(
    'mycontroller' => array(
        'type'    => 'Segment',
        'options' => array(
            'route'    => '/mycontroller[/]',
            'defaults' => array(
                'controller' => 'SomeModule\Controller\MyController',
                'action'     => 'index',
            ),
        ),
        'may_terminate' => true,
        'child_routes' => array(
            'login' => array(
                'type'    => 'Segment',
                'options' => array(
                    'route'    => '/login[/]',
                    'defaults' => array(
                        'controller' => 'SomeModule\Controller\MyController',
                        'action'     => 'login',
                    ),
                ),
                'may_terminate' => true,
            ),
        ),
    ),
);
Notice that I added "[/]" in the parent route because we removed the default child fallback route.
Now your route with forward slashes should work.

Monday, August 26, 2013

ZF2's MVC Event Trigger Sequence

Below is the trigger sequence of Zend Frameworks 2's MVC Event.

1.) MvcEvent::EVENT_BOOTSTRAP
    returns: Zend\Mvc\Application

    a.) MvcEvent::EVENT_ROUTE
        Returns: Zend\Mvc\Application

    b.) MvcEvent:EVENT_DISPATCH
        Returns: Zend\Mvc\Controller\AbstractController

    c.) MvcEvent:EVENT_RENDER
        Returns: Zend\Mvc\Application

    d.) MvcEvent:EVENT_FINISH
        Returns: Zend\Mvc\Application

You will usually attach to an event inside onBootstrap($e) in Module.php
//Module.php
namespace SomeModule;
use Zend\Mvc\MvcEvent;

class Module
{
    public function onBootstrap($e)
    {
        $e->getName(); // returns MvcEvent::EVENT_BOOTSTRAP
        $e->getTarget(); // returns Zend\Mvc\Application

        // retrieves the Event Manager within Bootstrap
        $em = $e->getApplication()->getEventManager();

        $em->attach(MvcEvent::EVENT_ROUTE, function ($e) {
             $e->getName(); // returns MvcEvent::EVENT_ROUTE
             $e->getTarget(); // returns Zend\Mvc\Application
        }

        $em->attach(MvcEvent::EVENT_DISPATCH, function ($e) {
             $e->getName(); // returns MvcEvent::EVENT_DISPATCH
             $e->getTarget(); // returns Zend\Mvc\Controller\AbstractController
        }

        $em->attach(MvcEvent::EVENT_RENDER, function ($e) {
             $e->getName(); // returns MvcEvent::EVENT_RENDER
             $e->getTarget(); // returns Zend\Mvc\Application
        }

        $em->attach(MvcEvent::EVENT_FINISH, function ($e) {
             $e->getName(); // returns MvcEvent::EVENT_FINISH
             $e->getTarget(); // returns Zend\Mvc\Application
        }
    }
}
It is only normal to return Zend\Mvc\Application as the object itself is being manipulated in the application process.

Thursday, August 22, 2013

REPOST: Setting up PHP to connect to a MS SQL Server using PDO_ODBC in Ubuntu

For those who are not familiar of ODBC like I was, its basically an API that lets you connect to other databases. PDO has its ODBC driver which in turn ODBC has its own also. To connect to MS SQL Server, we are using FreeTDS which is a library that lets you accomplish this.

sudo apt-get install freetds-bin freetds-common tdsodbc odbcinst php5-odbc unixodbc
sudo cp /usr/share/tdsodbc/odbcinst.ini /etc/
sudo apache2ctl restart

Connect using PDO in PHP:
$c = new PDO('odbc:Driver=FreeTDS; Server=hostname_or_ip; Port=port; Database=database_name; UID=username; PWD=password;');
Note: You will not see any reference of FreeTDS (the odbc driver) in the ODBC section of your PHP Info so don't weird out.

Reference:
https://secure.kitserve.org.uk/content/accessing-microsoft-sql-server-php-ubuntu-using-pdo-odbc-and-freetds

Tuesday, August 13, 2013

REPOST: Show and Compare Committed Files Using LOG and DIFF in Subversion

Show commited files for revision 63.

Syntax:
svn log --verbose -r <rev>
Example:
svn log --verbose -r 63

Show difference by comparing actual files. For example, show line by line difference between revision 64 against 63.

Syntax:
svn diff -r<rev-of-commit>:<rev-of-commit - 1>
Example:
svn diff -r64:63

Show the actual file version at the specific revision number:

Syntax:
svn cat -r <rev> <file> | less
Example 
svn cat -r 64 ./some_file.php | less

Reference:
http://stackoverflow.com/questions/6296284/svn-list-files-committed-for-a-revision

Monday, August 12, 2013

My take on Mocks VS Stubs

I sometimes get confused on the difference between a mock and a stub. Actually I still get confused from time to time but I'll spit out my thoughts right now.

Example:
class Math
{
    public function add($arg1, $arg2)
    {
        if (!is_numeric($arg1) || !is_numeric($arg2)) {
            throw new Exception('cannot add non numerics');
        }
        return $arg1 + $arg2;
    }
}

class Person
{
    public function getTotalItems(Math $math, $shoes, $shirts)
    {
        return $math->add($shoes, $shirts);
    }
}

Mock Unit Test:
public function getTotalItemsTest()
{
    $mockMath = $this->getMockBuilder('Math')
                     ->setMethods(array('add'))
                     ->getMock();
    $mockMath->expects($this->once())
             ->method('add')
             ->with($this->isType('numeric'), $this->isType('numeric'))
             ->will($this->returnCallback(function ($arg1, $arg2) {
                 return $arg1 + $arg2;
             }));

    $total = $person->getTotalItems($mockMath, 2, 3);
    $this->assertEquals(5, $total);
}

Stub Unit Test:
public function getTotalItemsTest()
{
    $stubMath = $this->getMockBuilder('Math')
                     ->setMethods(array('add'))
                     ->getMock();
    $stubMath->expects($this->any())
             ->method('add')
             ->will($this->returnValue(5));

    $total = $person->getTotalItems($stubMath, 2, 3);
    $this->assertEquals(5, $total);
}

This is not the best example but you get the groove? When you mock an object, you set an "expectation" for the object method you are mocking. In the case above, we are checking that add() only accepts numerical inputs. Usually, a dead give away is we are using with() to set our expectation. In contrast to a stub, we are just returning straight the result.

Note that the callback for a mock does not matter. I just use it for verbosity.

Thursday, August 8, 2013

NOTE: tar compress and extract

Just a simple reminder for linux tar commands for compressing and extracting:

Exctract:
tar -xvzf ./myfilename.tar.gz myfilename

Compress:
tar -cvzf ./myfilename.tar.gz myfilename