Commit e3df19d9 by Carsten Brandt

Redis AR WIP

- introduced RecordSchema class for schema definition
parent 82dc3b12
...@@ -268,7 +268,7 @@ class ActiveRecord extends Model ...@@ -268,7 +268,7 @@ class ActiveRecord extends Model
*/ */
public static function tableName() public static function tableName()
{ {
return 'tbl_' . Inflector::camel2id(StringHelper::basename(get_called_class()), '_'); return static::getTableSchema()->name;
} }
/** /**
......
...@@ -112,7 +112,7 @@ class ActiveQuery extends \yii\base\Component ...@@ -112,7 +112,7 @@ class ActiveQuery extends \yii\base\Component
} }
$rows = array(); $rows = array();
foreach($primaryKeys as $pk) { foreach($primaryKeys as $pk) {
$key = $modelClass::tableName() . ':a:' . (is_array($pk) ? implode('-', $pk) : $pk); // TODO escape PK glue $key = $modelClass::tableName() . ':a:' . $modelClass::hashPk($pk);
// get attributes // get attributes
$data = $db->executeCommand('HGETALL', array($key)); $data = $db->executeCommand('HGETALL', array($key));
$row = array(); $row = array();
...@@ -148,7 +148,7 @@ class ActiveQuery extends \yii\base\Component ...@@ -148,7 +148,7 @@ class ActiveQuery extends \yii\base\Component
$primaryKeys = $db->executeCommand('LRANGE', array($modelClass::tableName(), $start, $start + 1)); $primaryKeys = $db->executeCommand('LRANGE', array($modelClass::tableName(), $start, $start + 1));
} }
$pk = reset($primaryKeys); $pk = reset($primaryKeys);
$key = $modelClass::tableName() . ':a:' . (is_array($pk) ? implode('-', $pk) : $pk); // TODO escape PK glue $key = $modelClass::tableName() . ':a:' . $modelClass::hashPk($pk);
// get attributes // get attributes
$data = $db->executeCommand('HGETALL', array($key)); $data = $db->executeCommand('HGETALL', array($key));
if ($data === array()) { if ($data === array()) {
......
...@@ -20,7 +20,7 @@ use yii\db\TableSchema; ...@@ -20,7 +20,7 @@ use yii\db\TableSchema;
/** /**
* ActiveRecord is the base class for classes representing relational data in terms of objects. * ActiveRecord is the base class for classes representing relational data in terms of objects.
* *
* @include @yii/db/ActiveRecord.md *
* *
* @author Carsten Brandt <mail@cebe.cc> * @author Carsten Brandt <mail@cebe.cc>
* @since 2.0 * @since 2.0
...@@ -68,31 +68,19 @@ abstract class ActiveRecord extends \yii\db\ActiveRecord ...@@ -68,31 +68,19 @@ abstract class ActiveRecord extends \yii\db\ActiveRecord
return $query; return $query;
} }
public static function hashPk($pk)
{
return (is_array($pk) ? implode('-', $pk) : $pk); // TODO escape PK glue
}
/** /**
* Creates an [[ActiveQuery]] instance with a given SQL statement. * @inheritdoc
*
* Note that because the SQL statement is already specified, calling additional
* query modification methods (such as `where()`, `order()`) on the created [[ActiveQuery]]
* instance will have no effect. However, calling `with()`, `asArray()` or `indexBy()` is
* still fine.
*
* Below is an example:
*
* ~~~
* $customers = Customer::findBySql('SELECT * FROM tbl_customer')->all();
* ~~~
*
* @param string $sql the SQL statement to be executed
* @param array $params parameters to be bound to the SQL statement during execution.
* @return ActiveQuery the newly created [[ActiveQuery]] instance
*/ */
public static function findBySql($sql, $params = array()) public static function findBySql($sql, $params = array())
{ {
throw new NotSupportedException('findBySql() is not supported by redis ActiveRecord'); throw new NotSupportedException('findBySql() is not supported by redis ActiveRecord');
} }
/** /**
* Creates an [[ActiveQuery]] instance. * Creates an [[ActiveQuery]] instance.
* This method is called by [[find()]], [[findBySql()]] and [[count()]] to start a SELECT query. * This method is called by [[find()]], [[findBySql()]] and [[count()]] to start a SELECT query.
...@@ -107,6 +95,14 @@ abstract class ActiveRecord extends \yii\db\ActiveRecord ...@@ -107,6 +95,14 @@ abstract 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;
}
/** /**
* Returns the schema information of the DB table associated with this AR class. * Returns the schema information of the DB table associated with this AR class.
...@@ -114,6 +110,7 @@ abstract class ActiveRecord extends \yii\db\ActiveRecord ...@@ -114,6 +110,7 @@ abstract class ActiveRecord extends \yii\db\ActiveRecord
*/ */
public static function getTableSchema() public static function getTableSchema()
{ {
// TODO should be cached
throw new InvalidConfigException(__CLASS__.'::getTableSchema() needs to be overridden in subclasses and return a TableSchema.'); throw new InvalidConfigException(__CLASS__.'::getTableSchema() needs to be overridden in subclasses and return a TableSchema.');
} }
...@@ -173,9 +170,9 @@ abstract class ActiveRecord extends \yii\db\ActiveRecord ...@@ -173,9 +170,9 @@ abstract class ActiveRecord extends \yii\db\ActiveRecord
} }
// } // }
// save pk in a findall pool // save pk in a findall pool
$db->executeCommand('RPUSH', array(static::tableName(), implode('-', $pk))); // TODO escape PK glue $db->executeCommand('RPUSH', array(static::tableName(), static::hashPk($pk)));
$key = static::tableName() . ':a:' . implode('-', $pk); // TODO escape PK glue $key = static::tableName() . ':a:' . static::hashPk($pk);
// save attributes // save attributes
$args = array($key); $args = array($key);
foreach($values as $attribute => $value) { foreach($values as $attribute => $value) {
...@@ -216,7 +213,7 @@ abstract class ActiveRecord extends \yii\db\ActiveRecord ...@@ -216,7 +213,7 @@ abstract class ActiveRecord extends \yii\db\ActiveRecord
} }
$n=0; $n=0;
foreach($condition as $pk) { foreach($condition as $pk) {
$key = static::tableName() . ':a:' . (is_array($pk) ? implode('-', $pk) : $pk); // TODO escape PK glue $key = static::tableName() . ':a:' . static::hashPk($pk);
// save attributes // save attributes
$args = array($key); $args = array($key);
foreach($attributes as $attribute => $value) { foreach($attributes as $attribute => $value) {
...@@ -257,7 +254,7 @@ abstract class ActiveRecord extends \yii\db\ActiveRecord ...@@ -257,7 +254,7 @@ abstract class ActiveRecord extends \yii\db\ActiveRecord
} }
$n=0; $n=0;
foreach($condition as $pk) { // TODO allow multiple pks as condition foreach($condition as $pk) { // TODO allow multiple pks as condition
$key = static::tableName() . ':a:' . (is_array($pk) ? implode('-', $pk) : $pk); // TODO escape PK glue $key = static::tableName() . ':a:' . static::hashPk($pk);
foreach($counters as $attribute => $value) { foreach($counters as $attribute => $value) {
$db->executeCommand('HINCRBY', array($key, $attribute, $value)); $db->executeCommand('HINCRBY', array($key, $attribute, $value));
} }
...@@ -292,13 +289,11 @@ abstract class ActiveRecord extends \yii\db\ActiveRecord ...@@ -292,13 +289,11 @@ abstract class ActiveRecord extends \yii\db\ActiveRecord
} }
$attributeKeys = array(); $attributeKeys = array();
foreach($condition as $pk) { foreach($condition as $pk) {
if (is_array($pk)) { $pk = static::hashPk($pk);
$pk = implode('-', $pk); $db->executeCommand('LREM', array(static::tableName(), 0, $pk));
} $attributeKeys[] = static::tableName() . ':a:' . $pk;
$db->executeCommand('LREM', array(static::tableName(), 0, $pk)); // TODO escape PK glue
$attributeKeys[] = static::tableName() . ':a:' . $pk; // TODO escape PK glue
} }
return $db->executeCommand('DEL', $attributeKeys); return $db->executeCommand('DEL', $attributeKeys);// TODO make this atomic or document as NOT
} }
/** /**
......
...@@ -12,6 +12,7 @@ namespace yii\db\redis; ...@@ -12,6 +12,7 @@ namespace yii\db\redis;
use \yii\base\Component; use \yii\base\Component;
use yii\base\InvalidConfigException; use yii\base\InvalidConfigException;
use \yii\db\Exception; use \yii\db\Exception;
use yii\helpers\Inflector;
use yii\helpers\StringHelper; use yii\helpers\StringHelper;
/** /**
...@@ -337,7 +338,7 @@ class Connection extends Component ...@@ -337,7 +338,7 @@ class Connection extends Component
*/ */
public function __call($name, $params) public function __call($name, $params)
{ {
$redisCommand = strtoupper(StringHelper::camel2words($name, false)); $redisCommand = strtoupper(Inflector::camel2words($name, false));
if (in_array($redisCommand, $this->redisCommands)) { if (in_array($redisCommand, $this->redisCommands)) {
return $this->executeCommand($name, $params); return $this->executeCommand($name, $params);
} else { } else {
......
<?php
/**
*
*
* @author Carsten Brandt <mail@cebe.cc>
*/
namespace yii\db\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 = array($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
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
namespace yiiunit\data\ar\redis; namespace yiiunit\data\ar\redis;
use yii\db\TableSchema; use yii\db\redis\RecordSchema;
class Customer extends ActiveRecord class Customer extends ActiveRecord
{ {
...@@ -21,7 +21,8 @@ class Customer extends ActiveRecord ...@@ -21,7 +21,8 @@ class Customer extends ActiveRecord
public static function getTableSchema() public static function getTableSchema()
{ {
return new TableSchema(array( return new RecordSchema(array(
'name' => 'customer',
'primaryKey' => array('id'), 'primaryKey' => array('id'),
'columns' => array( 'columns' => array(
'id' => 'integer', 'id' => 'integer',
......
...@@ -2,19 +2,19 @@ ...@@ -2,19 +2,19 @@
namespace yiiunit\data\ar\redis; namespace yiiunit\data\ar\redis;
use yii\db\TableSchema; use yii\db\redis\RecordSchema;
class Item extends ActiveRecord class Item extends ActiveRecord
{ {
public static function tableName()
{
return 'tbl_item';
}
public static function getTableSchema() public static function getTableSchema()
{ {
return new TableSchema(array( return new RecordSchema(array(
'name' => 'item',
'primaryKey' => array('id'), 'primaryKey' => array('id'),
'sequenceName' => 'id',
'foreignKeys' => array(
// TODO for defining relations
),
'columns' => array( 'columns' => array(
'id' => 'integer', 'id' => 'integer',
'name' => 'string', 'name' => 'string',
......
...@@ -2,15 +2,10 @@ ...@@ -2,15 +2,10 @@
namespace yiiunit\data\ar\redis; namespace yiiunit\data\ar\redis;
use yii\db\TableSchema; use yii\db\redis\RecordSchema;
class Order extends ActiveRecord class Order extends ActiveRecord
{ {
public static function tableName()
{
return 'tbl_order';
}
public function getCustomer() public function getCustomer()
{ {
return $this->hasOne('Customer', array('id' => 'customer_id')); return $this->hasOne('Customer', array('id' => 'customer_id'));
...@@ -49,7 +44,8 @@ class Order extends ActiveRecord ...@@ -49,7 +44,8 @@ class Order extends ActiveRecord
public static function getTableSchema() public static function getTableSchema()
{ {
return new TableSchema(array( return new RecordSchema(array(
'name' => 'orders',
'primaryKey' => array('id'), 'primaryKey' => array('id'),
'columns' => array( 'columns' => array(
'id' => 'integer', 'id' => 'integer',
......
...@@ -2,15 +2,10 @@ ...@@ -2,15 +2,10 @@
namespace yiiunit\data\ar\redis; namespace yiiunit\data\ar\redis;
use yii\db\TableSchema; use yii\db\redis\RecordSchema;
class OrderItem extends ActiveRecord class OrderItem extends ActiveRecord
{ {
public static function tableName()
{
return 'tbl_order_item';
}
public function getOrder() public function getOrder()
{ {
return $this->hasOne('Order', array('id' => 'order_id')); return $this->hasOne('Order', array('id' => 'order_id'));
...@@ -23,7 +18,8 @@ class OrderItem extends ActiveRecord ...@@ -23,7 +18,8 @@ class OrderItem extends ActiveRecord
public static function getTableSchema() public static function getTableSchema()
{ {
return new TableSchema(array( return new RecordSchema(array(
'name' => 'order_item',
'primaryKey' => array('order_id', 'item_id'), 'primaryKey' => array('order_id', 'item_id'),
'columns' => array( 'columns' => array(
'order_id' => 'integer', 'order_id' => 'integer',
......
...@@ -9,10 +9,31 @@ use yiiunit\data\ar\redis\OrderItem; ...@@ -9,10 +9,31 @@ use yiiunit\data\ar\redis\OrderItem;
use yiiunit\data\ar\redis\Order; use yiiunit\data\ar\redis\Order;
use yiiunit\data\ar\redis\Item; use yiiunit\data\ar\redis\Item;
/*
Users:
1 - user1
2 - user2
3 - user3
Items: 1-5
Order: 1-3
OrderItem:
1 - order: 1
2 - order: 1
3 - order: 2
4 - order: 2
5 - order: 2
6 - order: 3
*/
class ActiveRecordTest extends RedisTestCase class ActiveRecordTest extends RedisTestCase
{ {
public function setUp() public function setUp()
{ {
parent::setUp();
ActiveRecord::$db = $this->getConnection(); ActiveRecord::$db = $this->getConnection();
$customer = new Customer(); $customer = new Customer();
...@@ -72,8 +93,6 @@ class ActiveRecordTest extends RedisTestCase ...@@ -72,8 +93,6 @@ class ActiveRecordTest extends RedisTestCase
$orderItem = new OrderItem(); $orderItem = new OrderItem();
$orderItem->setAttributes(array('order_id' => 3, 'item_id' => 2, 'quantity' => 1, 'subtotal' => 40.0), false); $orderItem->setAttributes(array('order_id' => 3, 'item_id' => 2, 'quantity' => 1, 'subtotal' => 40.0), false);
$orderItem->save(false); $orderItem->save(false);
parent::setUp();
} }
public function testFind() public function testFind()
...@@ -332,6 +351,8 @@ class ActiveRecordTest extends RedisTestCase ...@@ -332,6 +351,8 @@ class ActiveRecordTest extends RedisTestCase
$this->assertFalse($customer->isNewRecord); $this->assertFalse($customer->isNewRecord);
} }
// TODO test serial column incr
public function testUpdate() public function testUpdate()
{ {
// save // save
......
...@@ -4,7 +4,7 @@ namespace yiiunit\framework\db\redis; ...@@ -4,7 +4,7 @@ namespace yiiunit\framework\db\redis;
use yii\db\redis\Connection; use yii\db\redis\Connection;
class ConnectionTest extends RedisTestCase class RedisConnectionTest extends RedisTestCase
{ {
/** /**
* Empty DSN should throw exception * Empty DSN should throw exception
......
...@@ -12,7 +12,10 @@ class RedisTestCase extends TestCase ...@@ -12,7 +12,10 @@ class RedisTestCase extends TestCase
{ {
protected function setUp() protected function setUp()
{ {
$params = $this->getParam('redis'); $this->mockApplication();
$databases = $this->getParam('databases');
$params = isset($databases['redis']) ? $databases['redis'] : null;
if ($params === null || !isset($params['dsn'])) { if ($params === null || !isset($params['dsn'])) {
$this->markTestSkipped('No redis server connection configured.'); $this->markTestSkipped('No redis server connection configured.');
} }
...@@ -34,7 +37,8 @@ class RedisTestCase extends TestCase ...@@ -34,7 +37,8 @@ class RedisTestCase extends TestCase
*/ */
public function getConnection($reset = true) public function getConnection($reset = true)
{ {
$params = $this->getParam('redis'); $databases = $this->getParam('databases');
$params = isset($databases['redis']) ? $databases['redis'] : array();
$db = new \yii\db\redis\Connection; $db = new \yii\db\redis\Connection;
$db->dsn = $params['dsn']; $db->dsn = $params['dsn'];
$db->password = $params['password']; $db->password = $params['password'];
......
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