propel-util
[ class tree: propel-util ] [ index: propel-util ] [ all elements ]

Source for file BasePeer.php

Documentation is available at BasePeer.php

  1. <?php
  2. /*
  3.  *  $Id: BasePeer.php 821 2007-11-20 12:47:17Z hans $
  4.  *
  5.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  6.  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  7.  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  8.  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  9.  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  10.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  11.  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  12.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  13.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  14.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  15.  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  16.  *
  17.  * This software consists of voluntary contributions made by many individuals
  18.  * and is licensed under the LGPL. For more information please see
  19.  * <http://propel.phpdb.org>.
  20.  */
  21.  
  22. /**
  23.  * This is a utility class for all generated Peer classes in the system.
  24.  *
  25.  * Peer classes are responsible for isolating all of the database access
  26.  * for a specific business object.  They execute all of the SQL
  27.  * against the database.  Over time this class has grown to include
  28.  * utility methods which ease execution of cross-database queries and
  29.  * the implementation of concrete Peers.
  30.  *
  31.  * @author     Hans Lellelid <hans@xmpl.org> (Propel)
  32.  * @author     Kaspars Jaudzems <kaspars.jaudzems@inbox.lv> (Propel)
  33.  * @author     Frank Y. Kim <frank.kim@clearink.com> (Torque)
  34.  * @author     John D. McNally <jmcnally@collab.net> (Torque)
  35.  * @author     Brett McLaughlin <bmclaugh@algx.net> (Torque)
  36.  * @author     Stephen Haberman <stephenh@chase3000.com> (Torque)
  37.  * @version    $Revision: 821 $
  38.  * @package    propel.util
  39.  */
  40. class BasePeer
  41. {
  42.  
  43.     /** Array (hash) that contains the cached mapBuilders. */
  44.     private static $mapBuilders array();
  45.  
  46.     /** Array (hash) that contains cached validators */
  47.     private static $validatorMap array();
  48.  
  49.     /**
  50.      * phpname type
  51.      * e.g. 'AuthorId'
  52.      */
  53.     const TYPE_PHPNAME 'phpName';
  54.  
  55.     /**
  56.      * studlyphpname type
  57.      * e.g. 'authorId'
  58.      */
  59.     const TYPE_STUDLYPHPNAME 'studlyPhpName';
  60.  
  61.     /**
  62.      * column (peer) name type
  63.      * e.g. 'book.AUTHOR_ID'
  64.      */
  65.     const TYPE_COLNAME 'colName';
  66.  
  67.     /**
  68.      * column fieldname type
  69.      * e.g. 'author_id'
  70.      */
  71.     const TYPE_FIELDNAME 'fieldName';
  72.  
  73.     /**
  74.      * num type
  75.      * simply the numerical array index, e.g. 4
  76.      */
  77.     const TYPE_NUM 'num';
  78.  
  79.     static public function getFieldnames ($classname$type self::TYPE_PHPNAME{
  80.  
  81.         // TODO we should take care of including the peer class here
  82.  
  83.         $peerclass 'Base' $classname 'Peer'// TODO is this always true?
  84.         $callable array($peerclass'getFieldnames');
  85.         $args array($type);
  86.  
  87.         return call_user_func_array($callable$args);
  88.     }
  89.  
  90.     static public function translateFieldname($classname$fieldname$fromType$toType{
  91.  
  92.         // TODO we should take care of including the peer class here
  93.  
  94.         $peerclass 'Base' $classname 'Peer'// TODO is this always true?
  95.         $callable array($peerclass'translateFieldname');
  96.         $args array($fieldname$fromType$toType);
  97.  
  98.         return call_user_func_array($callable$args);
  99.     }
  100.  
  101.     /**
  102.      * Method to perform deletes based on values and keys in a
  103.      * Criteria.
  104.      *
  105.      * @param      Criteria $criteria The criteria to use.
  106.      * @param      PDO $con A PDO connection object.
  107.      * @return     int    The number of rows affected by last statement execution.  For most
  108.      *                  uses there is only one delete statement executed, so this number
  109.      *                  will correspond to the number of rows affected by the call to this
  110.      *                  method.  Note that the return value does require that this information
  111.      *                  is returned (supported) by the PDO driver.
  112.      * @throws     PropelException
  113.      */
  114.     public static function doDelete(Criteria $criteriaPDO $con)
  115.     {
  116.         $db Propel::getDB($criteria->getDbName());
  117.         $dbMap Propel::getDatabaseMap($criteria->getDbName());
  118.  
  119.         // Set up a list of required tables (one DELETE statement will
  120.         // be executed per table)
  121.  
  122.         $tables_keys array();
  123.         foreach ($criteria as $c{
  124.             foreach ($c->getAllTables(as $tableName{
  125.                 $tableName2 $criteria->getTableForAlias($tableName);
  126.                 if ($tableName2 !== null{
  127.                     $tables_keys[$tableName2 ' ' $tableNametrue;
  128.                 else {
  129.                     $tables_keys[$tableNametrue;
  130.                 }
  131.             }
  132.         // foreach criteria->keys()
  133.  
  134.         $affectedRows 0// initialize this in case the next loop has no iterations.
  135.  
  136.         $tables array_keys($tables_keys);
  137.  
  138.         foreach ($tables as $tableName{
  139.  
  140.             $whereClause array();
  141.             $selectParams array();
  142.             foreach ($dbMap->getTable($tableName)->getColumns(as $colMap{
  143.                 $key $tableName '.' $colMap->getColumnName();
  144.                 if ($criteria->containsKey($key)) {
  145.                     $sb "";
  146.                     $criteria->getCriterion($key)->appendPsTo($sb$selectParams);
  147.                     $whereClause[$sb;
  148.                 }
  149.             }
  150.  
  151.             if (empty($whereClause)) {
  152.                 throw new PropelException("Cowardly refusing to delete from table $tableName with empty WHERE clause.");
  153.             }
  154.  
  155.             // Execute the statement.
  156.             try {
  157.                 $sql "DELETE FROM " $tableName " WHERE " .  implode(" AND "$whereClause);
  158.                 Propel::log($sqlPropel::LOG_DEBUG);
  159.                 $stmt $con->prepare($sql);
  160.                 self::populateStmtValues($stmt$selectParams$dbMap$db);
  161.                 $stmt->execute();
  162.                 $affectedRows $stmt->rowCount();
  163.             catch (Exception $e{
  164.                 Propel::log($e->getMessage()Propel::LOG_ERR);
  165.                 throw new PropelException("Unable to execute DELETE statement.",$e);
  166.             }
  167.  
  168.         // for each table
  169.  
  170.         return $affectedRows;
  171.     }
  172.  
  173.     /**
  174.      * Method to deletes all contents of specified table.
  175.      *
  176.      * This method is invoked from generated Peer classes like this:
  177.      * <code>
  178.      * public static function doDeleteAll($con = null)
  179.      * {
  180.      *   if ($con === null) $con = Propel::getConnection(self::DATABASE_NAME);
  181.      *   BasePeer::doDeleteAll(self::TABLE_NAME, $con);
  182.      * }
  183.      * </code>
  184.      *
  185.      * @param      string $tableName The name of the table to empty.
  186.      * @param      PDO $con A PDO connection object.
  187.      * @return     int    The number of rows affected by the statement.  Note
  188.      *                  that the return value does require that this information
  189.      *                  is returned (supported) by the Creole db driver.
  190.      * @throws     PropelException - wrapping SQLException caught from statement execution.
  191.      */
  192.     public static function doDeleteAll($tableNamePDO $con)
  193.     {
  194.         try {
  195.             $sql "DELETE FROM " $tableName;
  196.             Propel::log($sqlPropel::LOG_DEBUG);
  197.             $stmt $con->prepare($sql);
  198.             $stmt->execute();
  199.             return $stmt->rowCount();
  200.         catch (Exception $e{
  201.             Propel::log($e->getMessage()Propel::LOG_ERR);
  202.             throw new PropelException("Unable to perform DELETE ALL operation."$e);
  203.         }
  204.     }
  205.  
  206.     /**
  207.      * Method to perform inserts based on values and keys in a
  208.      * Criteria.
  209.      * <p>
  210.      * If the primary key is auto incremented the data in Criteria
  211.      * will be inserted and the auto increment value will be returned.
  212.      * <p>
  213.      * If the primary key is included in Criteria then that value will
  214.      * be used to insert the row.
  215.      * <p>
  216.      * If no primary key is included in Criteria then we will try to
  217.      * figure out the primary key from the database map and insert the
  218.      * row with the next available id using util.db.IDBroker.
  219.      * <p>
  220.      * If no primary key is defined for the table the values will be
  221.      * inserted as specified in Criteria and null will be returned.
  222.      *
  223.      * @param      Criteria $criteria Object containing values to insert.
  224.      * @param      PDO $con A PDO connection.
  225.      * @return     mixed The primary key for the new row if (and only if!) the primary key
  226.      *                 is auto-generated.  Otherwise will return <code>null</code>.
  227.      * @throws     PropelException
  228.      */
  229.     public static function doInsert(Criteria $criteriaPDO $con{
  230.  
  231.         // the primary key
  232.         $id null;
  233.  
  234.         $db Propel::getDB($criteria->getDbName());
  235.  
  236.         // Get the table name and method for determining the primary
  237.         // key value.
  238.         $keys $criteria->keys();
  239.         if (!empty($keys)) {
  240.             $tableName $criteria->getTableName$keys[0);
  241.         else {
  242.             throw new PropelException("Database insert attempted without anything specified to insert");
  243.         }
  244.  
  245.         $dbMap Propel::getDatabaseMap($criteria->getDbName());
  246.         $tableMap $dbMap->getTable($tableName);
  247.         $keyInfo $tableMap->getPrimaryKeyMethodInfo();
  248.         $useIdGen $tableMap->isUseIdGenerator();
  249.         //$keyGen = $con->getIdGenerator();
  250.  
  251.         $pk self::getPrimaryKey($criteria);
  252.  
  253.         // only get a new key value if you need to
  254.         // the reason is that a primary key might be defined
  255.         // but you are still going to set its value. for example:
  256.         // a join table where both keys are primary and you are
  257.         // setting both columns with your own values
  258.  
  259.         // pk will be null if there is no primary key defined for the table
  260.         // we're inserting into.
  261.         if ($pk !== null && $useIdGen && !$criteria->containsKey($pk->getFullyQualifiedName()) && $db->isGetIdBeforeInsert()) {
  262.             try {
  263.                 $id $db->getId($con$keyInfo);
  264.             catch (Exception $e{
  265.                 throw new PropelException("Unable to get sequence id."$e);
  266.             }
  267.             $criteria->add($pk->getFullyQualifiedName()$id);
  268.         }
  269.  
  270.         try {
  271.             $adapter Propel::getDB($criteria->getDBName());
  272.  
  273.             $qualifiedCols $criteria->keys()// we need table.column cols when populating values
  274.             $columns array()// but just 'column' cols for the SQL
  275.             foreach ($qualifiedCols as $qualifiedCol{
  276.                 $columns[substr($qualifiedColstrpos($qualifiedCol'.'1);
  277.             }
  278.  
  279.             // add identifiers
  280.             if ($adapter->useQuoteIdentifier()) {
  281.                 $columns array_map(array($adapter'quoteIdentifier')$columns);
  282.             }
  283.  
  284.             $sql "INSERT INTO " $tableName
  285.                 . " (" implode(","$columns")"
  286.                 . " VALUES (" substr(str_repeat("?,"count($columns))0-1")";
  287.  
  288.             Propel::log($sqlPropel::LOG_DEBUG);
  289.  
  290.             $stmt $con->prepare($sql);
  291.             self::populateStmtValues($stmtself::buildParams($qualifiedCols$criteria)$dbMap$db);
  292.             $stmt->execute();
  293.  
  294.         catch (Exception $e{
  295.             Propel::log($e->getMessage()Propel::LOG_ERR);
  296.             throw new PropelException("Unable to execute INSERT statement."$e);
  297.         }
  298.  
  299.         // If the primary key column is auto-incremented, get the id now.
  300.         if ($pk !== null && $useIdGen && $db->isGetIdAfterInsert()) {
  301.             try {
  302.                 $id $db->getId($con$keyInfo);
  303.             catch (Exception $e{
  304.                 throw new PropelException("Unable to get autoincrement id."$e);
  305.             }
  306.         }
  307.  
  308.         return $id;
  309.     }
  310.  
  311.     /**
  312.      * Method used to update rows in the DB.  Rows are selected based
  313.      * on selectCriteria and updated using values in updateValues.
  314.      * <p>
  315.      * Use this method for performing an update of the kind:
  316.      * <p>
  317.      * WHERE some_column = some value AND could_have_another_column =
  318.      * another value AND so on.
  319.      *
  320.      * @param      $selectCriteria A Criteria object containing values used in where
  321.      *         clause.
  322.      * @param      $updateValues A Criteria object containing values used in set
  323.      *         clause.
  324.      * @param      PDO $con The PDO connection object to use.
  325.      * @return     int    The number of rows affected by last update statement.  For most
  326.      *                  uses there is only one update statement executed, so this number
  327.      *                  will correspond to the number of rows affected by the call to this
  328.      *                  method.  Note that the return value does require that this information
  329.      *                  is returned (supported) by the Creole db driver.
  330.      * @throws     PropelException
  331.      */
  332.     public static function doUpdate(Criteria $selectCriteriaCriteria $updateValuesPDO $con{
  333.  
  334.         $db Propel::getDB($selectCriteria->getDbName());
  335.         $dbMap Propel::getDatabaseMap($selectCriteria->getDbName());
  336.  
  337.         // Get list of required tables, containing all columns
  338.         $tablesColumns $selectCriteria->getTablesColumns();
  339.  
  340.         // we also need the columns for the update SQL
  341.         $updateTablesColumns $updateValues->getTablesColumns();
  342.  
  343.         $affectedRows 0// initialize this in case the next loop has no iterations.
  344.  
  345.         foreach ($tablesColumns as $tableName => $columns{
  346.  
  347.             $whereClause array();
  348.  
  349.             $selectParams array();
  350.             foreach ($columns as $colName{
  351.                 $sb "";
  352.                 $selectCriteria->getCriterion($colName)->appendPsTo($sb$selectParams);
  353.                 $whereClause[$sb;
  354.             }
  355.  
  356.             $stmt null;
  357.             try {
  358.  
  359.                 $sql "UPDATE " $tableName " SET ";
  360.                 foreach ($updateTablesColumns[$tableNameas $col{
  361.                     $updateColumnName substr($colstrpos($col'.'1);
  362.                     // add identifiers for the actual database?
  363.                     if ($db->useQuoteIdentifier()) {
  364.                         $updateColumnName $db->quoteIdentifier($updateColumnName);
  365.                     }
  366.                     $sql .= $updateColumnName " = ?,";
  367.                 }
  368.  
  369.                 $sql substr($sql0-1" WHERE " .  implode(" AND "$whereClause);
  370.  
  371.                 Propel::log($sqlPropel::LOG_DEBUG);
  372.  
  373.                 $stmt $con->prepare($sql);
  374.  
  375.                 // Replace '?' with the actual values
  376.                 self::populateStmtValues($stmtarray_merge(self::buildParams($updateTablesColumns[$tableName]$updateValues)$selectParams)$dbMap$db);
  377.  
  378.                 $stmt->execute();
  379.  
  380.                 $affectedRows $stmt->rowCount();
  381.  
  382.                 $stmt null// close
  383.  
  384.             catch (Exception $e{
  385.                 if ($stmt$stmt null// close
  386.                 Propel::log($e->getMessage()Propel::LOG_ERR);
  387.                 throw new PropelException("Unable to execute UPDATE statement."$e);
  388.             }
  389.  
  390.         // foreach table in the criteria
  391.  
  392.         return $affectedRows;
  393.     }
  394.  
  395.     /**
  396.      * Executes query build by createSelectSql() and returns ResultSet.
  397.      *
  398.      * @param      Criteria $criteria A Criteria.
  399.      * @param      PDO $con A PDO connection to use.
  400.      * @return     ResultSet The resultset.
  401.      * @throws     PropelException
  402.      * @see        createSelectSql()
  403.      */
  404.     public static function doSelect(Criteria $criteriaPDO $con null)
  405.     {
  406.         $dbMap Propel::getDatabaseMap($criteria->getDbName());
  407.         $db Propel::getDB($criteria->getDbName());
  408.  
  409.         if ($con === null{
  410.             $con Propel::getConnection($criteria->getDbName());
  411.         }
  412.  
  413.         $stmt null;
  414.  
  415.         try {
  416.  
  417.             // Transaction support exists for (only?) Postgres, which must
  418.             // have SELECT statements that include bytea columns wrapped w/
  419.             // transactions.
  420.             if ($criteria->isUseTransaction()) Transaction::begin($con);
  421.  
  422.             $params array();
  423.             $sql self::createSelectSql($criteria$params);
  424.  
  425.              $stmt $con->prepare($sql);
  426.  
  427.              // FIXME - add SQL-modification for LIMIT/OFFSET into DBAdapters & createSelectSql method.
  428.             // $stmt->setLimit($criteria->getLimit());
  429.             // $stmt->setOffset($criteria->getOffset());
  430.  
  431.             self::populateStmtValues($stmt$params$dbMap$db);
  432.  
  433.             $stmt->execute();
  434.  
  435.             if ($criteria->isUseTransaction()) Transaction::commit($con);
  436.  
  437.         catch (Exception $e{
  438.             if ($stmt$stmt null// close
  439.             if ($criteria->isUseTransaction()) Transaction::rollback($con);
  440.             Propel::log($e->getMessage()Propel::LOG_ERR);
  441.             throw new PropelException($e);
  442.         }
  443.  
  444.         return $stmt;
  445.     }
  446.  
  447.     /**
  448.      * Populates values in a prepared statement.
  449.      *
  450.      * @param      PreparedStatement $stmt 
  451.      * @param      array $params array('column' => ..., 'table' => ..., 'value' => ...)
  452.      * @param      DatabaseMap $dbMap 
  453.      * @return     int The number of params replaced.
  454.      */
  455.     private static function populateStmtValues($stmt$paramsDatabaseMap $dbMapDBAdapter $db)
  456.     {
  457.         $i 1;
  458.         foreach ($params as $param{
  459.             $tableName $param['table'];
  460.             $columnName $param['column'];
  461.             $value $param['value'];
  462.  
  463.             if ($value === null{
  464.  
  465.                 $stmt->bindValue($i++nullPDO::PARAM_NULL);
  466.                 // $stmt->setNull($i++);
  467.  
  468.             else {
  469.  
  470.                 $cMap $dbMap->getTable($tableName)->getColumn($columnName);
  471.                 $type $cMap->getType();
  472.                 $pdoType $cMap->getPdoType();
  473.  
  474.                 // FIXME - This is a temporary hack to get around apparent bugs w/ PDO+MYSQL
  475.                 // See http://pecl.php.net/bugs/bug.php?id=9919
  476.                 if ($pdoType == PDO::PARAM_BOOL && $db instanceof DBMySQL{
  477.                     $value = (int) $value;
  478.                     $pdoType PDO::PARAM_INT;
  479.                 elseif (is_numeric($value&& $cMap->isEpochTemporal()) // it's a timestamp that needs to be formatted
  480.                     if ($type == PropelColumnTypes::TIMESTAMP{
  481.                         $value date($db->getTimestampFormatter()$value);
  482.                     else if ($type == PropelColumnTypes::DATE{
  483.                         $value date($db->