Commit cb4504a1 by Carsten Brandt

refactored Model and redis AR to allow drop of RecordSchema

parent 8542448f
......@@ -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;
......
......@@ -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;
......
......@@ -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 <mail@cebe.cc>
* @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]].
......
......@@ -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 <mail@cebe.cc>
* @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
*/
......
......@@ -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 = '';
......
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\redis;
use yii\base\InvalidConfigException;
use yii\db\TableSchema;
/**
* Class RecordSchema defines the data schema for a redis active record
*
* As there is no schema in a redis DB this class is used to define one.
*
* @package yii\db\redis
*/
class RecordSchema extends TableSchema
{
/**
* @var string[] column names.
*/
public $columns = array();
/**
* @return string the column type
*/
public function getColumn($name)
{
parent::getColumn($name);
}
public function init()
{
if (empty($this->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
......@@ -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 {
......
......@@ -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
......@@ -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
......@@ -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
......@@ -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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment