Development

/plugins/sfPropelFinderPlugin/lib/sfPropelFinder.php

You must first sign up to be able to contribute.

root/plugins/sfPropelFinderPlugin/lib/sfPropelFinder.php

Revision 10118, 29.5 kB (checked in by francois, 5 years ago)

sfPropelFinderPlugin Cleaning up:

  • Added a prove.php test file to launch all tests at once in a test harness
  • Moved utility methods as static methods of a third-party class to take some weight off the main class
  • Expanded TODO a bit
Line 
1 <?php
2
3 /*
4  * This file is part of the sfPropelFinder package.
5  *
6  * (c) 2007 François Zaninotto <francois.zaninotto@symfony-project.com>
7  *
8  * For the full copyright and license information, please view the LICENSE
9  * file that was distributed with this source code.
10  */
11  
12 class sfPropelFinder
13 {
14   protected $class = null;
15   protected $peerClass = null;
16   protected $databaseMap = null;
17   protected $criteria = null;
18   protected $relations = null;
19   protected $latestQuery = '';
20   protected $criterions = array();
21   protected $withClasses = array();
22   protected $withColumns = array();
23   protected $culture = null;
24
25   public function __construct($class = '')
26   {
27     $this->criteria = new Criteria();
28     if($class)
29     {
30       $this->class = $class;
31       $tmp = new $class;
32       $this->setPeerClass(get_class($tmp->getPeer()));
33     }
34     if($this->peerClass)
35     {
36       $this->initialize();
37     }
38   }
39  
40   public function initialize()
41   {
42     $mapBuilder = call_user_func(array($this->peerClass, 'getMapBuilder'));
43     $mapBuilder->doBuild();
44     $this->databaseMap = $mapBuilder->getDatabaseMap();
45   }
46  
47   public function getPeerClass()
48   {
49     return $this->peerClass;
50   }
51
52   public function setPeerClass($peerClass)
53   {
54     $this->relations[]= $peerClass;
55     $this->peerClass = $peerClass;
56     
57     return $this;
58   }
59  
60   public function getCriteria()
61   {
62     return $this->buildCriteria();
63   }
64  
65   public function setCriteria($criteria)
66   {
67     $this->criteria = $criteria;
68     $this->criterions = array();
69     
70     return $this;
71   }
72
73   public function reinitCriteria()
74   {
75     return $this->setCriteria(new Criteria());
76   }
77  
78   public function getLatestQuery($con = null)
79   {
80     if(is_null($con)) $con = Propel::getConnection();
81     if(method_exists($con, 'getLastExecutedQuery'))
82     {
83       return $this->latestQuery;
84     }
85     else
86     {
87       throw new RuntimeException('sfPropelFinder::getLatestQuery() only works when debug mode is enabled');
88     }
89   }
90  
91   public function updateLatestQuery($con = null)
92   {
93     if(is_null($con)) $con = Propel::getConnection();
94     if(method_exists($con, 'getLastExecutedQuery'))
95     {
96       $this->latestQuery = $con->getLastExecutedQuery();
97     }
98   }
99  
100   public function addWithClass($class)
101   {
102     $this->withClasses []= $class;
103     
104     return $this;
105   }
106  
107   public function getWithClasses()
108   {
109     return $this->withClasses;
110   }
111  
112   public function reinitWithClasses()
113   {
114     $this->withClasses = array();
115     
116     return $this;
117   }
118
119   public function getWithColumns()
120   {
121     return $this->withColumns;
122   }
123  
124   public function reinitWithColumns()
125   {
126     $this->withColumns = array();
127     
128     return $this;
129   }
130  
131   // Finder Initializers
132  
133   /**
134    * Mixed initializer
135    * Accepts either a string (Propel class) or an array of Propel objects
136    *
137    * @param mixed $from The data to initialize the finder with
138    * @return sfPropelFinder a finder object
139    * @throws Exception If the data is neither a classname nor an array
140    */
141   public static function from($from)
142   {
143     if (is_string($from))
144     {
145       return self::fromClass($from);
146     }
147     if (is_array($from))
148     {
149       return self::fromCollection($from);
150     }
151     throw new Exception('sfPropelFinder::from() only accepts a Propel object classname or an array of Propel objects');
152   }
153
154   /**
155    * Class initializer
156    *
157    * @param string $from Propel classname on which the search will be done
158    * @return sfPropelFinder a finder object
159    */
160   public static function fromClass($class)
161   {
162     $me = __CLASS__;
163     $finder = new $me($class);
164     
165     return $finder;
166   }
167  
168   /**
169    * Collection initializer
170    *
171    * @param array $from Array of Propel objects of the same class
172    * @param string $class Optional classname of the desired objects
173    * @param string $class Optional column name of the primary key
174    *
175    * @return sfPropelFinder a finder object
176    * @throws Exception If the array is empty, contains not Propel objects or composite objects
177    */
178   public static function fromCollection($collection, $class = '', $pkName = '')
179   {
180     $pks = array();
181     foreach($collection as $object)
182     {
183       if($class != get_class($object))
184       {
185         if($class)
186         {
187           throw new Exception('A sfPropelFinder can only be initialized from an array of objects of a single class');
188         }
189         if($object instanceof BaseObject)
190         {
191           $class = get_class($object);
192         }
193         else
194         {
195           throw new Exception('A sfPropelFinder can only be initialized from an array of Propel objects');
196         }
197       }
198       $pks []= $object->getPrimaryKey();
199     }
200     if(!$class)
201     {
202       throw new Exception('A sfPropelFinder cannot be initialized with an empty array');
203     }
204     
205     $tempObject = new $class();
206     foreach ($tempObject->getPeer()->getTableMap()->getColumns() as $column)
207     {
208       if($column->isPrimaryKey())
209       {
210         if($pkName)
211         {
212           throw new Exception('A sfPropelFinder cannot be initialized from an array of objects with several foreign keys');
213         }
214         else
215         {
216           $pkName = $column->getFullyQualifiedName();
217         }
218       }
219     }
220     
221     return self::fromArray($pks, $class, $pkName);
222   }
223  
224   /**
225    * Array initializer
226    *
227    * @param array $array Array of Primary keys
228    * @param string $class Propel classname on which the search will be done
229    *
230    * @return sfPropelFinder a finder object
231    */
232   public static function fromArray($array, $class, $pkName)
233   {
234     $finder = self::fromClass($class);
235     $finder->add($pkName, $array, Criteria::IN);
236     
237     return $finder;
238   }
239  
240   // Finder Executers
241  
242   public function count($con = null, $reinitCriteria = true)
243   {
244     $ret = call_user_func(array($this->getPeerClass(), 'doCount'), $this->getCriteria(), $con);
245     $this->updateLatestQuery($con);
246     if($reinitCriteria)
247     {
248       $this->reinitCriteria();
249     }
250     
251     return $ret;
252   }
253  
254   public function find($limit = null, $con = null, $reinitCriteria = false)
255   {
256     if($limit)
257     {
258       $this->criteria->setLimit($limit);
259     }
260     $ret = $this->doFind($this->getCriteria(), $con);
261     $this->updateLatestQuery($con);
262     if($reinitCriteria)
263     {
264       $this->reinitCriteria();
265     }
266     
267     return $ret;
268   }
269  
270   public function findOne($con = null, $reinitCriteria = true)
271   {
272     $this->criteria->setLimit(1);
273     $ret = $this->doFind($this->getCriteria(), $con);
274     $this->updateLatestQuery($con);
275     if($reinitCriteria)
276     {
277       $this->reinitCriteria();
278     }
279     if ($ret)
280     {
281       return $ret[0];
282     }
283     return null;
284   }
285  
286   public function findLast($column = null, $con = null, $reinitCriteria = true)
287   {
288     if($column)
289     {
290       $this->orderBy($column, 'desc');
291     }
292     else
293     {
294       $this->guessOrder('desc');
295     }
296     
297     return $this->findOne($con, $reinitCriteria);
298   }
299  
300   public function findFirst($column = null, $con = null, $reinitCriteria = true)
301   {
302     if($column)
303     {
304       $this->orderBy($column, 'asc');
305     }
306     else
307     {
308       $this->guessOrder('asc');
309     }
310     
311     return $this->findOne($con, $reinitCriteria);
312   }
313  
314   public function findBy($columnName, $value, $limit = null, $con = null, $reinitCriteria = false)
315   {
316     $column = $this->getColName($columnName);
317     $this->addCondition('and', $column, $value, Criteria::EQUAL);
318     
319     return $this->find($limit, $con, $reinitCriteria);
320   }
321
322   public function findOneBy($columnName, $value, $con = null, $reinitCriteria = false)
323   {
324     $column = $this->getColName($columnName);
325     $this->addCondition('and', $column, $value, Criteria::EQUAL);
326     
327     return $this->findOne($con, $reinitCriteria);
328   }
329  
330   public function findPk($pk, $con = null)
331   {
332     if(is_array($pk))
333     {
334       $ret = call_user_func(array($this->getPeerClass(), 'retrieveByPks'), $pk, $con);
335     }
336     else
337     {
338       $ret = call_user_func(array($this->getPeerClass(), 'retrieveByPk'), $pk, $con);
339     }
340     $this->updateLatestQuery($con);
341     
342     return $ret;
343   }
344  
345   public function delete($con = null, $reinitCriteria = true)
346   {
347     $deleteCriteria = $this->getCriteria();
348     if($deleteCriteria->equals(new Criteria()))
349     {
350       // delete will delete nothing when passed an empty criteria
351       // while it should, in fact, delete all
352       $deleteCriteria = $this->addTrueCondition($deleteCriteria);
353     }
354     $ret = call_user_func(array($this->getPeerClass(), 'doDelete'), $deleteCriteria, $con);
355     $this->updateLatestQuery($con);
356     if($reinitCriteria)
357     {
358       $this->reinitCriteria();
359     }
360     
361     return $ret;
362   }
363  
364   public function paginate($page = 1, $maxPerPage = 10, $con = null)
365   {
366     $pager = new sfPropelFinderPager($this->class, $maxPerPage);
367     $pager->setFinder($this);
368     $pager->setConnection($con);
369     $pager->setPage($page);
370     $pager->init();
371     
372     return $pager;
373   }
374  
375   public function set($values, $forceIndividualSaves = false, $con = null, $reinitCriteria = true )
376   {
377     if (!is_array($values))
378     {
379       throw new Exception('sfPropelFinder::set() expects an array as first argument');
380     }
381     if($forceIndividualSaves)
382     {
383       $objects = $this->find(null, $con, $reinitCriteria);
384       foreach ($objects as $object)
385       {
386         foreach ($values as $key => $value)
387         {
388           $object->setByName($key, $value);
389         }
390         $object->save();
391       }
392       return count($objects);
393     }
394     else
395     {
396       $find = $this->getCriteria();
397       if (count($find->getJoins()))
398       {
399         throw new Exception('sfPropelFinder::set() does not support multitable updates, please do not use join');
400       }
401       if($find->equals(new Criteria()))
402       {
403         // doUpdate will delete nothing when passed an empty criteria
404         // while it should, in fact, update all
405         $find = $this->addTrueCondition($find);
406       }
407       
408       $set = new Criteria();
409       foreach ($values as $columnName => $value)
410       {
411         $set->add($this->getColName($columnName), $value);
412       }
413       
414       if(is_null($con)) $con = Propel::getConnection();
415       $ret = BasePeer::doUpdate($find, $set, $con);
416       $this->updateLatestQuery($con);
417       if($reinitCriteria)
418       {
419         $this->reinitCriteria();
420       }
421       
422       return $ret;
423     }
424   }
425  
426   public function doFind($criteria, $con = null)
427   {
428     if($this->getWithClasses() || $this->getWithColumns())
429     {
430       $c = $this->prepareCompositeCriteria($criteria);
431       if(method_exists($this->getPeerClass(), 'doSelectRS'))
432       {
433         $resultSet = call_user_func(array($this->getPeerClass(), 'doSelectRS'), $c, $con);
434         $propelVersion = '1.2';
435         $nextFunction = 'next';
436         $nextParam = null;
437       }
438       else
439       {
440         $resultSet = call_user_func(array($this->getPeerClass(), 'doSelectStmt'), $c, $con);
441         $propelVersion = '1.3';
442         $nextFunction = 'fetch';
443         $nextParam = PDO::FETCH_NUM;
444       }
445       
446       // Hydrate the objects based on the resultset
447       $omClass = call_user_func(array($this->getPeerClass(), 'getOMClass'));
448       $cls = substr('.'.$omClass, strrpos('.'.$omClass, '.') + 1);
449       $objects = array();
450       $withObjs = array();
451       while ($row = $resultSet->$nextFunction($nextParam))
452       {
453         // First come the columns of the main class
454         $obj = new $cls();
455         if($propelVersion == '1.2')
456         {
457           $startCol = $obj->hydrate($resultSet, 1);
458         }
459         else
460         {
461           $startCol = $obj->hydrate($row, 0);
462         }
463         if($this->culture)
464         {
465           $obj->setCulture($this->culture);
466         }
467         // Then the related classes added by way of 'with'
468         $objectsInJoin = array($obj);
469         foreach ($this->getWithClasses() as $className)
470         {
471           $withObj = new $className();
472           if($propelVersion == '1.2')
473           {
474             $startCol = $withObj->hydrate($resultSet, $startCol);
475           }
476           else
477           {
478             $startCol = $withObj->hydrate($row, $startCol);
479           }
480           
481           // initialize our object directory
482           if (!isset($withObjs[$className]))
483           {
484             $withObjs[$className] = array();
485           }
486           
487           // check if object is not already referenced in allObjects directory
488           $isNewObject = true;
489           foreach ($withObjs[$className] as $otherObject)
490           {
491             if ($otherObject->getPrimaryKey() === $withObj->getPrimaryKey())
492             {
493               $isNewObject = false;
494               $withObj = $otherObject;
495               break;
496             }
497           }
498           sfPropelFinderUtils::relateObjects($withObj, $objectsInJoin, $isNewObject);
499           $objectsInJoin []= $withObj;
500           if ($isNewObject)
501           {
502             $withObjs[$className][] = $withObj;
503           }
504         }
505         // Then the columns added one by one by way of 'withColumn'
506         foreach($this->getWithColumns() as $alias => $column)
507         {
508           // Additional columns are stored in the object, in a special 'namespace'
509           // see getColumn() for how to retrieve the value afterwards
510           // Using the third parameter of withColumn() as a type. defaults to $rs->get() (= $rs->getString())
511           $typedGetter = 'get'.ucfirst($column['type']);
512           if($propelVersion == '1.2')
513           {
514             $this->setColumn($obj, $alias, $resultSet->$typedGetter($startCol));
515           }
516           else
517           {
518             $this->setColumn($obj, $alias, $row[$startCol]);
519           }
520           $startCol++;
521         }
522         
523         $objects []= $obj;
524       }
525       
526       // activate custom column getter if asColumns were added
527       if($this->getWithColumns() && !sfMixer::getCallable('Base'.$cls.':getColumn'))
528       {
529         sfMixer::register('Base'.$cls, array($this, 'getColumn'));
530       }
531       
532       return $objects;
533     }
534     else
535     {
536       // No 'with', so we use the native Propel doSelect()
537       return call_user_func(array($this->getPeerClass(), 'doSelect'), $criteria, $con);
538     }
539   }
540  
541   /**
542    * Prepare the select columns and add the missing joins
543    */
544   protected function prepareCompositeCriteria($criteria)
545   {
546     $c = clone $criteria;
547     $c->clearSelectColumns();
548     // First come the columns of the main class
549     call_user_func(array($this->getPeerClass(), 'addSelectColumns'), $c);
550     // Then the related classes added by way of 'with'
551     foreach ($this->getWithClasses() as $className)
552     {
553       $tempClass = new $className();
554       call_user_func(array($tempClass->getPeer(), 'addSelectColumns'), $c);
555       // if join() wasn't called previously on this class, do a simple join
556       if(!in_array(sfPropelFinderUtils::getPeerClassFromClass($className), $this->relations))
557       {
558         list($column1, $column2) = $this->getRelation($className);
559         $c->addJoin($column1, $column2);
560         $this->relations[]= sfPropelFinderUtils::getPeerClassFromClass($className);
561       }
562     }
563     // Then the columns added one by one by way of 'withColumn'
564     foreach($this->getWithColumns() as $alias => $column)
565     {
566       // if the column is on a related object property
567       // and if join() wasn't called previously on this object, do a simple join
568       $peerClass = $column['peerClass'];
569       if($peerClass && !in_array($peerClass, $this->relations))
570       {
571         list($column1, $column2) = $this->getRelation(sfPropelFinderUtils::getClassFromPeerClass($peerClass));
572         $c->addJoin($column1, $column2);
573         $this->relations[]= $peerClass;
574       }
575       $c->addAsColumn('\''.$alias.'\'', $column['column']);
576     }
577     
578     return $c;
579   }
580  
581   // Hydrating
582  
583   public function with($classes)
584   {
585     if(!is_array($classes))
586     {
587       $classes = func_get_args();
588     }
589     foreach($classes as $class)
590     {
591       if(strtolower($class) == 'i18n')
592       {
593         $this->withI18n();
594       }
595       else
596       {
597         $this->addWithClass($class);
598       }
599     }
600     
601     return $this;
602   }
603  
604   public function withI18n($culture = null)
605   {
606     $i18nClass = $this->class.'I18n';
607     $tmp = new $i18nClass();
608     $i18nPeerClass = get_class($tmp->getPeer());
609     if(is_null($culture)) $culture = sfContext::getInstance()->getUser()->getCulture();
610     $this->addWithClass($this->class.'I18n');
611     $this->criteria->add(constant($i18nPeerClass.'::CULTURE'), $culture);
612     $this->culture = $culture;
613     
614     return $this;
615   }
616
617   public function withColumn($column, $alias = null, $type = null)
618   {
619     $isCalculationColumn = strpos($column, '(') !== false;
620     if(!$alias)
621     {
622       if($isCalculationColumn)
623       {
624         throw new Exception('Calculated colums added with sfPropelFinder::withColumn() need an alias as second parameter');
625       }
626       else
627       {
628         $alias = $column;
629       }
630     }
631     if($isCalculationColumn)
632     {
633       $peerClass = null;
634     }
635     else
636     {
637       list($peerClass, $columnName) = $this->getColName($column, null, true);
638     }
639     $this->withColumns [$alias]= array(
640       'column'    => $isCalculationColumn ? $column : $columnName,
641       'type'      => $type,
642       'peerClass' => $peerClass
643     );
644     
645     return $this;
646   }
647  
648   // Finder Filters
649  
650   /**
651    * Finder Fluid Interface for Criteria::setDistinct()
652    */
653   public function distinct()
654   {
655     $this->criteria->setDistinct();
656     
657     return $this;
658   }
659  
660   /**
661    * Finder Fluid Interface for Criteria::add()
662    * Infers $column, $value, $comparison from $columnName and some optional arguments
663    * Examples:
664    *   $articleFinder->where('IsPublished')
665    *    => $c->add(ArticlePeer::IS_PUBLISHED, true)
666    *   $articleFinder->where('CommentId', 3)
667    *    => $c->add(ArticlePeer::COMMENT_ID, 3)
668    *   $articleFinder->where('Title', 'like', '%foo')
669    *    => $c->add(ArticlePeer::TITLE, '%foo', Criteria::LIKE)
670    *
671    * @param      string  $columnName PHPName of the column bearing the condition
672    * @param      array   $arguments  Optional array of arguments
673    *
674    * @return     sfPropelFinder the current finder object
675    */
676   public function where($columnName, $arguments = array())
677   {
678     $column = $this->getColName($columnName);
679     if(!is_array($arguments))
680     {
681       $arguments = func_get_args();
682       array_shift($arguments);
683     }
684     list($value, $comparison) = sfPropelFinderUtils::getValueAndComparisonFromArguments($arguments);
685
686     $this->addCondition('and', $column, $value, $comparison );
687     
688     return $this;
689   }
690
691   /**
692    * Finder Fluid Interface for Criteria::addAnd()
693    * Infers $column, $value, $comparison from $columnName and some optional arguments
694    * Examples:
695    *   $articleFinder->_and('CommentId', 3)
696    *    => $c->addAnd(ArticlePeer::COMMENT_ID, 3)
697    *
698    * @param      string  $columnName PHPName of the column bearing the condition
699    * @param      array   $arguments  Optional array of arguments
700    *
701    * @return     sfPropelFinder the current finder object
702    */
703   public function _and($columnName, $arguments = array())
704   {
705     $column = $this->getColName($columnName);
706     if(!is_array($arguments))
707     {
708       $arguments = func_get_args();
709       array_shift($arguments);
710     }
711     list($value, $comparison) = sfPropelFinderUtils::getValueAndComparisonFromArguments($arguments);
712
713     $this->addCondition('and', $column, $value, $comparison);
714     
715     return $this;
716   }
717
718   /**
719    * Finder Fluid Interface for Criteria::addOr()
720    * Infers $column, $value, $comparison from $columnName and some optional arguments
721    * Examples:
722    *   $articleFinder->_or('CommentId', 3)
723    *    => $c->addOr(ArticlePeer::COMMENT_ID, 3)
724    *
725    * @param      string  $columnName PHPName of the column bearing the condition
726    * @param      array   $arguments  Optional array of arguments
727    *
728    * @return     sfPropelFinder the current finder object
729    */
730   public function _or($columnName, $arguments = array())
731   {
732     $column = $this->getColName($columnName);
733     if(!is_array($arguments))
734     {
735       $arguments = func_get_args();
736       array_shift($arguments);
737     }
738     list($value, $comparison) = sfPropelFinderUtils::getValueAndComparisonFromArguments($arguments);
739     $this->addCondition('or', $column, $value, $comparison);
740     
741     return $this;
742   }
743
744   /**
745    * Conditions have to be stocked before being really used
746    * cf. sfPropelFinder::buildCriteria()
747    */
748   protected function addCondition($cond, $column, $value, $comparison)
749   {
750     $criterion = $this->criteria->getNewCriterion($column, $value, $comparison);
751     $criterion->func = "add".$cond;
752     $this->criterions []= $criterion;
753   }
754
755   /**
756    * We want that the Finder fuild Interface works like:
757    *   PHP : whereA()->_andB->_orC()->_orD()->_andE()
758    *   SQL : where A=? AND (B=? OR (C=? OR (D=? AND E=?)))
759    * So we have to add condition starting by the last one!
760    */
761   protected function buildCriteria()
762   {
763     $criteria = clone $this->criteria;
764     $criterions = $this->criterions;
765
766     while ($criterion = array_pop($criterions))
767     {
768       if ($c = count($criterions))
769       {
770         call_user_func(array($criterions[$c-1], $criterion->func), $criterion);
771       }
772       else
773       {
774         call_user_func(array($criteria, $criterion->func), $criterion);
775       }
776     }
777     
778     return $criteria;
779   }
780  
781   /**
782    * Finder fluid method to restrict results to a related object
783    * Examples:
784    *   $commentFinder->relatedTo($article)
785    *    => $c->add(CommentPeer::ARTICLE_ID, $article->getId())
786    */
787   public function relatedTo($object)
788   {
789     $relatedObjectTableName = $object->getPeer()->getTableMap()->getName();
790     foreach (sfPropelFinderUtils::getColumnsForPeerClass($this->getPeerClass()) as $c)
791     {
792       if($c->getRelatedTableName() == $relatedObjectTableName)
793       {
794         $this->addCondition('and', $c->getFullyQualifiedName(), $object->getByName($c->getRelatedName(), BasePeer::TYPE_COLNAME), Criteria::EQUAL);
795       }
796     }
797     
798     return $this;
799   }
800  
801   /**
802    * Finder Fluid Interface for Criteria::addAscendingOrderByColumn()
803    * and Criteria::addDescendingOrderByColumn()
804    * Infers $column and $order from $columnName and some optional arguments
805    * Examples:
806    *   $articleFinder->orderBy('CreatedAt')
807    *    => $c->addAscendingOrderByColumn(ArticlePeer::CREATED_AT)
808    *   $articlefinder->orderBy('CategoryId', 'desc')
809    *    => $c->addDescendingOrderByColumn(ArticlePeer::CATEGORY_ID)
810    */
811   public function orderBy($columnName, $arguments = array())
812   {
813     $column = $this->getColName($columnName);
814     if(!is_array($arguments))
815     {
816       $arguments = func_get_args();
817       array_shift($arguments);
818     }
819     $order = strtoupper(array_shift($arguments));
820     if(!$order)
821     {
822       $order = Criteria::ASC;
823     }
824     
825     switch ($order)
826     {
827       case Criteria::ASC:
828         $this->criteria->addAscendingOrderByColumn($column);
829         break;
830       case Criteria::DESC:
831         $this->criteria->addDescendingOrderByColumn($column);
832         break;
833       default:
834         throw new Exception('sfPropelFinder::orderBy() only accepts "asc" or "desc" as argument');
835     }
836     
837     return $this;
838   }
839
840   /**
841    * Finder Fluid Interface for Criteria::addGroupByColumn()
842    * Infers $column and $order from $columnName and some optional arguments
843    * Examples:
844    *   $articleFinder->groupBy('CreatedAt')
845    *    => $c->addGroupByColumn(ArticlePeer::CREATED_AT)
846    */
847   public function groupBy($columnName)
848   {
849     $column = $this->getColName($columnName);
850     $this->criteria->addGroupByColumn($column);
851     
852     return $this;
853   }
854  
855   /**
856    * Guess sort column based on their names
857    * Will look primarily for columns named:
858    * 'UpdatedAt', 'UpdatedOn', 'CreatedAt', 'CreatedOn', 'Id'
859    * You can change this sequence by modifying the app_sfPropelFinder_sort_column_guesses value
860    *
861    * @param string $direction 'desc' (default) or 'asc'
862    *
863    * @return sfPropelFinder $this the current finder object
864    */
865   public function guessOrder($direction = 'desc')
866   {
867     $columnNames = array();
868     foreach (sfPropelFinderUtils::getColumnsForPeerClass($this->getPeerClass()) as $c)
869     {
870       $columnNames []= $c->getPhpName();
871     }
872     foreach(sfConfig::get('app_sfPropelFinder_sort_column_guesses', array('UpdatedAt', 'UpdatedOn', 'CreatedAt', 'CreatedOn', 'Id')) as $testColumnName)
873     {
874       if(in_array($testColumnName, $columnNames))
875       {
876         $this->orderBy($testColumnName, $direction);
877         return $this;
878       }
879     }
880     
881     throw new Exception('Unable to figure out the column to use to order rows in sfPropelFinder::guessOrder()');
882   }
883  
884   /**
885    * Finder Fluid Interface for Criteria::addJoin()
886    * Infers $column1, $column2 and $operator from $relatedClass and some optional arguments
887    * Uses the Propel column maps, based on the schema, to guess the related columns
888    * Examples:
889    *   $articleFinder->join('Comment')
890    *    => $c->addJoin(ArticlePeer::ID, CommentPeer::ARTICLE_ID)
891    *   $articleFinder->join('Category', 'RIGHT JOIN')
892    *    => $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;
907     }
908     $this->criteria->addJoin($column1, $column2, $operator);
909     
910     return $this;
911   }
912
913   public function getRelation($phpName)
914   {
915     foreach($this->relations as $peerClass)
916     {
917       // try to find many to one or one to one relationship
918       if($relation = $this->findRelation($phpName, $peerClass))
919       {
920         return $relation;
921       }
922       // try to find one to many relationship
923       if($relation = $this->findRelation(
924         sfPropelFinderUtils::getClassFromPeerClass($peerClass),
925         sfPropelFinderUtils::getPeerClassFromClass($phpName)))
926       {
927         return array_reverse($relation);
928       }
929     }
930     throw new Exception(sprintf('sfPropelFinder: %s has no %s related table', $this->peerClass, $phpName));
931   }
932  
933   protected function findRelation($phpName, $peerClass)
934   {
935     foreach (sfPropelFinderUtils::getColumnsForPeerClass($peerClass) as $c)
936     {
937       if ($c->isForeignKey())
938       {
939         if(!$this->databaseMap->containsTable($c->getRelatedTableName()))
940         {
941           $mapBuilder = call_user_func(array(sfPropelFinderUtils::getPeerClassFromClass($phpName), 'getMapBuilder'));
942           $mapBuilder->doBuild();
943         }
944         if($this->databaseMap->getTable($c->getRelatedTableName())->getPhpName() == $phpName)
945         {
946           return array(
947             constant($peerClass.'::'.$c->getColumnName()),
948             $c->getRelatedName()
949           );
950         }
951       }
952     }
953     
954     return false;
955   }
956  
957   /**
958    * Behavior-like supplementary getter for supplementary columns added by way of withColumn()
959    * Requires symfony and sfMixer enabled
960    */
961   public function getColumn($object, $alias)
962   {
963     $alias = 'a'.md5($alias);
964     return $object->$alias;
965   }
966
967   public function setColumn($object, $alias, $value)
968   {
969     $alias = 'a'.md5($alias);
970     $object->$alias = $value;
971   }
972  
973   protected function addTrueCondition(Criteria $c)
974   {
975     $fieldNames = call_user_func(array($this->getPeerClass(), 'getFieldNames'), BasePeer::TYPE_COLNAME);
976     $firstFieldName = $fieldNames[0];
977     $c->add($firstFieldName, true, Criteria::BINARY_OR);
978     
979     return $c;
980   }
981  
982   protected function getColName($phpName, $peerClass = null, $withPeerClass = false)
983   {
984     if(array_key_exists($phpName, $this->withColumns))
985     {
986       return $phpName;
987     }
988     if(strpos($phpName, '.') !== false)
989     {
990       // Table.Column
991       list($class, $phpName) = explode('.', $phpName);
992       $peerClass = sfPropelFinderUtils::getPeerClassFromClass($class);
993     }
994     else if(strpos($phpName, '_') !== false)
995     {
996       // Table_Column, or Table_Name_Column, so explode is not a solution here
997       $limit = strrpos($phpName, '_');
998       $class = substr($phpName, 0, $limit);
999       $phpName = substr($phpName, $limit + 1);
1000       $peerClass = sfPropelFinderUtils::getPeerClassFromClass($class);
1001     }
1002     if(!$peerClass)
1003     {
1004       // Column
1005       $peerClass = $this->peerClass;
1006     }
1007     try
1008     {
1009       $column = call_user_func(array($peerClass, 'translateFieldName'), $phpName, BasePeer::TYPE_PHPNAME, BasePeer::TYPE_COLNAME);
1010       return $withPeerClass ? array($peerClass, $column) : $column;
1011     }
1012     catch (PropelException $e)
1013     {
1014       throw new Exception(sprintf('sfPropelFinder: %s has no %s column', $peerClass, $phpName));
1015     }
1016   }
1017  
1018   public function __call($name, $arguments)
1019   {
1020     if(strpos($name, 'where') === 0)
1021     {
1022       return $this->where(substr($name, 5), $arguments);
1023     }
1024     if(strpos($name, 'orderBy') === 0)
1025     {
1026       return $this->orderBy(substr($name, 7), $arguments);
1027     }
1028     if(strpos($name, 'join') === 0)
1029     {
1030       return $this->join(substr($name, 4), $arguments);
1031     }
1032     if(strpos($name, '_and') === 0)
1033     {
1034       return $this->_and(substr($name, 4), $arguments);
1035     }
1036     if(strpos($name, 'and') === 0)
1037     {
1038       return $this->_and(substr($name, 3), $arguments);
1039     }
1040     if(strpos($name, '_or') === 0)
1041     {
1042       return $this->_or(substr($name, 3), $arguments);
1043     }
1044     if(strpos($name, 'or') === 0)
1045     {
1046       return $this->_or(substr($name, 2), $arguments);
1047     }
1048     if(strpos($name, 'findBy') === 0)
1049     {
1050       array_unshift($arguments, substr($name, 6));
1051       return call_user_func_array(array($this, 'findBy'), $arguments);
1052     }
1053     if(strpos($name, 'findOneBy') === 0)
1054     {
1055       array_unshift($arguments, substr($name, 9));
1056       return call_user_func_array(array($this, 'findOneBy'), $arguments);
1057     }
1058     if(method_exists($this->criteria, $name))
1059     {
1060       call_user_func_array(array($this->criteria, $name), $arguments);
1061       return $this;
1062     }
1063     throw new Exception(sprintf('Undefined method sfPropelFinder::%s()', $name));
1064   }
1065 }
Note: See TracBrowser for help on using the browser.