Development

Changeset 8117

You must first sign up to be able to contribute.

Changeset 8117

Show
Ignore:
Timestamp:
03/27/08 17:48:20 (7 months ago)
Author:
francois
Message:

sfPropelFinderPlugin Added joins

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • plugins/sfPropelFinderPlugin/README

    r8107 r8117  
    7575// Finding all Articles where published_at less than time() 
    7676$articles = $articleFinder->wherePublishedAt('<', time())->find(); 
     77// The whereXXX method accepts simple or composed column names 
     78$articles = $articleFinder->whereArticle_PublishedAt('<', time())->find(); 
    7779}}} 
    7880 
     
    117119The syntax should remind you of `sfFinder` and `sfTestBrowser`. 
    118120 
     121=== Joins === 
     122 
     123{{{ 
     124#!php 
     125<?php 
     126$article1 = new Article(); 
     127$article1->setTitle('Hello, world!'); 
     128$article1->setCategory($category1); 
     129$article1->save(); 
     130$author1 = new Author(); 
     131$author1->setName('John'); 
     132$author1->save(); 
     133$comment = new Comment(); 
     134$comment->setContent('You rock!'); 
     135$comment->setArticle($article1); 
     136$comment->setAuthor($author1); 
     137$comment->save(); 
     138// Add a join statement 
     139// No need to tell the finder which columns to use for the join, just the related Class 
     140// After all, the columns of the FK are already defined in the schema 
     141$article = sfPropelFinder::from('Article')-> 
     142  joinComment()-> 
     143  whereComment_Content('foo')-> 
     144  findOne(); 
     145// You can chain joins if you want to make it more complex 
     146$article = sfPropelFinder::from('Article')-> 
     147  joinComment()-> 
     148  joinAuthor()-> 
     149  whereAuthor_Name('You rock!')-> 
     150  findOne(); 
     151}}} 
     152 
    119153=== Writing your own business logic into a finder === 
    120154 
     
    144178== TODO == 
    145179 
    146  * Handle Joins 
     180 * Allow hydrating of related objects (doSelectJoinXXX equivalent) 
    147181 * Add more magic! 
    148182 * Merge with sfPropelImpersonatorPlugin? 
     
    152186=== 2008-03-27 | Trunk === 
    153187 
     188 * francois: Added `sfPropelFinder::joinXXX()` method 
     189 * francois: Added `sfPropelFinder::join()` method 
     190 * francois: added complete `whereClassName_ColumnName()` syntax 
    154191 * francois: Added `sfPropelFinder::count()` method 
    155192  
  • plugins/sfPropelFinderPlugin/lib/sfPropelFinder.php

    r8107 r8117  
    1313{ 
    1414  protected $peerClass = null; 
    15   protected $mapBuilder = null; 
    1615  protected $databaseMap = null; 
    17   protected $tableMap = null; 
    18   protected $columnMaps = null; 
    1916  protected $criteria = null; 
     17  protected $relations = null; 
    2018   
    2119  public function getPeerClass() 
     
    2624  public function setPeerClass($peerClass) 
    2725  { 
     26    $this->relations[]= $peerClass; 
    2827    return $this->peerClass = $peerClass; 
    29   } 
    30    
    31   public function getMapBuilder() 
    32   { 
    33     return $this->mapBuilder; 
    34   } 
    35    
    36   public function setMapBuilder($mapBuilder) 
    37   { 
    38     return $this->mapBuilder = $mapBuilder; 
    3928  } 
    4029   
     
    5645    $mapBuilder = call_user_func(array($this->peerClass, 'getMapBuilder')); 
    5746    $mapBuilder->doBuild(); 
    58     $this->mapBuilder = $mapBuilder; 
    59     $this->databaseMap = $this->getMapBuilder()->getDatabaseMap(); 
    60     $tableMaps = $this->databaseMap->getTables(); 
    61     $this->tableMap = array_pop($tableMaps); 
    62     $this->columnMaps = $this->tableMap->getColumns(); 
     47    $this->databaseMap = $mapBuilder->getDatabaseMap(); 
    6348  } 
    6449   
     
    8570     
    8671    return $ret; 
    87      
    8872  } 
    8973   
     
    158142  } 
    159143   
     144  public function join($column1, $column2, $operator = null) 
     145  { 
     146    $this->criteria->addJoin($column1, $column2, $operator); 
     147     
     148    return $this; 
     149  } 
     150   
    160151  public function __call($name, $arguments) 
    161152  { 
     
    163154    if(strpos($name, 'where') === 0) 
    164155    { 
    165       $column = $this->getColName(substr($name, 5)); 
     156      $columnName = substr($name, 5); 
     157      if(strpos($columnName, '_') !== false) 
     158      { 
     159        list($class, $columnName) = split('_', $columnName); 
     160        $column = $this->getColName($columnName, $class.'Peer'); 
     161      } 
     162      else 
     163      { 
     164        $column = $this->getColName($columnName); 
     165      } 
    166166      $comparison = Criteria::EQUAL; 
    167167      switch (count($arguments)) 
     
    185185          throw new Exception('{sfPropelFinder} whereXXX can only be called with one or two arguments'); 
    186186      } 
    187       $this->where($column, $value, $comparison); 
     187      return $this->where($column, $value, $comparison); 
    188188    } 
    189189     
     
    197197        $order = Criteria::ASC; 
    198198      } 
    199       $this->orderBy($column, $order); 
    200     } 
    201      
    202     return $this; 
    203   } 
    204    
    205   protected function getColName($phpName) 
    206   { 
     199      return $this->orderBy($column, $order); 
     200    } 
     201     
     202    // join 
     203    if(strpos($name, 'join') === 0) 
     204    { 
     205      $relatedClass = substr($name, 4); 
     206      list($column1, $column2) = $this->getRelation($relatedClass); 
     207      $this->relations[]= $relatedClass.'Peer'; 
     208      $operator = array_shift($arguments); 
     209      if(!$operator) 
     210      { 
     211        $operator = null; 
     212      } 
     213      return $this->join($column1, $column2, $operator); 
     214    } 
     215     
     216    return $this; 
     217  } 
     218   
     219  public function getRelation($phpName) 
     220  { 
     221    foreach($this->relations as $peerClass) 
     222    { 
     223      // try to find many to one or one to one relationship 
     224      if($relation = $this->findRelation($phpName, $peerClass)) 
     225      { 
     226        return $relation; 
     227      } 
     228      // try to find one to many relationship 
     229      if($relation = $this->findRelation(str_replace('Peer', '', $peerClass), $phpName.'Peer')) 
     230      { 
     231        return $relation; 
     232      } 
     233    } 
     234    throw new Exception(sprintf('{sfPropelFinder} %s has no %s related table', $this->peerClass, $phpName)); 
     235  } 
     236   
     237  protected function findRelation($phpName, $peerClass) 
     238  { 
     239    foreach ($this->getColumnsForPeerClass($peerClass) as $c) 
     240    { 
     241      if ($c->isForeignKey()) 
     242      { 
     243        if(sfInflector::camelize($c->getRelatedTableName()) == $phpName) 
     244        { 
     245          return array( 
     246            constant($peerClass.'::'.$c->getColumnName()), 
     247            $c->getRelatedName() 
     248          ); 
     249        } 
     250      } 
     251    } 
     252     
     253    return false; 
     254  } 
     255   
     256  protected function getColumnsForPeerClass($peerClass) 
     257  { 
     258    // try to find one to many relationship 
     259     $mapBuilderClass = str_replace('Peer', 'MapBuilder', $peerClass); 
     260     $tableName = strtolower(str_replace('Peer', '', $peerClass)); 
     261     if(class_exists($mapBuilderClass)) 
     262     { 
     263       $mapBuilder = new $mapBuilderClass(); 
     264       $mapBuilder->doBuild(); 
     265       return $this->databaseMap->getTable($tableName)->getColumns(); 
     266     } 
     267     return false; 
     268  } 
     269   
     270  protected function getColName($phpName, $peerClass = null) 
     271  { 
     272    if(!$peerClass) 
     273    { 
     274      $peerClass = $this->peerClass; 
     275    } 
    207276    try 
    208277    { 
    209       $column = call_user_func(array($this->peerClass, 'translateFieldName'), $phpName, BasePeer::TYPE_PHPNAME, BasePeer::TYPE_COLNAME); 
     278      $column = call_user_func(array($peerClass, 'translateFieldName'), $phpName, BasePeer::TYPE_PHPNAME, BasePeer::TYPE_COLNAME); 
    210279      return $column; 
    211280    } 
    212281    catch (PropelException $e) 
    213282    { 
    214       throw new Exception(sprintf('{sfPropelFinder} %s has no %s column', $this->peerClass, $phpName)); 
     283      throw new Exception(sprintf('{sfPropelFinder} %s has no %s column', $peerClass, $phpName)); 
    215284    } 
    216285  } 
  • plugins/sfPropelFinderPlugin/test/unit/sfPropelFinderTest.php

    r8108 r8117  
    99 */ 
    1010 
     11/* 
     12You need a model built with a running database to run these tests. 
     13The tests expect a model similar to this one: 
     14 
     15    propel: 
     16      article: 
     17        id:          ~ 
     18        title:       varchar(255) 
     19        category_id: ~ 
     20      category: 
     21        id:          ~ 
     22        name:        varchar(255) 
     23      comment: 
     24        id:          ~ 
     25        content:     varchar(255) 
     26        article_id:  ~ 
     27        author_id:   ~ 
     28      author: 
     29        id:          ~ 
     30        name:        varchar(255) 
     31 
     32Beware that the tables for these models will be emptied by the tests, so use a test database connection. 
     33*/ 
     34 
    1135// Autofind the first available app environment 
    1236$sf_root_dir = realpath(dirname(__FILE__).'/../../../../'); 
     
    3963ArticlePeer::doDeleteAll(); 
    4064 
    41 $t = new lime_test(42, new lime_output_color()); 
     65$t = new lime_test(53, new lime_output_color()); 
    4266 
    4367$t->diag('find()'); 
     
    202226 
    203227$article = sfPropelFinder::from('Article')->whereTitle('abc')->findOne(); 
    204 $t->is($article->getId(), $article1->getId(), 'whereXXX() adds a WHERE condition on the XXX column'); 
     228$t->is($article->getId(), $article1->getId(), 'whereXXX() accepts a simple column name like whereColumnName()'); 
     229$article = sfPropelFinder::from('Article')->whereArticle_Title('abc')->findOne(); 
     230$t->is($article->getId(), $article1->getId(), 'whereXXX() accepts a complete column name like whereClassName_ColumnName()'); 
    205231$article = sfPropelFinder::from('Article')->whereTitle('def')->findOne(); 
    206232$t->is($article->getId(), $article2->getId(), 'whereXXX() adds a WHERE condition on the XXX column'); 
     
    212238$t->is(count($articles), 3, 'whereXXX() accepts a text comparator and is permissive on syntax'); 
    213239 
     240 
    214241$t->diag('orderBy() magic'); 
    215242try 
     
    272299$article = sfPropelFinder::from('Article')->orderByTitle('desc')->findOne(); 
    273300$t->is($article->getTitle(), 'ccccc', 'orderByXXX() orders by column and takes an order as argment'); 
     301 
     302$t->diag('join() magic'); 
     303 
     304list($column1, $column2) = sfPropelFinder::from('Article')->getRelation('Category'); 
     305$t->is($column1, ArticlePeer::CATEGORY_ID, 'getRelation() guesses the two parts of a relation properly for many-to-one relationships'); 
     306$t->is($column2, CategoryPeer::ID, 'getRelation() guesses the two parts of a relation properly for many-to-one relationships'); 
     307list($column1, $column2) = sfPropelFinder::from('Article')->getRelation('Comment'); 
     308$t->is($column1, CommentPeer::ARTICLE_ID, 'getRelation() guesses the two parts of a relation properly for one-to-many relationships'); 
     309$t->is($column2, ArticlePeer::ID, 'getRelation() guesses the two parts of a relation properly for one-to-many relationships'); 
     310ArticlePeer::doDeleteAll(); 
     311CategoryPeer::doDeleteAll(); 
     312$category1 = new Category(); 
     313$category1->setName('cat1'); 
     314$category1->save(); 
     315$category2 = new Category(); 
     316$category2->setName('cat2'); 
     317$category2->save(); 
     318$article1 = new Article(); 
     319$article1->setTitle('aaaaa'); 
     320$article1->setCategory($category1); 
     321$article1->save(); 
     322$article2 = new Article(); 
     323$article2->setTitle('bbbbb'); 
     324$article2->setCategory($category1); 
     325$article2->save(); 
     326$article3 = new Article(); 
     327$article3->setTitle('ccccc'); 
     328$article3->setCategory($category2); 
     329$article3->save(); 
     330$nbArticles = sfPropelFinder::from('Article')->joinCategory()->where(CategoryPeer::NAME, 'cat1')->count(); 
     331$t->is($nbArticles, 2, 'joinXXX() allows to join to another table (many-to-one)'); 
     332$nbArticles = sfPropelFinder::from('Article')->joinCategory()->where(CategoryPeer::NAME, 'cat2')->count(); 
     333$t->is($nbArticles, 1, 'joinXXX() allows to join to another table (many-to-one)'); 
     334$article = sfPropelFinder::from('Article')->joinCategory()->where(CategoryPeer::NAME, 'cat2')->findOne(); 
     335$t->is($article->getTitle(), 'ccccc', 'joinXXX() allows to join to another table (many-to-one)'); 
     336ArticlePeer::doDeleteAll(); 
     337CommentPeer::doDeleteAll(); 
     338$article1 = new Article(); 
     339$article1->setTitle('aaaaa'); 
     340$article1->setCategory($category1); 
     341$article1->save(); 
     342$article2 = new Article(); 
     343$article2->setTitle('bbbbb'); 
     344$article2->setCategory($category1); 
     345$article2->save(); 
     346$comment = new Comment(); 
     347$comment->setContent('foo'); 
     348$comment->setArticle($article2); 
     349$comment->save(); 
     350$nbArticles = sfPropelFinder::from('Article')->joinComment()->where(CommentPeer::CONTENT, 'foo')->count(); 
     351$t->is($nbArticles, 1, 'joinXXX() allows to join to another table (one-to-many)'); 
     352$article = sfPropelFinder::from('Article')->joinComment()->where(CommentPeer::CONTENT, 'foo')->findOne(); 
     353$t->is($article->getTitle(), 'bbbbb', 'joinXXX() allows to join to another table (one-to-many)'); 
     354 
     355ArticlePeer::doDeleteAll(); 
     356CommentPeer::doDeleteAll(); 
     357AuthorPeer::doDeleteAll(); 
     358$article1 = new Article(); 
     359$article1->setTitle('aaaaa'); 
     360$article1->setCategory($category1); 
     361$article1->save(); 
     362$author1 = new Author(); 
     363$author1->setName('John'); 
     364$author1->save(); 
     365$comment = new Comment(); 
     366$comment->setContent('foo'); 
     367$comment->setArticle($article1); 
     368$comment->setAuthor($author1); 
     369$comment->save(); 
     370$article = sfPropelFinder::from('Article')->joinComment()->joinAuthor()->where(AuthorPeer::NAME, 'John')->findOne(); 
     371$t->is($article->getTitle(), 'aaaaa', 'you can chan several joinXXX() statements');