views:

1895

answers:

13

Zend Framework, being as flexible as it is, is ripe for plenty of tips and tricks. I know I've seen a lot of quirky, undocumented things that people put in their apps. I'd love for people to share their creative coding tricks using ZF.

There is a similar thread to this with Django that I found very helpful, so I thought I would start one of these for Zend Framework.

  • One tip/trick per reply
  • Add any required environment details (version, OS, etc)
+3  A: 

Zend Framework includes database adapters for some non-PDO PHP extensions:

  • ext/mysqli
  • ext/oci8
  • ext/ibm_db2
  • ext/interbase

In spite of the documentation, I'm always surprised by people who are under the impression that only PDO is supported.

Bill Karwin
+4  A: 

Zend_Db makes a lazy connection to the database. You can instantiate a Zend_Db_Adapter object any time you want, but it doesn't connect to the database unless and until you prepare a query, or call the getConnection() method on the adapter object.

Bill Karwin
+5  A: 

Integration with Firebug.

Trivially easy to dump structured data to the Firebug console.

Just as easy to profile database activity and dump a table of queries to Firebug again.

Just one of the many ways ZF saves time and makes life easy.

David Caunt
+4  A: 

Controller Action Helpers are not very well known but provide some really useful on demand functionality.

My two favourites are FlashMessenger, for easily passing messages to other pages, and ContextSwitch, for providing different response formats based on request data.

David Caunt
+6  A: 

My 3rd and final (for now) is Zend_Form

I've spoken to people using Zend Framework but not using Zend Form. It is without doubt one of the best components in the framework, providing trivially easy form generation, filtering and validation. It's also easy to add custom elements, decorators, validators and filters,

Though the documentation is long, it's very easy to get started with and you'll soon enjoy rapid development of forms across your application.

David Caunt
+3  A: 

Zend_Auth_Adapter and Zend_Auth_Storage are quite configurable - you don't have to stick with the builtins. For example, I'm twitter as my auth adapter backend!

Justin
That's quite cool! Got any code online or examples?
David Caunt
Yeah, pretty soon I'll post on my own blog, and probably comment again here with links.
Justin
+9  A: 

Adding caching to Zend_Db_Table_Abstract classes is really easy.

  • Setup caching in Bootstrap
  • Modify all your class foo extends Zend_Db_Table_Abstract to class foo extends CachingTable
  • Create class CachingTable extends Zend_Db_Table_Abstract and overwrite methods update, insert, delete, fetchRow and fetchAll
  • Bring caching object from bootstrap to init method
  • Create _purgeCache() or similar method which cleans cache
  • Use for example md5($where->__toString()) as caching identifier in fetchAll and fetchRow
  • Call $this->_purgeCache() on update, insert and delete
  • You can also add those caching "categorys" easily using table name as category so you don't have to clear all cached data at once.


Example code:

This example caches all select queries indefinitely and any insert/update/delete purges all cached stuff.

To Bootstrap:

// Caching
$cache_config = new Zend_Config_Ini(dirname(__FILE__) . '/../config.ini', 'cache');

$frontendOptions = array(
  'caching' => true,
  'lifetime' => null,
  'automatic_serialization' => true
);

$backendOptions = array(
  'cache_dir' => $cache_config->directory,
  'cache_file_umask' => 0607
);

$cache = Zend_Cache::factory('Core', 'File', $frontendOptions, $backendOptions);
Zend_Registry::set('Cache', $cache);

Where you keeps your extensions:

class CachingTable extends Zend_Db_Table_Abstract
{
  /**
   * @var Zend_Cache
   */
  protected $_cache = null;

  /**
   * @var bool
   */
  public $cache_result = true;

  /**
   * Initialize
   */
  public function init()
  {
    // Get from bootstrap
    $this->_cache = Zend_Registry::get('Cache');
  } // /function

  /**
   * Reset cache
   */
  public function _purgeCache()
  {
    $this->_cache->clean(Zend_Cache::CLEANING_MODE_ALL);
  } // /function

  /**
   * update
   */
  public function update(array $data, $where)
  {
    parent::update($data, $where);
    $this->_purgeCache();
  } // /function

  /**
   * insert
   */
  public function insert(array $data)
  {
    parent::insert($data);
    $this->_purgeCache();
  } // /function

  /**
   * delete
   */
  public function delete($where)
  {
    parent::delete($where);
    $this->_purgeCache();
  } // /function

  /**
   * Fetch all
   */
  public function fetchAll($where = null, $order = null, $count = null, $offset = null)
  {
    $id = md5($where->__toString());

    if ((!($this->_cache->test($id))) || (!$this->cache_result))
    {
      $result = parent::fetchAll($where, $order, $count, $offset);
      $this->_cache->save($result);

      return $result;
    }
    else
    {
      return $this->_cache->load($id);
    }

  } // /function

  /**
   * Fetch one result
   */
  public function fetchRow($where = null, $order = null)
  {
    $id = md5($where->__toString());

    if ((!($this->_cache->test($id))) || (!$this->cache_result))
    {
      $result = parent::fetchRow($where, $order);
      $this->_cache->save($result);

      return $result;
    }
    else
    {
      return $this->_cache->load($id);
    }

  } // /function

} // /class

Where you keep database-related stuff:

/**
 * Pages
 */
class Pages extends CachingTable
{
  /**
   * Table name
   * @var string
   */
  protected $_name = 'PAGES';

  /**
   * Primary key
   * @var string
   */
  protected $_primary = 'name';

} // /class

In your actual controller / model / view / whatever:

 $pages = new Pages();
 //$pages->cache_result = false; // Don't cache

 $select = $pages->select();
 $select->from($pages, array('content'));
 $select->where('name = ?', $page);
 $info = $pages->fetchRow($select);
 if(!is_null($info))
 {
   $info = $info->toArray();
 }
 else
 {
   // Add new page
   $pages->insert(array('name' => $page, 'added' => new Zend_Db_Expr('NOW()'), 'updated' => new Zend_Db_Expr('NOW()')));
 }
raspi
Do you have any sample code for this? Are you using the Class frontend?
David Caunt
Thanks for code!
David Caunt
+2  A: 

Check also ZF Snippets site

raspi
+4  A: 

Zend_Db_Adapter has a method quoteIdentifier() which allows you to use table names and column names that contain special characters, whitespace, SQL keywords, etc.

Quoting identifiers is different from quoting data values, and different brands of database quote identifiers in different ways.

I haven't seen any other framework or database access library that provides a function for quoting identifiers.

Bill Karwin
+3  A: 

Zend_Db_Table_Row has a function _transformColumn that can be used to convert column names. We have a custom record class that extends Zend_Db_Table_Row that we use to enforce coding guidlines (in the database a column post_id would be transformed to postId in the application).

Akeem
+1  A: 

I ran some quick tests and found the pdo adapter performed better in our application than mysqli (contrary to other tests found on the internet). Would like to know if this has been the experience of others as well. Blog post of results

Akeem
+3  A: 

Turn on MYSQL_ATTR_USE_BUFFERED_QUERY if you're running MySQL before version 5.1.17.

$pdoParams = array( PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true );

This will enable the MySQL query cache which is turned off due to prepared statement use w/ Zend Framework.

joedevon
Hmmm, are there any trade offs to turning this on?
Andy Baird
I know of no trade-offs on the ZF side, although there might be. However, there are of course to turning on the MySQL Query Cache. It has a relatively minor overhead. It doesn't apply to all queries. If there's an ORDER BY RAND() your query will never hit the cache. If you know a query is not likely to be cached (e.g. dynamically generated SQL that is likely to be unique), add SQL_NO_CACHE to your SELECT statement to turn it off. In most cases I think the cache will be a huge savings. Here's a page about it (Only skimmed it but looks very good):http://bit.ly/dmToa Note: tune query cache size.
joedevon
The downside to using buffered queries is that the client does a fetchall, which can blow out your memory limit if you do a query with a large result set.
Bill Karwin
+1  A: 

Using front controller singleton as an alternative to registry:

$front = Zend_Controller_Front::getInstance();
$front->setParam('yourUniqueID', $anything);

Then, (almost) anywhere in the app:

$this->getFrontController()->getParam('yourUniqueID');

or:

$front = Zend_Controller_Front::getInstance();
$front->getParam('yourUniqueID');
takeshin
That's interesting, but what's the use case for doing that instead of just using the registry?
Andy Baird
You already use a Front Controller which is a singleton as the Registry. One singleton less. By default, you may use `$front->getParam('bootstrap')` to get Bootstrap instance.
takeshin