From 142ea1f98fcaf47a483fa7de2fac2871f18f324d Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Tue, 24 Sep 2013 19:10:49 +0200 Subject: [PATCH] relation support and unit tests --- framework/yii/redis/ActiveRecord.php | 196 ------------------- tests/unit/framework/redis/ActiveRecordTest.php | 243 +++++++++++------------- 2 files changed, 106 insertions(+), 333 deletions(-) diff --git a/framework/yii/redis/ActiveRecord.php b/framework/yii/redis/ActiveRecord.php index d0005e9..7b91b14 100644 --- a/framework/yii/redis/ActiveRecord.php +++ b/framework/yii/redis/ActiveRecord.php @@ -404,200 +404,4 @@ abstract class ActiveRecord extends \yii\db\ActiveRecord 'multiple' => true, )); } - - /** - * Returns the relation object with the specified name. - * A relation is defined by a getter method which returns an [[ActiveRelation]] object. - * It can be declared in either the Active Record class itself or one of its behaviors. - * @param string $name the relation name - * @return ActiveRelation the relation object - * @throws InvalidParamException if the named relation does not exist. - */ - public function getRelation($name) - { - $getter = 'get' . $name; - try { - $relation = $this->$getter(); - if ($relation instanceof ActiveRelation) { - return $relation; - } - } catch (UnknownMethodException $e) { - } - throw new InvalidParamException(get_class($this) . ' has no relation named "' . $name . '".'); - } - - /** - * Establishes the relationship between two models. - * - * The relationship is established by setting the foreign key value(s) in one model - * to be the corresponding primary key value(s) in the other model. - * The model with the foreign key will be saved into database without performing validation. - * - * If the relationship involves a pivot table, a new row will be inserted into the - * pivot table which contains the primary key values from both models. - * - * Note that this method requires that the primary key value is not null. - * - * @param string $name the name of the relationship - * @param ActiveRecord $model the model to be linked with the current one. - * @param array $extraColumns additional column values to be saved into the pivot table. - * This parameter is only meaningful for a relationship involving a pivot table - * (i.e., a relation set with `[[ActiveRelation::via()]]` or `[[ActiveRelation::viaTable()]]`.) - * @throws InvalidCallException if the method is unable to link two models. - */ - public function link($name, $model, $extraColumns = array()) - { - $relation = $this->getRelation($name); - - if ($relation->via !== null) { - // TODO - - - } else { - $p1 = $model->isPrimaryKey(array_keys($relation->link)); - $p2 = $this->isPrimaryKey(array_values($relation->link)); - if ($p1 && $p2) { - if ($this->getIsNewRecord() && $model->getIsNewRecord()) { - throw new InvalidCallException('Unable to link models: both models are newly created.'); - } elseif ($this->getIsNewRecord()) { - $this->bindModels(array_flip($relation->link), $this, $model); - } else { - $this->bindModels($relation->link, $model, $this); - } - } elseif ($p1) { - $this->bindModels(array_flip($relation->link), $this, $model); - } elseif ($p2) { - $this->bindModels($relation->link, $model, $this); - } else { - throw new InvalidCallException('Unable to link models: the link does not involve any primary key.'); - } - } - - // update lazily loaded related objects - if (!$relation->multiple) { - $this->_related[$name] = $model; - } elseif (isset($this->_related[$name])) { - if ($relation->indexBy !== null) { - $indexBy = $relation->indexBy; - $this->_related[$name][$model->$indexBy] = $model; - } else { - $this->_related[$name][] = $model; - } - } - } - - /** - * @param array $link - * @param ActiveRecord $foreignModel - * @param ActiveRecord $primaryModel - * @throws InvalidCallException - */ - private function bindModels($link, $foreignModel, $primaryModel) - { - foreach ($link as $fk => $pk) { - $value = $primaryModel->$pk; - if ($value === null) { - throw new InvalidCallException('Unable to link models: the primary key of ' . get_class($primaryModel) . ' is null.'); - } - $foreignModel->$fk = $value; - } - $foreignModel->save(false); - } - - /** - * Destroys the relationship between two models. - * - * The model with the foreign key of the relationship will be deleted if `$delete` is true. - * Otherwise, the foreign key will be set null and the model will be saved without validation. - * - * @param string $name the name of the relationship. - * @param ActiveRecord $model the model to be unlinked from the current one. - * @param boolean $delete whether to delete the model that contains the foreign key. - * If false, the model's foreign key will be set null and saved. - * If true, the model containing the foreign key will be deleted. - * @throws InvalidCallException if the models cannot be unlinked - */ - public function unlink($name, $model, $delete = false) - { - // TODO - $relation = $this->getRelation($name); - - if ($relation->via !== null) { - if (is_array($relation->via)) { - /** @var $viaRelation ActiveRelation */ - list($viaName, $viaRelation) = $relation->via; - /** @var $viaClass ActiveRecord */ - $viaClass = $viaRelation->modelClass; - $viaTable = $viaClass::tableName(); - unset($this->_related[strtolower($viaName)]); - } else { - $viaRelation = $relation->via; - $viaTable = reset($relation->via->from); - } - $columns = array(); - foreach ($viaRelation->link as $a => $b) { - $columns[$a] = $this->$b; - } - foreach ($relation->link as $a => $b) { - $columns[$b] = $model->$a; - } - $command = static::getDb()->createCommand(); - if ($delete) { - $command->delete($viaTable, $columns)->execute(); - } else { - $nulls = array(); - foreach (array_keys($columns) as $a) { - $nulls[$a] = null; - } - $command->update($viaTable, $nulls, $columns)->execute(); - } - } else { - $p1 = $model->isPrimaryKey(array_keys($relation->link)); - $p2 = $this->isPrimaryKey(array_values($relation->link)); - if ($p1 && $p2 || $p2) { - foreach ($relation->link as $a => $b) { - $model->$a = null; - } - $delete ? $model->delete() : $model->save(false); - } elseif ($p1) { - foreach ($relation->link as $b) { - $this->$b = null; - } - $delete ? $this->delete() : $this->save(false); - } else { - throw new InvalidCallException('Unable to unlink models: the link does not involve any primary key.'); - } - } - - if (!$relation->multiple) { - unset($this->_related[$name]); - } elseif (isset($this->_related[$name])) { - /** @var $b ActiveRecord */ - foreach ($this->_related[$name] as $a => $b) { - if ($model->getPrimaryKey() == $b->getPrimaryKey()) { - unset($this->_related[$name][$a]); - } - } - } - } - - /** - * TODO duplicate code, refactor - * @param array $keys - * @return boolean - */ - private function isPrimaryKey($keys) - { - $pks = $this->primaryKey(); - foreach ($keys as $key) { - if (!in_array($key, $pks, true)) { - return false; - } - } - return true; - } - - - - // TODO implement link and unlink } diff --git a/tests/unit/framework/redis/ActiveRecordTest.php b/tests/unit/framework/redis/ActiveRecordTest.php index e4205c2..a7429b8 100644 --- a/tests/unit/framework/redis/ActiveRecordTest.php +++ b/tests/unit/framework/redis/ActiveRecordTest.php @@ -123,9 +123,11 @@ class ActiveRecordTest extends RedisTestCase $this->assertEquals('user2', $customerName); // find by column values - $customer = Customer::find(array('id' => 2)); + $customer = Customer::find(array('id' => 2, 'name' => 'user2')); $this->assertTrue($customer instanceof Customer); $this->assertEquals('user2', $customer->name); + $customer = Customer::find(array('id' => 2, 'name' => 'user1')); + $this->assertNull($customer); $customer = Customer::find(array('id' => 5)); $this->assertNull($customer); @@ -135,13 +137,14 @@ class ActiveRecordTest extends RedisTestCase $this->assertEquals(2, $customer->id); // find count, sum, average, min, max, scalar + $this->assertEquals(3, Customer::find()->count()); $this->assertEquals(6, Customer::find()->sum('id')); $this->assertEquals(2, Customer::find()->average('id')); $this->assertEquals(1, Customer::find()->min('id')); $this->assertEquals(3, Customer::find()->max('id')); // scope -// $this->assertEquals(2, Customer::find()->active()->count()); + $this->assertEquals(2, Customer::find()->active()->count()); // asArray $customer = Customer::find()->where(array('id' => 2))->asArray()->one(); @@ -261,125 +264,78 @@ class ActiveRecordTest extends RedisTestCase $this->assertEquals(3, $orders[0]->id); } -// public function testFindEager() -// { -// $customers = Customer::find()->with('orders')->all(); -// $this->assertEquals(3, count($customers)); -// $this->assertEquals(1, count($customers[0]->orders)); -// $this->assertEquals(2, count($customers[1]->orders)); -// } - -// public function testFindLazyVia() -// { -// /** @var $order Order */ -// $order = Order::find(1); -// $this->assertEquals(1, $order->id); -// $this->assertEquals(2, count($order->items)); -// $this->assertEquals(1, $order->items[0]->id); -// $this->assertEquals(2, $order->items[1]->id); -// -// $order = Order::find(1); -// $order->id = 100; -// $this->assertEquals(array(), $order->items); -// } - -// public function testFindEagerViaRelation() -// { -// $orders = Order::find()->with('items')->all(); -// $this->assertEquals(3, count($orders)); -// $order = $orders[0]; -// $this->assertEquals(1, $order->id); -// $this->assertEquals(2, count($order->items)); -// $this->assertEquals(1, $order->items[0]->id); -// $this->assertEquals(2, $order->items[1]->id); -// } + public function testFindEager() + { + $customers = Customer::find()->with('orders')->all(); + $this->assertEquals(3, count($customers)); + $this->assertEquals(1, count($customers[0]->orders)); + $this->assertEquals(2, count($customers[1]->orders)); + } -/* public function testFindLazyViaTable() + public function testFindLazyVia() { - /** @var $order Order * / + /** @var $order Order */ $order = Order::find(1); $this->assertEquals(1, $order->id); - $this->assertEquals(2, count($order->books)); + $this->assertEquals(2, count($order->items)); $this->assertEquals(1, $order->items[0]->id); $this->assertEquals(2, $order->items[1]->id); - $order = Order::find(2); - $this->assertEquals(2, $order->id); - $this->assertEquals(0, count($order->books)); + $order = Order::find(1); + $order->id = 100; + $this->assertEquals(array(), $order->items); } - public function testFindEagerViaTable() + public function testFindEagerViaRelation() { - $orders = Order::find()->with('books')->all(); + $orders = Order::find()->with('items')->all(); $this->assertEquals(3, count($orders)); - $order = $orders[0]; $this->assertEquals(1, $order->id); - $this->assertEquals(2, count($order->books)); - $this->assertEquals(1, $order->books[0]->id); - $this->assertEquals(2, $order->books[1]->id); - - $order = $orders[1]; - $this->assertEquals(2, $order->id); - $this->assertEquals(0, count($order->books)); - - $order = $orders[2]; - $this->assertEquals(3, $order->id); - $this->assertEquals(1, count($order->books)); - $this->assertEquals(2, $order->books[0]->id); - }*/ - -// public function testFindNestedRelation() -// { -// $customers = Customer::find()->with('orders', 'orders.items')->all(); -// $this->assertEquals(3, count($customers)); -// $this->assertEquals(1, count($customers[0]->orders)); -// $this->assertEquals(2, count($customers[1]->orders)); -// $this->assertEquals(0, count($customers[2]->orders)); -// $this->assertEquals(2, count($customers[0]->orders[0]->items)); -// $this->assertEquals(3, count($customers[1]->orders[0]->items)); -// $this->assertEquals(1, count($customers[1]->orders[1]->items)); -// } - -// public function testLink() -// { -// $customer = Customer::find(2); -// $this->assertEquals(2, count($customer->orders)); -// -// // has many -// $order = new Order; -// $order->total = 100; -// $this->assertTrue($order->isNewRecord); -// $customer->link('orders', $order); -// $this->assertEquals(3, count($customer->orders)); -// $this->assertFalse($order->isNewRecord); -// $this->assertEquals(3, count($customer->getOrders()->all())); -// $this->assertEquals(2, $order->customer_id); -// -// // belongs to -// $order = new Order; -// $order->total = 100; -// $this->assertTrue($order->isNewRecord); -// $customer = Customer::find(1); -// $this->assertNull($order->customer); -// $order->link('customer', $customer); -// $this->assertFalse($order->isNewRecord); -// $this->assertEquals(1, $order->customer_id); -// $this->assertEquals(1, $order->customer->id); -// -// // via table -// $order = Order::find(2); -// $this->assertEquals(0, count($order->books)); -// $orderItem = OrderItem::find(array('order_id' => 2, 'item_id' => 1)); -// $this->assertNull($orderItem); -// $item = Item::find(1); -// $order->link('books', $item, array('quantity' => 10, 'subtotal' => 100)); -// $this->assertEquals(1, count($order->books)); -// $orderItem = OrderItem::find(array('order_id' => 2, 'item_id' => 1)); -// $this->assertTrue($orderItem instanceof OrderItem); -// $this->assertEquals(10, $orderItem->quantity); -// $this->assertEquals(100, $orderItem->subtotal); -// + $this->assertEquals(2, count($order->items)); + $this->assertEquals(1, $order->items[0]->id); + $this->assertEquals(2, $order->items[1]->id); + } + + public function testFindNestedRelation() + { + $customers = Customer::find()->with('orders', 'orders.items')->all(); + $this->assertEquals(3, count($customers)); + $this->assertEquals(1, count($customers[0]->orders)); + $this->assertEquals(2, count($customers[1]->orders)); + $this->assertEquals(0, count($customers[2]->orders)); + $this->assertEquals(2, count($customers[0]->orders[0]->items)); + $this->assertEquals(3, count($customers[1]->orders[0]->items)); + $this->assertEquals(1, count($customers[1]->orders[1]->items)); + } + + public function testLink() + { + $customer = Customer::find(2); + $this->assertEquals(2, count($customer->orders)); + + // has many + $order = new Order; + $order->total = 100; + $this->assertTrue($order->isNewRecord); + $customer->link('orders', $order); + $this->assertEquals(3, count($customer->orders)); + $this->assertFalse($order->isNewRecord); + $this->assertEquals(3, count($customer->getOrders()->all())); + $this->assertEquals(2, $order->customer_id); + + // belongs to + $order = new Order; + $order->total = 100; + $this->assertTrue($order->isNewRecord); + $customer = Customer::find(1); + $this->assertNull($order->customer); + $order->link('customer', $customer); + $this->assertFalse($order->isNewRecord); + $this->assertEquals(1, $order->customer_id); + $this->assertEquals(1, $order->customer->id); + + // TODO support via // // via model // $order = Order::find(1); // $this->assertEquals(2, count($order->items)); @@ -394,17 +350,18 @@ class ActiveRecordTest extends RedisTestCase // $this->assertTrue($orderItem instanceof OrderItem); // $this->assertEquals(10, $orderItem->quantity); // $this->assertEquals(100, $orderItem->subtotal); -// } - -// public function testUnlink() -// { -// // has many -// $customer = Customer::find(2); -// $this->assertEquals(2, count($customer->orders)); -// $customer->unlink('orders', $customer->orders[1], true); -// $this->assertEquals(1, count($customer->orders)); -// $this->assertNull(Order::find(3)); -// + } + + public function testUnlink() + { + // has many + $customer = Customer::find(2); + $this->assertEquals(2, count($customer->orders)); + $customer->unlink('orders', $customer->orders[1], true); + $this->assertEquals(1, count($customer->orders)); + $this->assertNull(Order::find(3)); + + // TODO support via // // via model // $order = Order::find(2); // $this->assertEquals(3, count($order->items)); @@ -412,14 +369,7 @@ class ActiveRecordTest extends RedisTestCase // $order->unlink('items', $order->items[2], true); // $this->assertEquals(2, count($order->items)); // $this->assertEquals(2, count($order->orderItems)); -// -// // via table -// $order = Order::find(1); -// $this->assertEquals(2, count($order->books)); -// $order->unlink('books', $order->books[1], true); -// $this->assertEquals(1, count($order->books)); -// $this->assertEquals(1, count($order->orderItems)); -// } + } public function testInsert() { @@ -453,16 +403,6 @@ class ActiveRecordTest extends RedisTestCase $customer2 = Customer::find(2); $this->assertEquals('user2x', $customer2->name); - // updateCounters - $pk = array('order_id' => 2, 'item_id' => 4); - $orderItem = OrderItem::find($pk); - $this->assertEquals(1, $orderItem->quantity); - $ret = $orderItem->updateCounters(array('quantity' => -1)); - $this->assertTrue($ret); - $this->assertEquals(0, $orderItem->quantity); - $orderItem = OrderItem::find($pk); - $this->assertEquals(0, $orderItem->quantity); - // updateAll $customer = Customer::find(3); $this->assertEquals('user3', $customer->name); @@ -472,8 +412,21 @@ class ActiveRecordTest extends RedisTestCase $this->assertEquals(1, $ret); $customer = Customer::find(3); $this->assertEquals('temp', $customer->name); + } + public function testUpdateCounters() + { // updateCounters + $pk = array('order_id' => 2, 'item_id' => 4); + $orderItem = OrderItem::find($pk); + $this->assertEquals(1, $orderItem->quantity); + $ret = $orderItem->updateCounters(array('quantity' => -1)); + $this->assertTrue($ret); + $this->assertEquals(0, $orderItem->quantity); + $orderItem = OrderItem::find($pk); + $this->assertEquals(0, $orderItem->quantity); + + // updateAllCounters $pk = array('order_id' => 1, 'item_id' => 2); $orderItem = OrderItem::find($pk); $this->assertEquals(2, $orderItem->quantity); @@ -487,6 +440,22 @@ class ActiveRecordTest extends RedisTestCase $this->assertEquals(30, $orderItem->subtotal); } + public function testUpdatePk() + { + // updateCounters + $pk = array('order_id' => 2, 'item_id' => 4); + $orderItem = OrderItem::find($pk); + $this->assertEquals(2, $orderItem->order_id); + $this->assertEquals(4, $orderItem->item_id); + + $orderItem->order_id = 2; + $orderItem->item_id = 10; + $orderItem->save(); + + $this->assertNull(OrderItem::find($pk)); + $this->assertNotNull(OrderItem::find(array('order_id' => 2, 'item_id' => 10))); + } + public function testDelete() { // delete