Skip to content

Instantly share code, notes, and snippets.

@jkovacs618
Last active August 29, 2015 14:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jkovacs618/dc1663d82738ba641e71 to your computer and use it in GitHub Desktop.
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.
<?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