Browse Source

Redis AR WIP

- introduced RecordSchema class for schema definition
tags/2.0.0-beta
Carsten Brandt 11 years ago
parent
commit
e3df19d984
  1. 2
      framework/yii/db/ActiveRecord.php
  2. 4
      framework/yii/db/redis/ActiveQuery.php
  3. 53
      framework/yii/db/redis/ActiveRecord.php
  4. 3
      framework/yii/db/redis/Connection.php
  5. 53
      framework/yii/db/redis/RecordSchema.php
  6. 5
      tests/unit/data/ar/redis/Customer.php
  7. 14
      tests/unit/data/ar/redis/Item.php
  8. 10
      tests/unit/data/ar/redis/Order.php
  9. 10
      tests/unit/data/ar/redis/OrderItem.php
  10. 25
      tests/unit/framework/db/redis/ActiveRecordTest.php
  11. 2
      tests/unit/framework/db/redis/RedisConnectionTest.php
  12. 8
      tests/unit/framework/db/redis/RedisTestCase.php

2
framework/yii/db/ActiveRecord.php

@ -268,7 +268,7 @@ class ActiveRecord extends Model
*/
public static function tableName()
{
return 'tbl_' . Inflector::camel2id(StringHelper::basename(get_called_class()), '_');
return static::getTableSchema()->name;
}
/**

4
framework/yii/db/redis/ActiveQuery.php

@ -112,7 +112,7 @@ class ActiveQuery extends \yii\base\Component
}
$rows = array();
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
$data = $db->executeCommand('HGETALL', array($key));
$row = array();
@ -148,7 +148,7 @@ class ActiveQuery extends \yii\base\Component
$primaryKeys = $db->executeCommand('LRANGE', array($modelClass::tableName(), $start, $start + 1));
}
$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
$data = $db->executeCommand('HGETALL', array($key));
if ($data === array()) {

53
framework/yii/db/redis/ActiveRecord.php

@ -20,7 +20,7 @@ use yii\db\TableSchema;
/**
* 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>
* @since 2.0
@ -68,31 +68,19 @@ abstract class ActiveRecord extends \yii\db\ActiveRecord
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.
*
* 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
* @inheritdoc
*/
public static function findBySql($sql, $params = array())
{
throw new NotSupportedException('findBySql() is not supported by redis ActiveRecord');
}
/**
* Creates an [[ActiveQuery]] instance.
* 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
));
}
/**
* 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.
@ -114,6 +110,7 @@ abstract class ActiveRecord extends \yii\db\ActiveRecord
*/
public static function getTableSchema()
{
// TODO should be cached
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
}
// }
// 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
$args = array($key);
foreach($values as $attribute => $value) {
@ -216,7 +213,7 @@ abstract class ActiveRecord extends \yii\db\ActiveRecord
}
$n=0;
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
$args = array($key);
foreach($attributes as $attribute => $value) {
@ -257,7 +254,7 @@ abstract class ActiveRecord extends \yii\db\ActiveRecord
}
$n=0;
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) {
$db->executeCommand('HINCRBY', array($key, $attribute, $value));
}
@ -292,13 +289,11 @@ abstract class ActiveRecord extends \yii\db\ActiveRecord
}
$attributeKeys = array();
foreach($condition as $pk) {
if (is_array($pk)) {
$pk = implode('-', $pk);
}
$db->executeCommand('LREM', array(static::tableName(), 0, $pk)); // TODO escape PK glue
$attributeKeys[] = static::tableName() . ':a:' . $pk; // TODO escape PK glue
$pk = static::hashPk($pk);
$db->executeCommand('LREM', array(static::tableName(), 0, $pk));
$attributeKeys[] = static::tableName() . ':a:' . $pk;
}
return $db->executeCommand('DEL', $attributeKeys);
return $db->executeCommand('DEL', $attributeKeys);// TODO make this atomic or document as NOT
}
/**

3
framework/yii/db/redis/Connection.php

@ -12,6 +12,7 @@ namespace yii\db\redis;
use \yii\base\Component;
use yii\base\InvalidConfigException;
use \yii\db\Exception;
use yii\helpers\Inflector;
use yii\helpers\StringHelper;
/**
@ -337,7 +338,7 @@ class Connection extends Component
*/
public function __call($name, $params)
{
$redisCommand = strtoupper(StringHelper::camel2words($name, false));
$redisCommand = strtoupper(Inflector::camel2words($name, false));
if (in_array($redisCommand, $this->redisCommands)) {
return $this->executeCommand($name, $params);
} else {

53
framework/yii/db/redis/RecordSchema.php

@ -0,0 +1,53 @@
<?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.');
}
}
}
}

5
tests/unit/data/ar/redis/Customer.php

@ -2,7 +2,7 @@
namespace yiiunit\data\ar\redis;
use yii\db\TableSchema;
use yii\db\redis\RecordSchema;
class Customer extends ActiveRecord
{
@ -21,7 +21,8 @@ class Customer extends ActiveRecord
public static function getTableSchema()
{
return new TableSchema(array(
return new RecordSchema(array(
'name' => 'customer',
'primaryKey' => array('id'),
'columns' => array(
'id' => 'integer',

14
tests/unit/data/ar/redis/Item.php

@ -2,19 +2,19 @@
namespace yiiunit\data\ar\redis;
use yii\db\TableSchema;
use yii\db\redis\RecordSchema;
class Item extends ActiveRecord
{
public static function tableName()
{
return 'tbl_item';
}
public static function getTableSchema()
{
return new TableSchema(array(
return new RecordSchema(array(
'name' => 'item',
'primaryKey' => array('id'),
'sequenceName' => 'id',
'foreignKeys' => array(
// TODO for defining relations
),
'columns' => array(
'id' => 'integer',
'name' => 'string',

10
tests/unit/data/ar/redis/Order.php

@ -2,15 +2,10 @@
namespace yiiunit\data\ar\redis;
use yii\db\TableSchema;
use yii\db\redis\RecordSchema;
class Order extends ActiveRecord
{
public static function tableName()
{
return 'tbl_order';
}
public function getCustomer()
{
return $this->hasOne('Customer', array('id' => 'customer_id'));
@ -49,7 +44,8 @@ class Order extends ActiveRecord
public static function getTableSchema()
{
return new TableSchema(array(
return new RecordSchema(array(
'name' => 'orders',
'primaryKey' => array('id'),
'columns' => array(
'id' => 'integer',

10
tests/unit/data/ar/redis/OrderItem.php

@ -2,15 +2,10 @@
namespace yiiunit\data\ar\redis;
use yii\db\TableSchema;
use yii\db\redis\RecordSchema;
class OrderItem extends ActiveRecord
{
public static function tableName()
{
return 'tbl_order_item';
}
public function getOrder()
{
return $this->hasOne('Order', array('id' => 'order_id'));
@ -23,7 +18,8 @@ class OrderItem extends ActiveRecord
public static function getTableSchema()
{
return new TableSchema(array(
return new RecordSchema(array(
'name' => 'order_item',
'primaryKey' => array('order_id', 'item_id'),
'columns' => array(
'order_id' => 'integer',

25
tests/unit/framework/db/redis/ActiveRecordTest.php

@ -9,10 +9,31 @@ use yiiunit\data\ar\redis\OrderItem;
use yiiunit\data\ar\redis\Order;
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
{
public function setUp()
{
parent::setUp();
ActiveRecord::$db = $this->getConnection();
$customer = new Customer();
@ -72,8 +93,6 @@ class ActiveRecordTest extends RedisTestCase
$orderItem = new OrderItem();
$orderItem->setAttributes(array('order_id' => 3, 'item_id' => 2, 'quantity' => 1, 'subtotal' => 40.0), false);
$orderItem->save(false);
parent::setUp();
}
public function testFind()
@ -332,6 +351,8 @@ class ActiveRecordTest extends RedisTestCase
$this->assertFalse($customer->isNewRecord);
}
// TODO test serial column incr
public function testUpdate()
{
// save

2
tests/unit/framework/db/redis/ConnectionTest.php → tests/unit/framework/db/redis/RedisConnectionTest.php

@ -4,7 +4,7 @@ namespace yiiunit\framework\db\redis;
use yii\db\redis\Connection;
class ConnectionTest extends RedisTestCase
class RedisConnectionTest extends RedisTestCase
{
/**
* Empty DSN should throw exception

8
tests/unit/framework/db/redis/RedisTestCase.php

@ -12,7 +12,10 @@ class RedisTestCase extends TestCase
{
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'])) {
$this->markTestSkipped('No redis server connection configured.');
}
@ -34,7 +37,8 @@ class RedisTestCase extends TestCase
*/
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->dsn = $params['dsn'];
$db->password = $params['password'];

Loading…
Cancel
Save