active-record icon indicating copy to clipboard operation
active-record copied to clipboard

Faulty caching in AR relations with via and callable

Open svok opened this issue 8 years ago • 6 comments

The following code yields an unexpected behavior:

    public function getCity()
    {
        return $this->hasOne(Cities::className(), ['id' => 'city_id'])
            ->via('orderPts',function($q){
                $q->andWhere(['order_pts.idx' => 1]);
            })
    }

The problem is due to callable in via. If I use $model->city the result of the city relation is cached together with the additional condition from the callable. Then all requests to $model->orderPts return the result cached by $model->city with additional condition.

svok avatar Mar 10 '16 07:03 svok

Hm... relation should not be cached in this case... Would you be able to create a unit test case?

cebe avatar Mar 10 '16 14:03 cebe

@cebe I found this issue too. I can't create Unit test for you, but you can repeat problem in following way:

    public function getRealtyTypes()
    {
        return $this->hasMany(RealtyType::className(), ['realty_object_id' => 'id']);
    }

    public function getRealtyBuildings()
    {
        return $this->hasMany(RealtyBuilding::className(), ['id' => 'realty_building_id'])->via('realtyTypes', function($q){
            $q->andWhere(['is_active' => 0]); //wrongQuery. Result = 0 rows
        });
    }

    public function test(){
        $this->realtyTypes; //Now we have all Types here
        $this->realtyBuildings; //Now we overwrite all types by empty array
        //...
    }

This really bad thing, if you modify data in relation, and then this data was populated from via on another relation...

mrFleshka avatar Mar 13 '16 00:03 mrFleshka

Verified, created a PR with a failing test.

#11107

SilverFire avatar Mar 13 '16 19:03 SilverFire

bug.tar.gz test example failed, when i use $category->products all products listed, but after use of $category->versions with filter, this filter is also used in $category->products, if you use after 'version' a 'products' so it is listed with filter which is in "version filter - function($q){ $q->andWhere(['products.id' => 1]); }"

Asylzatwww avatar Mar 15 '16 10:03 Asylzatwww

Checking the code I only find ways to fix this that include a BC break. For now you should be able to work around the issue by using viaTable() instead of via().

cebe avatar Jul 05 '16 20:07 cebe

https://github.com/yiisoft/yii2/pull/11107

samdark avatar Apr 23 '19 21:04 samdark