Benjamin Tigano Developer at Large

2Feb/12Off

Creating menus in a Zend_Application web app…

When I first started exploring the Zend Framework (ZF), one of the issues I ran into was generating menu's. My solution (in an effort to not go too much deeper into what ZF had to offer while I focused on the basics) was to put some code in the layout file where I wanted the menu. The menu was an array of titles and URL's that I'd loop through and display. I wanted to add a CSS class to the menu item if that's where the user was, so I checked the controller and action off of the request to see if it matched the URL, and if it did, I added the CSS. It was simple and quick, but it was messy.

I recently discovered a component in ZF for solving this problem - Zend_Navigation. The best part about this component is that you can build the menu via code, or from an XML file. I've been testing out both, but I find the XML file to be much easier and clean-cut. Rather than putting the code into the layout again, I created a a view helper in /application/views/helpers named MenuHelper. Below is the view helper class

<?php
class Zend_View_Helper_MenuHelper
{
   public $view;

   public function menuHelper() {
      $config = new Zend_Config_Xml(APPLICATION_PATH.'/configs/menu.xml', 'nav');
      $container = new Zend_Navigation($config);

      $this->view->navigation($container)->UlClass = "main-nav";
      return $this->view->navigation()->menu()->render();
   }

   public function setView(Zend_View_Interface $view) {
      $this->view = $view;
   }
}

The lines specific to building the menu are highlighted. We start by reading in our configuration file, and defining the section we want to load - "nav". We then create the Zend_Navigation object, define a class for the ul element ("main-nav"), and then render the menu to the view.

In our layout, we call the menu helper just like any other view helper:

<?php echo $this->menuHelper(); ?>

Almost done. Next, we need to populate the XML file with the menu items. In my example, I added a link back to Zend.com, and a link to the homepage of my site (index/index.) Even though it's listed second, the home link will appear first because the order value is less than the order value on the zend link. The file is stored in the application/configs directory, named "menu.xml".

<?xml version="1.0" encoding="UTF-8"?>
<config>
   <nav>
      <zend>
         <label>Zend</label>
         <uri>http://www.zend.com</uri>
         <order>100</order>
      </zend>
      <home>
         <label>Home</label>
         <order>-100</order>
         <module>default</module>
         <controller>index</controller>
         <action>index</action>
      </home>
   </nav>
</config>

Lastly, we want to make sure that we apply a specific CSS class to the menu item if that's our current URL. To do that, we do nothing! Why? Because Zend_Navigation does that for us!

Want to read more about what Zend_Navigation has to offer? Check out the reference guide on Zend's website here.

Fork me on GitHub
2Feb/12Off

Early page cache plugin for Zend Framework MVC Applications

Zend Framework is one of the more popular PHP frameworks. I recommend it a lot for custom web applications, but speed is sometimes an issue. It's a very powerful framework, and as with anything, features and flexibility can effect performance where you don't need features. For simple pages (like legal statements, about, company profile, etc.), an early page cache system can help you gain performance for the less dynamic areas of a web application.

Yesterday, in an effort to test what an early page cache system would look like, I spent some time to write a plugin for a standard ZF application. I used the generic MVC application that Zend Studio will make for you, and cleaned it up a bit. Here's a breakdown of what I did to create the eary page cache plugin.

First, the ini configuration file. I added the section below to register the plugin with the front controller, and store the cache settings. Any kind of settings should ALWAYS be stored in configuration files. It can be XML, INI, or even a PHP array, but keep it separate from the rest of your application. In the case below, I'm using a lifetime of 3600 seconds (1 hour) for my cache. You'll likely want to adjust this (and the other settings) based on your application's needs.

resources.frontController.plugins.pagecache = "My_Controller_Plugin_PageCache"
pageCache.frontEnd = core
pageCache.backEnd = file
pageCache.frontEndOptions.lifetime = 3600
pageCache.frontEndOptions.automatic_serialization = true
pageCache.backEndOptions.lifetime = 3600
pageCache.backEndOptions.cache_dir = APPLICATION_PATH "/../cache/pageCache"

The next item to discuss is the Bootstrap file (/application/Bootstrap.php). While there isn't anything specific to the plugin we're creating, there is something that the plugin depends upon. In almost all of my ZF applications, I store the config in the Zend_Registry so that I can access it wherever I need it. Here is the section of code that does that:

protected function _initConfig()
{
    $config = new Zend_Config($this->getOptions(), true);
    Zend_Registry::set('config', $config);
    return $config;
}

Now, whenever we want to access the configuration settings, we can use the following code:

$config = Zend_Registry::get('config');

Another addition to the Bootstrap is registering the namespace where the plugin will live (line highlighted below):

protected function _initAutoload()
{
	// add autoloader empty namespace
	$autoLoader = Zend_Loader_AutoLoader::getInstance();
	$autoLoader->registerNamespace('My_'); // added this for the early page cache plugin
	$resourceLoader = new Zend_Loader_Autoloader_Resource(array(
	'basePath' 		=> APPLICATION_PATH,
	'namespace' 	=> '',
));

Last but not least is the plugin itself. I won't go into too much depth on this because if you're not familiar with at least the basics of a Zend MVC application, you're probably not understanding the rest of this article. We start by creating methods for the dispatchLoopStartup and dispatchLoopShutdown. We use dispatchLoopStartup because after all this is an EARLY page cache. We use the dispatchLoopShutdown because at that point, we've gone through the full dispatch loop and have a complete response that we can cache for the next request.

In the dispatchLoopStartup, we grab the configuration from the Zend_Registry that we stored it to during bootstrapping. We use those settings to create the Zend_Cache object. The first check we do is to make sure we're only caching GET requests. The next thing we do is create a cache pool key based on the page name. Next, we see if we can get a cache hit. If this is the first time we're visiting the page, it'll be a miss. If we do get a hit, we set the response based on what was in the cache, send the response, and then exit. If we didn't exit, we would go through the normal dispatch loop and avoid caching.

In the dispatchLoopShutdown, we do a few checks to make sure that the page is a good candidate for caching. If it is, we save it in the cache based on the same key we generated in the dispatchLoopStartup method.

Here is the code for the plugin class:

class My_Controller_Plugin_PageCache extends Zend_Controller_Plugin_Abstract
{
	public static $doNotCache = false;
	public $cache;
	public $key;

	public function dispatchLoopStartup(Zend_Controller_Request_Abstract $request)
	{
		$pageCacheConfig = Zend_Registry::get('config')->pageCache;

		$this->cache = Zend_Cache::factory(
		$pageCacheConfig->frontEnd,
		$pageCacheConfig->backEnd,
		$pageCacheConfig->frontEndOptions->toArray(),
		$pageCacheConfig->backEndOptions->toArray());

		if (!$request->isGet()) {
			self::$doNotCache = true;
			return;
		}

		$path = $request->getPathInfo();

		$this->key = md5($path);

		$data = $this->cache->load($this->key);
		if ($data)
		{
			$response = $data;
			$this->setResponse($response);
			$this->getResponse()->sendResponse();
			exit;
		}
	}

	public function dispatchLoopShutdown()
	{
		if (self::$doNotCache
		|| $this->getResponse()->isRedirect()
		|| (null === $this->key))
		{
			return;
		}
		$this->cache->save($this->getResponse(), $this->key);
	}
}

As always, there will be cases where you don't want a particular controller or action to be cached, in that case, we've built in a static property you can set in your controller that will stop the plugin from caching the response.

My_Controller_Plugin_PageCache::$doNotCache = true;

Below is a zip file contained the full MVC application, minus the Zend Framework library (to save space.) You should drop the Zend folder inside the library folder, and create the .htaccess file (assuming you're using Apache with mod_rewrite enabled) to point to public/index.php.

If you've got questions, comments, or suggestions, leave them in the comments!

Get The Code!

29Jan/12Off

Pagination with Zend Framework

I'm currently working on the photo gallery portion of a website for a constructions company that sells and installs fiberglass pools. Obviously, having images of gorgeous pools can help convince a potential buyer (especially when these particular pools are like the ones you see on MTV Cribs), so the gallery needs to be advanced - built from the ground up. I've been given hundreds of different galleries to post on the site, each with dozens of pictures per album. That creates the need to paginate. Like the rest of the site, I'm using components of Zend Framework. I haven't yet needed to paginate anything using ZF, so I figured I'd share my experience testing out what they've got to offer.

// first, include the files we'll need
require 'Zend/Paginator.php';
require 'Zend/Paginator/Adapter/Array.php';

$array = array('1', '2', '3', '4', '5', '6', '7'); // array of data to paginate
$paginator = new Zend_Paginator(new Zend_Paginator_Adapter_Array($array)); // create the paginator
$paginator->setCurrentPageNumber((int)($_GET['page'])); // get the current page number that we're on
$paginator->setItemCountPerPage(2); // only show two per page

// these are the variables we'll likely need in order to create the "prev/next page" links
$currentPageNumber = $paginator->getCurrentPageNumber();
$itemCountPerPage = $paginator->getItemCountPerPage();
$totalItemCount = $paginator->getTotalItemCount();
$totalPageCount = $paginator->count();

Now that we've got the paginator set-up and loaded with our data, we can now output the HTML for the prev/next links.

// if not page 1, show the "prev" page link
<!--?php if ($currentPageNumber != 1):?-->
<a href="/?page=<?php echo $currentPageNumber - 1 ?>">Prev</a>
<!--?php endif; ?--></pre>
<pre>
// if not the last page, show the "next" page link
<!--?php if ($currentPageNumber < $totalPageCount):?-->
   <a href="/?page=<?php echo $currentPageNumber + 1 ?>">Next</a>
<!--?php endif; ?-->

// display each item on the current page in the paginator
<!--?php if (count($paginator)): ?--></pre>
<pre>
<!--?php endif; ?-->

You'll likely want to display the results differently, but I just wanted to show a quick example since you'll have to show the prev/next links and traverse through each pages items regardless of your implementation.

Got comments or questions? Leave a message below.

 

2Jun/11Off

HTML5 and stuff…

HTML5I've been thinking a lot about HTML5 and how I can get started with it early on and hopefully stay ahead of the curb. I found a site/book that does a great job of laying things out - diveintohtml5.org.

I haven't gotten too deep into it, but just scraping the surface of a few chapters has already taught me some interesting facts about HTML5.

I also found a couple of other tools that would come in handy for web development (and design):

http://html-ipsum.com/ - Provides quick access to lorem ipsum text for quickly filling in templates.

http://jsfiddle.net/ - An interactive web design and development playground.