Development

Changeset 10765

You must first sign up to be able to contribute.

Changeset 10765

Show
Ignore:
Timestamp:
08/11/08 11:01:20 (3 months ago)
Author:
francois
Message:

sfPropelFinderPlugin Refactoring

  • Added abstract sfModelFinder class to keep all abstract methods out of DbFinder
  • Refactored DbFinder to allow agnostic finders on model objects to extend it
  • Fixed problem with lacking PHPDoc on DbFinder methods
  • Now custom finders must extend DbFinder to be ORM agnostic
Files:

Legend:

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

    r10755 r10765  
    447447### Writing your own business logic into a finder 
    448448 
    449 You can create a new finder for your objects, with custom methods. The only prerequisites are to extend `DbFinder` or its adapters ('sfPropelFinder`, `sfDoctrineFinder`) and to define a protected `$class` property.  
    450  
    451 For instance, you can create an child of `sfPropelFinder` to retrieve Propel `Article` objects. This new finder has access to a protected `$criteria` property, which is a Propel Criteria that can be augmented in the usual way. Don't forget to return the current object (`$this`) in the new methods. 
    452  
    453     class ArticleFinder extends sfPropelFinder 
     449You can create a new finder for your objects, with custom methods. The only prerequisites are to extend `DbFinder`, and to define a protected `$class` property. 
     450 
     451For instance, you can create an child of `sfModelFinder` to retrieve Propel `Article` objects. This new finder has access to a protected query object by way of `getQueryObject()`. This object is a Propel Criteria that can be augmented in the usual way. Don't forget to return the current object (`$this`) in the new methods. 
     452 
     453    class ArticleFinder extends DbFinder 
    454454    { 
    455455      protected $class = 'Article'; 
     
    531531--------- 
    532532 
    533 ### 2008-08-08 | Trunk 
    534  
     533### 2008-08-11 | Trunk 
     534 
     535* francois: Refactored `DbFinder` to allow agnostic finders on model objects to extend it, and to fix problem with lacking PHPDoc on `DbFinder` methods 
     536* francois: Added abstract `sfModelFinder` class to keep all abstract methods out of `DbFinder` 
    535537* francois: Implemented `sfDoctrineFinder::delete()` 
    536538* francois: Turned README into Markdown syntax, and changed the main name to `DbFinder` 
  • plugins/sfPropelFinderPlugin/lib/DbFinder.php

    r10670 r10765  
    99 * file that was distributed with this source code. 
    1010 */ 
    11   
    12 abstract class DbFinder 
     11 
     12/** 
     13 * DbFinder is an ORM compatibility layer working with both Propel and Doctrine 
     14 * Use it just like sfPropelFinder or sfDoctrineFinder 
     15 * According to the type of class used to initialize it (BaseObject or Doctrine_Record), 
     16 * It will initialize an adapter to sfPropelFinder or sfDoctrineFinder 
     17 * And handle all method calls to this adapter 
     18 */ 
     19class DbFinder extends sfModelFinder 
    1320{ 
    1421  const  
     
    1623    PROPEL   = "Propel"; 
    1724   
    18   protected $relations = array(); 
    19    
    20   public function __construct($class = '', $connection = null) 
    21   { 
    22     $this->connection = $connection; 
    23     $class = $class ? $class : $this->class; 
    24     if($class) 
    25     { 
    26       list($class, $alias) = $this->getClassAndAlias($class); 
    27       $this->setClass($class, $alias); 
     25  protected 
     26    $adapter = null, 
     27    $type = null; 
     28   
     29  public function __construct($adapter = '') 
     30  { 
     31    if(!$adapter) 
     32    { 
     33      // using $this->class as a fallback 
     34      if(class_exists($class = $this->class)) 
     35      { 
     36        $tmp = new $class; 
     37        if($tmp instanceof BaseObject) 
     38        { 
     39          $adapter = new sfPropelFinder($class); 
     40        } 
     41        else if($tmp instanceof Doctrine_Record) 
     42        { 
     43          $adapter = new sfDoctrineFinder($class); 
     44        } 
     45        else 
     46        { 
     47          throw new Exception('DbFinder constructor expects a protected $class property'); 
     48        } 
     49      } 
     50    } 
     51    else if(!$adapter instanceof sfModelFinder) 
     52    { 
     53      throw new Exception('DbFinder constructor expects a sfModelFinder instance'); 
     54    } 
     55    $this->adapter = $adapter; 
     56    if($adapter instanceof sfPropelFinder) 
     57    { 
     58      $this->type = self::PROPEL; 
     59    } 
     60    else if($adapter instanceof sfDoctrineFinder) 
     61    { 
     62      $this->type = self::DOCTRINE; 
     63    } 
     64  } 
     65   
     66  public function getAdapter() 
     67  { 
     68    return $this->adapter; 
     69  } 
     70   
     71  public function getType() 
     72  { 
     73    return $this->type; 
     74  } 
     75   
     76  public function getQueryObject() 
     77  { 
     78    if($this->type == self::PROPEL) 
     79    { 
     80      return $this->adapter->getCriteria(); 
     81    } 
     82    else if($this->type == self::DOCTRINE) 
     83    { 
     84      return $this->adapter->getQuery(); 
    2885    } 
    2986  } 
    3087   
    3188  // Finder Initializers 
    32    
     89 
    3390  /** 
    3491   * Mixed initializer 
    35    * Accepts either a string (Propel class) or an array of Propel objects 
     92   * Accepts either a string (Model object class) or an array of model objects 
    3693   * 
    3794   * @param mixed $from The data to initialize the finder with 
     
    55112   
    56113  /** 
     114   * Array initializer 
     115   * 
     116   * @param array $array Array of Primary keys 
     117   * @param string $class Model classname on which the search will be done 
     118   * 
     119   * @return DbFinder a finder object 
     120   */ 
     121  public static function fromArray($array, $class, $pkName) 
     122  { 
     123    $finder = self::fromClass($class); 
     124    $finder->where($pkName, 'in', $array); 
     125     
     126    return $finder; 
     127  } 
     128   
     129  /** 
    57130   * Class initializer 
    58131   * 
     
    72145      $realClass = $class; 
    73146    } 
    74     if(class_exists($realClass)) 
     147    if(class_exists($finderClass = $realClass.'Finder')) 
     148    { 
     149      $tmp = new $finderClass; 
     150      if($tmp instanceof sfModelFinder) 
     151      { 
     152        $finder = $tmp; 
     153        if($connection) 
     154        { 
     155          $finder->setConnection($connection); 
     156        } 
     157         
     158        return $finder; 
     159      } 
     160      else 
     161      { 
     162        throw new Exception('DbFinder::fromClass() only accepts a model object of a finder classname'); 
     163      } 
     164    } 
     165    else if(class_exists($realClass)) 
    75166    { 
    76167      $tmp = new $realClass; 
     
    88179      } 
    89180    } 
    90     else if(class_exists($finderClass = $realClass.'Finder')) 
    91     { 
    92       $tmp = new $finderClass; 
    93       if($tmp instanceof DbFinder) 
     181     
     182    $me = __CLASS__; 
     183    $dbFinder = new $me($finder); 
     184 
     185    return $dbFinder; 
     186  } 
     187   
     188  /** 
     189   * Collection initializer 
     190   * 
     191   * @param array $from Array of model objects of the same class 
     192   * @param string $class Optional classname of the desired objects 
     193   * @param string $class Optional column name of the primary key 
     194   * 
     195   * @return DbFinder a finder object 
     196   * @throws Exception If the array is empty, contains not model objects or composite objects 
     197   */ 
     198  public static function fromCollection($collection, $class = '', $pkName = '') 
     199  { 
     200    if(count($collection) == 0) 
     201    { 
     202      if(!$class) 
    94203      { 
    95         $finder = $tmp; 
    96         if($connection) 
    97         { 
    98           $finder->setConnection($connection); 
    99         } 
     204        throw new Exception('A DbFinder cannot be initialized with an empty array and no class'); 
    100205      } 
    101206      else 
    102207      { 
    103         throw new Exception('DbFinder::fromClass() only accepts a model object of a finder classname'); 
     208        return self::fromClass($class); 
    104209      } 
    105210    } 
    106211     
    107     return $finder; 
    108   } 
    109    
    110   /** 
    111    * Collection initializer 
    112    * 
    113    * @param array $from Array of model objects of the same class 
    114    * @param string $class Optional classname of the desired objects 
    115    * @param string $class Optional column name of the primary key 
    116    * 
    117    * @return DbFinder a finder object 
    118    * @throws Exception If the array is empty, contains not model objects or composite objects 
    119    */ 
    120   public static function fromCollection($collection, $class = '', $pkName = '') 
    121   { 
    122     $pks = array(); 
    123     $type = null; 
    124     foreach($collection as $object) 
    125     { 
    126       if($class != get_class($object)) 
    127       { 
    128         if($class) 
    129         { 
    130           throw new Exception('A DbFinder can only be initialized from an array of objects of a single class'); 
    131         } 
    132         if($object instanceof BaseObject) 
    133         { 
    134           $class = get_class($object); 
    135           $type = self::PROPEL; 
    136         } 
    137         else if($object instanceof Doctrine_Record) 
    138         { 
    139           $class = get_class($object); 
    140           $type = self::DOCTRINE; 
    141         } 
    142         else 
    143         { 
    144           throw new Exception('A sfPropelFinder can only be initialized from an array of Propel objects'); 
    145         } 
    146       } 
    147       if($type == self::PROPEL) 
    148       { 
    149         $pks []= $object->getPrimaryKey(); 
    150       } 
    151       else 
    152       { 
    153         $identifier = array_values($object->identifier()); 
    154         if(count($identifier) == 1) 
    155         { 
    156           $identifier = array_shift($identifier); 
    157         } 
    158         $pks []= $identifier; 
    159       } 
    160        
    161     } 
    162     if(!$class) 
    163     { 
    164       throw new Exception('A DbFinder cannot be initialized with an empty array'); 
    165     } 
    166     $tempObject = new $class(); 
    167     if($type == self::PROPEL) 
    168     { 
    169       foreach ($tempObject->getPeer()->getTableMap()->getColumns() as $column) 
    170       { 
    171         if($column->isPrimaryKey()) 
    172         { 
    173           if($pkName) 
    174           { 
    175             throw new Exception('A sfPropelFinder cannot be initialized from an array of objects with several foreign keys'); 
    176           } 
    177           else 
    178           { 
    179             $pkName = $column->getPhpName(); 
    180           } 
    181         } 
    182       } 
     212    $testObject = $collection[0]; 
     213    if($testObject instanceof BaseObject) 
     214    { 
     215      $finder = sfPropelFinder::fromCollection($collection, $class, $pkName); 
     216    } 
     217    else if($testObject instanceof Doctrine_Record) 
     218    { 
     219      $finder = sfDoctrineFinder::fromCollection($collection, $class, $pkName); 
    183220    } 
    184221    else 
    185222    { 
    186       foreach ($tempObject->getTable()->getColumns() as $name => $column) 
    187       { 
    188         if(array_key_exists('primary', $column)) 
    189         { 
    190           if($pkName) 
    191           { 
    192             throw new Exception('A sfDoctrineFinder cannot be initialized from an array of objects with several foreign keys'); 
    193           } 
    194           else 
    195           { 
    196             $pkName = $name; 
    197           } 
    198         } 
    199       } 
    200     } 
    201      
    202     return self::fromArray($pks, $class, $pkName); 
    203   } 
    204    
    205   /** 
    206    * Array initializer 
    207    * 
    208    * @param array $array Array of Primary keys 
    209    * @param string $class Model classname on which the search will be done 
    210    * 
    211    * @return DbFinder a finder object 
    212    */ 
    213   public static function fromArray($array, $class, $pkName) 
    214   { 
    215     $finder = self::fromClass($class); 
    216     $finder->where($pkName, 'in', $array); 
    217      
    218     return $finder; 
    219   } 
    220    
    221   protected function getClassAndAlias($class) 
    222   { 
    223     if(strpos($class, ' ') !== false) 
    224     { 
    225       list($class, $alias) = explode(' ', $class); 
    226     } 
    227     else 
    228     { 
    229       $alias = strtolower(substr($class, 0, 1)); 
    230       while(isset($this->relations[$alias])) 
    231       { 
    232         $alias .= '1'; 
    233       } 
    234     } 
    235     return array($class, $alias); 
     223      throw new Exception('A DbFinder can only be initialized from an array of Propel or Doctrine objects'); 
     224    } 
     225     
     226    $me = __CLASS__; 
     227    $dbFinder = new $me($finder); 
     228 
     229    return $dbFinder; 
    236230  } 
    237231   
    238232  // Finder accessors 
    239233   
    240   abstract public function getClass(); 
    241   abstract public function setClass($class, $alias = ''); 
    242   abstract public function getConnection(); 
    243   abstract public function setConnection($connection); 
     234  public function getClass() 
     235  { 
     236    return $this->adapter->getClass(); 
     237  } 
     238   
     239  public function setClass($class, $alias = '') 
     240  { 
     241    $this->adapter->setClass($class, $alias); 
     242     
     243    return $this; 
     244  } 
     245   
     246  public function getConnection() 
     247  { 
     248    return $this->adapter->getConnection(); 
     249  } 
     250   
     251  public function setConnection($connection) 
     252  { 
     253    $this->adapter->getsetConnection($connection); 
     254     
     255    return $this; 
     256  } 
    244257   
    245258  // Finder executers 
    246259   
    247   abstract public function count($distinct = false); 
    248   abstract public function find($limit = null); 
    249   abstract public function findOne(); 
    250   abstract public function findLast($column = null); 
    251   abstract public function findFirst($column = null); 
    252   abstract public function findBy($columnName, $value, $limit = null); 
    253   abstract public function findOneBy($columnName, $value); 
    254   abstract public function findPk($pk); 
    255   abstract public function delete($forceIndividualDeletes = false); 
    256   abstract public function paginate($page = 1, $maxPerPage = 10); 
    257   abstract public function set($values, $forceIndividualSaves = false); 
     260  /** 
     261   * Returns the number of records matching the finder 
     262   * 
     263   * @param Boolean $distinct Whether the count query has to add a DISTINCT keyword 
     264   * 
     265   * @return integer Number of records matching the finder 
     266   */ 
     267  public function count($distinct = false) 
     268  { 
     269    return $this->adapter->count($distinct); 
     270  } 
     271   
     272  /** 
     273   * Executes the finder and returns the matching Model objects 
     274   * 
     275   * @param integer $limit Optional maximum number of results to retrieve 
     276   * 
     277   * @return array A list of Model objects 
     278   */ 
     279  public function find($limit = null) 
     280  { 
     281    return $this->adapter->find($limit); 
     282  } 
     283   
     284  /** 
     285   * Limits the search to a single result, and executes the finder 
     286   * Returns the first model object matching the finder 
     287   * 
     288   * @return mixed a model object or null 
     289   */ 
     290  public function findOne() 
     291  { 
     292    return $this->adapter->findOne(); 
     293  } 
     294   
     295  /** 
     296   * Returns the last record matching the finder 
     297   * Optionally, you can specify the column to sort on 
     298   * If no column is passed, the finder guesses the column to use 
     299   * @see guessOrder() 
     300   * 
     301   * @param string $columnName Optional: The column to order by 
     302   * 
     303   * @return mixed a model object or null 
     304   */ 
     305  public function findLast($column = null) 
     306  { 
     307    return $this->adapter->findLast($column); 
     308  } 
     309   
     310  /** 
     311   * Returns the first record matching the finder 
     312   * Optionally, you can specify the column to sort on 
     313   * If no column is passed, the finder guesses the column to use 
     314   * @see guessOrder() 
     315   * 
     316   * @param string $columnName Optional: The column to order by 
     317   * 
     318   * @return mixed a model object or null 
     319   */ 
     320  public function findFirst($column = null) 
     321  { 
     322    return $this->adapter->findFirst($column); 
     323  } 
     324   
     325  /** 
     326   * Adds a condition on a column and returns the records matching the finder 
     327   * 
     328   * @param string $columnName Name of the columns 
     329   * @param mixed $value 
     330   * @param integer $limit Optional maximum number of records to return 
     331   * 
     332   * @return array A list of model objects 
     333   */ 
     334  public function findBy($columnName, $value, $limit = null) 
     335  { 
     336    return $this->adapter->findBy($columnName, $value, $limit); 
     337  } 
     338   
     339  /** 
     340   * Adds a condition on a column and returns the first record matching the finder 
     341   * Useful to retrieve objects by a column which is not the primary key 
     342   * 
     343   * @param string $columnName Name of the columns 
     344   * @param mixed $value 
     345   * 
     346   * @return mixed a model object or null 
     347   */ 
     348  public function findOneBy($columnName, $value) 
     349  { 
     350    return $this->adapter->findOneBy($columnName, $value); 
     351  } 
     352   
     353  /** 
     354   * Finds record(s) based on one or several primary keys 
     355   * Takes into account hydrating methods previously called on the finder 
     356   * 
     357   * @param mixed $pk A primary kay, a composite primary key, or an array of primary keys 
     358   * 
     359   * @return mixed One or several model records (based on the input) 
     360   */ 
     361  public function findPk($pk) 
     362  { 
     363    return $this->adapter->findPk($pk); 
     364  } 
     365   
     366  /** 
     367   * Deletes the records found by the finder 
     368   * Beware that behaviors based on hooks in the object's delete() method (such as sfPropelParanoidBehavior) 
     369   * Will only be triggered if you force individual deletes, i.e. if you pass true as first argument 
     370   * 
     371   * @param Boolean $forceIndividualDeletes If false (default), all deletes are done in a single query, ortherwise it is a series of delete() calls on all the found objects 
     372   * 
     373   * @return Integer Number of deleted rows 
     374   */ 
     375  public function delete($forceIndividualDeletes = false) 
     376  { 
     377    return $this->adapter->delete($forceIndividualDeletes); 
     378  } 
     379   
     380  /** 
     381   * Prepares a pager based on the finder 
     382   * The pager is initialized (it knows how many pages it contains) 
     383   * But it won't be populated until you call getResults() on it 
     384   * 
     385   * @param integer $page The current page (1 by default) 
     386   * @param integer $maxPerPage The maximum number of results per page (10 by default) 
     387   * 
     388   * @return mixed The initialized pager object (sfPropelFinderPager or sfDoctrineFinderPager) 
     389   */ 
     390  public function paginate($page = 1, $maxPerPage = 10) 
     391  { 
     392    return $this->adapter->paginate($page, $maxPerPage); 
     393  } 
     394   
     395  /** 
     396   * Updates the records found by the finder 
     397   * Beware that behaviors based on hooks in the object's save() method 
     398   * Will only be triggered if you force individual saves, i.e. if you pass true as second argument 
     399   * 
     400   * @param Array $values Associative array of keys and values to replace 
     401   * @param Boolean $forceIndividualSaves If false (default), all updates are done in a single query, ortherwise it is a series of save() calls on all the found objects 
     402   * 
     403   * @return Integer Number of deleted rows 
     404   */ 
     405  public function set($values, $forceIndividualSaves = false) 
     406  { 
     407    return $this->adapter->set($values, $forceIndividualSaves); 
     408  } 
    258409 
    259410  // Hydrating 
    260411   
    261   abstract public function with($classes); 
    262   abstract public function withI18n($culture = null); 
    263   abstract public function withColumn($column, $alias = null, $type = null); 
     412  /** 
     413   * Ask the finder to hydrate related records 
     414   * Examples: 
     415   *   // Article has an author, article has a category, and author has a group 
     416   *   $articleFinder->with('Author')->find(); 
     417   *   $articleFinder->with('Author', 'Category')->find(); 
     418   *   $articleFinder->with('Author', 'Group')->find(); 
     419   *   $articleFinder->join('Author')->with('Group')->find(); 
     420   *   // By default, the finder uses a simple join (where) but you can force another join 
     421   *   $articleFinder->leftJoin('Author')->with('Author')->find(); 
     422   * 
     423   * @return     DbFinder the current finder object 
     424   */ 
     425  public function with($classes) 
     426  { 
     427    $this->adapter->with($classes); 
     428     
     429    return $this; 
     430  } 
     431   
     432  /** 
     433   * Ask the finder to hydrate related i18n records 
     434   * 
     435   * @param string $culture Optional culture value. If no culture is given, the current user's culture is used 
     436   * 
     437   * @return     DbFinder the current finder object 
     438   */ 
     439  public function withI18n($culture = null) 
     440  { 
     441    $this->adapter->withI18n($culture); 
     442     
     443    return $this; 
     444  } 
     445   
     446  /** 
     447   * Ask the finder to hydrate related columns 
     448   * Columns hydrated by way of withColumn() can be retrieved on the object via getColumn() 
     449   * If the join was not explicitly declared earlier in the finder, it guesses it 
     450   * Examples: 
     451   *   $article = $articleFinder->join('Author')->withColumn('Author.Name')->findOne(); 
     452   *   // The join() can be omitted, in which case the finder will try to guess the join based on the schema 
     453   *   $article = $articleFinder->withColumn('Author.Name')->findOne(); 
     454   *   // Columns added by way of withColumn() can be retrieved by getColumn() 
     455   *   $authorName = $article->getColumn('Author.Name'); 
     456   * 
     457   *   // Alias support 
     458   *   $article = $articleFinder->withColumn('Author.Name', 'authorName')->findOne(); 
     459   *   $authorName = $article->getColumn('authorName'); 
     460   * 
     461   *   // type support 
     462   *   $article = $articleFinder->withColumn('Author.Name', 'authorName', 'string')->findOne(); 
     463   *   $authorName = $article->getColumn('authorName'); 
     464   * 
     465   *   // Support for calculated columns 
     466   *   $articles = articleFinder-> 
     467   *     join('Comment')-> 
     468   *     withColumn('COUNT(comment.ID)', 'NbComments')-> 
     469   *     groupBy('Article.Id')-> 
     470   *     find(); 
     471   * 
     472   * @param string $column The column to add. Can be a calculated column 
     473   * @param string $alias Optional alias for column retrieval 
     474   * @param string $type Optional type converter to be sure the retrieved column has the correct type 
     475   * 
     476   * @return     DbFinder the current finder object 
     477   */ 
     478  public function withColumn($column, $alias = null, $type = null) 
     479  { 
     480    $this->adapter->withColumn($column, $alias, $type); 
     481  } 
    264482   
    265483  // Finder Filters 
    266484   
    267   abstract public function distinct(); 
    268   abstract public function where(); 
    269   abstract public function _and(); 
    270   abstract public function _or(); 
    271   abstract public function combine($conditions, $operator = 'and', $namedCondition = null); 
    272   abstract public function relatedTo($object); 
    273   abstract public function orderBy($columnName, $order = null); 
    274   abstract public function groupBy($columnName); 
    275   abstract public function groupByClass($class); 
    276   abstract public function guessOrder($direction = 'desc'); 
    277   abstract public function join(); 
     485  /** 
     486   * Finder Fluid Interface for SQL DISTINCT 
     487   * 
     488   * @return     DbFinder the current finder object 
     489   */ 
     490  public function distinct() 
     491  { 
     492    $this->adapter->distinct(); 
     493     
     494    return $this; 
     495  } 
     496   
     497  /** 
     498   * Finder Fluid Interface for SQL WHERE 
     499   * Infers $column, $value, $comparison from $columnName and some optional arguments 
     500   * Examples: 
     501   *   $articleFinder->where('IsPublished') 
     502   *    => WHERE article.IS_PUBLISHED = true 
     503   *   $articleFinder->where('CommentId', 3) 
     504   *    => WHERE article.COMMENT_ID = 3 
     505   *   $articleFinder->where('Title', 'like', '%foo') 
     506   *    => WHERE article.TITLE LIKE '%foo' 
     507   *   $articleFinder->where('Title', 'like', '%foo', 'FooTitle') 
     508   *    => WHERE (article.TITLE LIKE '%foo') 
     509   * 
     510   * @param      string  $columnName PHPName of the column bearing the condition 
     511   * @param      string  $valueOrOperator  Value if 2 arguments, operator otherwise 
     512   * @param      string  $value  Value if 3 arguments (optional) 
     513   * @param      string  $namedCondition  If condition is to be stored for later combination (see combine()) 
     514   * 
     515   * @return     DbFinder the current finder object 
     516   */ 
     517  public function where() 
     518  { 
     519    call_user_func_array(array($this->adapter, 'where'), func_get_arg()); 
     520     
     521    return $this; 
     522  } 
     523   
     524  /** 
     525   * Finder Fluid Interface for AND in a WHERE 
     526   * Infers $column, $value, $comparison from $columnName and some optional arguments 
     527   * 
     528   * @see     where() 
     529   * @return  DbDinder the current finder object 
     530   */ 
     531  public function _and() 
     532  { 
     533    call_user_func_array(array($this->adapter, '_and'), func_get_arg()); 
     534     
     535    return $this; 
     536  } 
     537   
     538  /** 
     539   * Finder Fluid Interface for OR in a WHERE 
     540   * Infers $column, $value, $comparison from $columnName and some optional arguments 
     541   * Examples: 
     542   *   $articleFinder->_or('CommentId', 3) 
     543   *    => WHERE ... OR article.COMMENT_ID = 3 
     544   *   $articleFinder->_or('Title', 'like', '%foo') 
     545   *    => WHERE ... OR article.TITLE LIKE '%foo' 
     546   * 
     547   * @param      string  $columnName PHPName of the column bearing the condition 
     548   * @param      string  $valueOrOperator  Value if 2 arguments, operator otherwise 
     549   * @param      string  $value  Value if 3 arguments (optional) 
     550   * 
     551   * @return     DbFinder the current finder object 
     552   */ 
     553  public function _or() 
     554  { 
     555    call_user_func_array(array($this->adapter, '_or'), func_get_arg()); 
     556     
     557    return $this; 
     558  } 
     559   
     560  /** 
     561   * Combine named conditions into the main criteria or into a new named condition 
     562   * Named conditions are to be defined in where() 
     563   * 
     564   * @param Array $conditions list of named conditions already set by way of where() 
     565   * @param string $operator Combine operator ('and' or 'or') 
     566   * @param string $namedCondition  If combined condition is to be stored for later combination 
     567   *  
     568   * @see where() 
     569   * @return     DbFinder the current finder object 
     570   */ 
     571  public function combine($conditions, $operator = 'and', $namedCondition = null) 
     572  { 
     573    $this->adapter->combine($conditions, $operator, $namedCondition); 
     574     
     575    return $this; 
     576  } 
     577   
     578  /** 
     579   * Finder fluid method to restrict results to a related object 
     580   * Examples: 
     581   *   $commentFinder->relatedTo($article) 
     582   *    => 'WHERE comment.ARTICLE_ID = '.$article->getId() 
     583   * 
     584   * @param  mixed $object The related model object to restrict to 
     585   * @return DbFinder the current finder object 
     586   */ 
     587  public function relatedTo($object) 
     588  { 
     589    $this->adapter->relatedTo($object); 
     590     
     591    return $this; 
     592  } 
     593   
     594  /** 
     595   * Finder Fluid Interface for SQL ORDER BY 
     596   * Infers $column and $order from $columnName and some optional arguments 
     597   * Examples: 
     598   *   $articleFinder->orderBy('CreatedAt') 
     599   *    => ORDER BY article.CREATED_AT ASC 
     600   *   $articlefinder->orderBy('CategoryId', 'desc') 
     601   *    => ORDER BY article.CATEGORY_ID DESC 
     602   * 
     603   * @param  string $columnName The column to order by 
     604   * @param  string $order      The sorting order. 'asc' by default, also accepts 'desc' 
     605   * 
     606   * @return DbFinder the current finder object 
     607   */ 
     608  public function orderBy($columnName, $order = null) 
     609  { 
     610    $this->adapter->orderBy($columnName, $order); 
     611     
     612    return $this; 
     613  } 
     614   
     615  /** 
     616   * Finder Fluid Interface for SQL GROUP BY 
     617   * Infers $column and $order from $columnName and some optional arguments 
     618   * Examples: 
     619   *   $articleFinder->groupBy('CreatedAt') 
     620   *    => GROUP BY article.CREATED_AT 
     621   * 
     622   * @param  string $columnName The column to group by 
     623   * 
     624   * @return DbFinder the current finder object 
     625   */ 
     626  public function groupBy($columnName) 
     627  { 
     628    $this->adapter->groupBy($columnName); 
     629     
     630    return $this; 
     631  } 
     632   
     633  /** 
     634   * Finder Fluid Interface for SQL GROUP BY but this times we add all columns from given class. 
     635   * Examples: 
     636   *   $articleFinder->groupBy('Article') 
     637   *    => GROUP BY article.ID, article.TITLE, article.CREATED_AT...  
     638   * @param  string $class 
     639   * 
     640   * @return DbFinder the current finder object 
     641   */ 
     642  public function groupByClass($class) 
     643  { 
     644    $this->adapter->groupByClass($class); 
     645     
     646    return $this; 
     647  } 
     648   
     649  /** 
     650   * Guess sort column based on their names 
     651   * Will look primarily for columns named: 
     652   * 'UpdatedAt', 'UpdatedOn', 'CreatedAt', 'CreatedOn', 'Id' 
     653   * You can change this sequence by modifying the app_sfPropelFinder_sort_column_guesses value 
     654   * 
     655   * @param string $direction 'desc' (default) or 'asc' 
     656   * 
     657   * @return DbFinder the current finder object 
     658   */ 
     659  public function guessOrder($direction = 'desc') 
     660  { 
     661    $this->adapter->guessOrder($direction); 
     662     
     663    return $this; 
     664  } 
     665   
     666  /** 
     667   * Finder Fluid Interface for SQL JOIN 
     668   * Infers $column1, $column2 and $operator from $relatedClass and some optional arguments 
     669   * Uses the Propel column maps, based on the schema, to guess the related columns 
     670   * Examples: 
     671   *   $articleFinder->join('Comment') 
     672   *    => WHERE article.ID = comment.ARTICLE_ID 
     673   *   $articleFinder->join('Category', 'RIGHT JOIN') 
     674   *    => RIGHT JOIN category ON article.CATEGORY_ID = category.ID 
     675   *   $articleFinder->join('Article.CategoryId', 'Category.Id', 'RIGHT JOIN') 
     676   *    => RIGHT JOIN category ON article.CATEGORY_ID = category.ID 
     677   *  
     678   * @param  string $classOrColumn Class to join if 1 argument, first column of the join otherwise 
     679   * @param  string $column Second column of the join if more than 1 argument 
     680   * @param  string $joinType Accepted values are null, 'left join', 'right join', 'inner join' 
     681   * 
     682   * @return DFinder the current finder object 
     683   */ 
     684  public function join() 
     685  { 
     686    call_user_func_array(array($this->adapter, 'join'), func_get_arg()); 
     687     
     688    return $this; 
     689  } 
    278690   
    279691  // Finder utilities 
    280692   
    281   abstract public function keepQuery($keep = true); 
    282   abstract public function getLatestQuery(); 
    283   abstract protected function cleanup(); 
    284    
    285   // Finder Outputters 
    286    
    287   /** 
    288    * Array outputter 
    289    * Executes the finder and returns an array of results 
    290    * Each result being an associative array 
    291    * TODO: Bypass hydration for better performance 
    292    * 
    293    * @param $limit Integer Optional number of results to return 
    294    * 
    295    * @return Array the list of results as arrays 
    296    */ 
    297   public function toArray($limit = null) 
    298   { 
    299     $objects = $this->find($limit); 
    300     $res = array(); 
    301     foreach($objects as $object) 
    302     { 
    303       $res []= $object->toArray(); 
    304     } 
    305      
    306     return $res; 
    307   } 
    308  
    309   /** 
    310    * String outputter 
    311    * Executes the finder and returns a string (incidentally, YAML compliant) 
    312    * TODO: Bypass hydration for better performance 
    313    * 
    314    * @param $limit Integer Optional number of results to return 
    315    * 
    316    * @return String the list of results as YAML 
    317    */ 
    318   public function __toString($limit = null) 
    319   { 
    320     $objects = $this->find($limit); 
    321     $res = ''; 
    322     $i = 0; 
    323     foreach($objects as $object) 
    324     { 
    325       $res .= sprintf("%s_%d:\n", get_class($object), $i); 
    326       foreach ($object->toArray() as $key => $value) 
    327       { 
    328         $res .= sprintf("  %-10s %s\n", $key . ':', $value); 
    329       } 
    330       $i++; 
    331     } 
    332      
    333     return $res; 
    334   } 
    335    
    336   /** 
    337    * HTML outputter 
    338    * Executes the finder and returns a HTML table 
    339    * TODO: Bypass hydration for better performance 
    340    * 
    341    * @param $limit Integer Optional number of results to return 
    342    * 
    343    * @return String the list of results as an HTML table 
    344    */ 
    345   public function toHtml($limit = null) 
    346   { 
    347     $objects = $this->find($limit); 
    348     $res = "<table class=\"DbFinder\">\n"; 
    349     $isFirstLine = true; 
    350     foreach($objects as $object) 
    351     { 
    352       if($isFirstLine) 
    353       { 
    354         $res .= "  <tr>\n"; 
    355         foreach ($object->toArray() as $key => $value) 
    356         { 
    357           $res .= sprintf("    <th>%s</th>\n", $key); 
    358         } 
    359         $res .= "  </tr>\n"; 
    360       } 
    361       $res .= "  <tr>\n"; 
    362       foreach ($object->toArray() as $value) 
    363       { 
    364         $res .= sprintf("    <td>%s</td>\n", $value); 
    365       } 
    366       $res .= "  </tr>\n"; 
    367       $isFirstLine = false; 
    368     } 
    369     $res .= "</table>\n"; 
    370      
    371     return $res; 
    372   } 
     693  /** 
     694   * Do no reinitialize the finder object after a termination method 
     695   * 
     696   * @param  Boolean $keep true (default) or false 
     697   * 
     698   * @return DbFinder the current finder object 
     699   */ 
     700  public function keepQuery($keep = true) 
     701  { 
     702    $this->adapter->keepQuery($keep); 
     703     
     704    return $this; 
     705  } 
     706   
     707  /** 
     708   * Returns the SQL for the latest executed query 
     709   * Only works in debug mode 
     710   * 
     711   * @return string Latest SQL query 
     712   */ 
     713  public function getLatestQuery() 
     714  { 
     715    return $this->adapter->getLatestQuery(); 
     716  } 
     717   
     718  /** 
     719   * Cleans up the query object (if necessary) and logs the latest query 
     720   * 
     721   * @return DbFinder the current finder object 
     722   */ 
     723  protected function cleanup() 
     724  { 
     725    $this->adapter->cleanup(); 
     726     
     727    return $this; 
     728  } 
     729   
     730  public function __call($name, $arguments) 
     731  { 
     732    if(strpos($name, 'where') === 0) 
     733    { 
     734      array_unshift($arguments, substr($name, 5)); 
     735      return call_user_func_array(array($this, 'where'), $arguments); 
     736    } 
     737    if(strpos($name, 'orderBy') === 0) 
     738    { 
     739      array_unshift($arguments, substr($name, 7)); 
     740      return call_user_func_array(array($this, 'orderBy'), $arguments); 
     741    } 
     742    if(strpos($name, 'groupBy') === 0) 
     743    { 
     744      array_unshift($arguments, substr($name, 7)); 
     745      return call_user_func_array(array($this, 'groupBy'), $arguments); 
     746    } 
     747    if(strpos($name, 'join') === 0) 
     748    { 
     749      array_unshift($arguments, substr($name, 4)); 
     750      return call_user_func_array(array($this, 'join'), $arguments); 
     751 
     752    } 
     753    if(($pos = strpos($name, 'Join')) > 0) 
     754    { 
     755      $joinType = strtoupper(substr($name, 0, $pos)) . ' JOIN'; 
     756      array_push($arguments, $joinType); 
     757      return call_user_func_array(array($this, 'join'), $arguments); 
     758    } 
     759    if(strpos($name, '_and') === 0) 
     760    { 
     761      array_unshift($arguments, substr($name, 4)); 
     762      return call_user_func_array(array($this, '_and'), $arguments); 
     763    } 
     764    if(strpos($name, 'and') === 0) 
     765    { 
     766      array_unshift($arguments, substr($name, 3)); 
     767      return call_user_func_array(array($this, '_and'), $arguments); 
     768 
     769    } 
     770    if(strpos($name, '_or') === 0) 
     771    { 
     772      array_unshift($arguments, substr($name, 3)); 
     773      return call_user_func_array(array($this, '_or'), $arguments); 
     774 
     775    } 
     776    if(strpos($name, 'or') === 0) 
     777    { 
     778      array_unshift($arguments, substr($name, 2)); 
     779      return call_user_func_array(array($this, '_or'), $arguments); 
     780    } 
     781    if(strpos($name, 'findBy') === 0) 
     782    { 
     783      array_unshift($arguments, substr($name, 6)); 
     784      return call_user_func_array(array($this, 'findBy'), $arguments); 
     785    } 
     786    if(strpos($name, 'findOneBy') === 0) 
     787    { 
     788      array_unshift($arguments, substr($name, 9)); 
     789      return call_user_func_array(array($this, 'findOneBy'), $arguments); 
     790    } 
     791    if(method_exists($this->adapter, $name)) 
     792    { 
     793      call_user_func_array(array($this->adapter, $name), $arguments); 
     794      return $this; 
     795    } 
     796    throw new Exception(sprintf('Undefined method DbFinder::%s()', $name)); 
     797  } 
     798 
    373799} 
  • plugins/sfPropelFinderPlugin/lib/sfDoctrineFinder.php

    r10755 r10765  
    1010 */ 
    1111  
    12 class sfDoctrineFinder extends DbFinder 
     12class sfDoctrineFinder extends sfModelFinder 
    1313{ 
    1414  protected 
     
    1818    $object        = null, 
    1919    $reinit        = true, 
     20    $query         = null, 
    2021    $queryListener = null, 
    2122    $withClasses   = array(), 
     
    6061    return $this; 
    6162  } 
     63   
     64  public function getQuery() 
     65  { 
     66    return $this->query; 
     67  } 
     68   
     69  // Finder initializers 
     70   
     71  /** 
     72   * Mixed initializer 
     73   * Accepts either a string (Model object class) or an array of model objects 
     74   * 
     75   * @param mixed $from The data to initialize the finder with 
     76   * @param mixed $connection Optional connection object 
     77   * 
     78   * @return sfPropelFinder a finder object 
     79   * @throws Exception If the data is neither a classname nor an array 
     80   */ 
     81  public static function from($from, $connection = null) 
     82  { 
     83    if (is_string($from)) 
     84    { 
     85      return self::fromClass($from, $connection); 
     86    } 
     87    if (is_array($from) || $from instanceof Doctrine_Collection) 
     88    { 
     89      return self::fromCollection($from); 
     90    } 
     91    throw new Exception('sfPropelFinder::from() only accepts a model object classname or an array of model objects'); 
     92  } 
     93   
     94  /** 
     95   * Array initializer 
     96   * 
     97   * @param array $array Array of Primary keys 
     98   * @param string $class Model classname on which the search will be done 
     99   * 
     100   * @return sfPropelFinder a finder object 
     101   */ 
     102  public static function fromArray($array, $class, $pkName) 
     103  { 
     104    $finder = self::fromClass($class); 
     105    $finder->where($pkName, 'in', $array); 
     106     
     107    return $finder; 
     108  } 
     109   
     110  /** 
     111   * Class initializer 
     112   * 
     113   * @param string $from Model classname on which the search will be done 
     114   * @param mixed $connection Optional connection object 
     115   * 
     116   * @return sfDoctrineFinder a finder object 
     117   */ 
     118  public static function fromClass($class, $connection = null) 
     119  { 
     120    if(strpos($class, ' ') !== false) 
     121    { 
     122      list($realClass, $alias) = explode(' ', $class); 
     123    } 
     124    else 
     125    { 
     126      $realClass = $class; 
     127    } 
     128    if(class_exists($realClass)) 
     129    { 
     130      $tmp = new $realClass; 
     131      if($tmp instanceof Doctrine_Record) 
     132      { 
     133        $me = __CLASS__; 
     134        $finder = new $me($class, $connection); 
     135      } 
     136      else 
     137      { 
     138        throw new Exception('sfDoctrineFinder::fromClass() only accepts a Doctrine model classname'); 
     139      } 
     140    } 
     141    else 
     142    { 
     143       throw new Exception('sfDoctrineFinder::fromClass() only accepts a Doctrine model classname'); 
     144    } 
     145     
     146    return $finder; 
     147