PageRenderTime 55ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/runtime/lib/query/ModelCriteria.php

https://github.com/1989gaurav/Propel
PHP | 1613 lines | 715 code | 146 blank | 752 comment | 95 complexity | cf6de279f23db334a43e0fdc6e48c0de MD5 | raw file
  1. <?php
  2. /**
  3. * This file is part of the Propel package.
  4. * For the full copyright and license information, please view the LICENSE
  5. * file that was distributed with this source code.
  6. *
  7. * @license MIT License
  8. */
  9. /**
  10. * This class extends the Criteria by adding runtime introspection abilities
  11. * in order to ease the building of queries.
  12. *
  13. * A ModelCriteria requires additional information to be initialized.
  14. * Using a model name and tablemaps, a ModelCriteria can do more powerful things than a simple Criteria
  15. *
  16. * magic methods:
  17. *
  18. * @method ModelCriteria leftJoin($relation) Adds a LEFT JOIN clause to the query
  19. * @method ModelCriteria rightJoin($relation) Adds a RIGHT JOIN clause to the query
  20. * @method ModelCriteria innerJoin($relation) Adds a INNER JOIN clause to the query
  21. *
  22. * @author François Zaninotto
  23. * @version $Revision$
  24. * @package propel.runtime.query
  25. */
  26. class ModelCriteria extends Criteria
  27. {
  28. const MODEL_CLAUSE = "MODEL CLAUSE";
  29. const MODEL_CLAUSE_ARRAY = "MODEL CLAUSE ARRAY";
  30. const MODEL_CLAUSE_LIKE = "MODEL CLAUSE LIKE";
  31. const MODEL_CLAUSE_SEVERAL = "MODEL CLAUSE SEVERAL";
  32. const FORMAT_STATEMENT = 'PropelStatementFormatter';
  33. const FORMAT_ARRAY = 'PropelArrayFormatter';
  34. const FORMAT_OBJECT = 'PropelObjectFormatter';
  35. const FORMAT_ON_DEMAND = 'PropelOnDemandFormatter';
  36. protected $modelName;
  37. protected $modelPeerName;
  38. protected $modelAlias;
  39. protected $useAliasInSQL = false;
  40. protected $tableMap;
  41. protected $primaryCriteria;
  42. protected $formatter;
  43. protected $defaultFormatterClass = ModelCriteria::FORMAT_OBJECT;
  44. protected $with = array();
  45. protected $isWithOneToMany = false;
  46. protected $previousJoin = null; // this is introduced to prevent useQuery->join from going wrong
  47. protected $isKeepQuery = true; // whether to clone the current object before termination methods
  48. protected $select = null; // this is for the select method
  49. /**
  50. * Creates a new instance with the default capacity which corresponds to
  51. * the specified database.
  52. *
  53. * @param string $dbName The dabase name
  54. * @param string $modelName The phpName of a model, e.g. 'Book'
  55. * @param string $modelAlias The alias for the model in this query, e.g. 'b'
  56. */
  57. public function __construct($dbName = null, $modelName, $modelAlias = null)
  58. {
  59. $this->setDbName($dbName);
  60. $this->originalDbName = $dbName;
  61. $this->modelName = $modelName;
  62. $this->modelPeerName = constant($this->modelName . '::PEER');
  63. $this->modelAlias = $modelAlias;
  64. $this->tableMap = Propel::getDatabaseMap($this->getDbName())->getTableByPhpName($this->modelName);
  65. }
  66. /**
  67. * Returns the name of the class for this model criteria
  68. *
  69. * @return string
  70. */
  71. public function getModelName()
  72. {
  73. return $this->modelName;
  74. }
  75. /**
  76. * Sets the alias for the model in this query
  77. *
  78. * @param string $modelAlias The model alias
  79. * @param boolean $useAliasInSQL Whether to use the alias in the SQL code (false by default)
  80. *
  81. * @return ModelCriteria The current object, for fluid interface
  82. */
  83. public function setModelAlias($modelAlias, $useAliasInSQL = false)
  84. {
  85. if ($useAliasInSQL) {
  86. $this->addAlias($modelAlias, $this->tableMap->getName());
  87. $this->useAliasInSQL = true;
  88. }
  89. $this->modelAlias = $modelAlias;
  90. return $this;
  91. }
  92. /**
  93. * Returns the alias of the main class for this model criteria
  94. *
  95. * @return string The model alias
  96. */
  97. public function getModelAlias()
  98. {
  99. return $this->modelAlias;
  100. }
  101. /**
  102. * Return the string to use in a clause as a model prefix for the main model
  103. *
  104. * @return string The model alias if it exists, the model name if not
  105. */
  106. public function getModelAliasOrName()
  107. {
  108. return $this->modelAlias ? $this->modelAlias : $this->modelName;
  109. }
  110. /**
  111. * Returns the name of the Peer class for this model criteria
  112. *
  113. * @return string
  114. */
  115. public function getModelPeerName()
  116. {
  117. return $this->modelPeerName;
  118. }
  119. /**
  120. * Returns the TabkleMap object for this Criteria
  121. *
  122. * @return TableMap
  123. */
  124. public function getTableMap()
  125. {
  126. return $this->tableMap;
  127. }
  128. /**
  129. * Sets the formatter to use for the find() output
  130. * Formatters must extend PropelFormatter
  131. * Use the ModelCriteria constants for class names:
  132. * <code>
  133. * $c->setFormatter(ModelCriteria::FORMAT_ARRAY);
  134. * </code>
  135. *
  136. * @param string|PropelFormatter $formatter a formatter class name, or a formatter instance
  137. * @return ModelCriteria The current object, for fluid interface
  138. */
  139. public function setFormatter($formatter)
  140. {
  141. if(is_string($formatter)) {
  142. $formatter = new $formatter();
  143. }
  144. if (!$formatter instanceof PropelFormatter) {
  145. throw new PropelException('setFormatter() only accepts classes extending PropelFormatter');
  146. }
  147. $this->formatter = $formatter;
  148. return $this;
  149. }
  150. /**
  151. * Gets the formatter to use for the find() output
  152. * Defaults to an instance of ModelCriteria::$defaultFormatterClass, i.e. PropelObjectsFormatter
  153. *
  154. * @return PropelFormatter
  155. */
  156. public function getFormatter()
  157. {
  158. if (null === $this->formatter) {
  159. $formatterClass = $this->defaultFormatterClass;
  160. $this->formatter = new $formatterClass();
  161. }
  162. return $this->formatter;
  163. }
  164. /**
  165. * Adds a condition on a column based on a pseudo SQL clause
  166. * but keeps it for later use with combine()
  167. * Until combine() is called, the condition is not added to the query
  168. * Uses introspection to translate the column phpName into a fully qualified name
  169. * <code>
  170. * $c->condition('cond1', 'b.Title = ?', 'foo');
  171. * </code>
  172. *
  173. * @see Criteria::add()
  174. *
  175. * @param string $conditionName A name to store the condition for a later combination with combine()
  176. * @param string $clause The pseudo SQL clause, e.g. 'AuthorId = ?'
  177. * @param mixed $value A value for the condition
  178. *
  179. * @return ModelCriteria The current object, for fluid interface
  180. */
  181. public function condition($conditionName, $clause, $value = null)
  182. {
  183. $this->addCond($conditionName, $this->getCriterionForClause($clause, $value), null, null);
  184. return $this;
  185. }
  186. /**
  187. * Adds a condition on a column based on a column phpName and a value
  188. * Uses introspection to translate the column phpName into a fully qualified name
  189. * Warning: recognizes only the phpNames of the main Model (not joined tables)
  190. * <code>
  191. * $c->filterBy('Title', 'foo');
  192. * </code>
  193. *
  194. * @see Criteria::add()
  195. *
  196. * @param string $column A string representing thecolumn phpName, e.g. 'AuthorId'
  197. * @param mixed $value A value for the condition
  198. * @param string $comparison What to use for the column comparison, defaults to Criteria::EQUAL
  199. *
  200. * @return ModelCriteria The current object, for fluid interface
  201. */
  202. public function filterBy($column, $value, $comparison = Criteria::EQUAL)
  203. {
  204. return $this->add($this->getRealColumnName($column), $value, $comparison);
  205. }
  206. /**
  207. * Adds a list of conditions on the columns of the current model
  208. * Uses introspection to translate the column phpName into a fully qualified name
  209. * Warning: recognizes only the phpNames of the main Model (not joined tables)
  210. * <code>
  211. * $c->filterByArray(array(
  212. * 'Title' => 'War And Peace',
  213. * 'Publisher' => $publisher
  214. * ));
  215. * </code>
  216. *
  217. * @see filterBy()
  218. *
  219. * @param mixed $conditions An array of conditions, using column phpNames as key
  220. *
  221. * @return ModelCriteria The current object, for fluid interface
  222. */
  223. public function filterByArray($conditions)
  224. {
  225. foreach ($conditions as $column => $args) {
  226. call_user_func_array(array($this, 'filterBy' . $column), (array) $args);
  227. }
  228. return $this;
  229. }
  230. /**
  231. * Adds a condition on a column based on a pseudo SQL clause
  232. * Uses introspection to translate the column phpName into a fully qualified name
  233. * <code>
  234. * // simple clause
  235. * $c->where('b.Title = ?', 'foo');
  236. * // named conditions
  237. * $c->condition('cond1', 'b.Title = ?', 'foo');
  238. * $c->condition('cond2', 'b.ISBN = ?', 12345);
  239. * $c->where(array('cond1', 'cond2'), Criteria::LOGICAL_OR);
  240. * </code>
  241. *
  242. * @see Criteria::add()
  243. *
  244. * @param mixed $clause A string representing the pseudo SQL clause, e.g. 'Book.AuthorId = ?'
  245. * Or an array of condition names
  246. * @param mixed $value A value for the condition
  247. *
  248. * @return ModelCriteria The current object, for fluid interface
  249. */
  250. public function where($clause, $value = null)
  251. {
  252. if (is_array($clause)) {
  253. // where(array('cond1', 'cond2'), Criteria::LOGICAL_OR)
  254. $criterion = $this->getCriterionForConditions($clause, $value);
  255. } else {
  256. // where('Book.AuthorId = ?', 12)
  257. $criterion = $this->getCriterionForClause($clause, $value);
  258. }
  259. $this->addUsingOperator($criterion, null, null);
  260. return $this;
  261. }
  262. /**
  263. * Adds a condition on a column based on a pseudo SQL clause
  264. * Uses introspection to translate the column phpName into a fully qualified name
  265. * <code>
  266. * // simple clause
  267. * $c->orWhere('b.Title = ?', 'foo');
  268. * // named conditions
  269. * $c->condition('cond1', 'b.Title = ?', 'foo');
  270. * $c->condition('cond2', 'b.ISBN = ?', 12345);
  271. * $c->orWhere(array('cond1', 'cond2'), Criteria::LOGICAL_OR);
  272. * </code>
  273. *
  274. * @see Criteria::addOr()
  275. * @deprecated Use _or()->where() instead
  276. *
  277. * @param string $clause The pseudo SQL clause, e.g. 'AuthorId = ?'
  278. * @param mixed $value A value for the condition
  279. *
  280. * @return ModelCriteria The current object, for fluid interface
  281. */
  282. public function orWhere($clause, $value = null)
  283. {
  284. return $this
  285. ->_or()
  286. ->where($clause, $value);
  287. }
  288. /**
  289. * Adds a having condition on a column based on a pseudo SQL clause
  290. * Uses introspection to translate the column phpName into a fully qualified name
  291. * <code>
  292. * // simple clause
  293. * $c->having('b.Title = ?', 'foo');
  294. * // named conditions
  295. * $c->condition('cond1', 'b.Title = ?', 'foo');
  296. * $c->condition('cond2', 'b.ISBN = ?', 12345);
  297. * $c->having(array('cond1', 'cond2'), Criteria::LOGICAL_OR);
  298. * </code>
  299. *
  300. * @see Criteria::addHaving()
  301. *
  302. * @param mixed $clause A string representing the pseudo SQL clause, e.g. 'Book.AuthorId = ?'
  303. * Or an array of condition names
  304. * @param mixed $value A value for the condition
  305. *
  306. * @return ModelCriteria The current object, for fluid interface
  307. */
  308. public function having($clause, $value = null)
  309. {
  310. if (is_array($clause)) {
  311. // having(array('cond1', 'cond2'), Criteria::LOGICAL_OR)
  312. $criterion = $this->getCriterionForConditions($clause, $value);
  313. } else {
  314. // having('Book.AuthorId = ?', 12)
  315. $criterion = $this->getCriterionForClause($clause, $value);
  316. }
  317. $this->addHaving($criterion);
  318. return $this;
  319. }
  320. /**
  321. * Adds an ORDER BY clause to the query
  322. * Usability layer on top of Criteria::addAscendingOrderByColumn() and Criteria::addDescendingOrderByColumn()
  323. * Infers $column and $order from $columnName and some optional arguments
  324. * Examples:
  325. * $c->orderBy('Book.CreatedAt')
  326. * => $c->addAscendingOrderByColumn(BookPeer::CREATED_AT)
  327. * $c->orderBy('Book.CategoryId', 'desc')
  328. * => $c->addDescendingOrderByColumn(BookPeer::CATEGORY_ID)
  329. *
  330. * @param string $columnName The column to order by
  331. * @param string $order The sorting order. Criteria::ASC by default, also accepts Criteria::DESC
  332. *
  333. * @return ModelCriteria The current object, for fluid interface
  334. */
  335. public function orderBy($columnName, $order = Criteria::ASC)
  336. {
  337. list($column, $realColumnName) = $this->getColumnFromName($columnName, false);
  338. $order = strtoupper($order);
  339. switch ($order) {
  340. case Criteria::ASC:
  341. $this->addAscendingOrderByColumn($realColumnName);
  342. break;
  343. case Criteria::DESC:
  344. $this->addDescendingOrderByColumn($realColumnName);
  345. break;
  346. default:
  347. throw new PropelException('ModelCriteria::orderBy() only accepts Criteria::ASC or Criteria::DESC as argument');
  348. }
  349. return $this;
  350. }
  351. /**
  352. * Adds a GROUB BY clause to the query
  353. * Usability layer on top of Criteria::addGroupByColumn()
  354. * Infers $column $columnName
  355. * Examples:
  356. * $c->groupBy('Book.AuthorId')
  357. * => $c->addGroupByColumn(BookPeer::AUTHOR_ID)
  358. *
  359. * @param string $columnName The column to group by
  360. *
  361. * @return ModelCriteria The current object, for fluid interface
  362. */
  363. public function groupBy($columnName)
  364. {
  365. list($column, $realColumnName) = $this->getColumnFromName($columnName, false);
  366. $this->addGroupByColumn($realColumnName);
  367. return $this;
  368. }
  369. /**
  370. * Adds a GROUB BY clause for all columns of a model to the query
  371. * Examples:
  372. * $c->groupBy('Book');
  373. * => $c->addGroupByColumn(BookPeer::ID);
  374. * => $c->addGroupByColumn(BookPeer::TITLE);
  375. * => $c->addGroupByColumn(BookPeer::AUTHOR_ID);
  376. * => $c->addGroupByColumn(BookPeer::PUBLISHER_ID);
  377. *
  378. * @param string $class The class name or alias
  379. *
  380. * @return ModelCriteria The current object, for fluid interface
  381. */
  382. public function groupByClass($class)
  383. {
  384. if ($class == $this->getModelAliasOrName()) {
  385. // column of the Criteria's model
  386. $tableMap = $this->getTableMap();
  387. } elseif (isset($this->joins[$class])) {
  388. // column of a relations's model
  389. $tableMap = $this->joins[$class]->getTableMap();
  390. } else {
  391. throw new PropelException('Unknown model or alias ' . $class);
  392. }
  393. foreach ($tableMap->getColumns() as $column) {
  394. if (isset($this->aliases[$class])) {
  395. $this->addGroupByColumn($class . '.' . $column->getName());
  396. } else {
  397. $this->addGroupByColumn($column->getFullyQualifiedName());
  398. }
  399. }
  400. return $this;
  401. }
  402. /**
  403. * Adds a DISTINCT clause to the query
  404. * Alias for Criteria::setDistinct()
  405. *
  406. * @return ModelCriteria The current object, for fluid interface
  407. */
  408. public function distinct()
  409. {
  410. $this->setDistinct();
  411. return $this;
  412. }
  413. /**
  414. * Adds a LIMIT clause (or its subselect equivalent) to the query
  415. * Alias for Criteria:::setLimit()
  416. *
  417. * @param int $limit Maximum number of results to return by the query
  418. *
  419. * @return ModelCriteria The current object, for fluid interface
  420. */
  421. public function limit($limit)
  422. {
  423. $this->setLimit($limit);
  424. return $this;
  425. }
  426. /**
  427. * Adds an OFFSET clause (or its subselect equivalent) to the query
  428. * Alias for of Criteria::setOffset()
  429. *
  430. * @param int $offset Offset of the first result to return
  431. *
  432. * @return ModelCriteria The current object, for fluid interface
  433. */
  434. public function offset($offset)
  435. {
  436. $this->setOffset($offset);
  437. return $this;
  438. }
  439. /**
  440. * Makes the ModelCriteria return a string, array, or PropelArrayCollection
  441. * Examples:
  442. * ArticleQuery::create()->select('Name')->find();
  443. * => PropelArrayCollection Object ('Foo', 'Bar')
  444. *
  445. * ArticleQuery::create()->select('Name')->findOne();
  446. * => string 'Foo'
  447. *
  448. * ArticleQuery::create()->select(array('Id', 'Name'))->find();
  449. * => PropelArrayCollection Object (
  450. * array('Id' => 1, 'Name' => 'Foo'),
  451. * array('Id' => 2, 'Name' => 'Bar')
  452. * )
  453. *
  454. * ArticleQuery::create()->select(array('Id', 'Name'))->findOne();
  455. * => array('Id' => 1, 'Name' => 'Foo')
  456. *
  457. * @param mixed $columnArray A list of column names (e.g. array('Title', 'Category.Name', 'c.Content')) or a single column name (e.g. 'Name')
  458. *
  459. * @return ModelCriteria The current object, for fluid interface
  460. */
  461. public function select($columnArray)
  462. {
  463. if (!count($columnArray) || $columnArray == '') {
  464. throw new PropelException('You must ask for at least one column');
  465. }
  466. if ($columnArray == '*') {
  467. $columnArray = array();
  468. foreach (call_user_func(array($this->modelPeerName, 'getFieldNames'), BasePeer::TYPE_PHPNAME) as $column) {
  469. $columnArray []= $this->modelName . '.' . $column;
  470. }
  471. }
  472. $this->select = $columnArray;
  473. return $this;
  474. }
  475. /**
  476. * Retrieves the columns defined by a previous call to select().
  477. * @see select()
  478. *
  479. * @return array|string A list of column names (e.g. array('Title', 'Category.Name', 'c.Content')) or a single column name (e.g. 'Name')
  480. */
  481. public function getSelect()
  482. {
  483. return $this->select;
  484. }
  485. protected function configureSelectColumns()
  486. {
  487. if (is_null($this->select)) {
  488. // leave early
  489. return;
  490. }
  491. // select() needs the PropelSimpleArrayFormatter
  492. $this->setFormatter('PropelSimpleArrayFormatter');
  493. // clear only the selectColumns, clearSelectColumns() clears asColumns too
  494. $this->selectColumns = array();
  495. // We need to set the primary table name, since in the case that there are no WHERE columns
  496. // it will be impossible for the BasePeer::createSelectSql() method to determine which
  497. // tables go into the FROM clause.
  498. if (!$this->selectQueries) {
  499. $this->setPrimaryTableName(constant($this->modelPeerName . '::TABLE_NAME'));
  500. }
  501. // Add requested columns which are not withColumns
  502. $columnNames = is_array($this->select) ? $this->select : array($this->select);
  503. foreach ($columnNames as $columnName) {
  504. // check if the column was added by a withColumn, if not add it
  505. if (!array_key_exists($columnName, $this->getAsColumns())) {
  506. $column = $this->getColumnFromName($columnName);
  507. // always put quotes around the columnName to be safe, we strip them in the formatter
  508. $this->addAsColumn('"' . $columnName . '"', $column[1]);
  509. }
  510. }
  511. }
  512. /**
  513. * This method returns the previousJoin for this ModelCriteria,
  514. * by default this is null, but after useQuery this is set the to the join of that use
  515. *
  516. * @return Join the previousJoin for this ModelCriteria
  517. */
  518. public function getPreviousJoin()
  519. {
  520. return $this->previousJoin;
  521. }
  522. /**
  523. * This method sets the previousJoin for this ModelCriteria,
  524. * by default this is null, but after useQuery this is set the to the join of that use
  525. *
  526. * @param Join $previousJoin The previousJoin for this ModelCriteria
  527. */
  528. public function setPreviousJoin(Join $previousJoin)
  529. {
  530. $this->previousJoin = $previousJoin;
  531. }
  532. /**
  533. * This method returns an already defined join clause from the query
  534. *
  535. * @param string $name The name of the join clause
  536. *
  537. * @return Join A join object
  538. */
  539. public function getJoin($name)
  540. {
  541. return $this->joins[$name];
  542. }
  543. /**
  544. * Adds a JOIN clause to the query
  545. * Infers the ON clause from a relation name
  546. * Uses the Propel table maps, based on the schema, to guess the related columns
  547. * Beware that the default JOIN operator is INNER JOIN, while Criteria defaults to WHERE
  548. * Examples:
  549. * <code>
  550. * $c->join('Book.Author');
  551. * => $c->addJoin(BookPeer::AUTHOR_ID, AuthorPeer::ID, Criteria::INNER_JOIN);
  552. * $c->join('Book.Author', Criteria::RIGHT_JOIN);
  553. * => $c->addJoin(BookPeer::AUTHOR_ID, AuthorPeer::ID, Criteria::RIGHT_JOIN);
  554. * $c->join('Book.Author a', Criteria::RIGHT_JOIN);
  555. * => $c->addAlias('a', AuthorPeer::TABLE_NAME);
  556. * => $c->addJoin(BookPeer::AUTHOR_ID, 'a.ID', Criteria::RIGHT_JOIN);
  557. * </code>
  558. *
  559. * @param string $relation Relation to use for the join
  560. * @param string $joinType Accepted values are null, 'left join', 'right join', 'inner join'
  561. *
  562. * @return ModelCriteria The current object, for fluid interface
  563. */
  564. public function join($relation, $joinType = Criteria::INNER_JOIN)
  565. {
  566. // relation looks like '$leftName.$relationName $relationAlias'
  567. list($fullName, $relationAlias) = self::getClassAndAlias($relation);
  568. if (strpos($fullName, '.') === false) {
  569. // simple relation name, refers to the current table
  570. $leftName = $this->getModelAliasOrName();
  571. $relationName = $fullName;
  572. $previousJoin = $this->getPreviousJoin();
  573. $tableMap = $this->getTableMap();
  574. } else {
  575. list($leftName, $relationName) = explode('.', $fullName);
  576. // find the TableMap for the left table using the $leftName
  577. if ($leftName == $this->getModelAliasOrName()) {
  578. $previousJoin = $this->getPreviousJoin();
  579. $tableMap = $this->getTableMap();
  580. } elseif (isset($this->joins[$leftName])) {
  581. $previousJoin = $this->joins[$leftName];
  582. $tableMap = $previousJoin->getTableMap();
  583. } else {
  584. throw new PropelException('Unknown table or alias ' . $leftName);
  585. }
  586. }
  587. $leftTableAlias = isset($this->aliases[$leftName]) ? $leftName : null;
  588. // find the RelationMap in the TableMap using the $relationName
  589. if(!$tableMap->hasRelation($relationName)) {
  590. throw new PropelException('Unknown relation ' . $relationName . ' on the ' . $leftName .' table');
  591. }
  592. $relationMap = $tableMap->getRelation($relationName);
  593. // create a ModelJoin object for this join
  594. $join = new ModelJoin();
  595. $join->setJoinType($joinType);
  596. if(null !== $previousJoin) {
  597. $join->setPreviousJoin($previousJoin);
  598. }
  599. $join->setRelationMap($relationMap, $leftTableAlias, $relationAlias);
  600. // add the ModelJoin to the current object
  601. if($relationAlias !== null) {
  602. $this->addAlias($relationAlias, $relationMap->getRightTable()->getName());
  603. $this->addJoinObject($join, $relationAlias);
  604. } else {
  605. $this->addJoinObject($join, $relationName);
  606. }
  607. return $this;
  608. }
  609. /**
  610. * Add another condition to an already added join
  611. * @example
  612. * <code>
  613. * $query->join('Book.Author');
  614. * $query->addJoinCondition('Author', 'Book.Title LIKE ?', 'foo%');
  615. * </code>
  616. *
  617. * @param string $name The relation name or alias on which the join was created
  618. * @param string $clause SQL clause, may contain column and table phpNames
  619. * @param mixed $value An optional value to bind to the clause
  620. * @param string $operator The operator to use to add the condition. Defaults to 'AND'
  621. *
  622. * @return ModelCriteria The current object, for fluid interface
  623. */
  624. public function addJoinCondition($name, $clause, $value = null, $operator = null)
  625. {
  626. if (!isset($this->joins[$name])) {
  627. throw new PropelException(sprintf('Adding a condition to a nonexistent join, %s. Try calling join() first.', $name));
  628. }
  629. $join = $this->joins[$name];
  630. if (!$join->getJoinCondition() instanceof Criterion) {
  631. $join->buildJoinCondition($this);
  632. }
  633. $criterion = $this->getCriterionForClause($clause, $value);
  634. $method = $operator === Criteria::LOGICAL_OR ? 'addOr' : 'addAnd';
  635. $join->getJoinCondition()->$method($criterion);
  636. return $this;
  637. }
  638. /**
  639. * Replace the condition of an already added join
  640. * @example
  641. * <code>
  642. * $query->join('Book.Author');
  643. * $query->condition('cond1', 'Book.AuthorId = Author.Id')
  644. * $query->condition('cond2', 'Book.Title LIKE ?', 'War%')
  645. * $query->combine(array('cond1', 'cond2'), 'and', 'cond3')
  646. * $query->setJoinCondition('Author', 'cond3');
  647. * </code>
  648. *
  649. * @param string $name The relation name or alias on which the join was created
  650. * @param mixed $condition A Criterion object, or a condition name
  651. *
  652. * @return ModelCriteria The current object, for fluid interface
  653. */
  654. public function setJoinCondition($name, $condition)
  655. {
  656. if (!isset($this->joins[$name])) {
  657. throw new PropelException(sprintf('Setting a condition to a nonexistent join, %s. Try calling join() first.', $name));
  658. }
  659. if ($condition instanceof Criterion) {
  660. $this->getJoin($name)->setJoinCondition($condition);
  661. } elseif (isset($this->namedCriterions[$condition])) {
  662. $this->getJoin($name)->setJoinCondition($this->namedCriterions[$condition]);
  663. } else {
  664. throw new PropelException(sprintf('Cannot add condition %s on join %s. setJoinCondition() expects either a Criterion, or a condition added by way of condition()', $condition, $name));
  665. }
  666. return $this;
  667. }
  668. /**
  669. * Add a join object to the Criteria
  670. * @see Criteria::addJoinObject()
  671. * @param Join $join A join object
  672. *
  673. * @return ModelCriteria The current object, for fluid interface
  674. */
  675. public function addJoinObject(Join $join, $name = null)
  676. {
  677. if (!in_array($join, $this->joins)) { // compare equality, NOT identity
  678. if (null === $name) {
  679. $this->joins[] = $join;
  680. } else {
  681. $this->joins[$name] = $join;
  682. }
  683. }
  684. return $this;
  685. }
  686. /**
  687. * Adds a JOIN clause to the query and hydrates the related objects
  688. * Shortcut for $c->join()->with()
  689. * <code>
  690. * $c->joinWith('Book.Author');
  691. * => $c->join('Book.Author');
  692. * => $c->with('Author');
  693. * $c->joinWith('Book.Author a', Criteria::RIGHT_JOIN);
  694. * => $c->join('Book.Author a', Criteria::RIGHT_JOIN);
  695. * => $c->with('a');
  696. * </code>
  697. *
  698. * @param string $relation Relation to use for the join
  699. * @param string $joinType Accepted values are null, 'left join', 'right join', 'inner join'
  700. *
  701. * @return ModelCriteria The current object, for fluid interface
  702. */
  703. public function joinWith($relation, $joinType = Criteria::INNER_JOIN)
  704. {
  705. $this->join($relation, $joinType);
  706. $this->with(self::getRelationName($relation));
  707. return $this;
  708. }
  709. /**
  710. * Adds a relation to hydrate together with the main object
  711. * The relation must be initialized via a join() prior to calling with()
  712. * Examples:
  713. * <code>
  714. * $c->join('Book.Author');
  715. * $c->with('Author');
  716. *
  717. * $c->join('Book.Author a', Criteria::RIGHT_JOIN);
  718. * $c->with('a');
  719. * </code>
  720. * WARNING: on a one-to-many relationship, the use of with() combined with limit()
  721. * will return a wrong number of results for the related objects
  722. *
  723. * @param string $relation Relation to use for the join
  724. *
  725. * @return ModelCriteria The current object, for fluid interface
  726. */
  727. public function with($relation)
  728. {
  729. if (!isset($this->joins[$relation])) {
  730. throw new PropelException('Unknown relation name or alias ' . $relation);
  731. }
  732. $join = $this->joins[$relation];
  733. if ($join->getRelationMap()->getType() == RelationMap::MANY_TO_MANY) {
  734. throw new PropelException('with() does not allow hydration for many-to-many relationships');
  735. } elseif ($join->getRelationMap()->getType() == RelationMap::ONE_TO_MANY) {
  736. // For performance reasons, the formatters will use a special routine in this case
  737. $this->isWithOneToMany = true;
  738. }
  739. // check that the columns of the main class are already added (but only if this isn't a useQuery)
  740. if (!$this->hasSelectClause() && !$this->getPrimaryCriteria()) {
  741. $this->addSelfSelectColumns();
  742. }
  743. // add the columns of the related class
  744. $this->addRelationSelectColumns($relation);
  745. // list the join for later hydration in the formatter
  746. $this->with[$relation] = new ModelWith($join);
  747. return $this;
  748. }
  749. /**
  750. * Gets the array of ModelWith specifying which objects must be hydrated
  751. * together with the main object.
  752. *
  753. * @see with()
  754. * @return array
  755. */
  756. public function getWith()
  757. {
  758. return $this->with;
  759. }
  760. /**
  761. * Sets the array of ModelWith specifying which objects must be hydrated
  762. * together with the main object.
  763. *
  764. * @param array
  765. *
  766. * @return ModelCriteria The current object, for fluid interface
  767. */
  768. public function setWith($with)
  769. {
  770. $this->with = $with;
  771. return $this;
  772. }
  773. public function isWithOneToMany()
  774. {
  775. return $this->isWithOneToMany;
  776. }
  777. /**
  778. * Adds a supplementary column to the select clause
  779. * These columns can later be retrieved from the hydrated objects using getVirtualColumn()
  780. *
  781. * @param string $clause The SQL clause with object model column names
  782. * e.g. 'UPPER(Author.FirstName)'
  783. * @param string $name Optional alias for the added column
  784. * If no alias is provided, the clause is used as a column alias
  785. * This alias is used for retrieving the column via BaseObject::getVirtualColumn($alias)
  786. *
  787. * @return ModelCriteria The current object, for fluid interface
  788. */
  789. public function withColumn($clause, $name = null)
  790. {
  791. if (null === $name) {
  792. $name = str_replace(array('.', '(', ')'), '', $clause);
  793. }
  794. $clause = trim($clause);
  795. $this->replaceNames($clause);
  796. // check that the columns of the main class are already added (if this is the primary ModelCriteria)
  797. if (!$this->hasSelectClause() && !$this->getPrimaryCriteria()) {
  798. $this->addSelfSelectColumns();
  799. }
  800. $this->addAsColumn($name, $clause);
  801. return $this;
  802. }
  803. /**
  804. * Initializes a secondary ModelCriteria object, to be later merged with the current object
  805. *
  806. * @see ModelCriteria::endUse()
  807. * @param string $relationName Relation name or alias
  808. * @param string $secondCriteriaClass Classname for the ModelCriteria to be used
  809. *
  810. * @return ModelCriteria The secondary criteria object
  811. */
  812. public function useQuery($relationName, $secondaryCriteriaClass = null)
  813. {
  814. if (!isset($this->joins[$relationName])) {
  815. throw new PropelException('Unknown class or alias ' . $relationName);
  816. }
  817. $className = $this->joins[$relationName]->getTableMap()->getPhpName();
  818. if (null === $secondaryCriteriaClass) {
  819. $secondaryCriteria = PropelQuery::from($className);
  820. } else {
  821. $secondaryCriteria = new $secondaryCriteriaClass();
  822. }
  823. if ($className != $relationName) {
  824. $secondaryCriteria->setModelAlias($relationName, $relationName == $this->joins[$relationName]->getRelationMap()->getName() ? false : true);
  825. }
  826. $secondaryCriteria->setPrimaryCriteria($this, $this->joins[$relationName]);
  827. return $secondaryCriteria;
  828. }
  829. /**
  830. * Finalizes a secondary criteria and merges it with its primary Criteria
  831. *
  832. * @see Criteria::mergeWith()
  833. *
  834. * @return ModelCriteria The primary criteria object
  835. */
  836. public function endUse()
  837. {
  838. if (isset($this->aliases[$this->modelAlias])) {
  839. $this->removeAlias($this->modelAlias);
  840. }
  841. $primaryCriteria = $this->getPrimaryCriteria();
  842. $primaryCriteria->mergeWith($this);
  843. return $primaryCriteria;
  844. }
  845. /**
  846. * Add the content of a Criteria to the current Criteria
  847. * In case of conflict, the current Criteria keeps its properties
  848. * @see Criteria::mergeWith()
  849. *
  850. * @param Criteria $criteria The criteria to read properties from
  851. * @param string $operator The logical operator used to combine conditions
  852. * Defaults to Criteria::LOGICAL_AND, also accapts Criteria::LOGICAL_OR
  853. *
  854. * @return ModelCriteria The primary criteria object
  855. */
  856. public function mergeWith(Criteria $criteria, $operator = null)
  857. {
  858. parent::mergeWith($criteria, $operator);
  859. // merge with
  860. if ($criteria instanceof ModelCriteria) {
  861. $this->with = array_merge($this->getWith(), $criteria->getWith());
  862. }
  863. return $this;
  864. }
  865. /**
  866. * Clear the conditions to allow the reuse of the query object.
  867. * The ModelCriteria's Model and alias 'all the properties set by construct) will remain.
  868. *
  869. * @return ModelCriteria The primary criteria object
  870. */
  871. public function clear()
  872. {
  873. parent::clear();
  874. $this->with = array();
  875. $this->primaryCriteria = null;
  876. $this->formatter=null;
  877. return $this;
  878. }
  879. /**
  880. * Sets the primary Criteria for this secondary Criteria
  881. *
  882. * @param ModelCriteria $criteria The primary criteria
  883. * @param Join $previousJoin The previousJoin for this ModelCriteria
  884. */
  885. public function setPrimaryCriteria(ModelCriteria $criteria, Join $previousJoin)
  886. {
  887. $this->primaryCriteria = $criteria;
  888. $this->setPreviousJoin($previousJoin);
  889. }
  890. /**
  891. * Gets the primary criteria for this secondary Criteria
  892. *
  893. * @return ModelCriteria The primary criteria
  894. */
  895. public function getPrimaryCriteria()
  896. {
  897. return $this->primaryCriteria;
  898. }
  899. /**
  900. * Adds a Criteria as subQuery in the From Clause.
  901. *
  902. * @see Criteria::addSelectQuery()
  903. *
  904. * @param Criteria $subQueryCriteria Criteria to build the subquery from
  905. * @param string $alias alias for the subQuery
  906. * @param boolean $addAliasAndSelectColumns Set to false if you want to manually add the aliased select columns
  907. *
  908. * @return ModelCriteria The current object, for fluid interface
  909. */
  910. public function addSelectQuery(Criteria $subQueryCriteria, $alias = null, $addAliasAndSelectColumns = true)
  911. {
  912. if (!$subQueryCriteria->hasSelectClause()) {
  913. $subQueryCriteria->addSelfSelectColumns();
  914. }
  915. parent::addSelectQuery($subQueryCriteria, $alias);
  916. if ($addAliasAndSelectColumns) {
  917. // give this query-model same alias as subquery
  918. if (null === $alias) {
  919. end($this->selectQueries);
  920. $alias = key($this->selectQueries);
  921. }
  922. $this->setModelAlias($alias, true);
  923. // so we can add selfSelectColumns
  924. $this->addSelfSelectColumns();
  925. }
  926. return $this;
  927. }
  928. /**
  929. * Adds the select columns for a the current table
  930. *
  931. * @return ModelCriteria The current object, for fluid interface
  932. */
  933. public function addSelfSelectColumns()
  934. {
  935. call_user_func(array($this->modelPeerName, 'addSelectColumns'), $this, $this->useAliasInSQL ? $this->modelAlias : null);
  936. return $this;
  937. }
  938. /**
  939. * Adds the select columns for a relation
  940. *
  941. * @param string $relation The relation name or alias, as defined in join()
  942. *
  943. * @return ModelCriteria The current object, for fluid interface
  944. */
  945. public function addRelationSelectColumns($relation)
  946. {
  947. $join = $this->joins[$relation];
  948. call_user_func(array($join->getTableMap()->getPeerClassname(), 'addSelectColumns'), $this, $join->getRelationAlias());
  949. return $this;
  950. }
  951. /**
  952. * Returns the class and alias of a string representing a model or a relation
  953. * e.g. 'Book b' => array('Book', 'b')
  954. * e.g. 'Book' => array('Book', null)
  955. *
  956. * @param string $class The classname to explode
  957. *
  958. * @return array list($className, $aliasName)
  959. */
  960. public static function getClassAndAlias($class)
  961. {
  962. if(strpos($class, ' ') !== false) {
  963. list($class, $alias) = explode(' ', $class);
  964. } else {
  965. $alias = null;
  966. }
  967. return array($class, $alias);
  968. }
  969. /**
  970. * Returns the name of a relation from a string.
  971. * The input looks like '$leftName.$relationName $relationAlias'
  972. *
  973. * @param string $relation Relation to use for the join
  974. * @return string the relationName used in the join
  975. */
  976. public static function getRelationName($relation)
  977. {
  978. // get the relationName
  979. list($fullName, $relationAlias) = self::getClassAndAlias($relation);
  980. if ($relationAlias) {
  981. $relationName = $relationAlias;
  982. } elseif (false === strpos($fullName, '.')) {
  983. $relationName = $fullName;
  984. } else {
  985. list($leftName, $relationName) = explode('.', $fullName);
  986. }
  987. return $relationName;
  988. }
  989. /**
  990. * Triggers the automated cloning on termination.
  991. * By default, temrination methods don't clone the current object,
  992. * even though they modify it. If the query must be reused after termination,
  993. * you must call this method prior to temrination.
  994. *
  995. * @param boolean $isKeepQuery
  996. *
  997. * @return ModelCriteria The current object, for fluid interface
  998. */
  999. public function keepQuery($isKeepQuery = true)
  1000. {
  1001. $this->isKeepQuery = (bool) $isKeepQuery;
  1002. return $this;
  1003. }
  1004. /**
  1005. * Checks whether the automated cloning on termination is enabled.
  1006. *
  1007. * @return boolean true if cloning must be done before termination
  1008. */
  1009. public function isKeepQuery()
  1010. {
  1011. return $this->isKeepQuery;
  1012. }
  1013. /**
  1014. * Code to execute before every SELECT statement
  1015. *
  1016. * @param PropelPDO $con The connection object used by the query
  1017. */
  1018. protected function basePreSelect(PropelPDO $con)
  1019. {
  1020. return $this->preSelect($con);
  1021. }
  1022. protected function preSelect(PropelPDO $con)
  1023. {
  1024. }
  1025. /**
  1026. * Issue a SELECT query based on the current ModelCriteria
  1027. * and format the list of results with the current formatter
  1028. * By default, returns an array of model objects
  1029. *
  1030. * @param PropelPDO $con an optional connection object
  1031. *
  1032. * @return PropelObjectCollection|array|mixed the list of results, formatted by the current formatter
  1033. */
  1034. public function find($con = null)
  1035. {
  1036. $criteria = $this->isKeepQuery() ? clone $this : $this;
  1037. $stmt = $criteria->getSelectStatement($con);
  1038. return $criteria->getFormatter()->init($criteria)->format($stmt);
  1039. }
  1040. /**
  1041. * Issue a SELECT ... LIMIT 1 query based on the current ModelCriteria
  1042. * and format the result with the current formatter
  1043. * By default, returns a model object
  1044. *
  1045. * @param PropelPDO $con an optional connection object
  1046. *
  1047. * @return mixed the result, formatted by the current formatter
  1048. */
  1049. public function findOne($con = null)
  1050. {
  1051. $criteria = $this->isKeepQuery() ? clone $this : $this;
  1052. $criteria->limit(1);
  1053. $stmt = $criteria->getSelectStatement($con);
  1054. return $criteria->getFormatter()->init($criteria)->formatOne($stmt);
  1055. }
  1056. /**
  1057. * Issue a SELECT ... LIMIT 1 query based on the current ModelCriteria
  1058. * and format the result with the current formatter
  1059. * By default, returns a model object
  1060. *
  1061. * @param PropelPDO $con an optional connection object
  1062. *
  1063. * @return mixed the result, formatted by the current formatter
  1064. */
  1065. public function findOneOrCreate($con = null)
  1066. {
  1067. $criteria = $this->isKeepQuery() ? clone $this : $this;
  1068. $criteria->limit(1);
  1069. if (!$ret = $criteria->findOne($con)) {
  1070. $class = $this->getModelName();
  1071. $obj = new $class();
  1072. foreach ($this->keys() as $key) {
  1073. $obj->setByName($key, $this->getValue($key), BasePeer::TYPE_COLNAME);
  1074. }
  1075. $ret = $this->getFormatter()->formatRecord($obj);
  1076. }
  1077. return $ret;
  1078. }
  1079. /**
  1080. * Find object by primary key
  1081. * Behaves differently if the model has simple or composite primary key
  1082. * <code>
  1083. * // simple primary key
  1084. * $book = $c->findPk(12, $con);
  1085. * // composite primary key
  1086. * $bookOpinion = $c->findPk(array(34, 634), $con);
  1087. * </code>
  1088. * @param mixed $key Primary key to use for the query
  1089. * @param PropelPDO $con an optional connection object
  1090. *
  1091. * @return mixed the result, formatted by the current formatter
  1092. */
  1093. public function findPk($key, $con = null)
  1094. {
  1095. $pkCols = $this->getTableMap()->getPrimaryKeyColumns();
  1096. if (count($pkCols) == 1) {
  1097. // simple primary key
  1098. $pkCol = $pkCols[0];
  1099. $this->add($pkCol->getFullyQualifiedName(), $key);
  1100. return $this->findOne($con);
  1101. } else {
  1102. // composite primary key
  1103. foreach ($pkCols as $pkCol) {
  1104. $keyPart = array_shift($key);
  1105. $this->add($pkCol->getFullyQualifiedName(), $keyPart);
  1106. }
  1107. return $this->findOne($con);
  1108. }
  1109. }
  1110. /**
  1111. * Find objects by primary key
  1112. * Behaves differently if the model has simple or composite primary key
  1113. * <code>
  1114. * // simple primary key
  1115. * $books = $c->findPks(array(12, 56, 832), $con);
  1116. * // composite primary key
  1117. * $bookOpinion = $c->findPks(array(array(34, 634), array(45, 518), array(34, 765)), $con);
  1118. * </code>
  1119. * @param array $keys Primary keys to use for the query
  1120. * @param PropelPDO $con an optional connection object
  1121. *
  1122. * @return mixed the list of results, formatted by the current formatter
  1123. */
  1124. public function findPks($keys, $con = null)
  1125. {
  1126. $pkCols = $this->getTableMap()->getPrimaryKeyColumns();
  1127. if (count($pkCols) == 1) {
  1128. // simple primary key
  1129. $pkCol = array_shift($pkCols);
  1130. $this->add($pkCol->getFullyQualifiedName(), $keys, Criteria::IN);
  1131. } else {
  1132. // composite primary key
  1133. throw new PropelException('Multiple object retrieval is not implemented for composite primary keys');
  1134. }
  1135. return $this->find($con);
  1136. }
  1137. protected function getSelectStatement($con = null)
  1138. {
  1139. $dbMap = Propel::getDatabaseMap($this->getDbName());
  1140. $db = Propel::getDB($this->getDbName());
  1141. if ($con === null) {
  1142. $con = Propel::getConnection($this->getDbName(), Propel::CONNECTION_READ);
  1143. }
  1144. // check that the columns of the main class are already added (if this is the primary ModelCriteria)
  1145. if (!$this->hasSelectClause() && !$this->getPrimaryCriteria()) {
  1146. $this->addSelfSelectColumns();
  1147. }
  1148. $this->configureSelectColumns();
  1149. try {
  1150. $this->basePreSelect($con);
  1151. $params = array();
  1152. $sql = BasePeer::createSelectSql($this, $params);
  1153. $stmt = $con->prepare($sql);
  1154. $db->bindValues($stmt, $params, $dbMap);
  1155. $stmt->execute();
  1156. } catch (Exception $e) {
  1157. if (isset($stmt)) {
  1158. $stmt = null; // close
  1159. }
  1160. Propel::log($e->getMessage(), Propel::LOG_ERR);
  1161. throw new PropelException(sprintf('Unable to execute SELECT statement [%s]', $sql), $e);
  1162. }
  1163. return $stmt;
  1164. }
  1165. /**
  1166. * Apply a condition on a column and issues the SELECT query
  1167. *
  1168. * @see filterBy()
  1169. * @see find()
  1170. *
  1171. * @param string $column A string representing the column phpName, e.g. 'AuthorId'
  1172. * @param mixed $value A value for the condition
  1173. * @param PropelPDO $con An optional connection object
  1174. *
  1175. * @return mixed the list of results, formatted by the current formatter
  1176. */
  1177. public function findBy($column, $value, $con = null)
  1178. {
  1179. $method = 'filterBy' . $column;
  1180. $this->$method($value);
  1181. return $this->find($con);
  1182. }
  1183. /**
  1184. * Apply a list of conditions on columns and issues the SELECT query
  1185. * <code>
  1186. * $c->findByArray(array(
  1187. * 'Title' => 'War And Peace',
  1188. * 'Publisher' => $publisher
  1189. * ), $con);
  1190. * </code>
  1191. *
  1192. * @see filterByArray()
  1193. * @see find()
  1194. *
  1195. * @param mixed $conditions An array of conditions, using column phpNames as key
  1196. * @param PropelPDO $con an optional connection object
  1197. *
  1198. * @return mixed the list of results, formatted by the current formatter
  1199. */
  1200. public function findByArray($conditions, $con = null)
  1201. {
  1202. $this->filterByArray($conditions);
  1203. return $this->find($con);
  1204. }
  1205. /**
  1206. * Apply a condition on a column and issues the SELECT ... LIMIT 1 query
  1207. *
  1208. * @see filterBy()
  1209. * @see findOne()
  1210. *
  1211. * @param mixed $column A string representing thecolumn phpName, e.g. 'AuthorId'
  1212. * @param mixed $value A value for the condition
  1213. * @param PropelPDO $con an optional connection object
  1214. *
  1215. * @return mixed the result, formatted by the current formatter
  1216. */
  1217. public function findOneBy($column, $value, $con = null)
  1218. {
  1219. $method = 'filterBy' . $column;
  1220. $this->$method($value);
  1221. return $this->findOne($con);
  1222. }
  1223. /**
  1224. * Apply a list of conditions on columns and issues the SELECT ... LIMIT 1 query
  1225. * <code>
  1226. * $c->findOneByArray(array(
  1227. * 'Title' => 'War And Peace',
  1228. * 'Publisher' => $publisher
  1229. * ), $con);
  1230. * </code>
  1231. *
  1232. * @see filterByArray()
  1233. * @see findOne()
  1234. *
  1235. * @param mixed $conditions An array of conditions, using column phpNames as key
  1236. * @param PropelPDO $con an optional connection object
  1237. *
  1238. * @return mixed the list of results, formatted by the current formatter
  1239. */
  1240. public function findOneByArray($conditions, $con = null)
  1241. {
  1242. $this->filterByArray($conditions);
  1243. return $this->findOne($con);
  1244. }
  1245. /**
  1246. * Issue a SELECT COUNT(*) query based on the current ModelCriteria
  1247. *
  1248. * @param PropelPDO $con an optional connection object
  1249. *
  1250. * @return integer the number of results
  1251. */
  1252. public function count($con = null)
  1253. {
  1254. if ($con === null) {
  1255. $con = Propel::getConnection($this->getDbName(), Propel::CONNECTION_READ);
  1256. }
  1257. $criteria = $this->isKeepQuery() ? clone $this : $this;
  1258. $criteria->setDbName($this->getDbName()); // Set the correct dbName
  1259. $criteria->clearOrderByColumns(); // ORDER BY won't ever affect the count
  1260. // We need to set the primary table name, since in the case that there are no WHERE columns
  1261. // it will be impossible for the BasePeer::createSelectSql() method to determine which
  1262. // tables go into the FROM clause.
  1263. $criteria->setPrimaryTableName(constant($this->modelPeerName.'::TABLE_NAME'));
  1264. $stmt = $criteria->getCountStatement($con);
  1265. if ($row = $stmt->fetch(PDO::FETCH_NUM)) {
  1266. $count = (int) $row[0];
  1267. } else {
  1268. $count = 0; // no rows returned; we infer that means 0 matches.
  1269. }
  1270. $stmt->closeCursor();
  1271. return $count;
  1272. }
  1273. protected function getCountStatement($con = null)
  1274. {
  1275. $dbMap = Propel::getDatabaseMap($this->getDbName());
  1276. $db = Propel::getDB($this->getDbName());
  1277. if ($con === null) {
  1278. $con = Propel::getConnection($this->getDbName(), Propel::CONNECTION_READ);
  1279. }
  1280. // check that the columns of the main class are already added (if this is the primary ModelCriteria)
  1281. if (!$this->hasSelectClause() && !$this->getPrimaryCriteria()) {
  1282. $this->addSelfSelectColumns();
  1283. }
  1284. $this->configureSelectColumns();
  1285. $needsComplexCount = $this->getGroupByColumns()
  1286. || $this->getOffset()
  1287. || $this->getLimit()
  1288. || $this->getHaving()
  1289. || in_array(Criteria::DISTINCT, $this->getSelectModifiers());
  1290. try {
  1291. $this->basePreSelect($con);
  1292. $params = array();
  1293. if ($needsComplexCount) {
  1294. if (BasePeer::needsSelectAliases($this)) {
  1295. if ($this->getHaving()) {
  1296. throw new PropelException('Propel cannot create a COUNT query when using HAVING and duplicate column names in the SELECT part');
  1297. }
  1298. $db->turnSelectColumnsToAliases($this);
  1299. }
  1300. $selectSql = BasePeer::createSelectSql($this, $params);
  1301. $sql = 'SELECT COUNT(*) FROM (' . $selectSql . ') propelmatch4cnt';
  1302. } else {
  1303. // Replace SELECT columns with COUNT(*)
  1304. $this->clearSelectColumns()->addSelectColumn('COUNT(*)');
  1305. $sql = BasePeer::createSelectSql($this, $params);
  1306. }
  1307. $stmt = $con->prepare($sql);
  1308. $db->bindValues($stmt, $params, $dbMap);
  1309. $stmt->execute();
  1310. } catch (PropelException $e) {
  1311. if ($stmt) {
  1312. $stmt = null; // close
  1313. }
  1314. Propel::log($e->getMessage(), Propel::LOG_ERR);
  1315. throw new PropelException(sprintf('Unable to execute COUNT statement [%s]', $sql), $e);
  1316. }
  1317. return $stmt;
  1318. }
  1319. /**
  1320. * Issue a SELECT query based on the current ModelCriteria
  1321. * and uses a page and a maximum number of results per page
  1322. * to compute an offet and a limit.
  1323. *
  1324. * @param int $page number of the page to start the pager on. Page 1 means no offset
  1325. * @param int $maxPerPage maximum number of results per page. Determines the limit
  1326. * @param PropelPDO $con an optional connection object
  1327. *
  1328. * @return PropelModelPager a pager object, supporting iteration
  1329. */
  1330. public function paginate($page = 1, $maxPerPage = 10, $con = null)
  1331. {
  1332. $criteria = $this->isKeepQuery() ? clone $this : $this;
  1333. $pager = new PropelModelPager($criteria, $maxPerPage);
  1334. $pager->setPage($page);
  1335. $pager->init($con);
  1336. return $pager;
  1337. }
  1338. /**
  1339. * Code to execute before every DELETE statement
  1340. *
  1341. * @param PropelPDO $con The connection object used by the query
  1342. */
  1343. protected function basePreDelete(PropelPDO $con)
  1344. {
  1345. return $this->preDelete($con);
  1346. }
  1347. protected function preDelete(PropelPDO $con)
  1348. {
  1349. }
  1350. /**
  1351. * Code to execute after every DELETE statement
  1352. *
  1353. * @param int $affectedRows the number of deleted rows
  1354. * @param PropelPDO $con The connection object used by the query
  1355. */
  1356. protected function basePostDelete($affectedRows, PropelPDO $con)
  1357. {
  1358. return $this->postDelete($affectedRows, $con);
  1359. }
  1360. protected function postDelete($affectedRows, PropelPDO $con)
  1361. {
  1362. }
  1363. /**
  1364. * Issue a DELETE query based on the current ModelCriteria
  1365. * An optional hook on basePreDelete() can prevent the actual deletion
  1366. *
  1367. * @param PropelPDO $con an optional connection object
  1368. *
  1369. * @return integer the number of deleted rows
  1370. */
  1371. public function delete($con = null)
  1372. {
  1373. if (count($this->getMap()) == 0) {
  1374. throw new PropelException('delete() expects a Criteria with at least one condition. Use deleteAll() to delete all the rows of a table');
  1375. }
  1376. if ($con === null) {
  1377. $con = Propel::getConnection($this->getDbName(), Propel::CONNECTION_WRITE);
  1378. }
  1379. $criteria = $this->isKeepQuery() ? clone $this : $this;
  1380. $criteria->setDbName($this->getDbName());
  1381. $con->beginTransaction();
  1382. try {
  1383. if(!$affectedRows = $criteria->basePreDelete($con)) {
  1384. $affectedRows = $criteria->doDelete($con);
  1385. }
  1386. $criteria->basePostDelete($affectedRows, $con);
  1387. $con->commit();
  1388. } catch (PropelException $e) {
  1389. $con->rollback();
  1390. throw $e;
  1391. }
  1392. return $affectedRows;
  1393. }
  1394. /**
  1395. * Issue a DELETE query based on the current ModelCriteria
  1396. * This method is called by ModelCriteria::delete() inside a transaction
  1397. *
  1398. * @param PropelPDO $con a connection object
  1399. *
  1400. * @return integer the number of deleted rows
  1401. */
  1402. public function doDelete($con)
  1403. {
  1404. $affectedRows = call_user_func(array($this->modelPeerName, 'doDelete'), $this, $con);
  1405. return $affectedRows;
  1406. }
  1407. /**
  1408. * Issue a DELETE query based on the current ModelCriteria deleting all rows in the table
  1409. * An optional hook on basePreDelete() can prevent the actual deletion
  1410. *
  1411. * @param PropelPDO $con an optional connection object
  1412. *
  1413. * @return integer the number of deleted rows
  1414. */
  1415. public function deleteAll($con = null)
  1416. {
  1417. if ($con === null) {
  1418. $con = Propel::getConnection($this->getDbName(), Propel::CONNECTION_WRITE);
  1419. }
  1420. $con->beginTransaction();
  1421. try {
  1422. if(!$affectedRows = $this->basePreDelete($con)) {
  1423. $affectedRows = $this->doDeleteAll($con);
  1424. }
  1425. $this->basePostDelete($affectedRows, $con);
  1426. $con->commit();
  1427. return $affectedRows;
  1428. } catch (PropelException $e) {
  1429. $con->rollBack();
  1430. throw $e;
  1431. }
  1432. return $affectedRows;
  1433. }
  1434. /**
  1435. * Issue a DELETE query based on the current ModelCriteria deleting all rows in the table
  1436. * This method is called by ModelCriteria::deleteAll() inside a transaction
  1437. *
  1438. * @param PropelPDO $con a connection object
  1439. *
  1440. * @return integer the number of deleted rows
  1441. */
  1442. public function doDeleteAll($con)
  1443. {
  1444. $affectedRows = call_user_func(array($this->modelPeerName, 'doDeleteAll'), $con);
  1445. return $affectedRows;
  1446. }
  1447. /**
  1448. * Code to execute before every UPDATE statement
  1449. *
  1450. * @param array $values The associatiove array of columns and values for the update
  1451. * @param PropelPDO $con The connection object used by the query
  1452. * @param boolean $forceIndividualSaves If false (default), the resulting call is a BasePeer::doUpdate(), ortherwise it is a series of save() calls on all the found objects
  1453. */
  1454. protected function basePreUpdate(&$values, PropelPDO $con, $forceIndividualSaves = false)
  1455. {
  1456. return $this->preUpdate($values, $con, $forceIndividualSaves);
  1457. }
  1458. protected function preUpdate(&$values, PropelPDO $con, $forceIndividualSaves = false)
  1459. {
  1460. }
  1461. /**
  1462. * Code to execute after every UPDATE statement
  1463. *
  1464. * @param int $affectedRows the number of updated rows
  1465. * @param PropelPDO $con The connection object used by the query
  1466. */
  1467. protected function basePostUpdate($affectedRows, PropelPDO