2

I've been working on a bunch of projects lately with Doctrine 1.2 integrated into Zend Framework 1.11, and it has been really fun.

One of the most common methods I have implemented in my service layers are methods which returns a collection of my domain models according to a set of criteria being passed as arguments.

I've been creating them with interfaces like this:

//All books
$books = $service->getBooks();

//Books under certain categories and authored which matches the search term 'Hitchens'
$books = $service->getBooks(array(
                     'category' => array(1,2,3,4),
                     'author' => array('like' => '%Hitchens%', 'diedBefore' => Zend_Date::now()),
                     'orderBy' => 'bookTitle', 
                     'limit' => 10
                   ));

And the implementation look like this:

public function getBooks(array $options = null)
{

     $query = Doctrine_Query::create()->from('LibSys_Model_Book as book');

     if($options !== null){
          if(isset($options['author']){
               //Manipulate Doctrine_Query object here....               
          }
          //More 'ifs' and manipulations to the Doctrine_Query object...(additional criterias, orderBy clauses, limits, offsets, etc.)
     }
}

And as my the need for more criteria rises, the messier the implementations get. And needless to say, there are a lot of code re-use everywhere, and updating the code is so such a tedious task.

I've been thinking that it would be a lot better if I can pass in the criteria as objects instead of array segments, utilizing the Visitor Pattern with something like this:

$authorCriteria = new LibSys_Criteria_Author();
$authorCriteria->like('%Hitchens%');
$authorCriteria->diedBefore(Zend_Date::now());

$books = $service->getBooks(array(
    $authorCriteria,
    new LibSys_Criteria_Category(array(1,2,3,4))
));

With LibSys_Criteria_Category and LibSys_Criteria_Author implementing a common interface which might look like this:

interface LibSys_Doctrine_Criteria_Interface
{
     public function applyCriteria(Doctrine_Query $query);
}

Therefore, it will just be a matter of looping through the set of criteria objects provided and pass along the Doctrine_Query object to them as they take turns in manipulating it to suit the needs:

public function getBooks(array $criteria = null)
{

    $query = Doctrine_Query::create()->from('LibSys_Model_Book as book');

    if($criteria !== null){
        foreach($criteria as $criterion){

            if($criterion instanceof LibSys_Doctrine_Criteria_Interface){
                $criterion->applyCriteria($query);
            }

        }
    }
}

This way not only does it make them easier to use, but also make the services extendible, the criteria objects reusable, and everything much easier to maintain.

In order to make this work robustly, though, one would need to be able to inspect the Doctrine_Query object and check any existing joins, conditions, etc. This is because it is not impossible that two totally different criteria may require same set of conditions or same set of joins. In which case, the second criteria can either simply re-use an existing join and/or adjust accordingly. Also, it wouldn't be hard to imagine cases where a criterion's set of conditions are mutually incompatible with another, in which case should should warrant an exception to thrown.

So my question is:

Is there a way to inspect a Doctrine_Query and get information regarding which components has been joined, or any set of conditions already applied to it?

Bez Hermoso
  • 1,132
  • 13
  • 20

0 Answers0