Last active
August 29, 2015 14:05
-
-
Save jkovacs618/dc1663d82738ba641e71 to your computer and use it in GitHub Desktop.
Yii2 custom AppActiveRecord class used to extend yii\db\ActiveRecord to override populateRecord to improve performance of large find() queries.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace common\models\db; | |
/** | |
* AppActiveRecord: Extension of ActiveRecord class to override the static populateRecord function for performance. | |
*/ | |
class AppActiveRecord extends \yii\db\ActiveRecord | |
{ | |
/** | |
* Override private BaseActiveRecord->_attributes for use by overridden populateRecord function below. | |
* @var Array | |
*/ | |
protected $_attributes = []; | |
/** | |
* Override private BaseActiveRecord->_oldAttributes for use by overridden populateRecord function below. | |
* @var Array | |
*/ | |
protected $_oldAttributes; | |
/** | |
* Override private BaseActiveRecord->_related for use by overridden __get function below. | |
* @var Array | |
*/ | |
protected $_related = []; | |
/** | |
* Static Array of $columns arrays, indexed by Table Name, used by populateRecord below for performance. | |
* @var Array | |
*/ | |
public static $columnsByTable = []; | |
/** | |
* Override ActiveRecord::populateRecord and BaseActiveRecord::populateRecord for performance. | |
* Purpose: Set $record->_attributes and $record->_oldAttributes from the input $row key/value pairs. | |
* Skip result value type casting; Require the outside caller to cast values to (int) as needed. | |
* Use the static $columnsByTable array above to cache the $columns array calculated once and reused per record. | |
* | |
* @param common\models\db\AppActiveRecord $record | |
* @param Array $row | |
*/ | |
public static function populateRecord($record, $row) | |
{ | |
$tableName = $record->tableName(); | |
if(isset(self::$columnsByTable[$tableName])) { | |
$columns = self::$columnsByTable[$tableName]; | |
} | |
else { | |
$attributes = $record->attributes(); | |
$columns = array_flip($attributes); | |
self::$columnsByTable[$tableName] = $columns; | |
} | |
foreach ($row as $name => $value) { | |
if (isset($columns[$name])) { | |
$record->_attributes[$name] = $value; | |
} elseif ($record->canSetProperty($name)) { | |
$record->$name = $value; | |
} | |
} | |
$record->_oldAttributes = $record->_attributes; | |
} | |
/** | |
* PHP getter magic method. | |
* This method is overridden so that attributes and related objects can be accessed like properties. | |
* | |
* @param string $name property name | |
* @throws \yii\base\InvalidParamException if relation name is wrong | |
* @return mixed property value | |
* @see getAttribute() | |
*/ | |
public function __get($name) | |
{ | |
if (isset($this->_attributes[$name]) || array_key_exists($name, $this->_attributes)) { | |
return $this->_attributes[$name]; | |
} elseif ($this->hasAttribute($name)) { | |
return null; | |
} else { | |
if (isset($this->_related[$name]) || array_key_exists($name, $this->_related)) { | |
return $this->_related[$name]; | |
} | |
$value = parent::__get($name); | |
if ($value instanceof ActiveQueryInterface) { | |
return $this->_related[$name] = $value->findFor($name, $this); | |
} else { | |
return $value; | |
} | |
} | |
} | |
/** | |
* PHP setter magic method. | |
* This method is overridden so that AR attributes can be accessed like properties. | |
* @param string $name property name | |
* @param mixed $value property value | |
*/ | |
public function __set($name, $value) | |
{ | |
if ($this->hasAttribute($name)) { | |
$this->_attributes[$name] = $value; | |
} else { | |
parent::__set($name, $value); | |
} | |
} | |
/** | |
* Checks if a property value is null. | |
* This method overrides the parent implementation by checking if the named attribute is null or not. | |
* @param string $name the property name or the event name | |
* @return boolean whether the property value is null | |
*/ | |
public function __isset($name) | |
{ | |
try { | |
return $this->__get($name) !== null; | |
} catch (\Exception $e) { | |
return false; | |
} | |
} | |
/** | |
* Sets a component property to be null. | |
* This method overrides the parent implementation by clearing | |
* the specified attribute value. | |
* @param string $name the property name or the event name | |
*/ | |
public function __unset($name) | |
{ | |
if ($this->hasAttribute($name)) { | |
unset($this->_attributes[$name]); | |
} elseif (array_key_exists($name, $this->_related)) { | |
unset($this->_related[$name]); | |
} elseif ($this->getRelation($name, false) === null) { | |
parent::__unset($name); | |
} | |
} | |
/** | |
* Override this function in extended classes of AppActiveRecord, to set the DB Table Name that the AR model represents. | |
* | |
* @return string | |
*/ | |
public function tableName() | |
{ | |
return get_called_class(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment