2013-07-05 29 views
9

Tôi đang cố gắng để tái tạo một tham gia như vậy bằng cách sử dụng xây dựng truy vấn laravel:Laravel 4 hùng biện Query Builder - Complicated tham gia với biến

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

Tôi đã phát hiện ra rằng tôi có thể làm thêm "Tiện ích" bằng cách sử dụng sau đây chức năng trong mô hình của tôi mở rộng 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); 
    }); 
} 

Nhưng điều này tạo ra hai vấn đề. Đầu tiên tôi không thể lấy biến $ user_id vào hàm và thứ hai ngay cả khi tôi mã hóa nó cho mục đích thử nghiệm như tôi đã làm ở trên (tới int "10") Laravel mã hóa nó trong `có nghĩa là nó được hiểu là tên cột khi nó cần Không, như vậy:

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

Vì vậy, bây giờ tôi có hai vấn đề.

  1. tôi không thể nhận được $ user_id vào tham gia chức năng khi sử dụng truy vấn phạm vi
  2. Thậm chí nếu tôi có thể tôi không thể gửi một biến đến tham gia vì nó luôn diễn giải nó như một tên cột

Tại sao tôi muốn làm điều này? Tôi nhận thấy một câu trả lời có thể là đặt nó ở một nơi. Tuy nhiên tôi đang cố gắng thực hiện theo cách này vì kết nối có thể không nhất thiết trả về bất kỳ kết quả nào (do đó là phần nối trái), vì bảng content_userdata chứa những thứ như xếp hạng của người dùng cho một phần nội dung. Nếu tôi sử dụng một vị trí thì kết quả không có gì trong bảng content_userdata sẽ không được trả về, như thể tôi có thể đặt nó trong tham gia thì chúng sẽ được trả về do tham gia bên trái. Có anyway để đạt được điều này trong Laravel và nếu không phải là những lựa chọn thay thế, rõ ràng là thay đổi hoàn toàn mương Laravel là trên đầu nhưng thay thế duy nhất tôi có thể nghĩ là để có được userdata trong một truy vấn riêng biệt.

Trả lời

5

tôi quản lý để khắc phục điều này bản thân mình, có một lưu ý ở dưới cùng của lý do tại sao nó không phải là hoàn toàn tối ưu nhưng dưới đây là cách để làm điều đó anyway:

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

Lưu ý việc sử dụng "sử dụng ($ user_id)" theo đề nghị của @ Nửa điên rồ.

DB :: raw() được sử dụng để bọc $ user_id trong dấu ngoặc kép mặc dù đó là số nguyên chứ không phải chuỗi. Điều này sẽ ngăn chặn Laravel tự động sử dụng `mà làm cho nó MySQL giải thích nó như là một tên cột.

Hiệu suất: Một điều cần lưu ý là truy vấn MySQL có thể nhanh hơn đáng kể khi sử dụng số nguyên chứ không phải chuỗi và sẽ diễn giải dưới dạng chuỗi nếu được đưa vào dấu ngoặc kép. Tôi không lo lắng về điều đó cho bây giờ, nhưng tôi figured tôi nên đề cập đến nó nếu những người khác đang sử dụng này như là một giải pháp.

+0

Bạn nên thay đổi câu trả lời của mình theo cách mà nó không đưa ra lỗ hổng SQL injection. – vog

23

Bạn cần phải chuyển biến cho việc đóng bằng cách sử dụng từ khóa use - nhập biến vào phạm vi. Ví dụ:

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

Đây là một vấn đề liên quan đến cú pháp PHP chứ không phải giới hạn Laravel!

+1

Chúc mừng khắc phục điểm 1 nhưng điểm 2 là giới hạn Laravel thì sao? – robjbrain

+0

Bạn sẽ cần sử dụng 'DB :: raw ($ user_id)' trong trường hợp đó :) –

+0

Tôi đã cập nhật câu trả lời của mình cho bạn. –

4

Tại sao bạn không chỉ sử dụng mối quan hệ? Đó là toàn bộ quan điểm của một số ORM like Eloquent?

Một cái gì đó như thế này;

class User extends Eloquent { 
    public function userdata() 
    { 
     return $this->hasOne('Userdata'); 
    } 
} 

$result= User::find(1)->userdata(); 

chỉnh sửa để hiển thị bạn có thể làm bất cứ điều gì bạn muốn với các mối quan hệ

Lựa chọn 1:

$place = new Place; 

$array = $place->with(array('users' => function($query) 
{ 
    $query->where('user_id', $user_id); 
}))->get(); 

var_dump($array->toArray()); 

hoặc Lựa chọn 2:

$place = new Place; 

$array = $place->with('users')->where('user_id', $user_id)->get(); 

var_dump($array->toArray()); 

Cả hai cho kết quả khác nhau - nhưng bạn nhận ý tưởng

+0

Bạn có thể đặt thêm nơi khi sử dụng hasOne chẳng hạn như: $ this-> where ('user_id', $ user_id) -> hasOne ('Userdata'). Nếu không, tôi không chắc chắn làm thế nào để giải quyết vấn đề thực tế của việc có thể chọn bởi một user_id cụ thể. – robjbrain

+1

có - bạn có thể đưa querry vào các mối quan hệ - http://laravel.com/docs/eloquent#querying-relations – Laurence

+0

Liên kết đó không ngụ ý rằng bạn có thể làm như vậy theo cách tôi đã nói hoặc theo cách hữu ích trong trường hợp này? Tôi vẫn không chắc chắn rằng bạn đã đọc câu hỏi hoặc hiểu vấn đề đúng cách, đặc biệt là sau khi "tại sao tôi muốn làm điều này", dù sao tôi đã đăng và chấp nhận câu trả lời chính xác ngay bây giờ. – robjbrain

-1

Vấn đề đầu tiên của bạn: Bạn nên sử dụng cú pháp PHP để đóng như câu trả lời của Half. Về vấn đề thứ hai của bạn, tôi nghĩ rằng phần AND user_id = $user_id của truy vấn không thuộc về một mệnh đề JOIN nhưng một mệnh đề WHERE vì nó chỉ phụ thuộc vào một bảng, không phải cả hai trong mối quan hệ nối kết này. Tôi nghĩ rằng bạn nên sử dụng một subquery như thế này:

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

Tuy nhiên, như bạn thấy, chúng ta hãy chắc chắn rằng các biến $user_id là an toàn vì chúng tôi sử dụng phương pháp \DB:raw.

+0

Tôi đã phản hồi khả năng sử dụng vị trí trong câu hỏi, giải thích lý do tại sao cần phải tham gia. Tôi cũng đã tìm ra và trả lời câu hỏi của riêng mình nhưng tôi không được phép chấp nhận nó trong 8 giờ. – robjbrain

6

Trong câu trả lời được chấp nhận, chỉ cần thêm dấu ngoặc kép quanh phần DB::raw của truy vấn sẽ không bảo vệ hoàn toàn khỏi việc chèn sql. Chỉ cần vượt qua một số dấu ngoặc kép trong user_id của bạn và xem. Để tham số hóa, bạn có thể làm điều gì đó như sau:

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

Lưu ý trong ví dụ này bạn không phải chuyển biến đó vào đóng. Hoặc bạn có thể thử và viết phần này completely raw.

CẬP NHẬT: Taylor addedjoinWhere, leftJoinWhere ... nếu bạn có một chức năng join chỉ cần sử dụng ->where->orWhere từ bên trong Closure.

+0

Cảm ơn David, câu trả lời của tôi khá cũ. Hy vọng rằng người dùng sẽ thấy điều này. –