mirror of
https://github.com/yiisoft/yii2.git
synced 2025-08-26 06:15:19 +08:00
Fixes #2653: Fixed the bug that unsetting an unpopulated AR relation would trigger exception (qiangxue)
This commit is contained in:
@ -52,6 +52,7 @@ Yii Framework 2 Change Log
|
||||
- Bug #2559: Going back on browser history breaks GridView filtering with `Pjax` (tonydspaniard)
|
||||
- Bug #2607: `yii message` tool wasn't updating `message` table (mitalcoi)
|
||||
- Bug #2624: Html::textArea() should respect "name" option. (qiangxue)
|
||||
- Bug #2653: Fixed the bug that unsetting an unpopulated AR relation would trigger exception (qiangxue)
|
||||
- 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: Json::encode() did not handle objects that implement JsonSerializable interface correctly (cebe)
|
||||
|
@ -272,9 +272,10 @@ interface ActiveRecordInterface
|
||||
* (normally this would be a relational [[ActiveQuery]] object).
|
||||
* It can be declared in either the ActiveRecord class itself or one of its behaviors.
|
||||
* @param string $name the relation name
|
||||
* @param boolean $throwException whether to throw exception if the relation does not exist.
|
||||
* @return ActiveQueryInterface the relational query object
|
||||
*/
|
||||
public function getRelation($name);
|
||||
public function getRelation($name, $throwException = true);
|
||||
|
||||
/**
|
||||
* Establishes the relationship between two records.
|
||||
|
@ -305,7 +305,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
|
||||
unset($this->_attributes[$name]);
|
||||
} elseif (array_key_exists($name, $this->_related)) {
|
||||
unset($this->_related[$name]);
|
||||
} else {
|
||||
} elseif ($this->getRelation($name, false) === null) {
|
||||
parent::__unset($name);
|
||||
}
|
||||
}
|
||||
@ -1063,20 +1063,30 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
|
||||
* A relation is defined by a getter method which returns an [[ActiveQueryInterface]] object.
|
||||
* It can be declared in either the Active Record class itself or one of its behaviors.
|
||||
* @param string $name the relation name
|
||||
* @return ActiveQueryInterface|ActiveQuery the relational query object
|
||||
* @param boolean $throwException whether to throw exception if the relation does not exist.
|
||||
* @return ActiveQueryInterface|ActiveQuery the relational query object. If the relation does not exist
|
||||
* and `$throwException` is false, null will be returned.
|
||||
* @throws InvalidParamException if the named relation does not exist.
|
||||
*/
|
||||
public function getRelation($name)
|
||||
public function getRelation($name, $throwException = true)
|
||||
{
|
||||
$getter = 'get' . $name;
|
||||
try {
|
||||
// the relation could be defined in a behavior
|
||||
$relation = $this->$getter();
|
||||
} catch (UnknownMethodException $e) {
|
||||
throw new InvalidParamException(get_class($this) . ' has no relation named "' . $name . '".', 0, $e);
|
||||
if ($throwException) {
|
||||
throw new InvalidParamException(get_class($this) . ' has no relation named "' . $name . '".', 0, $e);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (!$relation instanceof ActiveQueryInterface) {
|
||||
throw new InvalidParamException(get_class($this) . ' has no relation named "' . $name . '".');
|
||||
if ($throwException) {
|
||||
throw new InvalidParamException(get_class($this) . ' has no relation named "' . $name . '".');
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (method_exists($this, $getter)) {
|
||||
@ -1084,7 +1094,11 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
|
||||
$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\".");
|
||||
if ($throwException) {
|
||||
throw new InvalidParamException('Relation names are case sensitive. ' . get_class($this) . " has a relation named \"$realName\" instead of \"$name\".");
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1367,4 +1381,16 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
|
||||
$fields = array_keys($this->getRelatedRecords());
|
||||
return array_combine($fields, $fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the element value at the specified offset to null.
|
||||
* This method is required by the SPL interface `ArrayAccess`.
|
||||
* It is implicitly called when you use something like `unset($model[$offset])`.
|
||||
* @param mixed $offset the offset to unset element
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
// use unset to trigger __unset()
|
||||
unset($this->$offset);
|
||||
}
|
||||
}
|
||||
|
@ -184,7 +184,7 @@ trait ActiveRecordTestTrait
|
||||
$this->assertEquals(['user3', 'user2', 'user1'], $this->callCustomerFind()->orderBy(['name' => SORT_DESC])->column('name'));
|
||||
}
|
||||
|
||||
public function testfindIndexBy()
|
||||
public function testFindIndexBy()
|
||||
{
|
||||
$customerClass = $this->getCustomerClass();
|
||||
/** @var TestCase|ActiveRecordTestTrait $this */
|
||||
@ -205,7 +205,7 @@ trait ActiveRecordTestTrait
|
||||
$this->assertTrue($customers['3-user3'] instanceof $customerClass);
|
||||
}
|
||||
|
||||
public function testfindIndexByAsArray()
|
||||
public function testFindIndexByAsArray()
|
||||
{
|
||||
/** @var TestCase|ActiveRecordTestTrait $this */
|
||||
// indexBy + asArray
|
||||
@ -399,6 +399,10 @@ trait ActiveRecordTestTrait
|
||||
$this->assertEquals(2, count($orders));
|
||||
$this->assertEquals(1, count($customer->relatedRecords));
|
||||
|
||||
// unset
|
||||
unset($customer['orders']);
|
||||
$this->assertFalse($customer->isRelationPopulated('orders'));
|
||||
|
||||
/** @var Customer $customer */
|
||||
$customer = $this->callCustomerFind(2);
|
||||
$this->assertFalse($customer->isRelationPopulated('orders'));
|
||||
@ -422,6 +426,9 @@ trait ActiveRecordTestTrait
|
||||
$this->assertEquals(1, count($customers[1]->orders));
|
||||
$this->assertEquals(2, count($customers[2]->orders));
|
||||
$this->assertEquals(0, count($customers[3]->orders));
|
||||
// unset
|
||||
unset($customers[1]->orders);
|
||||
$this->assertFalse($customers[1]->isRelationPopulated('orders'));
|
||||
|
||||
$customer = $this->callCustomerFind()->where(['id' => 1])->with('orders')->one();
|
||||
$this->assertTrue($customer->isRelationPopulated('orders'));
|
||||
|
Reference in New Issue
Block a user