Объяснение схемы
У меня есть две таблицы с одинаковой структурой внешних ключей. Они содержат значения из аналогичного домена - в общем, это информация о ценах пользователей. Существует две таблицы, потому что во второй таблице содержатся цены, которые были дополнительно сгенерированы для других пользователей, - существует связь 1: n между пользователями, указывающими, для кого можно создавать дополнительные цены.
Давайте назовем эти таблицы pricing
и referral_pricing
:
pricings:
id | date | user_id | tenant_id | zone_id | price
referral_pricings:
id | date | user_id | tenant_id | zone_id | price
users:
id | email | password | pricing_parent_id
1 test test null
2 test_2 test_2 1
Каждый раз, когда пользователь вставляет в таблицу pricing
, автоматически выполняется вторая вставка в таблицу referral_pricings
, например:
INSERT INTO pricings VALUES(null, NOW(), 1, 1, 1, 1000); // for user 1
INSERT INTO pricings VALUES(null, NOW(), 2, 1, 1, 1000); // for user 2
INSERT INTO referral_pricings VALUES(null, NOW(), 2, 1, 1, 200); // because pricing_parent_id != null for user with ID 2, I automatically insert 20% of total price as a referral price
Это означает, что когда я хочу получить общую цену для пользователя 1, то это то, что я хочу получить:
user_id | sum(price) | sum(referral_price)
1 1000 | 200
Поскольку я получаю общую цену для пользователя 1 из таблицы pricings
и общая цена от referral_pricings
пользователями, для которых users.pricing_parent_id == 1
.
Проблема
Проблема в том, что я хочу, чтобы этот запрос был очень динамичным c - разрешить группировку по внешним ключам и различным форматам даты. И поскольку они не связаны каким-либо общим внешним ключом:
Если я определю отношение, например:
Pricing.php:
public function referral_pricings()
{
return $this->belongsTo(ReferralPricing::class, 'date', 'date');
}
ReferralPricing.php:
public function pricings()
{
return $this->belongsTo(Pricing::class, 'date', 'date');
}
Тогда, когда я группирую по моему запросу, например, по году:
$pricings = Pricing::groupBy('YEAR(date)')->with('referral_pricing')->get();
Неправильный загруженный запрос:
SELECT date, sum(price)
FROM referral_pricings
WHERE date = 'YEAR(2020)'
GROUP BY YEAR(2020);
То же самое, если я хочу сгруппировать по большему количеству значений, например:
$pricings = Pricing::groupBy(['YEAR(date)', 'tenant_id'])->with('referral_pricing')->get();
Тогда запрос отношения:
SELECT date, tenant_id, sum(price)
FROM referral_pricings
WHERE date = 'YEAR(2020)'
GROUP BY YEAR(2020), tenant_id;
Что также не имеет смысла, потому что здесь нет никакого отношения к tenant_id
. Эти таблицы просто не могут быть связаны.
Решение
Я сделал это, используя объединение из обеих таблиц и применив group by к результату еще раз:
$pricings = Pricing::addSelect([
'YEAR(date) as date',
'tenant_id',
'sum(price) as price',
'0 as referral_price'
])
->groupBy(['date', 'tenant_id']);
$referral_pricings = ReferralPricing::addSelect([
'YEAR(date) as date',
'tenant_id',
'0 as price',
'sum(price) as referral_price'
])
->groupBy(['date', 'tenant_id'];
$union = DB::query()
->fromSub($pricings->union($referral_pricings), 't')
->addSelect([
'YEAR(date) as date',
'tenant_id',
'sum(price) as price',
'sum(referral_price) as referral_price'
])
->groupBy(['t.date', 't.tenant_id']);
Это прекрасно работает, но это означает ОЧЕНЬ МНОГО пользовательских логий c, гораздо меньшую гибкость и необходимость обрабатывать все объединения самостоятельно, без возможности использовать красноречивые отношения (потому что это DB::query()
из таблицы объединения в конце).
Есть ли лучшее решение для этого?