Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error in yii\db\ActiveQuery::populateInverseRelation() when primary model is an object and relation is an array #19886

Open
PowerGamer1 opened this issue Jul 8, 2023 · 3 comments
Labels

Comments

@PowerGamer1
Copy link
Contributor

When primary model has a relation that uses inverseOf, "finding" primary model as an object with relation as array produces PHP error. All other combinations of object/array for primary model and relation work fine.

What steps will reproduce the problem?

class Customer extends \yii\db\ActiveRecord
{
	public function getOrders()
	{
		return $this->hasMany(Order::class, ['customer_id' => 'id'])->inverseOf('customer');
	}
}
class Order extends \yii\db\ActiveRecord
{
	public function getCustomer()
	{
		return $this->hasOne(Customer::class, ['id' => 'customer_id']);
	}
}

$c = Customer::find()->where(['id' => 1])->asArray(false)->with(['orders' => function ($query) { $query->asArray(false); }])->one(); // object->object: OK
$c = Customer::find()->where(['id' => 1])->asArray(true)->with(['orders' => function ($query) { $query->asArray(true); }])->one(); // array->array: OK
$c = Customer::find()->where(['id' => 1])->asArray(true)->with(['orders' => function ($query) { $query->asArray(false); }])->one(); // array->object: OK
$c = Customer::find()->where(['id' => 1])->asArray(false)->with(['orders' => function ($query) { $query->asArray(true); }])->one(); // object->array: ERROR

What is the expected result?

The last line executes without errors.

What do you get instead?

PHP error: "Indirect modification of overloaded element of Customer has no effect" in yii\db\ActiveQuery::populateInverseRelation().

Additional info

Q A
Yii version 2.0.48.1
PHP version 8.2.8
@PowerGamer1
Copy link
Contributor Author

The problem arises in yii\db\ActiveQuery::populateInverseRelation():

$primaryModels[$i][$primaryName][$j][$name] = $primaryModel;

If translated to the example above:

$customers[$i]['orders'][$j]['customer'] = $customer;

$customers[$i]['orders'] calls class method returning a simple PHP array (no objects). Modifying that array will create a copy of an array and have no effect on the value stored in $customers[$i] object. This is what PHP complains about.
The easy solution would have been:

// Access array directly (not through some hidden class method):
$primaryModels[$i][_related'][$primaryName][$j][$name] = $primaryModel;
// $customers[$i][_related']['orders'][$j]['customer'] = $customer;

But that solution won't work because ActiveRecord::_related is a private property.

The bad solution with lots of array copying would be something like:

if($primaryModels[$i] instanceof ActiveRecordInterface) {
$relatedModels = $primaryModels[$i][$primaryName];
$relatedModels[$j][$name] = $primaryModel;
$primaryModels[$i]->populateRelation($primaryName, $relatedModels);
}

The better solution might be to fill in inverseOf related data BEFORE calling populateRelation() on primary models in ActiveRelationTrait::populateRelation().

@lubosdz
Copy link
Contributor

lubosdz commented Jul 10, 2023

Or change _related property scope from private to protected.

@PowerGamer1
Copy link
Contributor Author

Or change _related property scope from private to protected.

Won't help because this property needs to be accessed in a different class (_related is a property of \yii\db\BaseActiveRecord and access needs to be in yii\db\ActiveQuery::populateInverseRelation().

@samdark samdark added php8 type:bug Bug and removed php8 labels Jul 10, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants