diff --git a/docs/api/db/ActiveRecord.md b/docs/api/db/ActiveRecord.md new file mode 100644 index 0000000..6cca26a --- /dev/null +++ b/docs/api/db/ActiveRecord.md @@ -0,0 +1,9 @@ +- `beforeInsert`. Raised before the record is saved. + By setting [[\yii\base\ModelEvent::isValid]] to be false, the normal [[save()]] will be stopped. +- `afterInsert`. Raised after the record is saved. +- `beforeUpdate`. Raised before the record is saved. + By setting [[\yii\base\ModelEvent::isValid]] to be false, the normal [[save()]] will be stopped. +- `afterUpdate`. Raised after the record is saved. +- `beforeDelete`. Raised before the record is deleted. + By setting [[\yii\base\ModelEvent::isValid]] to be false, the normal [[delete()]] process will be stopped. +- `afterDelete`. Raised after the record is deleted. diff --git a/framework/base/ModelBehavior.php b/framework/base/ModelBehavior.php deleted file mode 100644 index 7332189..0000000 --- a/framework/base/ModelBehavior.php +++ /dev/null @@ -1,60 +0,0 @@ - - * @since 2.0 - */ -class ModelBehavior extends Behavior -{ - /** - * Declares event handlers for the owner's events. - * The default implementation returns the following event handlers: - * - * - `beforeValidate` event - * - `afterValidate` event - * - * You may override these event handler methods to respond to the corresponding owner events. - * @return array events (array keys) and the corresponding event handler methods (array values). - */ - public function events() - { - return array( - 'beforeValidate' => 'beforeValidate', - 'afterValidate' => 'afterValidate', - ); - } - - /** - * Responds to the owner's `beforeValidate` event. - * Override this method if you want to handle the `beforeValidate` event of the [[owner]]. - * You may set the [[ModelEvent::isValid|isValid]] property of the event parameter - * to be false to cancel the validation process. - * @param ModelEvent $event event parameter - */ - public function beforeValidate($event) - { - } - - /** - * Responds to the owner's `afterValidate` event. - * Override this method if you want to handle the `beforeValidate` event of the [[owner]]. - * @param Event $event event parameter - */ - public function afterValidate($event) - { - } -} diff --git a/framework/db/ActiveQuery.php b/framework/db/ActiveQuery.php index 7757716..0e9e5f3 100644 --- a/framework/db/ActiveQuery.php +++ b/framework/db/ActiveQuery.php @@ -68,7 +68,7 @@ class ActiveQuery extends Query if ($rows !== array()) { $models = $this->createModels($rows); if (!empty($this->with)) { - $this->fetchRelatedModels($models, $this->with); + $this->populateRelations($models, $this->with); } return $models; } else { @@ -92,7 +92,7 @@ class ActiveQuery extends Query $model = $class::create($row); if (!empty($this->with)) { $models = array($model); - $this->fetchRelatedModels($models, $this->with); + $this->populateRelations($models, $this->with); $model = $models[0]; } return $model; @@ -216,7 +216,7 @@ class ActiveQuery extends Query return $models; } - protected function fetchRelatedModels(&$models, $with) + protected function populateRelations(&$models, $with) { $primaryModel = new $this->modelClass; $relations = $this->normalizeRelations($primaryModel, $with); diff --git a/framework/db/ActiveRecord.php b/framework/db/ActiveRecord.php index 44b5a57..c09b950 100644 --- a/framework/db/ActiveRecord.php +++ b/framework/db/ActiveRecord.php @@ -11,9 +11,9 @@ namespace yii\db; use yii\base\Model; -use yii\base\Event; use yii\base\ModelEvent; use yii\base\BadMethodException; +use yii\base\BadParamException; use yii\db\Exception; use yii\db\Connection; use yii\db\TableSchema; @@ -23,23 +23,12 @@ use yii\util\StringHelper; /** * ActiveRecord is the base class for classes representing relational data. * - * @author Qiang Xue - * @since 2.0 + * @include @yii/db/ActiveRecord.md * * @property array $attributes attribute values indexed by attribute names * - * ActiveRecord provides a set of events for further customization: - * - * - `beforeInsert`. Raised before the record is saved. - * By setting [[\yii\base\ModelEvent::isValid]] to be false, the normal [[save()]] will be stopped. - * - `afterInsert`. Raised after the record is saved. - * - `beforeUpdate`. Raised before the record is saved. - * By setting [[\yii\base\ModelEvent::isValid]] to be false, the normal [[save()]] will be stopped. - * - `afterUpdate`. Raised after the record is saved. - * - `beforeDelete`. Raised before the record is deleted. - * By setting [[\yii\base\ModelEvent::isValid]] to be false, the normal [[delete()]] process will be stopped. - * - `afterDelete`. Raised after the record is deleted. - * + * @author Qiang Xue + * @since 2.0 */ abstract class ActiveRecord extends Model { @@ -620,9 +609,6 @@ abstract class ActiveRecord extends Model */ public function updateCounters($counters) { - if ($this->getIsNewRecord()) { - throw new Exception('The active record cannot be updated because it is new.'); - } $this->updateAllCounters($counters, $this->getOldPrimaryKey(true)); foreach ($counters as $name => $value) { $this->_attributes[$name] += $value; @@ -787,7 +773,8 @@ abstract class ActiveRecord extends Model /** * Creates an active record with the given attributes. - * Note that this method does not save the record into database. + * This method is called by [[ActiveQuery]] to populate the query results + * into Active Records. * @param array $row attribute values (name => value) * @return ActiveRecord the newly created active record. */ @@ -833,9 +820,12 @@ abstract class ActiveRecord extends Model } /** - * @param string $name - * @return ActiveRelation - * @throws Exception + * 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 BadParamException if the named relation does not exist. */ public function getRelation($name) { @@ -847,7 +837,7 @@ abstract class ActiveRecord extends Model } } catch (BadMethodException $e) { } - throw new Exception('Unknown relation: ' . $name); + throw new BadParamException(get_class($this) . ' has no relation named "' . $name . '".'); } /** diff --git a/framework/db/ActiveRecordBehavior.php b/framework/db/ActiveRecordBehavior.php deleted file mode 100644 index 01b3bca..0000000 --- a/framework/db/ActiveRecordBehavior.php +++ /dev/null @@ -1,102 +0,0 @@ - - * @since 2.0 - */ -class ActiveRecordBehavior extends ModelBehavior -{ - /** - * Declares events and the corresponding event handler methods. - * If you override this method, make sure you merge the parent result to the return value. - * @return array events (array keys) and the corresponding event handler methods (array values). - * @see \yii\base\Behavior::events() - */ - public function events() - { - return array_merge(parent::events(), array( - 'beforeInsert' => 'beforeInsert', - 'afterInsert' => 'afterInsert', - 'beforeUpdate' => 'beforeUpdate', - 'afterUpdate' => 'afterUpdate', - 'beforeDelete' => 'beforeDelete', - 'afterDelete' => 'afterDelete', - )); - } - - /** - * Responds to the owner's `beforeInsert` event. - * Overrides this method if you want to handle the corresponding event of the owner. - * You may set the [[ModelEvent::isValid|isValid]] property of the event parameter - * to be false to quit the ActiveRecord inserting process. - * @param \yii\base\ModelEvent $event event parameter - */ - public function beforeInsert($event) - { - } - - /** - * Responds to the owner's `afterInsert` event. - * Overrides this method if you want to handle the corresponding event of the owner. - * @param \yii\base\ModelEvent $event event parameter - */ - public function afterInsert($event) - { - } - - /** - * Responds to the owner's `beforeUpdate` event. - * Overrides this method if you want to handle the corresponding event of the owner. - * You may set the [[ModelEvent::isValid|isValid]] property of the event parameter - * to be false to quit the ActiveRecord updating process. - * @param \yii\base\ModelEvent $event event parameter - */ - public function beforeUpdate($event) - { - } - - /** - * Responds to the owner's `afterUpdate` event. - * Overrides this method if you want to handle the corresponding event of the owner. - * @param \yii\base\ModelEvent $event event parameter - */ - public function afterUpdate($event) - { - } - - /** - * Responds to the owner's `beforeDelete` event. - * Overrides this method if you want to handle the corresponding event of the owner. - * You may set the [[ModelEvent::isValid|isValid]] property of the event parameter - * to be false to quit the ActiveRecord deleting process. - * @param \yii\base\ModelEvent $event event parameter - */ - public function beforeDelete($event) - { - } - - /** - * Responds to the owner's `afterDelete` event. - * Overrides this method if you want to handle the corresponding event of the owner. - * @param \yii\base\ModelEvent $event event parameter - */ - public function afterDelete($event) - { - } -} diff --git a/tests/unit/data/ar/Customer.php b/tests/unit/data/ar/Customer.php index 0f331aa..d242f4d 100644 --- a/tests/unit/data/ar/Customer.php +++ b/tests/unit/data/ar/Customer.php @@ -8,6 +8,8 @@ class Customer extends ActiveRecord const STATUS_ACTIVE = 1; const STATUS_INACTIVE = 2; + public $status2; + public static function tableName() { return 'tbl_customer'; diff --git a/tests/unit/framework/db/ActiveRecordTest.php b/tests/unit/framework/db/ActiveRecordTest.php index e1208a0..6cf0ea2 100644 --- a/tests/unit/framework/db/ActiveRecordTest.php +++ b/tests/unit/framework/db/ActiveRecordTest.php @@ -49,6 +49,11 @@ class ActiveRecordTest extends \yiiunit\MysqlTestCase $this->assertTrue($customer instanceof Customer); $this->assertEquals(2, $customer->id); + // find custom column + $customer = Customer::find()->select(array('*', '(status*2) AS status2')) + ->where(array('name' => 'user3'))->one(); + $this->assertEquals(3, $customer->id); + $this->assertEquals(4, $customer->status2); // find count $this->assertEquals(3, Customer::count()->value()); @@ -271,210 +276,86 @@ class ActiveRecordTest extends \yiiunit\MysqlTestCase $this->assertEquals(1, count($order->orderItems)); } -// public function testInsert() -// { -// $customer = new Customer; -// $customer->email = 'user4@example.com'; -// $customer->name = 'user4'; -// $customer->address = 'address4'; -// -// $this->assertNull($customer->id); -// $this->assertTrue($customer->isNewRecord); -// -// $customer->save(); -// -// $this->assertEquals(4, $customer->id); -// $this->assertFalse($customer->isNewRecord); -// } -// -// public function testUpdate() -// { -// // save -// $customer = Customer::find(2); -// $this->assertTrue($customer instanceof Customer); -// $this->assertEquals('user2', $customer->name); -// $this->assertFalse($customer->isNewRecord); -// $customer->name = 'user2x'; -// $customer->save(); -// $this->assertEquals('user2x', $customer->name); -// $this->assertFalse($customer->isNewRecord); -// $customer2 = Customer::find(2); -// $this->assertEquals('user2x', $customer2->name); -// -// // updateCounters -// $pk = array('order_id' => 2, 'item_id' => 4); -// $orderItem = OrderItem::find()->where($pk)->one(); -// $this->assertEquals(1, $orderItem->quantity); -// $ret = $orderItem->updateCounters(array('quantity' => -1)); -// $this->assertTrue($ret); -// $this->assertEquals(0, $orderItem->quantity); -// $orderItem = OrderItem::find()->where($pk)->one(); -// $this->assertEquals(0, $orderItem->quantity); -// -// // updateAll -// $customer = Customer::find(3); -// $this->assertEquals('user3', $customer->name); -// $ret = Customer::updateAll(array( -// 'name' => 'temp', -// ), array('id' => 3)); -// $this->assertEquals(1, $ret); -// $customer = Customer::find(3); -// $this->assertEquals('temp', $customer->name); -// -// // updateCounters -// $pk = array('order_id' => 1, 'item_id' => 2); -// $orderItem = OrderItem::find()->where($pk)->one(); -// $this->assertEquals(2, $orderItem->quantity); -// $ret = OrderItem::updateAllCounters(array( -// 'quantity' => 3, -// ), $pk); -// $this->assertEquals(1, $ret); -// $orderItem = OrderItem::find()->where($pk)->one(); -// $this->assertEquals(5, $orderItem->quantity); -// } -// -// public function testDelete() -// { -// // delete -// $customer = Customer::find(2); -// $this->assertTrue($customer instanceof Customer); -// $this->assertEquals('user2', $customer->name); -// $customer->delete(); -// $customer = Customer::find(2); -// $this->assertNull($customer); -// -// // deleteAll -// $customers = Customer::find()->all(); -// $this->assertEquals(2, count($customers)); -// $ret = Customer::deleteAll(); -// $this->assertEquals(2, $ret); -// $customers = Customer::find()->all(); -// $this->assertEquals(0, count($customers)); -// } -// -// public function testFind() -// { -// // find one -// $result = Customer::find(); -// $this->assertTrue($result instanceof ActiveQuery); -// $customer = $result->one(); -// $this->assertTrue($customer instanceof Customer); -// $this->assertEquals(1, $result->count); -// -// // find all -// $result = Customer::find(); -// $customers = $result->all(); -// $this->assertTrue(is_array($customers)); -// $this->assertEquals(3, count($customers)); -// $this->assertTrue($customers[0] instanceof Customer); -// $this->assertTrue($customers[1] instanceof Customer); -// $this->assertTrue($customers[2] instanceof Customer); -// $this->assertEquals(3, $result->count); -// $this->assertEquals(3, count($result)); -// -// // check count first -// $result = Customer::find(); -// $this->assertEquals(3, $result->count); -// $customer = $result->one(); -// $this->assertTrue($customer instanceof Customer); -// $this->assertEquals(3, $result->count); -// -// // iterator -// $result = Customer::find(); -// $count = 0; -// foreach ($result as $customer) { -// $this->assertTrue($customer instanceof Customer); -// $count++; -// } -// $this->assertEquals($count, $result->count); -// -// // array access -// $result = Customer::find(); -// $this->assertTrue($result[0] instanceof Customer); -// $this->assertTrue($result[1] instanceof Customer); -// $this->assertTrue($result[2] instanceof Customer); -// -// // find by a single primary key -// $customer = Customer::find(2); -// $this->assertTrue($customer instanceof Customer); -// $this->assertEquals('user2', $customer->name); -// -// // find by attributes -// $customer = Customer::find()->where(array('name' => 'user2'))->one(); -// $this->assertTrue($customer instanceof Customer); -// $this->assertEquals(2, $customer->id); -// -// // find by Query -// $query = array( -// 'where' => 'id=:id', -// 'params' => array(':id' => 2), -// ); -// $customer = Customer::find($query)->one(); -// $this->assertTrue($customer instanceof Customer); -// $this->assertEquals('user2', $customer->name); -// -// // find count -// $this->assertEquals(3, Customer::find()->count()); -// $this->assertEquals(3, Customer::count()); -// $this->assertEquals(2, Customer::count(array( -// 'where' => 'id=1 OR id=2', -// ))); -// } -// -// public function testFindBySql() -// { -// // find one -// $customer = Customer::findBySql('SELECT * FROM tbl_customer ORDER BY id DESC')->one(); -// $this->assertTrue($customer instanceof Customer); -// $this->assertEquals('user3', $customer->name); -// -// // find all -// $customers = Customer::findBySql('SELECT * FROM tbl_customer')->all(); -// $this->assertEquals(3, count($customers)); -// -// // find with parameter binding -// $customer = Customer::findBySql('SELECT * FROM tbl_customer WHERE id=:id', array(':id' => 2))->one(); -// $this->assertTrue($customer instanceof Customer); -// $this->assertEquals('user2', $customer->name); -// -// // count -// $query = Customer::findBySql('SELECT * FROM tbl_customer ORDER BY id DESC'); -// $query->one(); -// $this->assertEquals(3, $query->count); -// $query = Customer::findBySql('SELECT * FROM tbl_customer ORDER BY id DESC'); -// $this->assertEquals(3, $query->count); -// } -// -// public function testQueryMethods() -// { -// $customer = Customer::find()->where('id=:id', array(':id' => 2))->one(); -// $this->assertTrue($customer instanceof Customer); -// $this->assertEquals('user2', $customer->name); -// -// $customer = Customer::find()->where(array('name' => 'user3'))->one(); -// $this->assertTrue($customer instanceof Customer); -// $this->assertEquals('user3', $customer->name); -// -// $customer = Customer::find()->select('id')->orderBy('id DESC')->one(); -// $this->assertTrue($customer instanceof Customer); -// $this->assertEquals(3, $customer->id); -// $this->assertEquals(null, $customer->name); -// -// // scopes -// $customers = Customer::find()->active()->all(); -// $this->assertEquals(2, count($customers)); -// $customers = Customer::find(array( -// 'scopes' => array('active'), -// ))->all(); -// $this->assertEquals(2, count($customers)); -// -// // asArray -// $customers = Customer::find()->orderBy('id')->asArray()->all(); -// $this->assertEquals('user2', $customers[1]['name']); -// -// // index -// $customers = Customer::find()->orderBy('id')->index('name')->all(); -// $this->assertEquals(2, $customers['user2']['id']); -// } + public function testInsert() + { + $customer = new Customer; + $customer->email = 'user4@example.com'; + $customer->name = 'user4'; + $customer->address = 'address4'; + + $this->assertNull($customer->id); + $this->assertTrue($customer->isNewRecord); + + $customer->save(); + + $this->assertEquals(4, $customer->id); + $this->assertFalse($customer->isNewRecord); + } + + public function testUpdate() + { + // save + $customer = Customer::find(2); + $this->assertTrue($customer instanceof Customer); + $this->assertEquals('user2', $customer->name); + $this->assertFalse($customer->isNewRecord); + $customer->name = 'user2x'; + $customer->save(); + $this->assertEquals('user2x', $customer->name); + $this->assertFalse($customer->isNewRecord); + $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); + $ret = Customer::updateAll(array( + 'name' => 'temp', + ), array('id' => 3)); + $this->assertEquals(1, $ret); + $customer = Customer::find(3); + $this->assertEquals('temp', $customer->name); + + // updateCounters + $pk = array('order_id' => 1, 'item_id' => 2); + $orderItem = OrderItem::find($pk); + $this->assertEquals(2, $orderItem->quantity); + $ret = OrderItem::updateAllCounters(array( + 'quantity' => 3, + 'subtotal' => -10, + ), $pk); + $this->assertEquals(1, $ret); + $orderItem = OrderItem::find($pk); + $this->assertEquals(5, $orderItem->quantity); + $this->assertEquals(30, $orderItem->subtotal); + } + public function testDelete() + { + // delete + $customer = Customer::find(2); + $this->assertTrue($customer instanceof Customer); + $this->assertEquals('user2', $customer->name); + $customer->delete(); + $customer = Customer::find(2); + $this->assertNull($customer); + + // deleteAll + $customers = Customer::find()->all(); + $this->assertEquals(2, count($customers)); + $ret = Customer::deleteAll(); + $this->assertEquals(2, $ret); + $customers = Customer::find()->all(); + $this->assertEquals(0, count($customers)); + } } \ No newline at end of file