Active Record relation and asArray() #1402

Closed
armpogart opened this Issue Dec 2, 2013 · 7 comments

Comments

Projects
None yet
2 participants
@armpogart

If we have Model extending from ActiveRecord, we can easily query data as array using ActiveQueryTrait asArray() modifier. But when we have relation, for example (through PivotTable):

class Order extends \yii\db\ActiveRecord
{
    public function getItems()
    {
        return $this->hasMany(Item::className(), ['id' => 'item_id'])
            ->viaTable('tbl_order_item', ['order_id' => 'id']);
    }
}

and now if we try to query data using lazy loading with asArray(), we won't have items at all. If we try to load related data using eager loading ->with('items') we will have error that we are using getDb() on non-object.

Any way to load ActiveRecord data as array and have related data in in associative array?

@qiangxue

This comment has been minimized.

Show comment
Hide comment
@qiangxue

qiangxue Dec 2, 2013

Member

Of course you can't use lazy loading with asArray() because you would be operating on an array.
You should use eager loading in this case.

Member

qiangxue commented Dec 2, 2013

Of course you can't use lazy loading with asArray() because you would be operating on an array.
You should use eager loading in this case.

@qiangxue qiangxue closed this Dec 2, 2013

@armpogart

This comment has been minimized.

Show comment
Hide comment
@armpogart

armpogart Dec 2, 2013

OK, I want to use eager loading, but I get error using that approach. When I query:

$orders = Order::find()->with('items')->asArray()->all();

Without ->asArray(), everything works as expected, but why I can't load data asArray with relation?

OK, I want to use eager loading, but I get error using that approach. When I query:

$orders = Order::find()->with('items')->asArray()->all();

Without ->asArray(), everything works as expected, but why I can't load data asArray with relation?

@qiangxue

This comment has been minimized.

Show comment
Hide comment
@qiangxue

qiangxue Dec 2, 2013

Member

What error did you get? What's the error call stack? How are Order and the items relation declared?

Member

qiangxue commented Dec 2, 2013

What error did you get? What's the error call stack? How are Order and the items relation declared?

@armpogart

This comment has been minimized.

Show comment
Hide comment
@armpogart

armpogart Dec 3, 2013

Here is my case:
I have 2 ActiveRecord models Driver and Car. Driver model is related to car through Pivot Table:

public function getCars()
{
    return $this->hasMany(Car::className(), ['id' => 'car_id'])
        ->viaTable('wpta_driver_car', ['driver_id' => 'id']);
}

When I load Driver model in Controller following way:

$drivers = Driver::find()->with('cars')->all();

everything loads just fine, and I can see that cars are loaded. But when I want to load drivers with their cars as array:

$drivers = Driver::find()->with('cars')->asArray()->all();

I get error: Call to a member function getDb() on a non-object in vendor\yiisoft\yii2\yii\db\ActiveRelationTrait.php at line 250. It is the following line: return $this->asArray()->all($primaryModel->getDb());.

Why I can't load related data as array. Sometimes it's crucial for applications.

Here is my case:
I have 2 ActiveRecord models Driver and Car. Driver model is related to car through Pivot Table:

public function getCars()
{
    return $this->hasMany(Car::className(), ['id' => 'car_id'])
        ->viaTable('wpta_driver_car', ['driver_id' => 'id']);
}

When I load Driver model in Controller following way:

$drivers = Driver::find()->with('cars')->all();

everything loads just fine, and I can see that cars are loaded. But when I want to load drivers with their cars as array:

$drivers = Driver::find()->with('cars')->asArray()->all();

I get error: Call to a member function getDb() on a non-object in vendor\yiisoft\yii2\yii\db\ActiveRelationTrait.php at line 250. It is the following line: return $this->asArray()->all($primaryModel->getDb());.

Why I can't load related data as array. Sometimes it's crucial for applications.

@armpogart

This comment has been minimized.

Show comment
Hide comment
@armpogart

armpogart Dec 3, 2013

Here is full screenshot of call stack:
h6ibtep

Here is full screenshot of call stack:
h6ibtep

@armpogart

This comment has been minimized.

Show comment
Hide comment
@armpogart

armpogart Dec 3, 2013

So after further investigation I've found out that there is simple fix. I've changed following function:

private function findPivotRows($primaryModels)
    {
        if (empty($primaryModels)) {
            return [];
        }
        $this->filterByModels($primaryModels);
        /** @var ActiveRecord $primaryModel */
        $primaryModel = reset($primaryModels);
        return $this->asArray()->all($primaryModel->getDb());
    }

to

private function findPivotRows($primaryModels)
    {
        if (empty($primaryModels)) {
            return [];
        }
        $this->filterByModels($primaryModels);
        /** @var ActiveRecord $primaryModel */
        $primaryModel = reset($primaryModels);
        return $this->asArray()->all();
    }

I've simply removed ->getDB() part in last line. In that case as far as I understand Yii::getDb() is used under the hood, so it may bring some problems if other than default connection was used.
This simple fix gives the exact expected result.
Sadly I can't come up with better fix without going deeper in the code, but I can do it in 2-3 days when I have some more spare time, if I can be of any help.

So after further investigation I've found out that there is simple fix. I've changed following function:

private function findPivotRows($primaryModels)
    {
        if (empty($primaryModels)) {
            return [];
        }
        $this->filterByModels($primaryModels);
        /** @var ActiveRecord $primaryModel */
        $primaryModel = reset($primaryModels);
        return $this->asArray()->all($primaryModel->getDb());
    }

to

private function findPivotRows($primaryModels)
    {
        if (empty($primaryModels)) {
            return [];
        }
        $this->filterByModels($primaryModels);
        /** @var ActiveRecord $primaryModel */
        $primaryModel = reset($primaryModels);
        return $this->asArray()->all();
    }

I've simply removed ->getDB() part in last line. In that case as far as I understand Yii::getDb() is used under the hood, so it may bring some problems if other than default connection was used.
This simple fix gives the exact expected result.
Sadly I can't come up with better fix without going deeper in the code, but I can do it in 2-3 days when I have some more spare time, if I can be of any help.

qiangxue added a commit that referenced this issue Dec 4, 2013

@armpogart

This comment has been minimized.

Show comment
Hide comment
@armpogart

armpogart Dec 4, 2013

Thanks!!!
Your team is doing fantastic job, I've learned many new things with the help of Yii 1 and now Yii 2!

Thanks!!!
Your team is doing fantastic job, I've learned many new things with the help of Yii 1 and now Yii 2!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment