Laravel 4 Eloquent Query Builder-сложные соединения с переменной
Я пытаюсь реплицировать соединение таким образом, используя построитель запросов laravel:
LEFT JOIN content_userdata
ON content_id = content.id
AND user_id = $user_id
я обнаружил, что могу делать дополнительные "ons", используя следующую функцию в моей модели, которая расширяет Eloquent
public function scopeJoinUserData($query, $user_id)
{
return $query->leftJoin('content_userdata', function($join)
{
$join->on('content_userdata_content_id', '=', 'content.content_id')->on('content_userdata_user_id', '=', 10);
});
}
но это создает две проблемы. Во-первых, я не могу получить переменную $user_id в функцию, а во-вторых, даже если я жестко закодирую ее для целей тестирования, как я сделал выше (для int "10"), Laravel заключает ее в " значение, что она интерпретируется как имя столбца, когда его не должно быть, например:
left join `content_userdata`
on `content_id` = `content`.`id`
and `user_id` = `10`
Итак, теперь у меня две проблемы.
- я не могу получить $user_id в функцию join при использовании областей запросов
- даже если бы я мог, я не могу отправить переменную в соединение, так как она всегда интерпретирует ее как имя столбца
зачем мне это делать? Я понимаю, что один из ответов может заключаться в том, чтобы поместить его в "где". Однако я пытаюсь сделать это таким образом, как присоединиться может не обязательно возвращать какие-либо результаты (следовательно, левое соединение), так как таблица content_userdata содержит такие вещи, как рейтинг пользователей для части контента. Если я использую where, то результаты ни с чем в таблице content_userdata не будут возвращены, где, как будто я могу поместить его в соединение, они будут возвращены из-за левого соединения.
есть ли в любом случае, чтобы достичь этого в Laravel, и если нет, то каковы альтернативы, очевидно, полностью меняя канаву Laravel сверху, но единственная альтернатива, которую я могу придумать, - получить userdata в отдельном запросе.
5 ответов
вам нужно передать переменную в закрытие с помощью use
ключевое слово-которое импортирует переменную в область. Пример:
public function scopeJoinUserData($query, $user_id)
{
return $query->leftJoin('content_userdata', function($join) use ($user_id)
{
$join->on('content_userdata_content_id', '=', 'content.content_id')
->on('content_userdata_user_id', '=', DB::raw($user_id));
});
}
это проблема, связанная с синтаксисом PHP, а не ограничение Laravel!
в принятом ответе, просто добавляя кавычки вокруг DB::raw
часть запроса не будет полностью защищать его от SQL-инъекции. Просто пройти несколько цитат в свой user_id и увидеть. Для параметризации вы можете сделать что-то вроде этого:
public function scopeJoinUserData($query, $user_id)
{
return $query->leftJoin('content_userdata', function($join)
{
$join->on('content_userdata_content_id', '=', 'content.content_id')
->on('content_userdata_user_id', '=', DB::raw('?'));
}
->setBindings(array_merge($query->getBindings(),array($user_id)));
}
обратите внимание в этом примере, что вам не нужно передавать переменную в закрытие. В качестве альтернативы вы можете попробовать написать эту часть совершенно сырой.
обновление: Тейлор добавил joinWhere
, leftJoinWhere
... если у вас есть функция join, просто используйте ->where
и ->orWhere
из-за закрытия.
мне удалось исправить это самостоятельно, внизу есть примечание, почему это не совсем оптимально, но вот как это сделать в любом случае:
public function scopeJoinUserData($query, $user_id)
{
return $query->leftJoin('content_userdata', function($join) use ($user_id)
{
$join->on('content_userdata_content_id', '=', 'content.content_id')->on('content_userdata_user_id', '=', DB::raw('"'.$user_id.'"'));
});
}
обратите внимание на использование "use ($user_id)", как предложил @Half Crazed.
DB:: raw() используется для обертывания $user_id в кавычки, даже если это целое число, а не строка. Это остановит Laravel автоматически используя", что заставляет MySQL интерпретировать его как имя столбца.
производительность: одна вещь, чтобы отметить что запросы MySQL могут быть значительно быстрее при использовании целого числа, а не строки, и будут интерпретировать его как строку, если она обернута в кавычки. Я не беспокоюсь об этом сейчас, но я подумал, что должен упомянуть об этом, если другие используют это как решение.
почему бы вам просто не использовать отношения? В этом весь смысл ан ОРМ вроде красноречив?
что-то вроде этого;
class User extends Eloquent {
public function userdata()
{
return $this->hasOne('Userdata');
}
}
$result= User::find(1)->userdata();
редактировать, чтобы показать, что вы можете делать все, что хотите с отношениями
Вариант 1:
$place = new Place;
$array = $place->with(array('users' => function($query)
{
$query->where('user_id', $user_id);
}))->get();
var_dump($array->toArray());
Вариант 2:
$place = new Place;
$array = $place->with('users')->where('user_id', $user_id)->get();
var_dump($array->toArray());
оба дают разные результаты - но вы получите идею
ваша первая проблема: вы должны использовать синтаксис PHP для закрытия в качестве ответа пол.
О вашей второй проблеме, я думаю, часть AND user_id = $user_id
запроса не относится к предложению JOIN, а к предложению WHERE, поскольку оно зависит только от одной таблицы, а не от обеих в этом отношении соединения. Я думаю, вы должны использовать такой подзапрос:
public function scopeJoinUserData($query, $user_id)
{
return $query->leftJoin(\DB:raw("(SELECT * FROM content_userdata WHERE user_id = {$user_id}) AS t"), function($join)
{
$join->on('t.content_id', '=', 'content.content_id');
});
}
однако, как вы видите, давайте убедимся, что $user_id
переменная безопасна, потому что мы используем \DB:raw
метод.