2013-07-05 1 views
9

Я пытаюсь повторить объединение, как это, используя конструктор Laravel запросов:Laravel 4 Eloquent Query Builder - Сложное соединяется с переменной

LEFT JOIN content_userdata 
ON content_id = content.id 
AND user_id = $user_id 

я обнаружил, что я могу сделать дополнительные «дополнения», используя следующие функция в моей модели, которая расширяет 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 заключает ее в «означает, что он интерпретируется как имя столбца, когда он должен t t, так:

left join `content_userdata` 
on `content_id` = `content`.`id` 
and `user_id` = `10` 

Итак, у меня есть две проблемы.

  1. Я не могу получить $ user_id в функцию присоединения при использовании запроса прицелов
  2. Даже если бы я мог, я не могу послать переменное объединение, поскольку она всегда интерпретирует его как имя столбца

Зачем мне это делать? Я понимаю, что один ответ может состоять в том, чтобы поместить его в место. Однако я пытаюсь сделать это таким образом, так как объединение может не обязательно возвращать какие-либо результаты (следовательно, левое объединение), так как таблица content_userdata содержит такие вещи, как рейтинг пользователей для части контента. Если я использую a, а затем результаты, которые ничего в таблице content_userdata не будут возвращены, где, как если бы я мог поместить его в соединение, они будут возвращены из-за левого соединения.

В любом случае, чтобы достичь этого в Laravel, и если нет, то какие альтернативы, очевидно, полностью меняющиеся канавы Laravel превышают вершину, но единственной альтернативой, о которой я могу думать, является получение пользовательских данных в отдельном запросе.

ответ

5

мне удалось это исправить себя, есть примечание в нижней части, почему это не совсем оптимальный, но вот как сделать это так или иначе:

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.'"')); 
    }); 
} 

Обратите внимание на использование «использования ($ user_id)» как это было предложено @ Половина Crazed.

DB :: raw() используется для переноса $ user_id в кавычки, даже если это целое число, а не строка. Это автоматически остановит Laravel, используя `, что делает его интерпретатором MySQL как имя столбца.

Производительность: Следует отметить, что запросы MySQL могут быть значительно быстрее при использовании целого числа, а не строки, и интерпретируют его как строку, если она заключена в кавычки. На данный момент я не беспокоюсь об этом, но я решил, что должен упомянуть об этом, если другие используют это как решение.

+0

Вы должны изменить свой ответ так, чтобы он не вводил уязвимость SQL-инъекции. – vog

23

Вам необходимо передать переменную в закрытие, используя ключевое слово 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!

+1

Приветствия, которые фиксируют точку 1, но как насчет точки 2, которая является ограничением Laravel? – robjbrain

+0

В этом случае вам нужно использовать 'DB :: raw ($ user_id)' :) –

+0

Я обновил свой ответ для вас. –

4

Почему вы не используете отношения? В этом весь смысл ORM like Eloquent?

Нечто подобное;

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()); 

Оба дают разные результаты - но вы получить идею

+0

Можете ли вы установить дополнительное место, где при использовании hasOne, например: $ this-> where ('user_id', $ user_id) -> hasOne ('Userdata'). Если нет, я не уверен, как это решает реальную проблему выбора конкретного пользователя_ид. – robjbrain

+1

Да - вы можете включить querrys в отношения - http://laravel.com/docs/eloquent#querying-relations – Laurence

+0

Эта ссылка не означает, что вы можете сделать это так, как я сказал или таким образом, чтобы это было полезно в этом случае? Я все еще не уверен, что вы прочитали вопрос или правильно поняли проблему, особенно бит после «зачем мне это делать», тем не менее я уже опубликовал и принял правильный ответ сейчас. – robjbrain

-1

Ваша первая проблема: вы должны использовать синтаксис PHP для закрытия в качестве ответа Half. О вашей второй проблеме, я думаю, что часть 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 метод.

+0

Я уже ответил на вопрос о том, как использовать вопрос в этом вопросе, объясняя, почему он должен быть в соединении. Я также понял это и ответил на свой вопрос, но я не могу принять его в течение 8 часов. – robjbrain

6

В принятом ответе только добавление котировок вокруг 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))); 
} 

Обратите внимание, что в данном примере вы не должны передать переменную в крышку. В качестве альтернативы вы можете попробовать и написать эту часть completely raw.

UPDATE: Taylor addedjoinWhere, leftJoinWhere ... если у вас есть функция присоединиться просто использовать ->where и ->orWhere изнутри закрытия.

+0

Спасибо, Дэвид, мой ответ довольно старый. Надеюсь, пользователи это увидят. –

 Смежные вопросы

  • Нет связанных вопросов^_^