Suppose we have AR relation with composite link and we want to get aggregated data on it. Here is modified example from guide:
class Customer extends \yii\db\ActiveRecord
{
/**
* Defines read-only virtual property for aggregation data.
*/
public function getOrdersCount()
{
if ($this->isNewRecord) {
return null; // this avoid calling a query searching for null primary keys
}
return empty($this->ordersAggregation) ? 0 : $this->ordersAggregation[0]['counted'];
}
/**
* Declares normal 'orders' relation.
*/
public function getOrders()
{
return $this->hasMany(Order::className(), ['customer_id' => 'id', 'extra' => 'extra']);
}
/**
* Declares new relation based on 'orders', which provides aggregation.
*/
public function getOrdersAggregation()
{
return $this->getOrders()
->select(['customer_id', 'extra', 'counted' => 'count(*)'])
->groupBy(['customer_id', 'extra'])
->asArray(true);
}
// ...
}
In this case lazy loading works fine, but eager loading always returns zero:
$customer = Customer::findOne($pk);
$customer->ordersCount; // works fine
foreach (Customer::find()->with('ordersAggregation')->all() as $customer) {
echo $customer->ordersCount; // always 0
}
The problem is in the way how Query builds buckets from query results:
private function getModelKey($model, $attributes)
{
$key = [];
foreach ($attributes as $attribute) {
$key[] = $this->normalizeModelKey($model[$attribute]);
}
if (count($key) > 1) {
return serialize($key); // <--- here is the problem
}
$key = reset($key);
return is_scalar($key) ? $key : serialize($key);
}
Related query results produce bucket keys like a:2:{i:0;s:1:\"7\";i:1;s:1:\"1\";}. At this time primary AR model produce keys like a:2:{i:0;i:7;i:1;i:1;} because they have typecasted attribute values. So related results buckets won't be found and we always get 0.
| Q | A
| ---------------- | ---
| Yii version | 2.0.15.1
| PHP version | 7.1.16
| Operating system | Mac OS 10.13.4
Is this mysql ? if so, what driver (mysql, mysqli, PDO_mysql) ?
ordersAggregation is not a relation so you can not eager-load it.
@cebe, are you serious? I took this example from official guide (last example) and just modified link in relation declaration. So if you think ordersAggregation can't be eager-loaded, you should remove this example from guide or add a warning describing this situation.
@lubosdz, yes, it is mysql, and your advice can help in this situation. And I think it worth to be mentioned in this chapter of guide. But this issue is not about typecasting, it's about making buckets from results and assigning them to primary models.
Actually, that's not a problem for me at all, because I can achieve needed result in another way. I've just opened this issue because I've found this situation interesting. Maybe converting keys to string two lines earlier $key[] = (string) $this->normalizeModelKey($model[$attribute]); will be enough to fix a problem.
@cebe, are you serious?
no, sorry, I misread your code. thanks for reopening, @rob006
Hello, I have same issue. Will this behavior be fixed? We have to patch ActiveRelationTrait::getModelKey method and cast all keys to string now.
We will gladly accept PR fixing this problem, if you would like to help.
I wonder... Does casting this value to string solve the problem?
Looks like it does. @DrummerKH could you please check if this fixes your issues? Also, I know it's like 2 years too late but maybe @ykorostelev could take a look?
Most helpful comment
Looks like it does. @DrummerKH could you please check if this fixes your issues? Also, I know it's like 2 years too late but maybe @ykorostelev could take a look?