Необходимо вывести список юзеров с количеством купленных товаров и суммой этих товаров.
Код урезан.
use yii\db\ActiveRecord;
use Yii;
use yii\db\Query;
/**
* @property int $id
* @property string $name
* @property Order[] $orders
* @property int $totalOrders
* @property double $totalOrdersPrice
*/
class User extends ActiveRecord
{
public function getOrders()
{
return $this->hasMany(Order::className(), ['user_id' => 'id']);
}
public function getTotalOrders()
{
return $this->hasOne(Order::className(), ['user_id' => 'id'])->count();
}
public function getTotalOrdersPrice()
{
return $this->hasOne(Order::className(), ['user_id' => 'id'])->sum('price');
}
}
/**
* @property int $id
* @property string $date_add
* @property double $price
* @property int $user_id
* @property User $user
*/
class Order extends ActiveRecord
{
public function getUser()
{
return $this->hasOne(User::className(), ['id' => 'user_id']);
}
}
Вариант #1$users = User::find()->with(['orders'])->all();
foreach ($users as $User) {
echo $User->name;
echo $User->totalOrders;
echo $User->totalOrdersPrice;
}
// Например если я вывожу 100 юзеров на страницу, то количество запросов к БД будет = 1 + 1 + 100 + 100 = 202!!
'SELECT * FROM users'; // x 1
'SELECT * FROM orders WHERE user_id IN ( ... )'; // x 1
'SELECT COUNT(*) FROM orders WHERE user_id = ?'; // x 100
'SELECT SUM(price) FROM orders WHERE user_id = ?'; // x 100
Вариант #2$users = User::find()->with(['orders'])->all();
$ids = array_map(function ($User) { return $User->id; }, $users);
$stat = (new Query())
->select(['COUNT(*)', 'SUM(price)'])
->from(Order::tableName())
->where(['in', 'user_id', $ids])
->groupBy(['user_id'])
->all();
// Надо объединить $stat и $users.
// Рефакторить это дело очень плохо!
Вариант #3/**
* @property int $total
* @property double $price
* @property int $user_id
* @property User $user
*/
class Stat
{
/**
* @param User[] $users
* @return Stat[]
*/
public static function fill($users)
{
// ...
'SELECT COUNT(*) as total, SUM(price) as price, user_id FROM orders WHERE user_id IN( ... ) GROUP BY user_id';
// $pdoStatement->fetchAll(PDO::FETCH_CLASS, 'Stat');
// Цепляем юзеров к екзеплярам Stat
// ...
}
}
$stats = Stat::fill(User::find()->with(['orders'])->all());
foreach ($stats as $Stat) {
echo $Stat->user->id;
echo $Stat->total;
}
UPD:#3 самый лучший вариант ( его понимает IDE + рефакторить очень легко). вот только я не знаю пока как его лучше протестировать.
Вопрос связан с вопросами
Подходит ли паттерн Decorator для моей задачи? и
Как можно заюзать PDO::FETCH_CLASS при чистых запросах к бд на Yii2?