Category: ZF2


Apigility rocks! It’s still in its early beta days (and hence a little buggy at times), but it’s WELL worth playing with now for building your API’s!

Anyway, you can create code connected and db connected services. Db connected REST services pretty much give you the auto CRUD functionality, but it would be nice to have some where, and order clauses!

This feature should be ready for version 1.2, but in the meantime, lets override it mannually!

You will notice in your Rest/ServiceName folder (in my particular case, Agents), that it has auto generated an AgentsCollection and an AgentsEntity PHP file.

We will create our own Resource Class which will extend the DBConnectedResource. So I created AgentsResource.php :

 

 <?php

namespace Allagents\V1\Rest\Agents;
use ZF\Apigility\DbConnectedResource;
use Zend\Paginator\Adapter\DbTableGateway as TableGatewayPaginator;

class AgentsResource extends DbConnectedResource
{
 public function fetchAll($data = array())
 {
 // WHERE CLAUSE
 $where = '';
 foreach($data->where as $clause)
 {
 $where .= $clause.' AND ';
 }
 //chop off the last AND
 $where = substr($where,0,-4);

 // ORDER CLAUSE
 $order = '';
 foreach($data->order as $clause)
 {
 $order .= $clause.' , ';
 }
 //chop off the last comma
 $order = substr($order,0,-2);

 // GROUP CLAUSE
 $group = '';
 foreach($data->group as $clause)
 {
 $group .= $clause.' , ';
 }
 //chop off the last comma
 $group = substr($group,0,-2);

 // HAVING CLAUSE
 $having = '';
 foreach($data->having as $clause)
 {
 $having .= $clause.' AND ';
 }
 //chop off the last AND
 $having = substr($having,0,-4);
 $adapter = new TableGatewayPaginator($this->table,$where, $order, $group, $having);
 return new $this->collectionClass($adapter);
 }
}

Ok, now we have to tell ZF2 to use our resource class instead of the default. As always, ZF2 modules are mega customisable, and usually involves tweaking your module.config. Apigility is no different, so jump into your module folder and open the module.config.php. We are adding one line to our DBConnectedResource. Find the section ‘zf-apigility’ => ‘db-connected’ and you will see your service’s entry. We add an array key like so; ‘resource_class’ => ‘Allagents\\V1\\Rest\\Agents\\AgentsResource’,

'zf-apigility' => array(
 'db-connected' => array(
 'Allagents\\V1\\Rest\\Agents\\AgentsResource' => array(
 'adapter_name' => 'DBAdapter',
 'table_name' => 'agents',
 'hydrator_name' => 'Zend\\Stdlib\\Hydrator\\ArraySerializable',
 'controller_service_name' => 'Allagents\\V1\\Rest\\Agents\\Controller',
 'entity_identifier_name' => 'aid',
 'resource_class' => 'Allagents\\V1\\Rest\\Agents\\AgentsResource',
 ),
 ),
 ),

Lastly, we need to whitelist the query parameters that we use, otherwise they will not appear in the HAL links!

Again, in the module.config.php, in the section ‘zf-rest’, we add an array defining the whitelisted parameters:

'zf-rest' => array(
 'Allagents\\V1\\Rest\\Agents\\Controller' => array(
 'listener' => 'Allagents\\V1\\Rest\\Agents\\AgentsResource',
 'route_name' => 'allagents.rest.agents',
 'route_identifier_name' => 'agents_id',
 'collection_name' => 'agents',
 'entity_http_methods' => array(
 0 => 'GET',
 1 => 'PATCH',
 2 => 'PUT',
 3 => 'DELETE',
 ),
 'collection_http_methods' => array(
 0 => 'GET',
 1 => 'POST',
 ),
 'collection_query_whitelist' => array(
 0 => 'where',
 1 => 'order',
 2 => 'having',
 3 => 'group',
 ),
),


To test it is working, call your URL (in my case /agents) and you should see the normal expected results. Now test with a query sting. I made a little script to build the query up:

<?php

$query = http_build_query(array(
 'where' => array(
 'name LIKE "A%"',
 ),
 'order' => array(
 'name asc',
 ),
 'group' => array(

 ),
 'having' => array(

 ),
));

echo $query.'<br />';

Which output:
where%5B0%5D=name+LIKE+%22A%25%22&order%5B0%5D=name+asc
So I called: /agents?where%5B0%5D=name+LIKE+%22A%25%22&order%5B0%5D=name+asc
You should have your filtered results, with HAL links in the JSON, like so!

{
“_links”: {
“self”: {
“href”: “http://api.allagents.del/agents?where%5B0%5D=name%20LIKE%20%22A%25%22&order%5B0%5D=name%20asc&page=1&#8221;
},
“first”: {
“href”: “http://api.allagents.del/agents?where%5B0%5D=name%20LIKE%20%22A%25%22&order%5B0%5D=name%20asc&#8221;
},
“last”: {
“href”: “http://api.allagents.del/agents?where%5B0%5D=name%20LIKE%20%22A%25%22&order%5B0%5D=name%20asc&page=44&#8221;
},
“next”: {
“href”: “http://api.allagents.del/agents?where%5B0%5D=name%20LIKE%20%22A%25%22&order%5B0%5D=name%20asc&page=2&#8221;
}
},
“_embedded”: {
“agents”: [
{
“aid”: “2842”,
“etc”:”etc!”

Have fun! Maybe I could even get this into the actual Apigility code by sending them a Pull Request! (Probably not though lol, I’m sure they’ll figure a better way of dealing with the query params than passing RAW SQL portions haha!)

Advertisements

As a Zend Framework 1 user I loved the simplicity of the class naming convention, being Folder_Subfolder_ClassName. However as you probably know, these class names get really quite long! The latest PHP as you already know uses namespaces and allows for shorter classnames that wont clash with each other. Now I have added an API to my website using the incredible Apigility (http://apigility.org) which was built in Zend Framework 2, I thought it would be nice to upgrade my existing classes to autoload PSR-0 style, so I can eventually migrate easily across.

First thing then, you need composer installed. If you’ve been following my blog, or using any other vendors packages, you’ll already have it in your project.

In ZF1, the library folder was where you would keep your different modules/packages/classes. I have a library called TTB. So in the TTB folder, create an src folder, and another TTB folder in there (this is a quirk of PSR-0, but trust me). In that folder, recreate your classes. Changes aren’t very difficult:

<?php
namespace TTB\Form;
use TTB\Form\Element;

class Contact extends \Zend_Form
{
    //etc
}

The line TTB_Form_Contact extends Zend_Form is shortened by way of the namespace line at the top to just become Contact, and the Zend_Form gets a backslash in front of it as it is in the global namespace. You also specify use  to import any other classes into the namespace. Now we can call Textbox instead of Element\Textbox or TTB\Form\Element\Textbox.

You probably know all this stuff anyway! The point is, to get it autoloading in your project!

So in your index file of your ZF1 project, require once vendor/autoload.php. And in your composer.json, add the following:

"autoload": {
     "psr-0": {
         "TTB" : "application/library/TTB/src/"
     }
 }

Finally, run composer dump-autoload in the terminal from your site root, and this will generate the classmap. You are now ready for PSR-0 compliance! Now you just need to spend all day refactoring! It’ll be worth it when you take your old project to a new framework!  😉

If you checked my last post, you will have read about how to get all your old ZF1 classes autoloading in your new shiny ZF2 project. However, the DB settings from your application.ini won’t have been set, and so you’ll get a 500 response with  No Db Adapter. Not a problem. We just create the db adapter ourselves and tell Zend_Db that it is the default.

I’ve put this in the ZF2 index.php just before the last line, Zend\Mvc\Application::init($appConfig)->run();

//Set up ZF1 DB
$params = array(
 'host' => '127.0.0.1',
 'username' => 'xxx',
 'password' => 'xxx',
 'dbname' => 'xxx'
);
$adapter = Zend_Db::factory('Pdo_Mysql',$params);
Zend_Db_Table_Abstract::setDefaultAdapter($adapter);

Now your autoloading ZF1 DB classes wont freak out, and should connect no problem!

I’ve started using Apigility for building my API for my mobile app of my site! It’s incredible, you have to try it!

Anyway, I wanted to be able to load my existing code into ZF2 so I didn’t have to reinvent the wheel.

My API is running on a subdomain in an api sub folder within the main project. In the api folder, I made a folder called library. Then I symlinked my Zend and other folders from my ZF1 project into it (this may not have been necessary, but I didn’t want ../.. relative link type stuff in my composer.json)

:~/www/site/api/library$ ln -s ../../application/library/Zend Zend
:~/www/site/api/library$ ln -s ../../application/library/ZendX ZendX
:~/www/site/api/library$ ln -s ../../application/library/TTB TTB
:~/www/site/api/library$ ln -s ../../application/library/AA AA

Next you need to do is tell composer.json about your libraries.

 "autoload": {
 "psr-0": {
 "AA_": "library/",
 "Zend_": "library/",
 "ZendX_": "library/"
 }
 },
 "include-path": [
 "library"
 ]

Finally get composer generating autoload files. Type in:

~/www/site/api$ composer dump-autoload
Generating autoload files

And thats it! You should now be able to call things like:

$awesome = new Zend_Pdf();
$old_skool = new AA_Old_Skool_Class();

Yay!

ZF2 is totally redefining PHP development, I love the direction they are going! Anyway, those who have shown interest will no doubt have played with Akrabats tutorial on the zf2 site. I for one have, and I also went and met the guys themselves at the PHPNW2012 Conference in Manchester at the start of this month! The ZF2 tutorial day was great! Thanks Evan & Rob! Anyway, i’m straying off topic here.

It’s all about the modules! Most sites have a login! A blog! A contact form! Facebook? Twitter? Whatever! Why reinvent the wheel? http://modules.zendframework.com allows you to choose from a growing number of modules, and installing them is a piece of cake (i should stop saying that, this is zend!) using the PHP Composer. So let’s have a look!

Completely starting from scratch, bung this in your Apache’s VirtualHosts config, tweaking the path to wherever your site is:

<VirtualHost *:80>
 ServerName zf2
 DocumentRoot /path/to/my-site/public/
 SetEnv APPLICATION_ENV "development" 
 <Directory /path/to/my-site/public/>
 DirectoryIndex index.php
 AllowOverride All
 Order allow,deny
 Allow from all
 </Directory>
</VirtualHost>

Now you’ve done that, time to install the Zend Framework Skeleton Application, by cloning it from the Git repository. Go into your sites folder, and say:

git clone git:/github.com/zendframework/ZendSkeletonApplication.git my-site
cd my-site
ls

You’ll notice a composer.json in there. Open this up, and you’ll see it depends on PHP and zendframework. In the command prompt:

php composer.phar self-update

This updates composer to the latest version, since it has updated its version since the SkeletonApplication was released. Then:

php composer.phar install

Composer kicks in, and downloads our dependency (zf2), after having made sure you have a sufficient version of PHP (>=5.3).

What’s this? ZF2 recommends installing more modules? Fine, lets do that, but first, lets choose even more modules to add! Change your composer.json to look like this:

{
 "name": "zendframework/skeleton-application",
 "description": "Skeleton Application for ZF2",
 "license": "BSD-3-Clause",
 "keywords": [
 "framework",
 "zf2"
 ],
 "homepage": "http://framework.zend.com/",
 "minimum-stability": "alpha",
 "repositories":[
{
 "type": "composer",
 "url": "http://packages.zendframework.com/"
 }],
 "require": {
 "php": ">=5.3.3",
 "zendframework/zendframework": "2.*",
 "doctrine/doctrine-orm-module": "dev-master",
 "doctrine/doctrine-module": "dev-master",
 "zendframework/zendpdf": "2.*",
 "zendframework/zendservice-recaptcha": "2.*",
 "symfony/yaml": "dev-master",
 "zendframework/zend-developer-tools": "dev-master"
 }
}

I’ve deliberately left two of ZF2s recommendations out of this, as they’re actually pecl extensions, not models, and I ain’t covering that just now, but I added Doctrine functionality. So, to get all this new stuff, we tell Composer to go update itself!

php composer.phar update

And bang, we now have the modules downloaded into the vendor folder. So! How to use them, I hear you cry? Well, the example I’ll show you is probably the best example to start with, and indeed, was the module we were shown at Evan & Rob’s ZF2 Tutorial day at PHPNW2012! ZfcUser.

Goto http://modules.zendframework.com
Scroll down to ZfcUser (formerly EdpUser) (that’s Evan btw) and click, which will take you to the github page.
Scroll down to Requirements. You’ll see you also need ZF2 and ZfcBase to use this module. We have ZF2, but not the ZFCommons base use Classes, so we’ll install both of these. Again, a simple case of adding the lines, then running php composer.phar update. Watch out for unnecessary tabs, brackets, and commas, or the json parser will take a fit!

"zf-commons/zfc-base": "dev-master",
 "zf-commons/zfc-user": "dev-master"

Great! All running smoothly I see! So jump in the ZfcUser module in the vendor folder and go into data, there you’ll get the sql for creating your user table. Run that on your MySQL in your database server, and you’re ready to rock! Now we just need to enable the modules (I’ll only show the relevent ones for the example, so I won’t be using the Doctrine stuff for now) and set up our db connection.

Stick your DB parameters in the config autoload local php file:

return array(
 'db' => array(
 'username' => 'user',
 'password' => 'pass',
 ),
);

and in the global file, we’ll put the meat and bones (the local php file is in .gitignore, which is why we keep the credentials there):

return array(
 'db' => array(
 'driver' => 'Pdo',
 'dsn' => 'mysql:dbname=my-site-db-here;host=localhost',
 'driver_options' => array(
 PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
 ),
 ),
 'service_manager' => array(
 'factories' => array(
 'Zend\Db\Adapter\Adapter'
 => 'Zend\Db\Adapter\AdapterServiceFactory',
 ),
 ),
);

Now if you open the application config you’ll see an array called modules, where we register the modules our application uses. Change it to this:

'modules' => array(
        'Application',
'ZfcBase', 
'ZfcUser'
    ),

And that’s literally it! try browsing to /user/login! register and login! with Gravatars and everything!

The really great thing about all of these modules is that they are extensible. Is something not happening in a module quite the way you would like it? Write a module extending it!
For instance, my first thought about ZfcUser was ‘what about activating your account by email when you register? Well, guess what? There’s a module for that. Evan told me to go check out CdliTwoStageSignup! (extending ZfcUser)

Working in this manner really is getting to the stage where functionality is literally drag, drop, ‘n’ tell your app! And by studying a module’s code, especially a module extending another module, we can start to see how it all connects up through the service and event managers, and how it’s all tied together! But that’s for you to mess around with! Have fun!