MediaWiki  REL1_21
ORMTable.php
Go to the documentation of this file.
00001 <?php
00031 class ORMTable extends DBAccessBase implements IORMTable {
00032 
00041         protected static $instanceCache = array();
00042 
00048         protected $tableName;
00049 
00055         protected $fields = array();
00056 
00062         protected $fieldPrefix = '';
00063 
00069         protected $rowClass = 'ORMRow';
00070 
00076         protected $defaults = array();
00077 
00086         protected $readDb = DB_SLAVE;
00087 
00099         public function __construct( $tableName = '', array $fields = array(), array $defaults = array(), $rowClass = null, $fieldPrefix = '' ) {
00100                 $this->tableName = $tableName;
00101                 $this->fields = $fields;
00102                 $this->defaults = $defaults;
00103 
00104                 if ( is_string( $rowClass ) ) {
00105                         $this->rowClass = $rowClass;
00106                 }
00107 
00108                 $this->fieldPrefix = $fieldPrefix;
00109         }
00110 
00119         public function getName() {
00120                 if ( $this->tableName === '' ) {
00121                         throw new MWException( 'The table name needs to be set' );
00122                 }
00123 
00124                 return $this->tableName;
00125         }
00126 
00134         protected function getFieldPrefix() {
00135                 return $this->fieldPrefix;
00136         }
00137 
00145         public function getRowClass() {
00146                 return $this->rowClass;
00147         }
00148 
00157         public function getFields() {
00158                 if ( $this->fields === array() ) {
00159                         throw new MWException( 'The table needs to have one or more fields' );
00160                 }
00161 
00162                 return $this->fields;
00163         }
00164 
00173         public function getDefaults() {
00174                 return $this->defaults;
00175         }
00176 
00186         public function getSummaryFields() {
00187                 return array();
00188         }
00189 
00203         public function select( $fields = null, array $conditions = array(),
00204                                                         array $options = array(), $functionName = null ) {
00205                 $res = $this->rawSelect( $fields, $conditions, $options, $functionName );
00206                 return new ORMResult( $this, $res );
00207         }
00208 
00223         public function selectObjects( $fields = null, array $conditions = array(),
00224                                                                    array $options = array(), $functionName = null ) {
00225                 $result = $this->selectFields( $fields, $conditions, $options, false, $functionName );
00226 
00227                 $objects = array();
00228 
00229                 foreach ( $result as $record ) {
00230                         $objects[] = $this->newRow( $record );
00231                 }
00232 
00233                 return $objects;
00234         }
00235 
00249         public function rawSelect( $fields = null, array $conditions = array(),
00250                                                            array $options = array(), $functionName = null ) {
00251                 if ( is_null( $fields ) ) {
00252                         $fields = array_keys( $this->getFields() );
00253                 }
00254                 else {
00255                         $fields = (array)$fields;
00256                 }
00257 
00258                 $dbr = $this->getReadDbConnection();
00259                 $result = $dbr->select(
00260                         $this->getName(),
00261                         $this->getPrefixedFields( $fields ),
00262                         $this->getPrefixedValues( $conditions ),
00263                         is_null( $functionName ) ? __METHOD__ : $functionName,
00264                         $options
00265                 );
00266 
00267                 /* @var Exception $error */
00268                 $error = null;
00269 
00270                 if ( $result === false ) {
00271                         // Database connection was in "ignoreErrors" mode. We don't like that.
00272                         // So, we emulate the DBQueryError that should have been thrown.
00273                         $error = new DBQueryError(
00274                                 $dbr,
00275                                 $dbr->lastError(),
00276                                 $dbr->lastErrno(),
00277                                 $dbr->lastQuery(),
00278                                 is_null( $functionName ) ? __METHOD__ : $functionName
00279                         );
00280                 }
00281 
00282                 $this->releaseConnection( $dbr );
00283 
00284                 if ( $error ) {
00285                         // Note: construct the error before releasing the connection,
00286                         // but throw it after.
00287                         throw $error;
00288                 }
00289 
00290                 return $result;
00291         }
00292 
00315         public function selectFields( $fields = null, array $conditions = array(),
00316                                                                   array $options = array(), $collapse = true, $functionName = null ) {
00317                 $objects = array();
00318 
00319                 $result = $this->rawSelect( $fields, $conditions, $options, $functionName );
00320 
00321                 foreach ( $result as $record ) {
00322                         $objects[] = $this->getFieldsFromDBResult( $record );
00323                 }
00324 
00325                 if ( $collapse ) {
00326                         if ( count( $fields ) === 1 ) {
00327                                 $objects = array_map( 'array_shift', $objects );
00328                         }
00329                         elseif ( count( $fields ) === 2 ) {
00330                                 $o = array();
00331 
00332                                 foreach ( $objects as $object ) {
00333                                         $o[array_shift( $object )] = array_shift( $object );
00334                                 }
00335 
00336                                 $objects = $o;
00337                         }
00338                 }
00339 
00340                 return $objects;
00341         }
00342 
00356         public function selectRow( $fields = null, array $conditions = array(),
00357                                                            array $options = array(), $functionName = null ) {
00358                 $options['LIMIT'] = 1;
00359 
00360                 $objects = $this->select( $fields, $conditions, $options, $functionName );
00361 
00362                 return ( !$objects || $objects->isEmpty() ) ? false : $objects->current();
00363         }
00364 
00378         public function rawSelectRow( array $fields, array $conditions = array(),
00379                                                                   array $options = array(), $functionName = null ) {
00380                 $dbr = $this->getReadDbConnection();
00381 
00382                 $result = $dbr->selectRow(
00383                         $this->getName(),
00384                         $fields,
00385                         $conditions,
00386                         is_null( $functionName ) ? __METHOD__ : $functionName,
00387                         $options
00388                 );
00389 
00390                 $this->releaseConnection( $dbr );
00391                 return $result;
00392         }
00393 
00411         public function selectFieldsRow( $fields = null, array $conditions = array(),
00412                                                                          array $options = array(), $collapse = true, $functionName = null ) {
00413                 $options['LIMIT'] = 1;
00414 
00415                 $objects = $this->selectFields( $fields, $conditions, $options, $collapse, $functionName );
00416 
00417                 return empty( $objects ) ? false : $objects[0];
00418         }
00419 
00430         public function has( array $conditions = array() ) {
00431                 return $this->selectRow( array( 'id' ), $conditions ) !== false;
00432         }
00433 
00441         public function exists() {
00442                 $dbr = $this->getReadDbConnection();
00443                 $exists = $dbr->tableExists( $this->getName() );
00444                 $this->releaseConnection( $dbr );
00445 
00446                 return $exists;
00447         }
00448 
00463         public function count( array $conditions = array(), array $options = array() ) {
00464                 $res = $this->rawSelectRow(
00465                         array( 'rowcount' => 'COUNT(*)' ),
00466                         $this->getPrefixedValues( $conditions ),
00467                         $options,
00468                         __METHOD__
00469                 );
00470 
00471                 return $res->rowcount;
00472         }
00473 
00484         public function delete( array $conditions, $functionName = null ) {
00485                 $dbw = $this->getWriteDbConnection();
00486 
00487                 $result = $dbw->delete(
00488                         $this->getName(),
00489                         $conditions === array() ? '*' : $this->getPrefixedValues( $conditions ),
00490                         is_null( $functionName ) ? __METHOD__ : $functionName
00491                 ) !== false; // DatabaseBase::delete does not always return true for success as documented...
00492 
00493                 $this->releaseConnection( $dbw );
00494                 return $result;
00495         }
00496 
00507         public function getAPIParams( $requireParams = false, $setDefaults = false ) {
00508                 $typeMap = array(
00509                         'id' => 'integer',
00510                         'int' => 'integer',
00511                         'float' => 'NULL',
00512                         'str' => 'string',
00513                         'bool' => 'integer',
00514                         'array' => 'string',
00515                         'blob' => 'string',
00516                 );
00517 
00518                 $params = array();
00519                 $defaults = $this->getDefaults();
00520 
00521                 foreach ( $this->getFields() as $field => $type ) {
00522                         if ( $field == 'id' ) {
00523                                 continue;
00524                         }
00525 
00526                         $hasDefault = array_key_exists( $field, $defaults );
00527 
00528                         $params[$field] = array(
00529                                 ApiBase::PARAM_TYPE => $typeMap[$type],
00530                                 ApiBase::PARAM_REQUIRED => $requireParams && !$hasDefault
00531                         );
00532 
00533                         if ( $type == 'array' ) {
00534                                 $params[$field][ApiBase::PARAM_ISMULTI] = true;
00535                         }
00536 
00537                         if ( $setDefaults && $hasDefault ) {
00538                                 $default = is_array( $defaults[$field] ) ? implode( '|', $defaults[$field] ) : $defaults[$field];
00539                                 $params[$field][ApiBase::PARAM_DFLT] = $default;
00540                         }
00541                 }
00542 
00543                 return $params;
00544         }
00545 
00555         public function getFieldDescriptions() {
00556                 return array();
00557         }
00558 
00566         public function getReadDb() {
00567                 return $this->readDb;
00568         }
00569 
00577         public function setReadDb( $db ) {
00578                 $this->readDb = $db;
00579         }
00580 
00588         public function getTargetWiki() {
00589                 return $this->wiki;
00590         }
00591 
00599         public function setTargetWiki( $wiki ) {
00600                 $this->wiki = $wiki;
00601         }
00602 
00613         public function getReadDbConnection() {
00614                 return $this->getConnection( $this->getReadDb(), array() );
00615         }
00616 
00627         public function getWriteDbConnection() {
00628                 return $this->getConnection( DB_MASTER, array() );
00629         }
00630 
00641         public function releaseConnection( DatabaseBase $db ) {
00642                 parent::releaseConnection( $db ); // just make it public
00643         }
00644 
00657         public function update( array $values, array $conditions = array() ) {
00658                 $dbw = $this->getWriteDbConnection();
00659 
00660                 $result = $dbw->update(
00661                         $this->getName(),
00662                         $this->getPrefixedValues( $values ),
00663                         $this->getPrefixedValues( $conditions ),
00664                         __METHOD__
00665                 ) !== false; // DatabaseBase::update does not always return true for success as documented...
00666 
00667                 $this->releaseConnection( $dbw );
00668                 return $result;
00669         }
00670 
00679         public function updateSummaryFields( $summaryFields = null, array $conditions = array() ) {
00680                 $slave = $this->getReadDb();
00681                 $this->setReadDb( DB_MASTER );
00682 
00686                 foreach ( $this->select( null, $conditions ) as $item ) {
00687                         $item->loadSummaryFields( $summaryFields );
00688                         $item->setSummaryMode( true );
00689                         $item->save();
00690                 }
00691 
00692                 $this->setReadDb( $slave );
00693         }
00694 
00706         public function getPrefixedValues( array $values ) {
00707                 $prefixedValues = array();
00708 
00709                 foreach ( $values as $field => $value ) {
00710                         if ( is_integer( $field ) ) {
00711                                 if ( is_array( $value ) ) {
00712                                         $field = $value[0];
00713                                         $value = $value[1];
00714                                 }
00715                                 else {
00716                                         $value = explode( ' ', $value, 2 );
00717                                         $value[0] = $this->getPrefixedField( $value[0] );
00718                                         $prefixedValues[] = implode( ' ', $value );
00719                                         continue;
00720                                 }
00721                         }
00722 
00723                         $prefixedValues[$this->getPrefixedField( $field )] = $value;
00724                 }
00725 
00726                 return $prefixedValues;
00727         }
00728 
00739         public function getPrefixedFields( array $fields ) {
00740                 foreach ( $fields as &$field ) {
00741                         $field = $this->getPrefixedField( $field );
00742                 }
00743 
00744                 return $fields;
00745         }
00746 
00756         public function getPrefixedField( $field ) {
00757                 return $this->getFieldPrefix() . $field;
00758         }
00759 
00769         public function unprefixFieldNames( array $fieldNames ) {
00770                 return array_map( array( $this, 'unprefixFieldName' ), $fieldNames );
00771         }
00772 
00782         public function unprefixFieldName( $fieldName ) {
00783                 return substr( $fieldName, strlen( $this->getFieldPrefix() ) );
00784         }
00785 
00794         public static function singleton() {
00795                 $class = get_called_class();
00796 
00797                 if ( !array_key_exists( $class, self::$instanceCache ) ) {
00798                         self::$instanceCache[$class] = new $class;
00799                 }
00800 
00801                 return self::$instanceCache[$class];
00802         }
00803 
00815         public function getFieldsFromDBResult( stdClass $result ) {
00816                 $result = (array)$result;
00817                 return array_combine(
00818                         $this->unprefixFieldNames( array_keys( $result ) ),
00819                         array_values( $result )
00820                 );
00821         }
00822 
00833         public function newFromDBResult( stdClass $result ) {
00834                 return self::newRowFromDBResult( $result );
00835         }
00836 
00846         public function newRowFromDBResult( stdClass $result ) {
00847                 return $this->newRow( $this->getFieldsFromDBResult( $result ) );
00848         }
00849 
00861         public function newFromArray( array $data, $loadDefaults = false ) {
00862                 return static::newRow( $data, $loadDefaults );
00863         }
00864 
00875         public function newRow( array $data, $loadDefaults = false ) {
00876                 $class = $this->getRowClass();
00877                 return new $class( $this, $data, $loadDefaults );
00878         }
00879 
00887         public function getFieldNames() {
00888                 return array_keys( $this->getFields() );
00889         }
00890 
00900         public function canHaveField( $name ) {
00901                 return array_key_exists( $name, $this->getFields() );
00902         }
00903 
00904 }