Author: Leon.van.der.Ree
Date: 2010-03-23 13:00:24 +0100 (Tue, 23 Mar 2010)
New Revision: 28701

Added:
   plugins/sfPropelObjectPathBehaviorPlugin/branches/
   plugins/sfPropelObjectPathBehaviorPlugin/branches/1.5/
   plugins/sfPropelObjectPathBehaviorPlugin/branches/1.5/README
   plugins/sfPropelObjectPathBehaviorPlugin/branches/1.5/lib/
   
plugins/sfPropelObjectPathBehaviorPlugin/branches/1.5/lib/ObjectPathBehavior.php
   
plugins/sfPropelObjectPathBehaviorPlugin/branches/1.5/lib/ObjectPathCriteria.php
   plugins/sfPropelObjectPathBehaviorPlugin/branches/1.5/test/
   
plugins/sfPropelObjectPathBehaviorPlugin/branches/1.5/test/ObjectPathCriteriaTest.php
   plugins/sfPropelObjectPathBehaviorPlugin/tags/
   plugins/sfPropelObjectPathBehaviorPlugin/trunk/
Log:
initial commit of sfPropelObjectPathBehaviorPlugin

Added: plugins/sfPropelObjectPathBehaviorPlugin/branches/1.5/README
===================================================================
--- plugins/sfPropelObjectPathBehaviorPlugin/branches/1.5/README                
                (rev 0)
+++ plugins/sfPropelObjectPathBehaviorPlugin/branches/1.5/README        
2010-03-23 12:00:24 UTC (rev 28701)
@@ -0,0 +1,61 @@
+Introduction
+============
+
+This plugin adds ObjectPath support to Propel (1.5+). 
+
+This syntax makes it very easy to do sorting and filtering on foreign fields.
+
+ObjectPaths are dot-seperated relation-names that relate from one object to 
another and this behavior translates the objectPaths to table-aliasses.
+
+
+Example
+=======
+
+So a very simple ObjectPath from City to Country is simply "Country" and from 
Country to City it would be "City":
+In other words the relation-name.
+
+However ObjectPaths support dots to 'jump' from object to object. So for 
example from Review to Book to Author would be "Book.Author".
+
+In Php-code this shows up as:
+
+    [php]
+    $query = new ReviewQuery();
+    $reviews = $query->
+      joinByObjectPath('Book.Author')->
+      orderBy('Book.Author.FirstName', Criteria::ASC)->
+      find();
+
+to find all reviews, joined with their Books, joined with their Authors.
+
+TODO: explain all changes and the behavior in more detail
+
+Installation
+============
+
+To install this plugin you need to checkout this code in your symfony plugins 
folder under `sfPropelObjectPathBehaviorPlugin`
+
+After the checkout, modify your `project/config/propel.ini`
+
+add
+
+    [ini]
+    propel.behavior.object_path.class              = 
plugins.sfPropelObjectPathBehaviorPlugin.lib.ObjectPathBehavior
+
+
+and add the default behavior, by modifying
+
+    [ini]
+    propel.behavior.default                        = 
symfony,symfony_i18n,object_path
+
+    
+ 
+now rebuild your model and you are good to go!
+    
+    $ php symfony propel:build-model
+    
+
+
+Note
+====
+
+The objectPathsBehavior is used in the sfDataSourcePlugin.
\ No newline at end of file

Added: 
plugins/sfPropelObjectPathBehaviorPlugin/branches/1.5/lib/ObjectPathBehavior.php
===================================================================
--- 
plugins/sfPropelObjectPathBehaviorPlugin/branches/1.5/lib/ObjectPathBehavior.php
                            (rev 0)
+++ 
plugins/sfPropelObjectPathBehaviorPlugin/branches/1.5/lib/ObjectPathBehavior.php
    2010-03-23 12:00:24 UTC (rev 28701)
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * This file is part of the Propel package.
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @license    MIT License
+ */ 
+
+class ObjectPathBehavior extends Behavior
+{
+  public function getQueryBuilderModifier()
+  {
+    if (is_null($this->queryBuilderModifier))
+    {
+      $this->queryBuilderModifier = new ObjectPathQueryBuilderModifier($this);
+    }
+    return $this->queryBuilderModifier;
+  }
+}
+
+
+class ObjectPathQueryBuilderModifier
+{ 
+  public function parentClass()
+  {
+    return 'ObjectPathCriteria';
+  }  
+
+}

Added: 
plugins/sfPropelObjectPathBehaviorPlugin/branches/1.5/lib/ObjectPathCriteria.php
===================================================================
--- 
plugins/sfPropelObjectPathBehaviorPlugin/branches/1.5/lib/ObjectPathCriteria.php
                            (rev 0)
+++ 
plugins/sfPropelObjectPathBehaviorPlugin/branches/1.5/lib/ObjectPathCriteria.php
    2010-03-23 12:00:24 UTC (rev 28701)
@@ -0,0 +1,134 @@
+<?php
+
+/**
+ * An ObjectPath is a chain of relationNames, seperated by a dot, resulting in 
a joined object
+ * 
+ * @author leonvanderree
+ *
+ */
+class ObjectPathCriteria extends ModelCriteria 
+{
+  /**
+   * Recursively performs JoinWith on the provided ObjectPaths 
+   * 
+   * @param     string $objectPath  one or more strings with objectPaths
+   * @return    string
+   */
+  public function joinByObjectPath($objectPath)
+  {
+       $objectPaths = func_get_args();
+       
+    // process all objectPaths
+    foreach ($objectPaths as $objectPath)
+    {
+      // brake up the objectPath into an array of relationsNames
+      $relationsNames = explode('.', $objectPath, 2);
+      
+      // resolve current object and the (first) relationName
+      if (count($relationsNames)<1)
+      {
+        throw new PropelException('no relation provided');
+      }
+      $relationName = $relationsNames[0];
+      $alias = $this->translateObjectPathToAlias($relationName);
+      
+      // don't perform JoinWith, if already joined
+      // alternatively I could test if the join is not already in the 
with-array 
+      // (this is a little more computational intense, but will perform with 
even though the join has already been performed) 
+      if (!isset($this->joins[$alias]))
+      {
+       $this->joinWith($relationName.' '.$alias);
+      }
+
+      // if more relations are provided, continue (recursively) parsing the 
objectPath
+      if (isset($relationsNames[1]))
+      {
+               $this->useQuery($alias)
+                       ->joinByObjectPath($relationsNames[1])
+               ->endUse();
+      }
+    }
+    
+    //fluid interface
+    return $this;
+  }
+  
+  /**
+   * Resolves the database alias for an objectPath
+   * 
+   * Basically this comes down to replacing all dots with an underscore
+   * and preceeding it with the queryModel-alias
+   * 
+   * so $reviewQuery->translateObjectPathToAlias('Book.Author')
+   * becomes Review_Book_Author as the table-alias for Book-Author
+   * 
+   * @param string $objectPath
+   * @return string the constructed alias
+   */
+  public function translateObjectPathToAlias($objectPath)
+  {
+    // brake up the objectPath into an array of relationsNames
+    $relationsNames = explode('.', $objectPath, 2);
+    
+    // resolve current object and the (first) relationName
+    if (count($relationsNames)<1)
+    {
+      throw new PropelException('no relation provided');
+    }
+    $relationName = $relationsNames[0];
+    $alias = $this->getModelAliasOrName().'_'.$relationName;
+
+    // if more relations are provided, continue (recursively) parsing the 
objectPath
+    if (isset($relationsNames[1]))
+    {
+      $alias = $this->useQuery($alias)
+        ->translateObjectPathToAlias($relationsNames[1]);
+    }
+  
+    return $alias;
+  }
+
+       /**
+        * Finds a column and a SQL translation for a pseudo SQL column name
+        * Respects (and requires) table aliases previously registered in a 
join() or addAlias()
+        * Examples:
+        * <code>
+        * $c->getColumnFromName('Book.Title');
+        *   => array($bookTitleColumnMap, 'book.TITLE')
+        * $c->join('Book.Author a')
+        *   ->getColumnFromName('a.FirstName');
+        *   => array($authorFirstNameColumnMap, 'a.FIRST_NAME')
+        *
+        * // NEW: propertyPaths can be used after joining by ObjectPaths
+        * $reviewQuery->joinByObjectPath('Book.Author')
+        *   ->getColumnFromName('Book.Author.FirstName');
+        *   => array($authorFirstNameColumnMap, 
'Review_Book_Author.FIRST_NAME')
+        * </code>
+        *
+        * @param      string $propertyPath String representing the column name 
in a pseudo SQL clause, e.g. 'Book.Title'
+        *
+        * @return     array List($columnMap, $realColumnName)
+        */
+       public function getColumnFromName($propertyPath, $failSilently = true)
+       {
+               // if we have a propertyPath containing an objectPath (not only 
a columnName)
+               // we recursively parse the objectPath first to get into the 
joined queryModel
+               if (($lastDot = strrpos($propertyPath, '.')) !== false) {
+                       $objectPath = substr($propertyPath, 0, $lastDot);
+                       $columnName = substr($propertyPath, $lastDot + 1);
+                       
+                       //recursively get alias-name
+                       $alias = $this->translateObjectPathToAlias($objectPath);
+                       
+                       if (isset($this->joins[$alias])) {
+                               return 
$this->useQuery($alias)->getColumnFromName($columnName, $failSilently);
+                       }
+                       
+               }
+               
+               // only columnName provided, or relation-name is unknown (which 
might be a table alias)
+         // process normal column as before
+         return parent::getColumnFromName($propertyPath, $failSilently);
+       }  
+
+}
\ No newline at end of file

Added: 
plugins/sfPropelObjectPathBehaviorPlugin/branches/1.5/test/ObjectPathCriteriaTest.php
===================================================================
--- 
plugins/sfPropelObjectPathBehaviorPlugin/branches/1.5/test/ObjectPathCriteriaTest.php
                               (rev 0)
+++ 
plugins/sfPropelObjectPathBehaviorPlugin/branches/1.5/test/ObjectPathCriteriaTest.php
       2010-03-23 12:00:24 UTC (rev 28701)
@@ -0,0 +1,75 @@
+<?php
+
+/**
+ * 
+ * TODO: I will translate these tests to lime to make it easier to test them 
in symfony
+ * 
+ * This file is part of the Propel package.
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @license    MIT License
+ */
+
+require_once 'tools/helpers/bookstore/BookstoreTestBase.php';
+require_once 'tools/helpers/bookstore/BookstoreDataPopulator.php';
+
+/**
+ * Test class for ObjectPathCriteria.
+ *
+ * @author     Leon van der Ree
+ * @version    $Id: ObjectPathCriteriaTest.php  $
+ * @package    runtime.query
+ */
+class ObjectPathCriteriaTest extends BookstoreTestBase
+{
+       
+       public function testJoinByObjectPath()
+       {
+               // test one objectPath
+               $c = new ObjectPathCriteria('bookstore', 'Review');
+               $c->joinByObjectPath('Book.Author');
+               
+               $con = Propel::getConnection(BookPeer::DATABASE_NAME);
+               $c->find($con);
+               // TODO: probably don't test against sql, but against 
ModelCriteria
+               $expectedSQL = "SELECT review.ID, review.REVIEWED_BY, 
review.REVIEW_DATE, review.RECOMMENDED, review.STATUS, review.BOOK_ID, 
Review_Book.ID, Review_Book.TITLE, Review_Book.ISBN, Review_Book.PRICE, 
Review_Book.PUBLISHER_ID, Review_Book.AUTHOR_ID, Review_Book_Author.ID, 
Review_Book_Author.FIRST_NAME, Review_Book_Author.LAST_NAME, 
Review_Book_Author.EMAIL, Review_Book_Author.AGE FROM `review` INNER JOIN book 
Review_Book ON (review.BOOK_ID=Review_Book.ID) INNER JOIN author 
Review_Book_Author ON (Review_Book.AUTHOR_ID=Review_Book_Author.ID)";
+               $this->assertEquals($expectedSQL, $con->getLastExecutedQuery(), 
'joinByObjectPath recursively performs joinWith on all relations, and creates 
correct aliasses');
+               
+               
+               // test multiple objectPaths
+               $c = new ObjectPathCriteria('bookstore', 'Review');
+               $c->joinByObjectPath('Book.Author', 'Book.Publisher');
+               
+               $con = Propel::getConnection(BookPeer::DATABASE_NAME);
+               $c->find($con);
+               // TODO: probably don't test against sql, but against 
ModelCriteria
+               $expectedSQL = "SELECT review.ID, review.REVIEWED_BY, 
review.REVIEW_DATE, review.RECOMMENDED, review.STATUS, review.BOOK_ID, 
Review_Book.ID, Review_Book.TITLE, Review_Book.ISBN, Review_Book.PRICE, 
Review_Book.PUBLISHER_ID, Review_Book.AUTHOR_ID, Review_Book_Author.ID, 
Review_Book_Author.FIRST_NAME, Review_Book_Author.LAST_NAME, 
Review_Book_Author.EMAIL, Review_Book_Author.AGE, Review_Book_Publisher.ID, 
Review_Book_Publisher.NAME FROM `review` INNER JOIN book Review_Book ON 
(review.BOOK_ID=Review_Book.ID) INNER JOIN author Review_Book_Author ON 
(Review_Book.AUTHOR_ID=Review_Book_Author.ID) INNER JOIN publisher 
Review_Book_Publisher ON (Review_Book.PUBLISHER_ID=Review_Book_Publisher.ID)";
+               $this->assertEquals($expectedSQL, $con->getLastExecutedQuery(), 
'joinByObjectPath can hande multiple objectPaths');
+       }               
+               
+       public function testTranslateObjectPathToAlias()
+       {       
+               // test objectPath translation
+               $c = new ObjectPathCriteria('bookstore', 'Review');
+               $c->joinByObjectPath('Book.Author');
+               
+               $tableAlias = $c->translateObjectPathToAlias('Book.Author');
+               $this->assertEquals('Review_Book_Author', $tableAlias, 
'translateObjectPathToAlias resolves table-alias correctly');
+
+       }
+
+       public function testGetColumnFromName()
+       {       
+               $c = new ObjectPathCriteria('bookstore', 'Review');
+               $c->joinByObjectPath('Book.Author');
+               
+               list($columnMap, $realColumnName) = 
$c->getColumnFromName('Book.Title');
+               $this->assertEquals('Review_Book.TITLE', $realColumnName, 
'getColumnFromName resolves realColumnName correctly');
+               $this->assertTrue($columnMap instanceof ColumnMap, 
'getColumnFromName returns instane of ColumnMap');
+
+               list($columnMap, $realColumnName) = 
$c->getColumnFromName('Book.Author.FirstName', false);
+               $this->assertEquals('Review_Book_Author.FIRST_NAME', 
$realColumnName, 'getColumnFromName resolves deeper realColumnNames correctly');
+               $this->assertTrue($columnMap instanceof ColumnMap, 
'getColumnFromName returns deeper instanes of ColumnMap');
+       }
+}
\ No newline at end of file

-- 
You received this message because you are subscribed to the Google Groups 
"symfony SVN" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/symfony-svn?hl=en.

Reply via email to