Commit 3ff408b9 by Carsten Brandt

Merge pull request #1997 from cebe/1993-fixed-ar-afterFind

afterFind event in AR is now called after relations have been populated
parents 519cc688 7e961eee
...@@ -116,6 +116,11 @@ class ActiveQuery extends Query implements ActiveQueryInterface ...@@ -116,6 +116,11 @@ class ActiveQuery extends Query implements ActiveQueryInterface
if (!empty($this->with)) { if (!empty($this->with)) {
$this->findWith($this->with, $models); $this->findWith($this->with, $models);
} }
if (!$this->asArray) {
foreach($models as $model) {
$model->afterFind();
}
}
return $models; return $models;
} }
...@@ -151,6 +156,9 @@ class ActiveQuery extends Query implements ActiveQueryInterface ...@@ -151,6 +156,9 @@ class ActiveQuery extends Query implements ActiveQueryInterface
$this->findWith($this->with, $models); $this->findWith($this->with, $models);
$model = $models[0]; $model = $models[0];
} }
if (!$this->asArray) {
$model->afterFind();
}
return $model; return $model;
} }
...@@ -177,6 +185,11 @@ class ActiveQuery extends Query implements ActiveQueryInterface ...@@ -177,6 +185,11 @@ class ActiveQuery extends Query implements ActiveQueryInterface
if (!empty($this->with)) { if (!empty($this->with)) {
$this->findWith($this->with, $models); $this->findWith($this->with, $models);
} }
if (!$this->asArray) {
foreach($models as $model) {
$model->afterFind();
}
}
$result['hits']['hits'] = $models; $result['hits']['hits'] = $models;
} }
return $result; return $result;
......
...@@ -91,7 +91,9 @@ class ActiveRecord extends BaseActiveRecord ...@@ -91,7 +91,9 @@ class ActiveRecord extends BaseActiveRecord
$command = static::getDb()->createCommand(); $command = static::getDb()->createCommand();
$result = $command->get(static::index(), static::type(), $primaryKey, $options); $result = $command->get(static::index(), static::type(), $primaryKey, $options);
if ($result['exists']) { if ($result['exists']) {
return static::create($result); $model = static::create($result);
$model->afterFind();
return $model;
} }
return null; return null;
} }
...@@ -118,7 +120,9 @@ class ActiveRecord extends BaseActiveRecord ...@@ -118,7 +120,9 @@ class ActiveRecord extends BaseActiveRecord
$models = []; $models = [];
foreach($result['docs'] as $doc) { foreach($result['docs'] as $doc) {
if ($doc['exists']) { if ($doc['exists']) {
$models[] = static::create($doc); $model = static::create($doc);
$model->afterFind();
$models[] = $model;
} }
} }
return $models; return $models;
...@@ -267,7 +271,9 @@ class ActiveRecord extends BaseActiveRecord ...@@ -267,7 +271,9 @@ class ActiveRecord extends BaseActiveRecord
{ {
$record = parent::create($row['_source']); $record = parent::create($row['_source']);
$pk = static::primaryKey()[0]; $pk = static::primaryKey()[0];
$record->$pk = $row['_id']; if ($pk === '_id') {
$record->$pk = $row['_id'];
}
$record->_score = isset($row['_score']) ? $row['_score'] : null; $record->_score = isset($row['_score']) ? $row['_score'] : null;
$record->_version = isset($row['_version']) ? $row['_version'] : null; // TODO version should always be available... $record->_version = isset($row['_version']) ? $row['_version'] : null; // TODO version should always be available...
return $record; return $record;
......
...@@ -4,6 +4,7 @@ Yii Framework 2 elasticsearch extension Change Log ...@@ -4,6 +4,7 @@ Yii Framework 2 elasticsearch extension Change Log
2.0.0 beta under development 2.0.0 beta under development
---------------------------- ----------------------------
- Bug #1993: afterFind event in AR is now called after relations have been populated (cebe, creocoder)
- Enh #1382: Added a debug toolbar panel for elasticsearch (cebe) - Enh #1382: Added a debug toolbar panel for elasticsearch (cebe)
- Enh #1765: Added support for primary key path mapping, pk can now be part of the attributes when mapping is defined (cebe) - Enh #1765: Added support for primary key path mapping, pk can now be part of the attributes when mapping is defined (cebe)
- Chg #1765: Changed handling of ActiveRecord primary keys, removed getId(), use getPrimaryKey() instead (cebe) - Chg #1765: Changed handling of ActiveRecord primary keys, removed getId(), use getPrimaryKey() instead (cebe)
......
...@@ -53,6 +53,11 @@ class ActiveQuery extends Query implements ActiveQueryInterface ...@@ -53,6 +53,11 @@ class ActiveQuery extends Query implements ActiveQueryInterface
if (!empty($this->with)) { if (!empty($this->with)) {
$this->findWith($this->with, $models); $this->findWith($this->with, $models);
} }
if (!$this->asArray) {
foreach($models as $model) {
$model->afterFind();
}
}
return $models; return $models;
} else { } else {
return []; return [];
...@@ -83,6 +88,9 @@ class ActiveQuery extends Query implements ActiveQueryInterface ...@@ -83,6 +88,9 @@ class ActiveQuery extends Query implements ActiveQueryInterface
$this->findWith($this->with, $models); $this->findWith($this->with, $models);
$model = $models[0]; $model = $models[0];
} }
if (!$this->asArray) {
$model->afterFind();
}
return $model; return $model;
} else { } else {
return null; return null;
......
...@@ -53,6 +53,11 @@ class ActiveQuery extends Query implements ActiveQueryInterface ...@@ -53,6 +53,11 @@ class ActiveQuery extends Query implements ActiveQueryInterface
if (!empty($this->with)) { if (!empty($this->with)) {
$this->findWith($this->with, $models); $this->findWith($this->with, $models);
} }
if (!$this->asArray) {
foreach($models as $model) {
$model->afterFind();
}
}
return $models; return $models;
} else { } else {
return []; return [];
...@@ -83,6 +88,9 @@ class ActiveQuery extends Query implements ActiveQueryInterface ...@@ -83,6 +88,9 @@ class ActiveQuery extends Query implements ActiveQueryInterface
$this->findWith($this->with, $models); $this->findWith($this->with, $models);
$model = $models[0]; $model = $models[0];
} }
if (!$this->asArray) {
$model->afterFind();
}
return $model; return $model;
} else { } else {
return null; return null;
...@@ -91,7 +99,7 @@ class ActiveQuery extends Query implements ActiveQueryInterface ...@@ -91,7 +99,7 @@ class ActiveQuery extends Query implements ActiveQueryInterface
/** /**
* Returns the Mongo collection for this query. * Returns the Mongo collection for this query.
* @param \yii\mongo\Connection $db Mongo connection. * @param \yii\mongodb\Connection $db Mongo connection.
* @return Collection collection instance. * @return Collection collection instance.
*/ */
public function getCollection($db = null) public function getCollection($db = null)
......
...@@ -76,6 +76,11 @@ class ActiveQuery extends \yii\base\Component implements ActiveQueryInterface ...@@ -76,6 +76,11 @@ class ActiveQuery extends \yii\base\Component implements ActiveQueryInterface
if (!empty($this->with)) { if (!empty($this->with)) {
$this->findWith($this->with, $models); $this->findWith($this->with, $models);
} }
if (!$this->asArray) {
foreach($models as $model) {
$model->afterFind();
}
}
return $models; return $models;
} else { } else {
return []; return [];
...@@ -114,6 +119,9 @@ class ActiveQuery extends \yii\base\Component implements ActiveQueryInterface ...@@ -114,6 +119,9 @@ class ActiveQuery extends \yii\base\Component implements ActiveQueryInterface
$this->findWith($this->with, $models); $this->findWith($this->with, $models);
$model = $models[0]; $model = $models[0];
} }
if (!$this->asArray) {
$model->afterFind();
}
return $model; return $model;
} }
......
...@@ -4,6 +4,7 @@ Yii Framework 2 redis extension Change Log ...@@ -4,6 +4,7 @@ Yii Framework 2 redis extension Change Log
2.0.0 beta under development 2.0.0 beta under development
---------------------------- ----------------------------
- Bug #1993: afterFind event in AR is now called after relations have been populated (cebe, creocoder)
- Enh #1773: keyPrefix property of Session and Cache is not restricted to alnum characters anymore (cebe) - Enh #1773: keyPrefix property of Session and Cache is not restricted to alnum characters anymore (cebe)
2.0.0 alpha, December 1, 2013 2.0.0 alpha, December 1, 2013
......
...@@ -110,6 +110,11 @@ class ActiveQuery extends Query implements ActiveQueryInterface ...@@ -110,6 +110,11 @@ class ActiveQuery extends Query implements ActiveQueryInterface
$this->findWith($this->with, $models); $this->findWith($this->with, $models);
} }
$models = $this->fillUpSnippets($models); $models = $this->fillUpSnippets($models);
if (!$this->asArray) {
foreach($models as $model) {
$model->afterFind();
}
}
return $models; return $models;
} else { } else {
return []; return [];
...@@ -142,6 +147,9 @@ class ActiveQuery extends Query implements ActiveQueryInterface ...@@ -142,6 +147,9 @@ class ActiveQuery extends Query implements ActiveQueryInterface
$model = $models[0]; $model = $models[0];
} }
list ($model) = $this->fillUpSnippets([$model]); list ($model) = $this->fillUpSnippets([$model]);
if (!$this->asArray) {
$model->afterFind();
}
return $model; return $model;
} else { } else {
return null; return null;
......
...@@ -641,7 +641,6 @@ abstract class ActiveRecord extends BaseActiveRecord ...@@ -641,7 +641,6 @@ abstract class ActiveRecord extends BaseActiveRecord
} }
} }
$record->setOldAttributes($record->getAttributes()); $record->setOldAttributes($record->getAttributes());
$record->afterFind();
return $record; return $record;
} }
......
...@@ -4,6 +4,7 @@ Yii Framework 2 sphinx extension Change Log ...@@ -4,6 +4,7 @@ Yii Framework 2 sphinx extension Change Log
2.0.0 beta under development 2.0.0 beta under development
---------------------------- ----------------------------
- Bug #1993: afterFind event in AR is now called after relations have been populated (cebe, creocoder)
- Enh #1398: Refactor ActiveRecord to use BaseActiveRecord class of the framework (klimov-paul) - Enh #1398: Refactor ActiveRecord to use BaseActiveRecord class of the framework (klimov-paul)
2.0.0 alpha, December 1, 2013 2.0.0 alpha, December 1, 2013
......
...@@ -668,7 +668,7 @@ class Query extends Component implements QueryInterface ...@@ -668,7 +668,7 @@ class Query extends Component implements QueryInterface
* Fills the query result rows with the snippets built from source determined by * Fills the query result rows with the snippets built from source determined by
* [[snippetCallback]] result. * [[snippetCallback]] result.
* @param array $rows raw query result rows. * @param array $rows raw query result rows.
* @return array query result rows with filled up snippets. * @return array|ActiveRecord[] query result rows with filled up snippets.
*/ */
protected function fillUpSnippets($rows) protected function fillUpSnippets($rows)
{ {
......
...@@ -30,6 +30,7 @@ Yii Framework 2 Change Log ...@@ -30,6 +30,7 @@ Yii Framework 2 Change Log
- Bug #1937: Fixed wrong behavior or advanced app's `init --env` when called without parameter actually specified (samdark) - Bug #1937: Fixed wrong behavior or advanced app's `init --env` when called without parameter actually specified (samdark)
- Bug #1959: `Html::activeCheckbox` wasn't respecting custom values for checked/unchecked state (klevron, samdark) - Bug #1959: `Html::activeCheckbox` wasn't respecting custom values for checked/unchecked state (klevron, samdark)
- Bug #1965: `Controller::findLayoutFile()` returns incorrect file path when layout name starts with a slash (qiangxue) - Bug #1965: `Controller::findLayoutFile()` returns incorrect file path when layout name starts with a slash (qiangxue)
- Bug #1993: afterFind event in AR is now called after relations have been populated (cebe, creocoder)
- Bug: Fixed `Call to a member function registerAssetFiles() on a non-object` in case of wrong `sourcePath` for an asset bundle (samdark) - Bug: Fixed `Call to a member function registerAssetFiles() on a non-object` in case of wrong `sourcePath` for an asset bundle (samdark)
- Bug: Fixed incorrect event name for `yii\jui\Spinner` (samdark) - Bug: Fixed incorrect event name for `yii\jui\Spinner` (samdark)
- Bug: Json::encode() did not handle objects that implement JsonSerializable interface correctly (cebe) - Bug: Json::encode() did not handle objects that implement JsonSerializable interface correctly (cebe)
......
...@@ -74,6 +74,11 @@ class ActiveQuery extends Query implements ActiveQueryInterface ...@@ -74,6 +74,11 @@ class ActiveQuery extends Query implements ActiveQueryInterface
if (!empty($this->with)) { if (!empty($this->with)) {
$this->findWith($this->with, $models); $this->findWith($this->with, $models);
} }
if (!$this->asArray) {
foreach($models as $model) {
$model->afterFind();
}
}
return $models; return $models;
} else { } else {
return []; return [];
...@@ -146,6 +151,9 @@ class ActiveQuery extends Query implements ActiveQueryInterface ...@@ -146,6 +151,9 @@ class ActiveQuery extends Query implements ActiveQueryInterface
$this->findWith($this->with, $models); $this->findWith($this->with, $models);
$model = $models[0]; $model = $models[0];
} }
if (!$this->asArray) {
$model->afterFind();
}
return $model; return $model;
} else { } else {
return null; return null;
......
...@@ -983,11 +983,19 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface ...@@ -983,11 +983,19 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
} }
/** /**
* Creates an active record object using a row of data. * Creates an active record object using a row of data from the database/storage.
* This method is called by [[ActiveQuery]] to populate the query results *
* into Active Records. It is not meant to be used to create new records. * This method is *not* meant to be used to create new records.
*
* It is an internal method meant to be called to create active record objects after
* fetching data from the database. It is mainly used by [[ActiveQuery]] to populate
* the query results into Active Records.
*
* When calling this method manually you should call [[afterFind()]] on the created
* record to trigger the [[EVENT_AFTER_FIND|afterFind Event]].
*
* @param array $row attribute values (name => value) * @param array $row attribute values (name => value)
* @return ActiveRecord the newly created active record. * @return static the newly created active record.
*/ */
public static function create($row) public static function create($row)
{ {
...@@ -1001,7 +1009,6 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface ...@@ -1001,7 +1009,6 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
} }
} }
$record->_oldAttributes = $record->_attributes; $record->_oldAttributes = $record->_attributes;
$record->afterFind();
return $record; return $record;
} }
...@@ -1013,7 +1020,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface ...@@ -1013,7 +1020,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
* For example, by creating a record based on the value of a column, * For example, by creating a record based on the value of a column,
* you may implement the so-called single-table inheritance mapping. * you may implement the so-called single-table inheritance mapping.
* @param array $row row data to be populated into the record. * @param array $row row data to be populated into the record.
* @return ActiveRecord the newly created active record * @return static the newly created active record
*/ */
public static function instantiate($row) public static function instantiate($row)
{ {
......
...@@ -41,4 +41,10 @@ class Exception extends \yii\base\Exception ...@@ -41,4 +41,10 @@ class Exception extends \yii\base\Exception
{ {
return 'Database Exception'; return 'Database Exception';
} }
public function __toString()
{
return parent::__toString() . PHP_EOL
. 'Additional Information:' . PHP_EOL . print_r($this->errorInfo, true);
}
} }
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
namespace yiiunit\extensions\elasticsearch; namespace yiiunit\extensions\elasticsearch;
use yii\base\Event;
use yii\base\Exception;
use yii\db\BaseActiveRecord;
use yii\elasticsearch\Connection; use yii\elasticsearch\Connection;
use yii\helpers\Json; use yii\helpers\Json;
use yiiunit\framework\ar\ActiveRecordTestTrait; use yiiunit\framework\ar\ActiveRecordTestTrait;
...@@ -10,6 +13,7 @@ use yiiunit\data\ar\elasticsearch\Customer; ...@@ -10,6 +13,7 @@ use yiiunit\data\ar\elasticsearch\Customer;
use yiiunit\data\ar\elasticsearch\OrderItem; use yiiunit\data\ar\elasticsearch\OrderItem;
use yiiunit\data\ar\elasticsearch\Order; use yiiunit\data\ar\elasticsearch\Order;
use yiiunit\data\ar\elasticsearch\Item; use yiiunit\data\ar\elasticsearch\Item;
use yiiunit\TestCase;
/** /**
* @group elasticsearch * @group elasticsearch
...@@ -341,8 +345,8 @@ class ActiveRecordTest extends ElasticSearchTestCase ...@@ -341,8 +345,8 @@ class ActiveRecordTest extends ElasticSearchTestCase
public function testBooleanAttribute() public function testBooleanAttribute()
{ {
$db = $this->getConnection(); $db = $this->getConnection();
Customer::setUpMapping($db->createCommand(), true);
Customer::deleteAll(); Customer::deleteAll();
Customer::setUpMapping($db->createCommand(), true);
$customerClass = $this->getCustomerClass(); $customerClass = $this->getCustomerClass();
$customer = new $customerClass(); $customer = new $customerClass();
...@@ -375,7 +379,7 @@ class ActiveRecordTest extends ElasticSearchTestCase ...@@ -375,7 +379,7 @@ class ActiveRecordTest extends ElasticSearchTestCase
$this->assertEquals(2, count($customers)); $this->assertEquals(2, count($customers));
} }
public function testfindAsArrayFields() public function testFindAsArrayFields()
{ {
$customerClass = $this->getCustomerClass(); $customerClass = $this->getCustomerClass();
/** @var TestCase|ActiveRecordTestTrait $this */ /** @var TestCase|ActiveRecordTestTrait $this */
...@@ -399,7 +403,7 @@ class ActiveRecordTest extends ElasticSearchTestCase ...@@ -399,7 +403,7 @@ class ActiveRecordTest extends ElasticSearchTestCase
$this->assertArrayNotHasKey('status', $customers[2]); $this->assertArrayNotHasKey('status', $customers[2]);
} }
public function testfindIndexByFields() public function testFindIndexByFields()
{ {
$customerClass = $this->getCustomerClass(); $customerClass = $this->getCustomerClass();
/** @var TestCase|ActiveRecordTestTrait $this */ /** @var TestCase|ActiveRecordTestTrait $this */
...@@ -450,7 +454,7 @@ class ActiveRecordTest extends ElasticSearchTestCase ...@@ -450,7 +454,7 @@ class ActiveRecordTest extends ElasticSearchTestCase
$this->assertNull($customers['3-user3']->status); $this->assertNull($customers['3-user3']->status);
} }
public function testfindIndexByAsArrayFields() public function testFindIndexByAsArrayFields()
{ {
$customerClass = $this->getCustomerClass(); $customerClass = $this->getCustomerClass();
/** @var TestCase|ActiveRecordTestTrait $this */ /** @var TestCase|ActiveRecordTestTrait $this */
...@@ -495,5 +499,34 @@ class ActiveRecordTest extends ElasticSearchTestCase ...@@ -495,5 +499,34 @@ class ActiveRecordTest extends ElasticSearchTestCase
$this->assertArrayNotHasKey('status', $customers['3-user3']); $this->assertArrayNotHasKey('status', $customers['3-user3']);
} }
public function testAfterFindGet()
{
/** @var BaseActiveRecord $customerClass */
$customerClass = $this->getCustomerClass();
$afterFindCalls = [];
Event::on(BaseActiveRecord::className(), BaseActiveRecord::EVENT_AFTER_FIND, function($event) use (&$afterFindCalls) {
/** @var BaseActiveRecord $ar */
$ar = $event->sender;
$afterFindCalls[] = [get_class($ar), $ar->getIsNewRecord(), $ar->getPrimaryKey(), $ar->isRelationPopulated('orders')];
});
$customer = Customer::get(1);
$this->assertNotNull($customer);
$this->assertEquals([[$customerClass, false, 1, false]], $afterFindCalls);
$afterFindCalls = [];
$customer = Customer::mget([1, 2]);
$this->assertNotNull($customer);
$this->assertEquals([
[$customerClass, false, 1, false],
[$customerClass, false, 2, false],
], $afterFindCalls);
$afterFindCalls = [];
Event::off(BaseActiveRecord::className(), BaseActiveRecord::EVENT_AFTER_FIND);
}
// TODO test AR with not mapped PK
} }
\ No newline at end of file
...@@ -7,7 +7,9 @@ ...@@ -7,7 +7,9 @@
namespace yiiunit\framework\ar; namespace yiiunit\framework\ar;
use yii\base\Event;
use yii\db\ActiveQueryInterface; use yii\db\ActiveQueryInterface;
use yii\db\BaseActiveRecord;
use yiiunit\TestCase; use yiiunit\TestCase;
use yiiunit\data\ar\Customer; use yiiunit\data\ar\Customer;
use yiiunit\data\ar\Order; use yiiunit\data\ar\Order;
...@@ -835,4 +837,62 @@ trait ActiveRecordTestTrait ...@@ -835,4 +837,62 @@ trait ActiveRecordTestTrait
$customers = $this->callCustomerFind()->where(['status' => false])->all(); $customers = $this->callCustomerFind()->where(['status' => false])->all();
$this->assertEquals(1, count($customers)); $this->assertEquals(1, count($customers));
} }
public function testAfterFind()
{
/** @var BaseActiveRecord $customerClass */
$customerClass = $this->getCustomerClass();
/** @var BaseActiveRecord $orderClass */
$orderClass = $this->getOrderClass();
/** @var TestCase|ActiveRecordTestTrait $this */
$afterFindCalls = [];
Event::on(BaseActiveRecord::className(), BaseActiveRecord::EVENT_AFTER_FIND, function($event) use (&$afterFindCalls) {
/** @var BaseActiveRecord $ar */
$ar = $event->sender;
$afterFindCalls[] = [get_class($ar), $ar->getIsNewRecord(), $ar->getPrimaryKey(), $ar->isRelationPopulated('orders')];
});
$customer = $this->callCustomerFind(1);
$this->assertNotNull($customer);
$this->assertEquals([[$customerClass, false, 1, false]], $afterFindCalls);
$afterFindCalls = [];
$customer = $this->callCustomerFind()->where(['id' => 1])->one();
$this->assertNotNull($customer);
$this->assertEquals([[$customerClass, false, 1, false]], $afterFindCalls);
$afterFindCalls = [];
$customer = $this->callCustomerFind()->where(['id' => 1])->all();
$this->assertNotNull($customer);
$this->assertEquals([[$customerClass, false, 1, false]], $afterFindCalls);
$afterFindCalls = [];
$customer = $this->callCustomerFind()->where(['id' => 1])->with('orders')->all();
$this->assertNotNull($customer);
$this->assertEquals([
[$this->getOrderClass(), false, 1, false],
[$customerClass, false, 1, true],
], $afterFindCalls);
$afterFindCalls = [];
if ($this instanceof \yiiunit\extensions\redis\ActiveRecordTest) { // TODO redis does not support orderBy() yet
$customer = $this->callCustomerFind()->where(['id' => [1, 2]])->with('orders')->all();
} else {
// orderBy is needed to avoid random test failure
$customer = $this->callCustomerFind()->where(['id' => [1, 2]])->with('orders')->orderBy('name')->all();
}
$this->assertNotNull($customer);
$this->assertEquals([
[$orderClass, false, 1, false],
[$orderClass, false, 2, false],
[$orderClass, false, 3, false],
[$customerClass, false, 1, true],
[$customerClass, false, 2, true],
], $afterFindCalls);
$afterFindCalls = [];
Event::off(BaseActiveRecord::className(), BaseActiveRecord::EVENT_AFTER_FIND);
}
} }
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