Laravel5: сложное отношение со ссылками на hasManyThrouh (с псевдонимами и вычислениями SQL) - PullRequest
0 голосов
/ 26 апреля 2018

Я перестраиваю проект в laravel, и моя текущая проблема состоит в том, чтобы определить сложное самосвязанное отношение hasManyThrough с псевдонимами таблиц и вычислениями SQL.

Это отношение должно найти связанных торговцев в зависимости от нисходящей суммы соответствующих тегов. Чем больше одинаковых тегов у моделей, тем больше они связаны.

Пока все хорошо. В моем старом проекте я просто записал следующий подходящий SQL-запрос:

SELECT source_merchant.id, target_merchant.id, COUNT(target_merchant.id) /
((
    (SELECT COUNT(*) FROM tagged WHERE model = 'Merchant' AND model_id = source_merchant.id) + 
    (SELECT COUNT(*) FROM tagged WHERE model = 'Merchant' AND model_id = target_merchant.id) 
) /2 ) as similarity 
FROM merchants source_merchant 
LEFT JOIN tagged source_merchant_tags ON (
    source_merchant.id = source_merchant_tags.model_id AND 
    source_merchant_tags.model = 'Merchant'
) 
INNER JOIN tagged target_merchant_tags ON (
    source_merchant_tags.tag_id = target_merchant_tags.tag_id 
    AND (source_merchant_tags.model = 'Merchant' AND target_merchant_tags.model = 'Merchant') 
    AND (source_merchant_tags.model_id != target_merchant_tags.model_id)
)  
LEFT JOIN merchants target_merchant ON (
    target_merchant_tags.model_id = target_merchant.id AND target_merchant_tags.model = 'Merchant'
) 
WHERE source_merchant.id = 2 
GROUP BY source_merchant.id, target_merchant.id 
ORDER BY similarity DESC
LIMIT 5

Лучше всего было бы поймать что-то вроде

public function related_merchants() {
    return $this->hasManyThroug(relations_stuff_i_cannot_imagine...)
        ->selectRaw("SELECT source_merchant.id, target_merchant.id, COUNT(target_merchant.id) /
        ((
            (SELECT COUNT(*) FROM tagged WHERE model = 'Merchant' AND model_id = source_merchant.id) + 
            (SELECT COUNT(*) FROM tagged WHERE model = 'Merchant' AND model_id = target_merchant.id) 
        ) /2 ) as similarity")
        ->groupBy('source_merchant.id', 'target_merchant.id ')
        ->orderBy('similarity')
        ->limit(5);
}

Вот и все :-) К сожалению, я не могу найти решение, потому что я не знаю, как определить подходящие параметры отношения в hasManyThrough() ...

Редактировать - Попытка построить запрос Laravel, как предложено :

public function getRelatedMerchantsAttribute() {

    return $this->from('merchants AS source_merchant')
        ->selectRaw('source_merchant.id, target_merchant.id, COUNT(target_merchant.id) /
            ((
                (' . DB::table('tagged')->whereRaw("model = 'Merchant' AND model_id = source_merchant.id")->count() . ') +
                ' .  DB::table('tagged')->whereRaw("model = 'Merchant' AND model_id = target_merchant.id")->count() . ')
                /2 ) AS similarity')
        ->lefJoin('tagged AS source_merchant_tags', function ($join) {
            $join->on('source_merchant.id', '=', 'source_merchant_tags.model_id')
            ->on('source_merchant_tags.model', '=', 'Merchant');
        })
        ->join('tagged AS target_merchant_tags', function ($join) {
            $join->on('source_merchant_tags.tag_id', '=', 'target_merchant_tags.tag_id')
            ->on('source_merchant_tags.model', '=', 'Merchant')
            ->on('target_merchant_tags.model', '=', 'Merchant')
            ->on('source_merchant_tags.model_id', '!=', 'target_merchant_tags.model_id');
        })
        ->leftJoin('merchants AS target_merchant', function ($join) {
            $join->on('target_merchant_tags.model_id ', '=', 'target_merchant.id')
            ->on('target_merchant_tags.model', '=', 'Merchant');
        })
        ->whereRaw('source_merchant.id = ?', [ $this->id ])
        ->groupBy('source_merchant.id', 'target_merchant.id ')
        ->orderBy('similarity')
        ->limit(5)
        ->get();

}

Это решение пока не работает из-за:

SQLSTATE[42S22]: Column not found: 1054 Unknown column 'merchants.id'
in 'where clause' (SQL: select count(*) as aggregate from `tagged`
where model = 'Merchant' AND model_id = merchants.id)

Редактировать - При использовании этот запрос приводит к следующей ошибке Laravel:

SQLSTATE [HY093]: неверный номер параметра. SQL:

select source_merchant.id, target_merchant.id, 
COUNT(target_merchant.id) /
((
    (select COUNT(*) from `tagged` where `model` = Merchant and `model_id` = source_merchant.id) +
    (select COUNT(*) from `tagged` where `model` = Merchant and `model_id` = target_merchant.id)
) /2 ) AS similarity
from `merchants` as `source_merchant`
left join `tagged` as `source_merchant_tags` on 
    `source_merchant`.`id` = `source_merchant_tags`.`model_id`
    and `source_merchant_tags`.`model` = Merchant
inner join `tagged` as `target_merchant_tags` on 
    `source_merchant_tags`.`tag_id` = `target_merchant_tags`.`tag_id` 
    and `source_merchant_tags`.`model` = Merchant
    and `target_merchant_tags`.`model` = 2
    and `source_merchant_tags`.`model_id` != `target_merchant_tags`.`model_id` 
left join `merchants` as `target_merchant` on 
    `target_merchant_tags`.`model_id` = `target_merchant`.`id`
    and `target_merchant_tags`.`model` = ?
where `source_merchant`.`id` = ? 
group by `source_merchant`.`id`, `target_merchant`.`id`
order by `similarity` desc
limit 5

Мне даже интересно, почему здесь используется текущий идентификатор модели: and target_merchant_tags.model = 2 ...

Посмотрите на этот скриншот. Условный параметр «Продавец» становится идентификатором текущей модели, в данном случае это 2 (выделенный текст). Два параметра в красных кружках остаются пустыми. Что здесь не так?

SQLSTATE[HY093]: Invalid parameter number

1 Ответ

0 голосов
/ 28 апреля 2018

Я не думаю, что отношения - это правильный выбор для вашей ситуации. Торговцы-источники и цели на самом деле не связаны в смысле Laravel / Eloquent, так как общие теги используются только для определения порядка.

Я бы просто преобразовал ваш сырой SQL в запрос Laravel и переименовал ваш метод в getRelatedMerchantsAttribute. Затем вы можете получить к ним доступ с помощью $merchant->relatedMerchants.

Попробуйте этот запрос:

$sourceCount = DB::table('tagged')
    ->selectRaw('COUNT(*)')
    ->where('model', 'Merchant')
    ->where('model_id', DB::raw('source_merchant.id'));
$targetCount = DB::table('tagged')
    ->selectRaw('COUNT(*)')
    ->where('model', 'Merchant')
    ->where('model_id', DB::raw('target_merchant.id'));
$this->from('merchants AS source_merchant')
    ->addBinding($sourceCount->getBindings(), 'select')
    ->addBinding($targetCount->getBindings(), 'select')
    ->selectRaw('source_merchant.id, target_merchant.id, COUNT(target_merchant.id) /
    ((
        (' . $sourceCount->toSql() . ') +
        (' .  $targetCount->toSql() . '))
        /2 ) AS similarity')
    ->leftJoin('tagged AS source_merchant_tags', function ($join) {
        $join->on('source_merchant.id', '=', 'source_merchant_tags.model_id')
            ->where('source_merchant_tags.model', '=', 'Merchant');
    })
    ->join('tagged AS target_merchant_tags', function ($join) {
        $join->on('source_merchant_tags.tag_id', '=', 'target_merchant_tags.tag_id')
            ->where('source_merchant_tags.model', '=', 'Merchant')
            ->where('target_merchant_tags.model', '=', 'Merchant')
            ->on('source_merchant_tags.model_id', '!=', 'target_merchant_tags.model_id');
    })
    ->leftJoin('merchants AS target_merchant', function ($join) {
        $join->on('target_merchant_tags.model_id', '=', 'target_merchant.id')
            ->where('target_merchant_tags.model', '=', 'Merchant');
    })
    ->where('source_merchant.id', $this->id)
    ->groupBy('source_merchant.id', 'target_merchant.id')
    ->orderByDesc('similarity')
    ->limit(5)
    ->get();
...