From 42d8748e6e9b3bab6a855dbbffab5b6afe014e88 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sat, 21 Dec 2013 23:26:35 -0500 Subject: [PATCH] Fixes #1579: throw exception when the given AR relation name does not match in a case sensitive manner. Renamed `ActiveRecord::getPopulatedRelations()` to `getRelatedRecords()` --- extensions/yii/sphinx/ActiveRecord.php | 4 +-- framework/CHANGELOG.md | 2 ++ framework/yii/db/BaseActiveRecord.php | 35 ++++++++++++++++------ .../unit/extensions/mongodb/ActiveRelationTest.php | 4 +-- .../unit/extensions/sphinx/ActiveRelationTest.php | 4 +-- .../sphinx/ExternalActiveRelationTest.php | 4 +-- tests/unit/framework/ar/ActiveRecordTestTrait.php | 6 ++-- 7 files changed, 39 insertions(+), 20 deletions(-) diff --git a/extensions/yii/sphinx/ActiveRecord.php b/extensions/yii/sphinx/ActiveRecord.php index e7bda34..0f9a48e 100644 --- a/extensions/yii/sphinx/ActiveRecord.php +++ b/extensions/yii/sphinx/ActiveRecord.php @@ -29,7 +29,7 @@ use yii\helpers\StringHelper; * @property mixed $oldPrimaryKey The old primary key value. An array (column name => column value) is * returned if the primary key is composite. A string is returned otherwise (null will be returned if the key * value is null). This property is read-only. - * @property array $populatedRelations An array of relation data indexed by relation names. This property is + * @property array $relatedRecords An array of the populated related records indexed by relation names. This property is * read-only. * @property mixed $primaryKey The primary key value. An array (column name => column value) is returned if * the primary key is composite. A string is returned otherwise (null will be returned if the key value is null). @@ -668,4 +668,4 @@ abstract class ActiveRecord extends BaseActiveRecord $transactions = $this->transactions(); return isset($transactions[$scenario]) && ($transactions[$scenario] & $operation); } -} \ No newline at end of file +} diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 9694cb7..0d2bd31 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -21,11 +21,13 @@ Yii Framework 2 Change Log - Enh #1469: ActiveRecord::find() now works with default conditions (default scope) applied by createQuery (cebe) - Enh #1523: Query conditions now allow to use the NOT operator (cebe) - Enh #1552: It is now possible to use multiple bootstrap NavBar in a single page (Alex-Code) +- Enh #1579: throw exception when the given AR relation name does not match in a case sensitive manner (qiangxue) - Enh: Added `favicon.ico` and `robots.txt` to defauly application templates (samdark) - Enh: Added `Widget::autoIdPrefix` to support prefixing automatically generated widget IDs (qiangxue) - Enh: Support for file aliases in console command 'message' (omnilight) - Enh: Sort and Paginiation can now create absolute URLs (cebe) - Chg: Renamed `yii\jui\Widget::clientEventsMap` to `clientEventMap` (qiangxue) +- Chg: Renamed `ActiveRecord::getPopulatedRelations()` to `getRelatedRecords()` (qiangxue) - Chg: Added `yii\widgets\InputWidget::options` (qiangxue) - New #1438: [MongoDB integration](https://github.com/yiisoft/yii2-mongodb) ActiveRecord and Query (klimov-paul) - New #1393: [Codeception testing framework integration](https://github.com/yiisoft/yii2-codeception) (Ragazzo) diff --git a/framework/yii/db/BaseActiveRecord.php b/framework/yii/db/BaseActiveRecord.php index dae7134..6c947b8 100644 --- a/framework/yii/db/BaseActiveRecord.php +++ b/framework/yii/db/BaseActiveRecord.php @@ -30,7 +30,7 @@ use yii\helpers\Inflector; * @property mixed $oldPrimaryKey The old primary key value. An array (column name => column value) is * returned if the primary key is composite. A string is returned otherwise (null will be returned if the key * value is null). This property is read-only. - * @property array $populatedRelations An array of relation data indexed by relation names. This property is + * @property array $relatedRecords An array of the populated related records indexed by relation names. This property is * read-only. * @property mixed $primaryKey The primary key value. An array (column name => column value) is returned if * the primary key is composite. A string is returned otherwise (null will be returned if the key value is null). @@ -232,6 +232,13 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface } $value = parent::__get($name); if ($value instanceof ActiveRelationInterface) { + if (method_exists($this, 'get' . $name)) { + $method = new \ReflectionMethod($this, 'get' . $name); + $realName = lcfirst(substr($method->getName(), 3)); + if ($realName !== $name) { + throw new InvalidParamException('Relation names are case sensitive. ' . get_class($this) . " has a relation named \"$realName\" instead of \"$name\"."); + } + } return $this->_related[$name] = $value->multiple ? $value->all() : $value->one(); } else { return $value; @@ -390,10 +397,10 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface } /** - * Returns all populated relations. - * @return array an array of relation data indexed by relation names. + * Returns all populated related records. + * @return array an array of related records indexed by relation names. */ - public function getPopulatedRelations() + public function getRelatedRecords() { return $this->_related; } @@ -999,15 +1006,25 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface { $getter = 'get' . $name; try { + // the relation could be defined in a behavior $relation = $this->$getter(); - if ($relation instanceof ActiveRelationInterface) { - return $relation; - } else { - throw new InvalidParamException(get_class($this) . ' has no relation named "' . $name . '".'); - } } catch (UnknownMethodException $e) { throw new InvalidParamException(get_class($this) . ' has no relation named "' . $name . '".', 0, $e); } + if (!$relation instanceof ActiveRelationInterface) { + throw new InvalidParamException(get_class($this) . ' has no relation named "' . $name . '".'); + } + + if (method_exists($this, $getter)) { + // relation name is case sensitive, trying to validate it when the relation is defined within this class + $method = new \ReflectionMethod($this, $getter); + $realName = lcfirst(substr($method->getName(), 3)); + if ($realName !== $name) { + throw new InvalidParamException('Relation names are case sensitive. ' . get_class($this) . " has a relation named \"$realName\" instead of \"$name\"."); + } + } + + return $relation; } /** diff --git a/tests/unit/extensions/mongodb/ActiveRelationTest.php b/tests/unit/extensions/mongodb/ActiveRelationTest.php index 8736d52..2baeab4 100644 --- a/tests/unit/extensions/mongodb/ActiveRelationTest.php +++ b/tests/unit/extensions/mongodb/ActiveRelationTest.php @@ -69,7 +69,7 @@ class ActiveRelationTest extends MongoDbTestCase $this->assertTrue($order->isRelationPopulated('customer')); $this->assertTrue($customer instanceof Customer); $this->assertEquals((string)$customer->_id, (string)$order->customer_id); - $this->assertEquals(1, count($order->populatedRelations)); + $this->assertEquals(1, count($order->relatedRecords)); } public function testFindEager() @@ -83,4 +83,4 @@ class ActiveRelationTest extends MongoDbTestCase $this->assertTrue($orders[1]->customer instanceof Customer); $this->assertEquals((string)$orders[1]->customer->_id, (string)$orders[1]->customer_id); } -} \ No newline at end of file +} diff --git a/tests/unit/extensions/sphinx/ActiveRelationTest.php b/tests/unit/extensions/sphinx/ActiveRelationTest.php index cd58035..d85c6b9 100644 --- a/tests/unit/extensions/sphinx/ActiveRelationTest.php +++ b/tests/unit/extensions/sphinx/ActiveRelationTest.php @@ -29,7 +29,7 @@ class ActiveRelationTest extends SphinxTestCase $index = $article->index; $this->assertTrue($article->isRelationPopulated('index')); $this->assertTrue($index instanceof ArticleIndex); - $this->assertEquals(1, count($article->populatedRelations)); + $this->assertEquals(1, count($article->relatedRecords)); $this->assertEquals($article->id, $index->id); } @@ -42,4 +42,4 @@ class ActiveRelationTest extends SphinxTestCase $this->assertTrue($articles[0]->index instanceof ArticleIndex); $this->assertTrue($articles[1]->index instanceof ArticleIndex); } -} \ No newline at end of file +} diff --git a/tests/unit/extensions/sphinx/ExternalActiveRelationTest.php b/tests/unit/extensions/sphinx/ExternalActiveRelationTest.php index e30a0cf..1740c42 100644 --- a/tests/unit/extensions/sphinx/ExternalActiveRelationTest.php +++ b/tests/unit/extensions/sphinx/ExternalActiveRelationTest.php @@ -32,7 +32,7 @@ class ExternalActiveRelationTest extends SphinxTestCase $source = $article->source; $this->assertTrue($article->isRelationPopulated('source')); $this->assertTrue($source instanceof ArticleDb); - $this->assertEquals(1, count($article->populatedRelations)); + $this->assertEquals(1, count($article->relatedRecords)); // has many : /*$this->assertFalse($article->isRelationPopulated('tags')); @@ -71,4 +71,4 @@ class ExternalActiveRelationTest extends SphinxTestCase ->all(); $this->assertEquals(2, count($articles)); } -} \ No newline at end of file +} diff --git a/tests/unit/framework/ar/ActiveRecordTestTrait.php b/tests/unit/framework/ar/ActiveRecordTestTrait.php index 50a5f81..95def9d 100644 --- a/tests/unit/framework/ar/ActiveRecordTestTrait.php +++ b/tests/unit/framework/ar/ActiveRecordTestTrait.php @@ -392,14 +392,14 @@ trait ActiveRecordTestTrait $orders = $customer->orders; $this->assertTrue($customer->isRelationPopulated('orders')); $this->assertEquals(2, count($orders)); - $this->assertEquals(1, count($customer->populatedRelations)); + $this->assertEquals(1, count($customer->relatedRecords)); /** @var Customer $customer */ $customer = $this->callCustomerFind(2); $this->assertFalse($customer->isRelationPopulated('orders')); $orders = $customer->getOrders()->where(['id' => 3])->all(); $this->assertFalse($customer->isRelationPopulated('orders')); - $this->assertEquals(0, count($customer->populatedRelations)); + $this->assertEquals(0, count($customer->relatedRecords)); $this->assertEquals(1, count($orders)); $this->assertEquals(3, $orders[0]->id); @@ -421,7 +421,7 @@ trait ActiveRecordTestTrait $customer = $this->callCustomerFind()->where(['id' => 1])->with('orders')->one(); $this->assertTrue($customer->isRelationPopulated('orders')); $this->assertEquals(1, count($customer->orders)); - $this->assertEquals(1, count($customer->populatedRelations)); + $this->assertEquals(1, count($customer->relatedRecords)); } public function testFindLazyVia()