Development

Changeset 10120

You must first sign up to be able to contribute.

Changeset 10120

Show
Ignore:
Timestamp:
07/04/08 16:11:04 (2 months ago)
Author:
francois
Message:

sfPropelFinderPlugin Added the ability to do left, right, and inner joins in a simple way

Files:

Legend:

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

    r10119 r10120  
    193193#!php 
    194194<?php 
     195// Test data 
    195196$article1 = new Article(); 
    196197$article1->setTitle('Hello, world!'); 
     
    202203 
    203204// Add a join statement 
    204 // No need to tell the finder which columns to use for the join, just the related Class 
    205 // After all, the columns of the FK are already defined in the schema 
    206205$article = sfPropelFinder::from('Article')-> 
    207206  join('Comment')-> 
    208207  where('Comment.Content', 'You rock!')-> 
    209208  findOne(); 
    210  
    211 // As a matter of fact, join() is optional 
    212 // You can omit it if the join is simple and the finder can guess it 
    213 // That is, if you add conditions with explicit column names 
     209// No need to tell the finder which columns to use for the join, just the related Class 
     210// After all, the columns of the FK are already defined in the schema. 
     211 
     212// If subsequent conditions use explicit column names, 
     213// The finder can even guess the join table and you can omit the join() statement. 
     214// This is the case here with Comment.Content, so the following also works 
    214215$article = sfPropelFinder::from('Article')-> 
    215216  where('Comment.Content', 'You rock!')-> 
    216217  findOne(); 
    217218 
    218 // You can chain joins if you want to make it more complex 
     219// So join() is mostly useful if you need to specify the members of the join 
     220$article = sfPropelFinder::from('Article')-> 
     221  join('Article.Id', 'Comment.ArticleId')-> 
     222  where('Comment.Content', 'You rock!')-> 
     223  findOne(); 
     224 
     225// Or if you want a special type of join (left, right, inner) 
     226$article = sfPropelFinder::from('Article')-> 
     227  innerJoin('Comment')-> 
     228  where('Comment.Content', 'You rock!')-> 
     229  findOne(); 
     230 
     231// Or both 
     232$article = sfPropelFinder::from('Article')-> 
     233  innerJoin('Article.Id', 'Comment.ArticleId')-> 
     234  where('Comment.Content', 'You rock!')-> 
     235  findOne(); 
     236 
     237// You can chain joins if you want to make more complex queries 
    219238$article2 = new Article(); 
    220239$article2->setTitle('Hello again, world!'); 
     
    499518 * Merge with sfPropelImpersonatorPlugin! 
    500519 * Add support for `with()` in `findPk()` (and document the method) 
    501  * Add the ability to do left, right, inner joins, etc. 
    502  * Change `with()` and `join()` to accept only one relation at a time, but with the ability to set the local and foreign parts of the relation. maybe replace the current multiple ability of these methods by `joins()` and `withs()` 
    503520 * Handle complex queries with And and Or 
    504521 * Add a `__toString()` method which returns a var_export() of the results, or a description of the conditions if not yet executed 
     
    510527=== 2008-07-04 | Trunk === 
    511528 
     529 * francois: Added the ability to do left, right, and inner joins in a simple way 
    512530 * francois: Made `join()` useless if there is an explicit `where()` on the table afterwards 
    513531 * francois: Added a `prove.php` test file to launch all tests at once in a test harness 
  • plugins/sfPropelFinderPlugin/lib/sfPropelFinder.php

    r10119 r10120  
    891891   *   $articleFinder->join('Category', 'RIGHT JOIN') 
    892892   *    => $c->addJoin(ArticlePeer::CATEGORY_ID, CategoryPeer::ID, Criteria::RIGHT_JOIN) 
    893    */ 
    894   public function join($relatedClass, $arguments = array()) 
    895   { 
    896     list($column1, $column2) = $this->getRelation($relatedClass); 
    897     $this->relations[]= sfPropelFinderUtils::getPeerClassFromClass($relatedClass); 
    898     if(!is_array($arguments)) 
    899     { 
    900       $arguments = func_get_args(); 
    901       array_shift($arguments); 
    902     }  
    903     $operator = array_shift($arguments); 
    904     if(!$operator) 
    905     { 
    906       $operator = null; 
     893   *   $articleFinder->join('Article.CategoryId', 'Category.Id', 'RIGHT JOIN') 
     894   *    => $c->addJoin(ArticlePeer::CATEGORY_ID, CategoryPeer::ID, Criteria::RIGHT_JOIN) 
     895   */ 
     896  public function join() 
     897  { 
     898    $args = func_get_args(); 
     899    switch(count($args)) 
     900    { 
     901      case 0: 
     902        throw new Exception('sfPropelFinder::join() expects at least one argument'); 
     903        break; 
     904      case 1: 
     905      case 2: 
     906        // $articleFinder->join('Comment') 
     907        // $articleFinder->join('Category', 'RIGHT JOIN') 
     908        $relatedClass = $args[0]; 
     909        list($column1, $column2) = $this->getRelation($relatedClass); 
     910        $this->relations[]= sfPropelFinderUtils::getPeerClassFromClass($relatedClass); 
     911        $operator = isset($args[1]) ? $args[1] : null; 
     912        break; 
     913      case 3: 
     914        // $articleFinder->join('Article.CategoryId', 'Category.Id', 'RIGHT JOIN') 
     915        list($column1, $column2, $operator) = $args; 
     916        list($peerClass1, $column1) = $this->getColName($column1, $peerClass = null, $withPeerClass = true, $autoAddJoin = false); 
     917        if($peerClass1 != $this->peerClass && !$this->hasRelation($peerClass1)) 
     918        { 
     919          $this->relations []= $peerClass1; 
     920        } 
     921        list($peerClass2, $column2) = $this->getColName($column2, $peerClass = null, $withPeerClass = true, $autoAddJoin = false); 
     922        if($peerClass2 != $this->peerClass && !$this->hasRelation($peerClass2)) 
     923        { 
     924          $this->relations []= $peerClass2; 
     925        } 
     926        break; 
    907927    } 
    908928    $this->criteria->addJoin($column1, $column2, $operator); 
     
    9851005  } 
    9861006   
    987   protected function getColName($phpName, $peerClass = null, $withPeerClass = false
     1007  protected function getColName($phpName, $peerClass = null, $withPeerClass = false, $autoAddJoin = true
    9881008  { 
    9891009    if(array_key_exists($phpName, $this->withColumns)) 
     
    10101030      $peerClass = $this->peerClass; 
    10111031    } 
    1012     if($peerClass != $this->peerClass && !$this->hasRelation($peerClass)
     1032    if($peerClass != $this->peerClass && !$this->hasRelation($peerClass) && $autoAddJoin
    10131033    { 
    10141034      $this->join($class); 
     
    10391059      return $this->join(substr($name, 4), $arguments); 
    10401060    } 
     1061    if(strpos($name, 'Join') > 0) 
     1062    { 
     1063      $pos = strpos($name, 'Join'); 
     1064      $joinType = strtoupper(substr($name, 0, $pos)) . ' JOIN'; 
     1065      array_push($arguments, $joinType); 
     1066      return call_user_func_array(array($this, 'join'), $arguments); 
     1067    } 
    10411068    if(strpos($name, '_and') === 0) 
    10421069    { 
  • plugins/sfPropelFinderPlugin/test/unit/sfPropelFinderRelationsTest.php

    r10101 r10120  
    1212You need a model built with a running database to run these tests. 
    1313The tests expect a model similar to this one: 
     14 
     15    propel: 
     16      article: 
     17        id:          ~ 
     18        title:       varchar(255) 
     19        category_id: ~ 
     20      article_i18n: 
     21        content:     varchar(255) 
     22      category: 
     23        id:          ~ 
     24        name:        varchar(255) 
     25      comment: 
     26        id:          ~ 
     27        content:     varchar(255) 
     28        article_id:  ~ 
     29        author_id:   ~ 
     30      author: 
     31        id:          ~ 
     32        name:        varchar(255) 
     33 
     34And a second model similar to: 
    1435 
    1536    propel: 
     
    5879$con = Propel::getConnection(); 
    5980 
    60 $t = new lime_test(4, new lime_output_color()); 
     81$t = new lime_test(71, new lime_output_color()); 
    6182 
    6283$t->diag('findRelation()'); 
     
    92113$t->is($person->getName(), 'John Doe', 'findRelation() can find a relation when the foreign phpName is not the camelCase version of the foreign Tablename'); 
    93114$t->is($person->getCivility()->getIsMan(), true, 'findRelation() can find a relation when the foreign phpName is not the camelCase version of the foreign Tablename'); 
     115 
     116$t->diag('getRelation()'); 
    94117 
    95118ClubPeer::doDeleteAll(); 
     
    106129$person1->setFoo($club1->getId()); 
    107130$person1->save(); 
     131 
     132list($column1, $column2) = sfPropelFinder::from('Article')->getRelation('Category'); 
     133$t->is($column1, ArticlePeer::CATEGORY_ID, 'getRelation() guesses the two parts of a relation properly for many-to-one relationships'); 
     134$t->is($column2, CategoryPeer::ID, 'getRelation() guesses the two parts of a relation properly for many-to-one relationships'); 
     135list($column1, $column2) = sfPropelFinder::from('Article')->getRelation('Comment'); 
     136$t->is($column1, ArticlePeer::ID, 'getRelation() guesses the two parts of a relation properly for one-to-many relationships'); 
     137$t->is($column2, CommentPeer::ARTICLE_ID, 'getRelation() guesses the two parts of a relation properly for one-to-many relationships'); 
     138 
     139$t->diag('join()'); 
     140 
     141$finder = sfPropelFinder::from('Article')->join('Comment'); 
     142$join = array_pop($finder->getCriteria()->getJoins()); 
     143$t->is($join->getJoinType(), null, 'join() ends up in a simple join'); 
     144$t->is($join->getLeftColumnName(), 'ID', 'join($table) guesses the left column name'); 
     145$t->is($join->getLeftTableName(), 'article', 'join($table) guesses the left table name'); 
     146$t->is($join->getRightColumnName(), 'ARTICLE_ID', 'join($table) guesses the right column name'); 
     147$t->is($join->getRightTableName(), 'comment', 'join($table) guesses the right table name'); 
     148 
     149$finder = sfPropelFinder::from('Article')->join('Comment', Criteria::LEFT_JOIN); 
     150$join = array_pop($finder->getCriteria()->getJoins()); 
     151$t->is($join->getJoinType(), 'LEFT JOIN', 'join($table, $type) creates a typed join'); 
     152$t->is($join->getLeftColumnName(), 'ID', 'join($table, $type) guesses the left column name'); 
     153$t->is($join->getLeftTableName(), 'article', 'join($table, $type) guesses the left table name'); 
     154$t->is($join->getRightColumnName(), 'ARTICLE_ID', 'join($table, $type) guesses the right column name'); 
     155$t->is($join->getRightTableName(), 'comment', 'join($table, $type) guesses the right table name'); 
     156 
     157$finder = sfPropelFinder::from('Article')->join('Article.Id', 'Comment.ArticleId', Criteria::LEFT_JOIN); 
     158$join = array_pop($finder->getCriteria()->getJoins()); 
     159$t->is($join->getJoinType(), 'LEFT JOIN', 'join($start, $end, $type) creates a typed join'); 
     160$t->is($join->getLeftColumnName(), 'ID', 'join($start, $end, $type) converts the left column name'); 
     161$t->is($join->getLeftTableName(), 'article', 'join($start, $end, $type) converts the left table name'); 
     162$t->is($join->getRightColumnName(), 'ARTICLE_ID', 'join($start, $end, $type) converts the right column name'); 
     163$t->is($join->getRightTableName(), 'comment', 'join($start, $end, $type) converts the right table name'); 
     164 
     165ArticlePeer::doDeleteAll(); 
     166CategoryPeer::doDeleteAll(); 
     167$category1 = new Category(); 
     168$category1->setName('cat1'); 
     169$category1->save(); 
     170$category2 = new Category(); 
     171$category2->setName('cat2'); 
     172$category2->save(); 
     173$article1 = new Article(); 
     174$article1->setTitle('aaaaa'); 
     175$article1->setCategory($category1); 
     176$article1->save(); 
     177$article2 = new Article(); 
     178$article2->setTitle('bbbbb'); 
     179$article2->setCategory($category1); 
     180$article2->save(); 
     181$article3 = new Article(); 
     182$article3->setTitle('ccccc'); 
     183$article3->setCategory($category2); 
     184$article3->save(); 
     185$nbArticles = sfPropelFinder::from('Article')->join('Category')->where('Category.Name', 'cat1')->count(); 
     186$t->is($nbArticles, 2, 'join() allows to join to another table (many-to-one)'); 
     187$nbArticles = sfPropelFinder::from('Article')->where('Category.Name', 'cat1')->count(); 
     188$t->is($nbArticles, 2, 'join() can be omitted if column names are explicit (many-to-one)'); 
     189$nbArticles = sfPropelFinder::from('Article')->join('Category')->where('Category.Name', 'cat2')->count(); 
     190$t->is($nbArticles, 1, 'join() allows to join to another table (many-to-one)'); 
     191$nbArticles = sfPropelFinder::from('Article')->join('Category')->where('Category.Name', 'cat2')->count(); 
     192$t->is($nbArticles, 1, 'join() can be omitted if column names are explicit (many-to-one)'); 
     193$article = sfPropelFinder::from('Article')->join('Category')->where('Category.Name', 'cat2')->findOne(); 
     194$t->is($article->getTitle(), 'ccccc', 'join() allows to join to another table (many-to-one)'); 
     195ArticlePeer::doDeleteAll(); 
     196CommentPeer::doDeleteAll(); 
     197$article1 = new Article(); 
     198$article1->setTitle('aaaaa'); 
     199$article1->setCategory($category1); 
     200$article1->save(); 
     201$article2 = new Article(); 
     202$article2->setTitle('bbbbb'); 
     203$article2->setCategory($category1); 
     204$article2->save(); 
     205$comment = new Comment(); 
     206$comment->setContent('foo'); 
     207$comment->setArticleId($article2->getId()); 
     208$comment->save(); 
     209$nbArticles = sfPropelFinder::from('Article')->join('Comment')->where('Comment.Content', 'foo')->count(); 
     210$t->is($nbArticles, 1, 'join() allows to join to another table (one-to-many)'); 
     211$nbArticles = sfPropelFinder::from('Article')->where('Comment.Content', 'foo')->count(); 
     212$t->is($nbArticles, 1, 'join() can be omitted if column names are explicit (one-to-many)'); 
     213$article = sfPropelFinder::from('Article')->join('Comment')->where('Comment.Content', 'foo')->findOne(); 
     214$t->is($article->getTitle(), 'bbbbb', 'join() allows to join to another table (one-to-many)'); 
     215 
     216ArticlePeer::doDeleteAll(); 
     217CommentPeer::doDeleteAll(); 
     218AuthorPeer::doDeleteAll(); 
     219$article1 = new Article(); 
     220$article1->setTitle('aaaaa'); 
     221$article1->setCategory($category1); 
     222$article1->save(); 
     223$author1 = new Author(); 
     224$author1->setName('John'); 
     225$author1->save(); 
     226$comment = new Comment(); 
     227$comment->setContent('foo'); 
     228$comment->setArticleId($article1->getId()); 
     229$comment->setAuthor($author1); 
     230$comment->save(); 
     231$article = sfPropelFinder::from('Article')->join('Comment')->join('Author')->where('Author.Name', 'John')->findOne(); 
     232$t->is($article->getTitle(), 'aaaaa', 'you can chain several join() statements'); 
     233$article = sfPropelFinder::from('Article')->join('Comment')->where('Author.Name', 'John')->findOne(); 
     234$t->is($article->getTitle(), 'aaaaa', 'join() can be omitted if column names are explicit'); 
     235$article = sfPropelFinder::from('Article')->joinComment()->joinAuthor()->where('Author.Name', 'John')->findOne(); 
     236$t->is($article->getTitle(), 'aaaaa', 'joinXXX() does a join according to the XXX column name'); 
     237 
     238$t->diag('leftJoin(), rightJoin(), innerJoin()'); 
     239 
     240$finder = sfPropelFinder::from('Article')->leftJoin('Comment'); 
     241$join = array_pop($finder->getCriteria()->getJoins()); 
     242$t->is($join->getJoinType(), 'LEFT JOIN', 'leftJoin($table) ends up in a left join'); 
     243$finder = sfPropelFinder::from('Article')->rightJoin('Comment'); 
     244$join = array_pop($finder->getCriteria()->getJoins()); 
     245$t->is($join->getJoinType(), 'RIGHT JOIN', 'rightJoin($table) ends up in a right join'); 
     246$finder = sfPropelFinder::from('Article')->innerJoin('Comment'); 
     247$join = array_pop($finder->getCriteria()->getJoins()); 
     248$t->is($join->getJoinType(), 'INNER JOIN', 'innerJoin($table) ends up in an inner join'); 
     249 
     250$finder = sfPropelFinder::from('Article')->leftJoin('Article.Id', 'Comment.ArticleId'); 
     251$join = array_pop($finder->getCriteria()->getJoins()); 
     252$t->is($join->getJoinType(), 'LEFT JOIN', 'leftJoin($start, $end) creates a left join'); 
     253$t->is($join->getLeftColumnName(), 'ID', 'leftJoin($start, $end) converts the left column name'); 
     254$t->is($join->getLeftTableName(), 'article', 'leftJoin($start, $end) converts the left table name'); 
     255$t->is($join->getRightColumnName(), 'ARTICLE_ID', 'leftJoin($start, $end) converts the right column name'); 
     256$t->is($join->getRightTableName(), 'comment', 'leftJoin($start, $end) converts the right table name'); 
     257 
     258$t->diag('with()'); 
     259 
     260ArticlePeer::doDeleteAll(); 
     261CategoryPeer::doDeleteAll(); 
     262$category1 = new Category(); 
     263$category1->setName('cat1'); 
     264$category1->save(); 
     265$article1 = new Article(); 
     266$article1->setTitle('aaaaa'); 
     267$article1->setCategory($category1); 
     268$article1->save(); 
     269$finder = sfPropelFinder::from('Article')->join('Category')->with('Category'); 
     270$article = $finder->findOne(); 
     271$sql = 'SELECT article.ID, article.VERSION, article.TITLE, article.CATEGORY_ID, category.ID, category.NAME FROM article, category WHERE article.CATEGORY_ID=category.ID LIMIT 1'; 
     272$t->is($finder->getLatestQuery(), $sql, 'with() gets the columns of the with class in addition to the columns of the current class'); 
     273$finder = sfPropelFinder::from('Article')->with('Category'); 
     274$article = $finder->findOne(); 
     275$t->is($finder->getLatestQuery(), $sql, 'with() adds a join if not already added'); 
     276$t->is($article->getTitle(), 'aaaaa', 'fetching objects with a with() returns the correct main object'); 
     277$category = $article->getCategory(); 
     278$t->is($category->getName(), 'cat1', 'fetching objects with a with() returns the correct related object'); 
     279$con = Propel::getConnection(); 
     280$latestQuery = $con->getLastExecutedQuery(); 
     281$t->is($latestQuery, $sql, 'calling a FK getter on an object hydrated with with() does not issue a new query'); 
     282 
     283ArticlePeer::doDeleteAll(); 
     284CommentPeer::doDeleteAll(); 
     285AuthorPeer::doDeleteAll(); 
     286$article1 = new Article(); 
     287$article1->setTitle('bbbbb'); 
     288$article1->setCategory($category1); 
     289$article1->save(); 
     290$author1 = new Author(); 
     291$author1->setName('John'); 
     292$author1->save(); 
     293$comment = new Comment(); 
     294$comment->setContent('foo'); 
     295$comment->setArticleId($article1->getId()); 
     296$comment->setAuthor($author1); 
     297$comment->save(); 
     298$finder = sfPropelFinder::from('Comment')->with('Article')->with('Author'); 
     299$comment = $finder->findOne(); 
     300$t->is($comment->getContent(), 'foo', 'you can call with() several times to hydrate more than one related object'); 
     301$t->is($comment->getArticle()->getTitle(), 'bbbbb', 'you can call with() several times to hydrate more than one related object'); 
     302$t->is($comment->getAuthor()->getName(), 'John', 'you can call with() several times to hydrate more than one related object'); 
     303$t->is($finder->getLatestQuery(), 'SELECT comment.ID, comment.CONTENT, comment.ARTICLE_ID, comment.AUTHOR_ID, article.ID, article.VERSION, article.TITLE, article.CATEGORY_ID, author.ID, author.NAME FROM comment, article, author WHERE comment.ARTICLE_ID=article.ID AND comment.AUTHOR_ID=author.ID LIMIT 1', 'you can call with() several times to hydrate more than one related object'); 
     304 
     305$finder = sfPropelFinder::from('Comment')->with('Article')->with('Category'); 
     306$comment = $finder->findOne(); 
     307$t->is($finder->getLatestQuery(), 'SELECT comment.ID, comment.CONTENT, comment.ARTICLE_ID, comment.AUTHOR_ID, article.ID, article.VERSION, article.TITLE, article.CATEGORY_ID, category.ID, category.NAME FROM comment, article, category WHERE comment.ARTICLE_ID=article.ID AND article.CATEGORY_ID=category.ID LIMIT 1', 'with() can even hydrate related objects via a related object'); 
     308 
     309$finder = sfPropelFinder::from('Comment')->with('Article', 'Category'); 
     310$comment = $finder->findOne(); 
     311$t->is($finder->getLatestQuery(), 'SELECT comment.ID, comment.CONTENT, comment.ARTICLE_ID, comment.AUTHOR_ID, article.ID, article.VERSION, article.TITLE, article.CATEGORY_ID, category.ID, category.NAME FROM comment, article, category WHERE comment.ARTICLE_ID=article.ID AND article.CATEGORY_ID=category.ID LIMIT 1', 'with() accepts several arguments, so you don\'t need to call it several times'); 
     312 
     313$t->diag('withI18n()'); 
     314 
     315ArticlePeer::doDeleteAll(); 
     316ArticleI18nPeer::doDeleteAll(); 
     317$article1 = new Article(); 
     318$article1->setTitle('aaa'); 
     319$article1->setCulture('en'); 
     320$article1->setContent('english content'); 
     321$article1->setCulture('fr'); 
     322$article1->setContent('contenu français'); 
     323$article1->save(); 
     324 
     325sfContext::getInstance()->getUser()->setCulture('en'); 
     326$finder = sfPropelFinder::from('Article')->withI18n(); 
     327$article = $finder->findOne(); 
     328 
     329$t->is($finder->getLatestQuery(), 'SELECT article.ID, article.VERSION, article.TITLE, article.CATEGORY_ID, article_i18n.CONTENT, article_i18n.ID, article_i18n.CULTURE FROM article, article_i18n WHERE article_i18n.CULTURE=\'en\' AND article.ID=article_i18n.ID LIMIT 1', 'withI18n() hydrates the related I18n object with a culture taken from the user object'); 
     330$t->is($article->getContent(), 'english content', 'withI18n() considers the current user culture for hydration'); 
     331 
     332sfContext::getInstance()->getUser()->setCulture('fr'); 
     333$finder = sfPropelFinder::from('Article')->withI18n(); 
     334$article = $finder->findOne(); 
     335 
     336$t->is($finder->getLatestQuery(), 'SELECT article.ID, article.VERSION, article.TITLE, article.CATEGORY_ID, article_i18n.CONTENT, article_i18n.ID, article_i18n.CULTURE FROM article, article_i18n WHERE article_i18n.CULTURE=\'fr\' AND article.ID=article_i18n.ID LIMIT 1', 'withI18n() hydrates the related I18n object with a culture taken from the user object'); 
     337$t->is($article->getContent(), 'contenu français', 'withI18n() considers the current user culture for hydration'); 
     338 
     339sfContext::getInstance()->getUser()->setCulture('fr'); 
     340$article = sfPropelFinder::from('Article')-> 
     341  withI18n('en')-> 
     342  findOne(); 
     343$t->is($article->getContent(), 'english content', 'withI18n() accepts a culture parameter to override the user culture'); 
     344 
     345sfContext::getInstance()->getUser()->setCulture('en'); 
     346$finder = sfPropelFinder::from('Article')->with('I18n'); 
     347$article = $finder->findOne(); 
     348$t->is($finder->getLatestQuery(), 'SELECT article.ID, article.VERSION, article.TITLE, article.CATEGORY_ID, article_i18n.CONTENT, article_i18n.ID, article_i18n.CULTURE FROM article, article_i18n WHERE article_i18n.CULTURE=\'en\' AND article.ID=article_i18n.ID LIMIT 1', 'with(\'I18n\') is a synonym for withI18n()'); 
     349$finder = sfPropelFinder::from('Article')->with('i18n'); 
     350$article = $finder->findOne(); 
     351$t->is($finder->getLatestQuery(), 'SELECT article.ID, article.VERSION, article.TITLE, article.CATEGORY_ID, article_i18n.CONTENT, article_i18n.ID, article_i18n.CULTURE FROM article, article_i18n WHERE article_i18n.CULTURE=\'en\' AND article.ID=article_i18n.ID LIMIT 1', 'with(\'i18n\') is a synonym for withI18n()'); 
     352 
     353$t->diag('withColumn()'); 
     354 
     355ArticlePeer::doDeleteAll(); 
     356CommentPeer::doDeleteAll(); 
     357 
     358$article1 = new Article(); 
     359$article1->setTitle('bbbbb'); 
     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->setArticleId($article1->getId()); 
     368$comment->setAuthor($author1); 
     369$comment->save(); 
     370 
     371$comment = sfPropelFinder::from('Comment')-> 
     372  join('Article')-> 
     373  withColumn('Article.Title')-> 
     374  findOne(); 
     375$t->is($comment->getColumn('Article.Title'), 'bbbbb', 'Additional columns added with withColumn() are stored in the object and can be retrieved with getColumn()'); 
     376 
     377$comment = sfPropelFinder::from('Comment')-> 
     378  join('Article')-> 
     379  findOne(); 
     380try 
     381{ 
     382  $comment->getColumn('Article.Title'); 
     383  $t->fail('getColumn() is not available as long as you don\'t add a column with withColumn()'); 
     384} 
     385catch(Exception $e) 
     386{ 
     387  $t->pass('getColumn() is not available as long as you don\'t add a column with withColumn()'); 
     388} 
     389 
     390$comment = sfPropelFinder::from('Comment')-> 
     391  withColumn('Article.Title')-> 
     392  findOne(); 
     393$t->is($comment->getColumn('Article.Title'), 'bbbbb', 'If withColumn() is called on a related object column with no join on this class, the finder adds the join automatically'); 
     394 
     395$comment = sfPropelFinder::from('Comment')-> 
     396  join('Article')-> 
     397  withColumn('Article.Title', 'ArticleTitle')-> 
     398  findOne(); 
     399$t->is($comment->getColumn('ArticleTitle'), 'bbbbb', 'withColumn() second parameter serves as a column alias'); 
     400 
     401$comment = sfPropelFinder::from('Comment')-> 
     402  join('Article')-> 
     403  withColumn('Article.Title', 'ArticleTitle', 'int')-> 
     404  findOne(); 
     405$t->is($comment->getColumn('ArticleTitle'), '0', 'withColumn() third parameter serves as a type caster'); 
     406 
     407$comment = sfPropelFinder::from('Comment')-> 
     408  join('Article')->join('Author')-> 
     409  withColumn('Article.Title')-> 
     410  withColumn('Author.Name')-> 
     411  findOne(); 
     412$t->is($comment->getColumn('Article.Title'), 'bbbbb', 'withColumn() can be called several times'); 
     413$t->is($comment->getColumn('Author.Name'), 'John', 'withColumn() can be called several times'); 
     414 
     415$comment = sfPropelFinder::from('Comment')-> 
     416  join('Article')->with('Author')-> 
     417  withColumn('Article.Title')-> 
     418  findOne(); 
     419$t->is($comment->getColumn('Article.Title'), 'bbbbb', 'Columns added with withColumn() live together well with related objects added with with()'); 
     420$t->is($comment->getAuthor()->getName(), 'John', 'Related objects added with with() live together well with columns added with withColumn()'); 
     421 
     422$article = sfPropelFinder::from('Article')-> 
     423  join('Comment')-> 
     424  groupBy('Article.Id')-> 
     425  withColumn('COUNT(comment.ID)', 'NbComments')-> 
     426  findOne(); 
     427$t->is($article->getColumn('NbComments'), '1', 'withColumn() accepts complex SQL calculations as additional column'); 
     428 
     429$finder = sfPropelFinder::from('Article')-> 
     430  join('Comment')-> 
     431  groupBy('Article.Id')-> 
     432  withColumn('COUNT(comment.ID)', 'NbComments')-> 
     433  orderBy('NbComments'); 
     434$article = $finder->findOne(); 
     435$t->is($finder->getLatestQuery(), 'SELECT article.ID, article.VERSION, article.TITLE, article.CATEGORY_ID, COUNT(comment.ID) AS \'NbComments\' FROM article, comment WHERE article.ID=comment.ARTICLE_ID GROUP BY article.ID ORDER BY NbComments ASC LIMIT 1', 'Columns added with withColumn() can be used for sorting'); 
  • plugins/sfPropelFinderPlugin/test/unit/sfPropelFinderTest.php

    r10119 r10120  
    6565ArticlePeer::doDeleteAll(); 
    6666 
    67 $t = new lime_test(146, new lime_output_color()); 
     67$t = new lime_test(102, new lime_output_color()); 
    6868 
    6969$t->diag('find()'); 
     
    596596$t->is($article->getTitle(), 'ccccc', 'orderByXXX() takes an order as argument'); 
    597597 
    598 $t->diag('join()'); 
    599  
    600 list($column1, $column2) = sfPropelFinder::from('Article')->getRelation('Category'); 
    601 $t->is($column1, ArticlePeer::CATEGORY_ID, 'getRelation() guesses the two parts of a relation properly for many-to-one relationships'); 
    602 $t->is($column2, CategoryPeer::ID, 'getRelation() guesses the two parts of a relation properly for many-to-one relationships'); 
    603 list($column1, $column2) = sfPropelFinder::from('Article')->getRelation('Comment'); 
    604 $t->is($column1, ArticlePeer::ID, 'getRelation() guesses the two parts of a relation properly for one-to-many relationships'); 
    605 $t->is($column2, CommentPeer::ARTICLE_ID, 'getRelation() guesses the two parts of a relation properly for one-to-many relationships'); 
    606 ArticlePeer::doDeleteAll(); 
    607 CategoryPeer::doDeleteAll(); 
    608 $category1 = new Category(); 
    609 $category1->setName('cat1'); 
    610 $category1->save(); 
    611 $category2 = new Category(); 
    612 $category2->setName('cat2'); 
    613 $category2->save(); 
    614 $article1 = new Article(); 
    615 $article1->setTitle('aaaaa'); 
    616 $article1->setCategory($category1); 
    617 $article1->save(); 
    618 $article2 = new Article(); 
    619 $article2->setTitle('bbbbb'); 
    620 $article2->setCategory($category1); 
    621 $article2->save(); 
    622 $article3 = new Article(); 
    623 $article3->setTitle('ccccc'); 
    624 $article3->setCategory($category2); 
    625 $article3->save(); 
    626 $nbArticles = sfPropelFinder::from('Article')->join('Category')->where('Category.Name', 'cat1')->count(); 
    627 $t->is($nbArticles, 2, 'join() allows to join to another table (many-to-one)'); 
    628 $nbArticles = sfPropelFinder::from('Article')->where('Category.Name', 'cat1')->count(); 
    629 $t->is($nbArticles, 2, 'join() can be omitted if column names are explicit (many-to-one)'); 
    630 $nbArticles = sfPropelFinder::from('Article')->join('Category')->where('Category.Name', 'cat2')->count(); 
    631 $t->is($nbArticles, 1, 'join() allows to join to another table (many-to-one)'); 
    632 $nbArticles = sfPropelFinder::from('Article')->join('Category')->where('Category.Name', 'cat2')->count(); 
    633 $t->is($nbArticles, 1, 'join() can be omitted if column names are explicit (many-to-one)'); 
    634 $article = sfPropelFinder::from('Article')->join('Category')->where('Category.Name', 'cat2')->findOne(); 
    635 $t->is($article->getTitle(), 'ccccc', 'join() allows to join to another table (many-to-one)'); 
    636 ArticlePeer::doDeleteAll(); 
    637 CommentPeer::doDeleteAll(); 
    638 $article1 = new Article(); 
    639 $article1->setTitle('aaaaa'); 
    640 $article1->setCategory($category1); 
    641 $article1->save(); 
    642 $article2 = new Article(); 
    643 $article2->setTitle('bbbbb'); 
    644 $article2->setCategory($category1); 
    645 $article2->save(); 
    646 $comment = new Comment(); 
    647 $comment->setContent('foo'); 
    648 $comment->setArticleId($article2->getId()); 
    649 $comment->save(); 
    650 $nbArticles = sfPropelFinder::from('Article')->join('Comment')->where('Comment.Content', 'foo')->count(); 
    651 $t->is($nbArticles, 1, 'join() allows to join to another table (one-to-many)'); 
    652 $nbArticles = sfPropelFinder::from('Article')->where('Comment.Content', 'foo')->count(); 
    653 $t->is($nbArticles, 1, 'join() can be omitted if column names are explicit (one-to-many)'); 
    654 $article = sfPropelFinder::from('Article')->join('Comment')->where('Comment.Content', 'foo')->findOne(); 
    655 $t->is($article->getTitle(), 'bbbbb', 'join() allows to join to another table (one-to-many)'); 
    656  
    657 ArticlePeer::doDeleteAll(); 
    658 CommentPeer::doDeleteAll(); 
    659 AuthorPeer::doDeleteAll(); 
    660 $article1 = new Article(); 
    661 $article1->setTitle('aaaaa'); 
    662 $article1->setCategory($category1); 
    663 $article1->save(); 
    664 $author1 = new Author(); 
    665 $author1->setName('John'); 
    666 $author1->save(); 
    667 $comment = new Comment(); 
    668 $comment->setContent('foo'); 
    669 $comment->setArticleId($article1->getId()); 
    670 $comment->setAuthor($author1); 
    671 $comment->save(); 
    672 $article = sfPropelFinder::from('Article')->join('Comment')->join('Author')->where('Author.Name', 'John')->findOne(); 
    673 $t->is($article->getTitle(), 'aaaaa', 'you can chain several join() statements'); 
    674 $article = sfPropelFinder::from('Article')->join('Comment')->where('Author.Name', 'John')->findOne(); 
    675 $t->is($article->getTitle(), 'aaaaa', 'join() can be omitted if column names are explicit'); 
    676 $article = sfPropelFinder::from('Article')->joinComment()->joinAuthor()->where('Author.Name', 'John')->findOne(); 
    677 $t->is($article->getTitle(), 'aaaaa', 'joinXXX() does a join according to the XXX column name'); 
    678  
    679 $t->diag('with()'); 
    680  
    681 ArticlePeer::doDeleteAll(); 
    682 CategoryPeer::doDeleteAll(); 
    683 $category1 = new Category(); 
    684 $category1->setName('cat1'); 
    685 $category1->save(); 
    686 $article1 = new Article(); 
    687 $article1->setTitle('aaaaa'); 
    688 $article1->setCategory($category1); 
    689 $article1->save(); 
    690 $finder = sfPropelFinder::from('Article')->join('Category')->with('Category'); 
    691 $article = $finder->findOne(); 
    692 $sql = 'SELECT article.ID, article.VERSION, article.TITLE, article.CATEGORY_ID, category.ID, category.NAME FROM article, category WHERE article.CATEGORY_ID=category.ID LIMIT 1'; 
    693 $t->is($finder->getLatestQuery(), $sql, 'with() gets the columns of the with class in addition to the columns of the current class'); 
    694 $finder = sfPropelFinder::from('Article')->with('Category'); 
    695 $article = $finder->findOne(); 
    696 $t->is($finder->getLatestQuery(), $sql, 'with() adds a join if not already added'); 
    697 $t->is($article->getTitle(), 'aaaaa', 'fetching objects with a with() returns the correct main object'); 
    698 $category = $article->getCategory(); 
    699 $t->is($category->getName(), 'cat1', 'fetching objects with a with() returns the correct related object'); 
    700 $con = Propel::getConnection(); 
    701 $latestQuery = $con->getLastExecutedQuery(); 
    702 $t->is($latestQuery, $sql, 'calling a FK getter on an object hydrated with with() does not issue a new query'); 
    703  
    704 ArticlePeer::doDeleteAll(); 
    705 CommentPeer::doDeleteAll(); 
    706 AuthorPeer::doDeleteAll(); 
    707 $article1 = new Article(); 
    708 $article1->setTitle('bbbbb'); 
    709 $article1->setCategory($category1); 
    710 $article1->save(); 
    711 $author1 = new Author(); 
    712 $author1->setName('John'); 
    713 $author1->save(); 
    714 $comment = new Comment(); 
    715 $comment->setContent('foo'); 
    716 $comment->setArticleId($article1->getId()); 
    717 $comment->setAuthor($author1); 
    718 $comment->save(); 
    719 $finder = sfPropelFinder::from('Comment')->with('Article')->with('Author'); 
    720 $comment = $finder->findOne(); 
    721 $t->is($comment->getContent(), 'foo', 'you can call with() several times to hydrate more than one related object'); 
    722 $t->is($comment->getArticle()->getTitle(), 'bbbbb', 'you can call with() several times to hydrate more than one related object'); 
    723 $t->is($comment->getAuthor()->getName(), 'John', 'you can call with() several times to hydrate more than one related object'); 
    724 $t->is($finder->getLatestQuery(), 'SELECT comment.ID, comment.CONTENT, comment.ARTICLE_ID, comment.AUTHOR_ID, article.ID, article.VERSION, article.TITLE, article.CATEGORY_ID, author.ID, author.NAME FROM comment, article, author WHERE comment.ARTICLE_ID=article.ID AND comment.AUTHOR_ID=author.ID LIMIT 1', 'you can call with() several times to hydrate more than one related object'); 
    725  
    726 $finder = sfPropelFinder::from('Comment')->with('Article')->with('Category'); 
    727 $comment = $finder->findOne(); 
    728 $t->is($finder->getLatestQuery(), 'SELECT comment.ID, comment.CONTENT, comment.ARTICLE_ID, comment.AUTHOR_ID, article.ID, article.VERSION, article.TITLE, article.CATEGORY_ID, category.ID, category.NAME FROM comment, article, category WHERE comment.ARTICLE_ID=article.ID AND article.CATEGORY_ID=category.ID LIMIT 1', 'with() can even hydrate related objects via a related object'); 
    729  
    730 $finder = sfPropelFinder::from('Comment')->with('Article', 'Category'); 
    731 $comment = $finder->findOne(); 
    732 $t->is($finder->getLatestQuery(), 'SELECT comment.ID, comment.CONTENT, comment.ARTICLE_ID, comment.AUTHOR_ID, article.ID, article.VERSION, article.TITLE, article.CATEGORY_ID, category.ID, category.NAME FROM comment, article, category WHERE comment.ARTICLE_ID=article.ID AND article.CATEGORY_ID=category.ID LIMIT 1', 'with() accepts several arguments, so you don\'t need to call it several times'); 
    733  
    734 $t->diag('withI18n()'); 
    735  
    736 ArticlePeer::doDeleteAll(); 
    737 ArticleI18nPeer::doDeleteAll(); 
    738 $article1 = new Article(); 
    739 $article1->setTitle('aaa'); 
    740 $article1->setCulture('en'); 
    741 $article1->setContent('english content'); 
    742 $article1->setCulture('fr'); 
    743 $article1->setContent('contenu français'); 
    744 $article1->save(); 
    745  
    746 sfContext::getInstance()->getUser()->setCulture('en'); 
    747 $finder = sfPropelFinder::from('Article')->withI18n(); 
    748 $article = $finder->findOne(); 
    749  
    750 $t->is($finder->getLatestQuery(), 'SELECT article.ID, article.VERSION, article.TITLE, article.CATEGORY_ID, article_i18n.CONTENT, article_i18n.ID, article_i18n.CULTURE FROM article, article_i18n WHERE article_i18n.CULTURE=\'en\' AND article.ID=article_i18n.ID LIMIT 1', 'withI18n() hydrates the related I18n object with a culture taken from the user object'); 
    751 $t->is($article->getContent(), 'english content', 'withI18n() considers the current user culture for hydration'); 
    752  
    753 sfContext::getInstance()->getUser()->setCulture('fr'); 
    754 $finder = sfPropelFinder::from('Article')->withI18n(); 
    755 $article = $finder->findOne(); 
    756  
    757 $t->is($finder->getLatestQuery(), 'SELECT article.ID, article.VERSION, article.TITLE, article.CATEGORY_ID, article_i18n.CONTENT, article_i18n.ID, article_i18n.CULTURE FROM article, article_i18n WHERE article_i18n.CULTURE=\'fr\' AND article.ID=article_i18n.ID LIMIT 1', 'withI18n() hydrates the related I18n object with a culture taken from the user object'); 
    758 $t->is($article->getContent(), 'contenu français', 'withI18n() considers the current user culture for hydration'); 
    759  
    760 sfContext::getInstance()->getUser()->setCulture('fr'); 
    761 $article = sfPropelFinder::from('Article')-> 
    762   withI18n('en')-> 
    763   findOne(); 
    764 $t->is($article->getContent(), 'english content', 'withI18n() accepts a culture parameter to override the user culture'); 
    765  
    766 sfContext::getInstance()->getUser()->setCulture('en'); 
    767 $finder = sfPropelFinder::from('Article')->with('I18n'); 
    768 $article = $finder->findOne(); 
    769 $t->is($finder->getLatestQuery(), 'SELECT article.ID, article.VERSION, article.TITLE, article.CATEGORY_ID, article_i18n.CONTENT, article_i18n.ID, article_i18n.CULTURE FROM article, article_i18n WHERE article_i18n.CULTURE=\'en\' AND article.ID=article_i18n.ID LIMIT 1', 'with(\'I18n\') is a synonym for withI18n()'); 
    770 $finder = sfPropelFinder::from('Article')->with('i18n'); 
    771 $article = $finder->findOne(); 
    772 $t->is($finder->getLatestQuery(), 'SELECT article.ID, article.VERSION, article.TITLE, article.CATEGORY_ID, article_i18n.CONTENT, article_i18n.ID, article_i18n.CULTURE FROM article, article_i18n WHERE article_i18n.CULTURE=\'en\' AND article.ID=article_i18n.ID LIMIT 1', 'with(\'i18n\') is a synonym for withI18n()'); 
    773  
    774 $t->diag('withColumn()'); 
    775  
    776 ArticlePeer::doDeleteAll(); 
    777 CommentPeer::doDeleteAll(); 
    778  
    779 $article1 = new Article(); 
    780 $article1->setTitle('bbbbb'); 
    781 $article1->setCategory($category1); 
    782 $article1->save(); 
    783 $author1 = new Author(); 
    784 $author1->setName('John'); 
    785 $author1->save(); 
    786 $comment = new Comment(); 
    787 $comment->setContent('foo'); 
    788 $comment->setArticleId($article1->getId()); 
    789 $comment->setAuthor($author1); 
    790 $comment->save(); 
    791  
    792 $comment = sfPropelFinder::from('Comment')-> 
    793   join('Article')-> 
    794   withColumn('Article.Title')-> 
    795   findOne(); 
    796 $t->is($comment->getColumn('Article.Title'), 'bbbbb', 'Additional columns added with withColumn() are stored in the object and can be retrieved with getColumn()'); 
    797  
    798 $comment = sfPropelFinder::from('Comment')-> 
    799   join('Article')-> 
    800   findOne(); 
    801 try 
    802 { 
    803   $comment->getColumn('Article.Title'); 
    804   $t->fail('getColumn() is not available as long as you don\'t add a column with withColumn()'); 
    805 } 
    806 catch(Exception $e) 
    807 { 
    808   $t->pass('getColumn() is not available as long as you don\'t add a column with withColumn()'); 
    809 } 
    810  
    811 $comment = sfPropelFinder::from('Comment')-> 
    812   withColumn('Article.Title')-> 
    813   findOne(); 
    814 $t->is($comment->getColumn('Article.Title'), 'bbbbb', 'If withColumn() is called on a related object column with no join on this class, the finder adds the join automatically'); 
    815  
    816 $comment = sfPropelFinder::from('Comment')-> 
    817   join('Article')-> 
    818   withColumn('Article.Title', 'ArticleTitle')-> 
    819   findOne(); 
    820 $t->is($comment->getColumn('ArticleTitle'), 'bbbbb', 'withColumn() second parameter serves as a column alias'); 
    821  
    822 $comment = sfPropelFinder::from('Comment')-> 
    823   join('Article')-> 
    824   withColumn('Article.Title', 'ArticleTitle', 'int')-> 
    825   findOne(); 
    826 $t->is($comment->getColumn('ArticleTitle'), '0', 'withColumn() third parameter serves as a type caster'); 
    827  
    828 $comment = sfPropelFinder::from('Comment')-> 
    829   join('Article')->join('Author')-> 
    830   withColumn('Article.Title')-> 
    831   withColumn('Author.Name')-> 
    832   findOne(); 
    833 $t->is($comment->getColumn('Article.Title'), 'bbbbb', 'withColumn() can be called several times'); 
    834 $t->is($comment->getColumn('Author.Name'), 'John', 'withColumn() can be called several times'); 
    835  
    836 $comment = sfPropelFinder::from('Comment')-> 
    837   join('Article')->with('Author')-> 
    838   withColumn('Article.Title')-> 
    839   findOne(); 
    840 $t->is($comment->getColumn('Article.Title'), 'bbbbb', 'Columns added with withColumn() live together well with related objects added with with()'); 
    841 $t->is($comment->getAuthor()->getName(), 'John', 'Related objects added with with() live together well with columns added with withColumn()'); 
    842  
    843 $article = sfPropelFinder::from('Article')-> 
    844   join('Comment')-> 
    845   groupBy('Article.Id')-> 
    846   withColumn('COUNT(comment.ID)', 'NbComments')-> 
    847   findOne(); 
    848 $t->is($article->getColumn('NbComments'), '1', 'withColumn() accepts complex SQL calculations as additional column'); 
    849  
    850 $finder = sfPropelFinder::from('Article')-> 
    851   join('Comment')-> 
    852   groupBy('Article.Id')-> 
    853   withColumn('COUNT(comment.ID)', 'NbComments')-> 
    854   orderBy('NbComments'); 
    855 $article = $finder->findOne(); 
    856 $t->is($finder->getLatestQuery(), 'SELECT article.ID, article.VERSION, article.TITLE, article.CATEGORY_ID, COUNT(comment.ID) AS \'NbComments\' FROM article, comment WHERE article.ID=comment.ARTICLE_ID GROUP BY article.ID ORDER BY NbComments ASC LIMIT 1', 'Columns added with withColumn() can be used for sorting'); 
    857  
    858598$t->diag('set()'); 
    859599