yiisoft / yii2

Yii 2: The Fast, Secure and Professional PHP Framework
http://www.yiiframework.com
BSD 3-Clause "New" or "Revised" License
14.23k stars 6.92k forks source link

unexpected null return when retriving model from multi-level relational database using via() #19167

Closed m24km closed 2 years ago

m24km commented 2 years ago

What steps will reproduce the problem?

Given 4 activeRecord (Country, City, District, SubDistrict)with relation Country has many City (City hasOne Country) City has many District ( District hasOne City) District has many SubDistrict ( SubDistrict hasOne District )

Define getCountry() in SubDistrict Class like that :

    public function getDistrict() 
    {
      return $this->hasOne(District::className(), ['id' => 'district_id']);
    }
    public function getCity()
    {
    return $this->hasOne(City::className(), ['id' => 'city_id'])->via('district');`
    }
    public function getCountry()
    {
       return $this->hasOne(Country::className(), ['id' => 'country_id'])->via('city');
    }
country table id title
1 country1
city table id title country_id
1 city1 1
2 city2 1
district table id title city_id
1 district1 1
2 district2 2
sub_district table id title district_id
1 subdistrict1 1
2 subdistrict2 2

run command to : $result = SubDistrict::find()->with('country')->where(['in','id',[1,2])->all();

What is the expected result?

$result[0]->country = {coutry object} $result[1]->country = {coutry object}

What do you get instead?

$result[0]->country = null $result[1]->country = {coutry object}

Additional info

mapVia($map, $viaMap) in ActiveRelationTrait return incorrect link array (it drop some links)

Q A
Yii version 2.0.42
PHP version 7.4
Operating system ubuntu 20.04
bizley commented 2 years ago

Does it help if you alias the main model and specify that alias for id in the condition? This is interesting case.

bizley commented 2 years ago

Oh, and please check it on the current Yii version, maybe it is already taken care of.

m24km commented 2 years ago

I updated yii to 2.044 I used alias for main model and used joinWith in different test case with alias for main model and related model and I am getting same results for this specific data set ( but correct result returned when: city1 and city2 belong to different Countries OR district1 and district2 belong to same City)

longthanhtran commented 2 years ago

I have tried with this case, via latest dev-master (2.0.45, php 8.1.1) and it gives the expected results, the 2 country objects.

image

when use the query

$subDistricts = SubDistrict::find()
    ->with('country')
    ->where(['in', 'id', [1, 2]])
    ->all();

and then extract the country from each subDistrict $subDistrict[0]->country ..

Following are some relations inside SubDistrict class

...
class SubDistrict extends ActiveRecord
{
    public function getDistrict()
    {
        return $this->hasOne(District::class, ['id' => 'district_id']);
    }

    public function getCity()
    {
        return $this->hasOne(City::class, ['id' => 'city_id'])->via('district');
    }

    public function getCountry()
    {
        return $this->hasOne(Country::class, ['id' => 'country_id'])->via('city');
    }
}
bizley commented 2 years ago

I forgot to ask which DB is it.

m24km commented 2 years ago

I used Mysql through debugging my project I tried to figure out why I am getting this results I found implementation of mapVia function drop some links

    private function mapVia($map, $viaMap) {
        $resultMap = [];
        foreach ($map as $key => $linkKeys) {
            foreach (array_keys($linkKeys) as $linkKey) {
                $resultMap[$key] = $viaMap[$linkKey];
            }
        }
        return $resultMap;
    }

The following implementation return correct result for the case illustrated in issue;

    private function mapVia($map, $viaMap)
    {
        $resultMap = [];
        foreach ($map as $key => $linkKeys) {
            $oneLinkMap = [];
            foreach (array_keys($linkKeys) as $linkKey) {
                foreach ($viaMap[$linkKey] as $k => $v) {
                    $oneLinkMap [$k] = $v;
                }
            }
            $resultMap[$key] = $oneLinkMap;
        }
        return $resultMap;
    }
bizley commented 2 years ago

@m24km so, is it fixed in the current dev-master like @longthanhtran mentioned?

m24km commented 2 years ago

No ,I have not seen or tested dev-master

yii-bot commented 2 years ago

It has been 2 or more weeks with no response on our request for more information. In order for our issue tracker to be effective, we are closing this issue.

If you want it to be reopened again, feel free to supply us with the requested information.

Thanks!

This is an automated comment, triggered by adding the label expired.