LogoPhly, boy, phly
the weblog and site of Matthew Weier O'Phinney

Thursday, February 4. 2010

Creating Re-Usable Zend_Application Resource Plugins

In my last article, I wrote about how to get started with Zend_Application, including some information about how to write resource methods, as well as listing available resource plugins. What happens when you need a re-usable resource for which there is no existing plugin shipped? Why, write your own, of course!

All plugins in Zend Framework follow a common pattern. Basically, you group plugins under a common directory, with a common class prefix, and then notify the pluggable class of their location.

For this post, let's consider that you may want a resource plugin to do the following:

  • Set the view doctype
  • Set the default page title and title separator

Getting Started

First, let's determine the class prefix we want to use. If we follow Zend Framework Coding Standards, we can leverage autoloading, while simultaneously ensuring a common class prefix for our resources.

For the purposes of this exercise, we'll use the class prefix Phly_Resource, located in Phly/Resource/ on our include_path.

We'll call our particular resource "Layouthelpers", with a full class name of Phly_Resource_Layouthelpers, and place it in Phly/Resource/Layouthelpers.php. It needs to implement Zend_Application_Resource_Resource, but it's often even easier to extend Zend_Application_Resource_ResourceAbstract. In both cases, you need to define an init() method. Let's set up our skeleton accordingly:


<?php
// Phly/Resource/Layouthelpers.php
//
class Phly_Resource_Layouthelpers
    extends Zend_Application_Resource_ResourceAbstract
{
    public function init()
    {
    }
}
 

On Dependency Tracking

In my previous article, I showed an example of dependency tracking in Zend_Application. We will need it in this exercise as well, as both of our tasks operate on the view object, which we will retrieve via the View resource.

When creating resource methods directly in your bootstrap, you can simply call $this->getResource($name). However, within a plugin resource class, you need to first get access to the bootstrap object itself -- which you can do with the getBootstrap() method.

Let's ensure the View resource is initialized, and retrieve it.


<?php
// Phly/Resource/Layouthelpers.php
//
class Phly_Resource_Layouthelpers
    extends Zend_Application_Resource_ResourceAbstract
{
    public function init()
    {
        $bootstrap = $this->getBootstrap();
        $bootstrap->bootstrap('View');
        $view = $bootstrap->getResource('View');

        // ...
    }
}
 

Configuring the resource

Now that we've got our view object, we can do some work. Since we want the resource to be re-usable, we should likely allow some configuration options. Zend_Application_Resource_ResourceAbstract provides some boilerplate functionality for doing so.

First, we'll provide some default options via the $_options property.


<?php
// Phly/Resource/Layouthelpers.php
//
class Phly_Resource_Layouthelpers
    extends Zend_Application_Resource_ResourceAbstract
{
    protected $_options = array(
        'doctype'         => 'XHTML1_STRICT',
        'title'           => 'Site Title',
        'title_separator' => ' :: ',
    );

    public function init()
    {
        $bootstrap = $this->getBootstrap();
        $bootstrap->bootstrap('View');
        $view = $bootstrap->getResource('View');

        // ...
    }
}
 

We can then grab options using the getOptions() method.


<?php
// Phly/Resource/Layouthelpers.php
//
class Phly_Resource_Layouthelpers
    extends Zend_Application_Resource_ResourceAbstract
{
    protected $_options = array(
        'doctype'         => 'XHTML1_STRICT',
        'title'           => 'Site Title',
        'title_separator' => ' :: ',
    );

    public function init()
    {
        $bootstrap = $this->getBootstrap();
        $bootstrap->bootstrap('View');
        $view = $bootstrap->getResource('View');

        $options = $this->getOptions();
        // ...
    }
}
 

Now, in configuration files, developers can override the defaults:


[production]
; ...
resources.layouthelpers.doctype = "HTML5"
resources.layouthelpers.title = "My Snazzy New Website"
resources.layouthelpers.title_separator = " &emdash; "
 

Doing some work

Now that we have the bits and pieces of naming and configuration out of the way, let's do some work:


<?php
// Phly/Resource/Layouthelpers.php
//
class Phly_Resource_Layouthelpers
    extends Zend_Application_Resource_ResourceAbstract
{
    protected $_options = array(
        'doctype'         => 'XHTML1_STRICT',
        'title'           => 'Site Title',
        'title_separator' => ' :: ',
    );

    public function init()
    {
        $bootstrap = $this->getBootstrap();
        $bootstrap->bootstrap('View');
        $view = $bootstrap->getResource('View');

        $options = $this->getOptions();
       
        $view->doctype($options['doctype']);
        $view->headTitle()->setSeparator($options['title_separator'])
                          ->append($options['title']);
    }
}
 

And that's it!

Telling the Bootstrap about us

Well, that's it for the plugin resource, that is. But how do we tell our bootstrap class about it? Via our configuration file, using the "pluginPaths" key. This is an array, with the keys being plugin class prefixes, and the values the path that corresponds to that prefix.


[production]
; ...
pluginPaths.Phly_Resource = "Phly/Resource"
resources.layouthelpers.doctype = "HTML5"
resources.layouthelpers.title = "My Snazzy New Website"
resources.layouthelpers.title_separator = " &emdash; "
 

You can register as many plugin paths as you desire. As this key is processed before any resources are processed, it can also be defined at any time in your configuration.

Further Considerations

The example in this post was admittedly trivial. One aspect not discussed was creating a resource that would be reused throughout your application. As an example, you might want to create a resource you'll use at different times in your application. If you return a value in your init() method, the bootstrap object will store this for later retrieval. A good example of this we saw earlier: the View resource registers a Zend_View object with the bootstrap simply by returning the instance from its resource plugin.

Conclusions

Hopefully this post and the post prior have helped shed some light on Zend_Application, and in particular, how to write and bootstrap resources.

If you have further questions, you can find me on the ZF mailing lists, on IRC via the Freenode servers, or on twitter. Good luck!

Posted by Matthew Weier O'Phinney in PHP at 14:55 | Comments (12) | Trackbacks (2)
Defined tags for this entry: mvc, php, zend framework
Related entries by tags:
Responding to Different Content Types in RESTful ZF Apps
Symfony Live 2010
Quick Start to Zend_Application_Bootstrap
Real-time ZF Monitoring via Zend Server
Building RESTful Services with Zend Framework

Trackbacks
Trackback specific URI for this entry

Creating Re-Usable Zend_Application Resource Plugins - phly, boy, phly
In my last article, I wrote about how to get started with Zend_Application, including some information about how to write resource methods, as well as listing available resource plugins. What happens when you need a re-usable resource for which there is no existing plugin shipped? Why, write your own, of course!
Weblog: abcphp.com
Tracked: Feb 05, 07:51
???????? ???????? ???????????? ???????? ???????? Zend_Application
??????? ??????????? ?????? [Matthew Weier O'Phinney](http://weierophinney.net/matthew/) ? `Zend_Application`. ? ???? ??? ????? ???????????? ? ?????????? ??????????? ???????? ????????....
Weblog: ?????.info
Tracked: Feb 08, 01:07

Comments
Display comments as (Linear | Threaded)

Nice article, Matthew.

Using the Zend_Application I'm able to wire up the objects well throughout the bootstrap, controller, view, layout, front controller plugins, action helpers and view helpers. But I'm stuck at wiring them 'model' to other parts of the application.

Isnt't it time ZF offers a dependency injection solution?
#1 Sudheer (Link) on 2010-02-04 15:17 (Reply)
We already are; and we aren't. Let me clarify.

There are two concepts when considering DI: the actual mechanics of injecting dependencies, and DI containers. When it comes to the first, we're actually already in fairly good shape; ZF components typically already allow for injecting dependencies either via the constructor, setters, or both. This support is ubiquitous across the framework. As for the latter, DI containers, we're still evaluating whether we will add one to ZF. While very powerful, they also require a lot more up-front definition of dependencies, adding complexity to application setup that many developers may decide is more than they want to work with.
#1.1 Matthew Weier O'Phinney (Link) on 2010-02-05 08:10 (Reply)
I have to write a resource in my library for simplifying its setup. It would be dependent on the Doctrine resource, how is the proposed integration of Doctrine 1 & 2 going?
#2 Giorgio Sironi (Link) on 2010-02-04 16:41 (Reply)
I believe that Benjamin has support for Doctrine 2 working at this time; I'm not sure of the status of 1.X support. At this time, I think he's awaiting more feedback from the community before marking the resources as ready for recommendation.
#2.1 Matthew Weier O'Phinney (Link) on 2010-02-05 08:03 (Reply)
Nice article! I've been using this approach for all of my custom bootstrapping needs and it works out very nicely.

After reviewing some of the shipped resource plugins, I noticed some of them use getters/setters for the options instead of dealing with the options array. It appears that each config option is automatically be passed to a matching setter if it exists. This can make your resource plugin easier to work with when using tools like PhpDocumentor.
#3 Hector Virgen (Link) on 2010-02-04 17:47 (Reply)
I think the only problem I have thus far with application resources is retrieving them later. I'm not 100% clear on the best method to, say, utilize my custom Multidb resource in my model or service layers.

Nice rundown of how resources work, by the way.
#4 Ryan Chouinard (Link) on 2010-02-05 10:35 (Reply)
From your controller, you can retrieve it as follows: $multidb = $this->getInvokeArg('bootstrap')->getResource('multidb');

You would then inject it into your domain model.
#4.1 Matthew Weier O'Phinney (Link) on 2010-02-05 11:35 (Reply)
What's the best practice for using the resources outside of your controllers?

I have for example a custom ACL component in the library (/library/MyApp/MyApp_Acl) which makes use of a database connection. How should I retrieve the Db resource? Should I retrieve it from the Front Controller like this: $db = Zend_Controller_Front::getInstance()->getParam('bootstrap')->getResource('db');

For now I always used a global static object that has a Zend_Db instance as a static property:
$db = Globals::getDbConnection();

Anyway, your articles on using Bootstrap resources has helped me a great deal already. thx
#4.1.1 Stijn Huyberechts on 2010-02-08 05:02 (Reply)
When it comes to things such as your ACL, you will typically first pull the resource you need from the bootstrap, and then pass it as either a constructor argument to the class, or to a setter on the object. How you retrieve the bootstrap in the first place will depend on context -- if you're in an action controller, $this->getInvokeParam('bootstrap') will do that; elsewhere, you would pull it from the front controller as you describe.
#4.1.1.1 Matthew Weier O'Phinney (Link) on 2010-02-08 07:34 (Reply)
Nice post, Matthew. Must be very useful for beginners. So I translated it into Russian - http://lobach.info/develop/zf/creating-re-usable-zend_application-resource-plugins/

Hopefully this will help my fellow citizens to get acquainted with this article.
#5 Oleg Lobach (Link) on 2010-02-08 10:11 (Reply)
Was there a reason you didn't name the resource with camelcase i.e. "LayoutHelpers.php" ?
#6 Gerard (Link) on 2010-02-21 06:06 (Reply)
Yes -- resource names must be only Titlecased. This is a limitation we'll be fixing in ZF 2.0.
#6.1 Matthew Weier O'Phinney (Link) on 2010-03-09 07:43 (Reply)

Add Comment

Standard emoticons like :-) and ;-) are converted to images.
E-Mail addresses will not be displayed and will only be used for E-Mail notifications

To prevent automated Bots from commentspamming, please enter the string you see in the image below in the appropriate input box. Your comment will only be submitted if the strings match. Please ensure that your browser supports and accepts cookies, or your comment cannot be verified correctly.
CAPTCHA

 
 
  • Home
  • Resume
  • Blog
  • Phly PEAR Channel
  • Contact Me
  • About this site

ZCE

Zend Education Advisory Board Member

Add to Technorati Favorites

Calendar

Back March '10 Forward
Mon Tue Wed Thu Fri Sat Sun
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31        

Quicksearch

Links

  • PHLY - PHp LibrarY
  • Paul M. Jones
  • Mike Naberezny
  • Shahar Evron
  • Planet PHP
  • Zend Where I now work
  • Garden.org Where I once worked

Archives

March 2010
February 2010
January 2010
Recent...
Older...

Categories

XML Linux
XML Personal
XML Aikido
XML Family
XML Programming
XML Dojo
XML Perl
XML PHP

All categories

Syndicate This Blog

XML RSS 0.91 feed
XML RSS 1.0 feed
XML RSS 2.0 feed
ATOM/XML ATOM 0.3 feed
ATOM/XML ATOM 1.0 feed
XML RSS 2.0 Comments

Show tagged entries

xml best practices
xml books
xml conferences
xml cw09
xml decorators
xml dojo
xml dpc08
xml file_fortune
xml git
xml linux
xml mvc
xml oop
xml pear
xml perl
xml personal
xml php
xml phpworks08
xml programming
xml rest
xml ubuntu
xml vim
xml webinar
xml zendcon
xml zendcon08
xml zendcon09
xml zend framework
© 2004 - present, Matthew Weier O'Phinney
matthew-web <at> weierophinney.net