From cb4504a10f82b43e812f07958f7664a485823567 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Fri, 22 Nov 2013 18:17:31 +0100 Subject: [PATCH] refactored Model and redis AR to allow drop of RecordSchema --- framework/yii/base/Model.php | 7 +-- framework/yii/db/ActiveRecord.php | 8 +-- framework/yii/redis/ActiveRecord.php | 82 ++++++++++++++++------------ framework/yii/redis/Connection.php | 27 --------- framework/yii/redis/LuaScriptBuilder.php | 5 ++ framework/yii/redis/RecordSchema.php | 52 ------------------ framework/yii/validators/UniqueValidator.php | 10 ++-- tests/unit/data/ar/redis/Customer.php | 22 ++------ tests/unit/data/ar/redis/Item.php | 15 +---- tests/unit/data/ar/redis/Order.php | 23 ++------ tests/unit/data/ar/redis/OrderItem.php | 24 ++++---- 11 files changed, 85 insertions(+), 190 deletions(-) delete mode 100644 framework/yii/redis/RecordSchema.php diff --git a/framework/yii/base/Model.php b/framework/yii/base/Model.php index b366a9f..caa6b61 100644 --- a/framework/yii/base/Model.php +++ b/framework/yii/base/Model.php @@ -229,14 +229,13 @@ class Model extends Component implements IteratorAggregate, ArrayAccess * You may override this method to change the default behavior. * @return array list of attribute names. */ - public function attributes() + public static function attributes() { - $class = new ReflectionClass($this); + $class = new ReflectionClass(get_called_class()); $names = []; foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) { - $name = $property->getName(); if (!$property->isStatic()) { - $names[] = $name; + $names[] = $property->getName(); } } return $names; diff --git a/framework/yii/db/ActiveRecord.php b/framework/yii/db/ActiveRecord.php index 83e5c7e..3de4b2b 100644 --- a/framework/yii/db/ActiveRecord.php +++ b/framework/yii/db/ActiveRecord.php @@ -568,9 +568,9 @@ class ActiveRecord extends Model * The default implementation will return all column names of the table associated with this AR class. * @return array list of attribute names. */ - public function attributes() + public static function attributes() { - return array_keys($this->getTableSchema()->columns); + return array_keys(static::getTableSchema()->columns); } /** @@ -580,7 +580,7 @@ class ActiveRecord extends Model */ public function hasAttribute($name) { - return isset($this->_attributes[$name]) || isset($this->getTableSchema()->columns[$name]); + return isset($this->_attributes[$name]) || in_array($name, $this->attributes()); } /** @@ -1244,7 +1244,7 @@ class ActiveRecord extends Model public static function create($row) { $record = static::instantiate($row); - $columns = static::getTableSchema()->columns; + $columns = array_flip(static::attributes()); foreach ($row as $name => $value) { if (isset($columns[$name])) { $record->_attributes[$name] = $value; diff --git a/framework/yii/redis/ActiveRecord.php b/framework/yii/redis/ActiveRecord.php index acdb4cd..f9fa3f3 100644 --- a/framework/yii/redis/ActiveRecord.php +++ b/framework/yii/redis/ActiveRecord.php @@ -9,23 +9,34 @@ namespace yii\redis; use yii\base\InvalidConfigException; use yii\base\NotSupportedException; -use yii\db\TableSchema; use yii\helpers\StringHelper; /** * ActiveRecord is the base class for classes representing relational data in terms of objects. * + * This class implements the ActiveRecord pattern for the [redis](http://redis.io/) key-value store. + * + * For defining a record a subclass should at least implement the [[attributes()]] method to define + * attributes. A primary key can be defined via [[primaryKey()]] which defaults to `id` if not specified. + * + * The following is an example model called `Customer`: + * + * ```php + * class Customer extends \yii\redis\ActiveRecord + * { + * public function attributes() + * { + * return ['id', 'name', 'address', 'registration_date']; + * } + * } + * ``` + * * @author Carsten Brandt * @since 2.0 */ class ActiveRecord extends \yii\db\ActiveRecord { /** - * @var array cache for TableSchema instances - */ - private static $_tables = []; - - /** * Returns the database connection used by this AR class. * By default, the "redis" application component is used as the database connection. * You may override this method if you want to use a different database connection. @@ -37,14 +48,6 @@ class ActiveRecord extends \yii\db\ActiveRecord } /** - * @inheritdoc - */ - public static function findBySql($sql, $params = []) - { - throw new NotSupportedException('findBySql() is not supported by redis ActiveRecord'); - } - - /** * @inheritDoc */ public static function createQuery() @@ -61,35 +64,26 @@ class ActiveRecord extends \yii\db\ActiveRecord } /** - * Declares the name of the database table associated with this AR class. - * @return string the table name - */ - public static function tableName() - { - return static::getTableSchema()->name; - } - - /** - * This method is ment to be overridden in redis ActiveRecord subclasses to return a [[RecordSchema]] instance. - * @return RecordSchema - * @throws \yii\base\InvalidConfigException + * Returns the primary key name(s) for this AR class. + * This method should be overridden by child classes to define the primary key. + * + * Note that an array should be returned even when it is a single primary key. + * + * @return string[] the primary keys of this record. */ - public static function getRecordSchema() + public static function primaryKey() { - throw new InvalidConfigException(__CLASS__.'::getRecordSchema() needs to be overridden in subclasses and return a RecordSchema.'); + return ['id']; } /** - * Returns the schema information of the DB table associated with this AR class. - * @return TableSchema the schema information of the DB table associated with this AR class. + * Returns the list of all attribute names of the model. + * This method must be overridden by child classes to define available attributes. + * @return array list of attribute names. */ - public static function getTableSchema() + public static function attributes() { - $class = get_called_class(); - if (isset(self::$_tables[$class])) { - return self::$_tables[$class]; - } - return self::$_tables[$class] = static::getRecordSchema(); + throw new InvalidConfigException('The attributes() method of redis ActiveRecord has to be implemented by child classes.'); } /** @@ -300,6 +294,22 @@ class ActiveRecord extends \yii\db\ActiveRecord } /** + * @inheritdoc + */ + public static function getTableSchema() + { + throw new NotSupportedException('getTableSchema() is not supported by redis ActiveRecord'); + } + + /** + * @inheritdoc + */ + public static function findBySql($sql, $params = []) + { + throw new NotSupportedException('findBySql() is not supported by redis ActiveRecord'); + } + + /** * Returns a value indicating whether the specified operation is transactional in the current [[scenario]]. * This method will always return false as transactional operations are not supported by redis. * @param integer $operation the operation to check. Possible values are [[OP_INSERT]], [[OP_UPDATE]] and [[OP_DELETE]]. diff --git a/framework/yii/redis/Connection.php b/framework/yii/redis/Connection.php index 8a7a5d4..66df71d 100644 --- a/framework/yii/redis/Connection.php +++ b/framework/yii/redis/Connection.php @@ -21,8 +21,6 @@ use yii\helpers\Inflector; * @property string $driverName Name of the DB driver. This property is read-only. * @property boolean $isActive Whether the DB connection is established. This property is read-only. * @property LuaScriptBuilder $luaScriptBuilder This property is read-only. - * @property Transaction $transaction The currently active transaction. Null if no active transaction. This - * property is read-only. * * @author Carsten Brandt * @since 2.0 @@ -202,10 +200,6 @@ class Connection extends Component 'ZUNIONSTORE', // destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX] Add multiple sorted sets and store the resulting sorted set in a new key ]; /** - * @var Transaction the currently active transaction - */ - private $_transaction; - /** * @var resource redis socket connection */ private $_socket; @@ -297,27 +291,6 @@ class Connection extends Component } /** - * Returns the currently active transaction. - * @return Transaction the currently active transaction. Null if no active transaction. - */ - public function getTransaction() - { - return $this->_transaction && $this->_transaction->isActive ? $this->_transaction : null; - } - - /** - * Starts a transaction. - * @return Transaction the transaction initiated - */ - public function beginTransaction() - { - $this->open(); - $this->_transaction = new Transaction(['db' => $this]); - $this->_transaction->begin(); - return $this->_transaction; - } - - /** * Returns the name of the DB driver for the current [[dsn]]. * @return string name of the DB driver */ diff --git a/framework/yii/redis/LuaScriptBuilder.php b/framework/yii/redis/LuaScriptBuilder.php index 7932a76..c151d49 100644 --- a/framework/yii/redis/LuaScriptBuilder.php +++ b/framework/yii/redis/LuaScriptBuilder.php @@ -129,6 +129,10 @@ class LuaScriptBuilder extends \yii\base\Object */ private function build($query, $buildResult, $return) { + if (!empty($query->orderBy)) { + throw new NotSupportedException('orderBy is currently not supported by redis ActiveRecord.'); + } + $columns = []; if ($query->where !== null) { $condition = $this->buildCondition($query->where, $columns); @@ -139,6 +143,7 @@ class LuaScriptBuilder extends \yii\base\Object $start = $query->offset === null ? 0 : $query->offset; $limitCondition = 'i>' . $start . ($query->limit === null ? '' : ' and i<=' . ($start + $query->limit)); + /** @var ActiveRecord $modelClass */ $modelClass = $query->modelClass; $key = $this->quoteValue($modelClass::tableName()); $loadColumnValues = ''; diff --git a/framework/yii/redis/RecordSchema.php b/framework/yii/redis/RecordSchema.php deleted file mode 100644 index e63f8d3..0000000 --- a/framework/yii/redis/RecordSchema.php +++ /dev/null @@ -1,52 +0,0 @@ -name)) { - throw new InvalidConfigException('name of RecordSchema must not be empty.'); - } - if (empty($this->primaryKey)) { - throw new InvalidConfigException('primaryKey of RecordSchema must not be empty.'); - } - if (!is_array($this->primaryKey)) { - $this->primaryKey = [$this->primaryKey]; - } - foreach($this->primaryKey as $pk) { - if (!isset($this->columns[$pk])) { - throw new InvalidConfigException('primaryKey '.$pk.' is not a colum of RecordSchema.'); - } - } - } -} \ No newline at end of file diff --git a/framework/yii/validators/UniqueValidator.php b/framework/yii/validators/UniqueValidator.php index 7006cc4..fd5d8cc 100644 --- a/framework/yii/validators/UniqueValidator.php +++ b/framework/yii/validators/UniqueValidator.php @@ -64,13 +64,13 @@ class UniqueValidator extends Validator $className = $this->className === null ? get_class($object) : $this->className; $attributeName = $this->attributeName === null ? $attribute : $this->attributeName; - $table = $className::getTableSchema(); - if (($column = $table->getColumn($attributeName)) === null) { - throw new InvalidConfigException("Table '{$table->name}' does not have a column named '$attributeName'."); + $attributes = $className::attributes(); + if (!in_array($attribute, $attributes)) { + throw new InvalidConfigException("'$className' does not have an attribute named '$attributeName'."); } $query = $className::find(); - $query->where([$column->name => $value]); + $query->where([$attribute => $value]); if (!$object instanceof ActiveRecord || $object->getIsNewRecord()) { // if current $object isn't in the database yet then it's OK just to call exists() @@ -82,7 +82,7 @@ class UniqueValidator extends Validator $n = count($objects); if ($n === 1) { - if ($column->isPrimaryKey) { + if (in_array($attribute, $className::primaryKey())) { // primary key is modified and not unique $exists = $object->getOldPrimaryKey() != $object->getPrimaryKey(); } else { diff --git a/tests/unit/data/ar/redis/Customer.php b/tests/unit/data/ar/redis/Customer.php index 9dfd98b..b48953f 100644 --- a/tests/unit/data/ar/redis/Customer.php +++ b/tests/unit/data/ar/redis/Customer.php @@ -2,8 +2,6 @@ namespace yiiunit\data\ar\redis; -use yii\redis\RecordSchema; - class Customer extends ActiveRecord { const STATUS_ACTIVE = 1; @@ -11,6 +9,11 @@ class Customer extends ActiveRecord public $status2; + public static function attributes() + { + return ['id', 'email', 'name', 'address', 'status']; + } + /** * @return \yii\redis\ActiveRelation */ @@ -23,19 +26,4 @@ class Customer extends ActiveRecord { $query->andWhere(['status' => 1]); } - - public static function getRecordSchema() - { - return new RecordSchema(array( - 'name' => 'customer', - 'primaryKey' => array('id'), - 'columns' => array( - 'id' => 'integer', - 'email' => 'string', - 'name' => 'string', - 'address' => 'string', - 'status' => 'integer' - ) - )); - } } \ No newline at end of file diff --git a/tests/unit/data/ar/redis/Item.php b/tests/unit/data/ar/redis/Item.php index 5e16c3c..1163265 100644 --- a/tests/unit/data/ar/redis/Item.php +++ b/tests/unit/data/ar/redis/Item.php @@ -2,21 +2,10 @@ namespace yiiunit\data\ar\redis; -use yii\redis\RecordSchema; - class Item extends ActiveRecord { - public static function getRecordSchema() + public static function attributes() { - return new RecordSchema([ - 'name' => 'item', - 'primaryKey' => ['id'], - 'sequenceName' => 'id', - 'columns' => [ - 'id' => 'integer', - 'name' => 'string', - 'category_id' => 'integer' - ] - ]); + return ['id', 'name', 'category_id']; } } \ No newline at end of file diff --git a/tests/unit/data/ar/redis/Order.php b/tests/unit/data/ar/redis/Order.php index 7ac6763..33d289a 100644 --- a/tests/unit/data/ar/redis/Order.php +++ b/tests/unit/data/ar/redis/Order.php @@ -2,10 +2,13 @@ namespace yiiunit\data\ar\redis; -use yii\redis\RecordSchema; - class Order extends ActiveRecord { + public static function attributes() + { + return ['id', 'customer_id', 'create_time', 'total']; + } + public function getCustomer() { return $this->hasOne(Customer::className(), ['id' => 'customer_id']); @@ -40,20 +43,4 @@ class Order extends ActiveRecord return false; } } - - - public static function getRecordSchema() - { - return new RecordSchema(array( - 'name' => 'orders', - 'primaryKey' => ['id'], - 'columns' => array( - 'id' => 'integer', - 'customer_id' => 'integer', - 'create_time' => 'integer', - 'total' => 'decimal', - ) - )); - } - } \ No newline at end of file diff --git a/tests/unit/data/ar/redis/OrderItem.php b/tests/unit/data/ar/redis/OrderItem.php index 32830bb..38def6b 100644 --- a/tests/unit/data/ar/redis/OrderItem.php +++ b/tests/unit/data/ar/redis/OrderItem.php @@ -6,6 +6,16 @@ use yii\redis\RecordSchema; class OrderItem extends ActiveRecord { + public static function primaryKey() + { + return ['order_id', 'item_id']; + } + + public static function attributes() + { + return ['order_id', 'item_id', 'quantity', 'subtotal']; + } + public function getOrder() { return $this->hasOne(Order::className(), ['id' => 'order_id']); @@ -15,18 +25,4 @@ class OrderItem extends ActiveRecord { return $this->hasOne(Item::className(), ['id' => 'item_id']); } - - public static function getRecordSchema() - { - return new RecordSchema(array( - 'name' => 'order_item', - 'primaryKey' => ['order_id', 'item_id'], - 'columns' => array( - 'order_id' => 'integer', - 'item_id' => 'integer', - 'quantity' => 'integer', - 'subtotal' => 'decimal', - ) - )); - } } \ No newline at end of file