sfPropelLazyHydrationIteratorPlugin - 0.1.1

This plugin simplifies working with large result sets in Propel.

You are currently browsing
the website for symfony 1

Visit the Symfony2 website


« Back to the Plugins Home

Signin


Forgot your password?
Create an account

Tools

Stats

advanced search
Information Readme Releases Changelog Contribute
Show source

sfPropelLazyHydrationIteration plugin

The sfPropelLazyHydrationIterationPlugin is a Symfony plugin designed for Propel 1.2-based projects that make use of "lazy hydration" to deal with larger database result sets. Specifically, it provides a higher-level API for doing so, that relieves your application's own business logic from dealing with Creole's ResultSet objects.

Essentially, code that would normally need to look like this:

$rs = AuthorPeer::doSelectRS( new Criteria() );
while( $rs->next() )
{
  $author = new Author();
  $author->hydrate( $rs );
  echo "{$author->getLastName()}, {$author->getFirstName()}";
}

Can instead look like this:

$authors = new sfPropelLazyHydrationIterator('Author', new Criteria());
foreach( $authors as $author )
{
  echo "{$author->getLastName()}, {$author->getFirstName()}";
}

Or even this (with just a touch of effort):

foreach( AuthorPeer::doSelectLazy(new Criteria()) as $author )
{
  echo "{$author->getLastName()}, {$author->getFirstName()}";
}

Sound appealing? Let's start.

Installation

First, let me reiterate: you really need Propel 1.2. This is the version that ships with Symfony 1.0 and 1.1. At the time of this writing, Propel 1.2 is not available for later versions of Symfony.

  1. Install the plugin:

    $ symfony plugin:install sfPropelLazyHydrationIterationPlugin
    

    Alternatively, you may check out the sources from the svn repository: http://svn.symfony-project.com/plugins/sfLightboxPlugin and publish the assets with the symfony plugin:publish-assets task)

  2. Clear your cache

    $ symfony cc
    

Usage

The main class in the plugin is the sfPropelLazyHydrationIterator. This class actually implements the "lazy hydration" example it replaces internally, but hides it away into an object that implements PHP's Iterator interface (more info).

Let's take a very simple look at the class in use:

// prepare the Criteria to describe the query you'd like to issue
$crit = new Criteria();
 
// let's identify the name of the model class we're going to retrieve
$model_class = 'Author';
 
// finally, let's create the iterator
$authors = new sfPropelLazyHydrationIterator($model_class, $crit);
 
// finally, let's iterate over each Author that matches our query
foreach( $autors as $author )
{
  // do something with $author
}

The sample above shows the most basic use of the sfPropelLazyHydrationIterator. The constructor requires at least two parameters: the name of the Propel model class that must be retrieved, and a Criteria object.

The sfPropelLazyHydrationIterator also allows for the specification of a particular Propel Connection, as follows:

try
{
  $crit = new Criteria();
  $model_class = 'Author';
 
  // let's create a transaction
  $con = Propel::getConnection();
  $con->begin();
 
  $authors = new sfPropelLazyHydrationIterator($model_class, $crit, $con);
 
  // finally, let's iterate over each Author that matches our query
  foreach( $autors as $author )
  {
    // change some data in the $author
    $author->save( $con );
  }
}
catch (SQLException $sqle)
{
  $con->rollback();
  throw $sqle;
}

As shown in the example above, the constructor accepts both Criteria and Connection objects, as do each of the the *Peer::doSelect() and *Peer::doSelectRS() methods in your model.

Model Integration

In order to get the most utility out of this new class, let's get this functionality into all of each peer class for which it's useful.

Keeping with our examples, let's look at AuthorPeer:

# lib/model/AuthorPeer.php
class AuthorPeer extends BaseAuthorPeer
{
 
  /**
   * Create a `sfPropelLazyHydrationIterator` instance for use in
   * resource-efficient iteration of query results.
   *
   * @param Criteria $c 
   * @param Connection $con 
   * @return Iterator
   */
  public static function doSelectLazy($c, $con=null)
  {
    return new sfPropelLazyHydrationIterator('Author', $crit, $con);
  }
 
  // other methods ...
 
}

This simple method allows the following code block to be used throughout your project:

foreach( AuthorPeer::doSelectLazy(new Criteria()) as $author )
{
  // do something with $author
}

When adding a doSelectLazy() method to each peer in your model, do note that you'll have to vary the first parameter of the constructor call, which instead of 'Author' should reflect the peer's own model class counterpart.

Considerations

It may seem tempting to replace all your doSelect() invocations with doSelectLazy() calls, but there are a couple of points to consider before you do, since doSelect() returns an array of objects, while doSelectLazy() returns an Iterator which is not an array.

Let's look at some differences between the two.

Since the traditional traditional doSelect() code returns a PHP array, a sample template file might look like this:

<?php if (! empty($authors_array)): ?>
  <div class="status">Found <?php echo count($authors_array) ?>.</div>
  <ul class="authors">
    <?php foreach ( $authors_array as $author ): ?>
      <li><?php echo $author->getLastName() ?>, <?php echo $author->getFirstName() ?></li>
    <?php endforeach ?>
  </ul>
<?php else: ?>
  <p class="status no_results">No authors were found.</p>
<?php endif ?>

Note the use of PHP functions such as count(), and empty(). Since the sfPropelLazyHydrationIterator isn't a PHP array, these techniques aren't suitable. Luckily, the class does provide a number of methods that provide parallel utility:

<?php if (! $authors_lazy->isEmpty()): ?>
  <div class="status">Found <?php echo $authors_lazy->count() ?>.</div>
  <ul class="authors">
    <?php foreach ( $authors_lazy as $author ): ?>
      <li><?php echo $author->getLastName() ?>, <?php echo $author->getFirstName() ?></li>
    <?php endforeach ?>
  </ul>
<?php else: ?>
  <p class="status no_results">No authors were found.</p>
<?php endif ?>

All this to say that you should be able to accomplish all the same types of things, regardless of which selection mechanism you decide to use, but that they are not transparently interchangeable with each other in existing code.

Known Issues

This plugin does not yet work with Propel 1.3.

It is not presently possible to perform a PEAR install into a Symfony 1.0 project (1.1 works great, though). Here is what you will likely see:

$ ./symfony plugin-install ~/Projects/LazyHydration/sfPropelLazyHydrationIteratorPlugin-0.1.0.tgz
>> plugin    installing plugin "/Users/yanni...rationIteratorPlugin-0.1.0.tgz"
>> pear      Unknown channel "plugins.symfony-project.org"
>> pear      Parsing of package.xml from file
>> pear      "/Users/yanni/Sites/Hosts/plugintest-sf1.0/cache///package.xml"
>> pear      failed
>> pear      Cannot initialize
>> pear      '/Users/yanni/temp/sfPropelLazy...ationIteratorPlugin-0.1.0.tgz',
>> pear      invalid or missing package file
>> pear      Package
>> pear      "/Users/yanni/temp/sfPropelLazy...rationIteratorPlugin-0.1.0.tgz"
>> pear      is not valid
 
 
  [Exception]     
  install failed  

To Do

  • fix PEAR installation for symfony 1.0 projects (please use svn:externals or manual install)
  • compatibility with Propel 1.3
  • unit tests