Querying relations Laravel


Để hạn chế các câu truy vấn với điều kiện ở nhiều bảng liên quan đến nhau. Tận dụng quan hệ đã được khai báo trong model, chúng ta sẽ hạn chế được các câu query và giúp code trở nên gọn gàng hơn. Bắt tay ngay nào!

Giới thiệu

Bài trước mình đã giới thiệu đến các bạn về các mối quan hệ mà Laravel hỗ trợ.
Sau khi chúng ta xác định các mối quan hệ trong model, ta có thể coi nó như 1 thuộc tính của model để lấy dữ liệu. Tuy nhiên, ta cũng có thể gọi nó chỉ để trả về 1 instance của mối quan hệ đó mà không thực sự cần trả về dữ liệu.

Truy vấn sự tồn tại của Relationship

Vấn đề đặt ra khi bạn muốn truy vấn dựa trên sự tồn tại của relationship của đối tượng nào đó. Ví dụ: bạn muốn lấy ra các post mà có ít nhất 1 bình luận. Bạn sẽ làm như thế nào? Không lẽ chúng ta lấy post_id từ những bình luận, sau đó dùng whereIn để lấy những bài viết đó? Rất mất công phải không nào.

Rất đơn giản, chúng ta dùng phương thức has hoặc orHas:

$posts = Post::has('comments')->get();

hoặc thêm chút ràng buộc về số lượng của quan hệ:

$posts = Post::has('comments', '>=', 3)->get();

Nếu muốn truy vấn dựa trên các relation lồng nhau, ta dùng dấu .. Ví dụ, truy vấn các post có ít nhất 1 comment có 1 votes:

$posts = Post::has('comments.votes')->get();

Nếu bạn cần những ràng buộc mạnh mẽ hơn thì dùng whereHasorWhereHas. Các phương thức này cho phép chúng ta thêm các ràng buộc relationship. Ví dụ, lấy các post có comment có nội dung phù hợp:

use Illuminate\Database\Eloquent\Builder;

// lấy ra các post có ít nhất 10 comment có chứa từ foo
$posts = Post::whereHas('comments', function (Builder $query) {
    $query->where('content', 'like', 'foo%');
}, '>=', 10)->get();

$query: là builder của relationship comments hay nói cách khác đang đứng ở model Comment, và chúng ta cũng viết những câu query bên trong này.

Truy vấn dựa trên sự KHÔNG tồn tại relationship

Ngược lại với phần trên, ở đây chúng ta sẽ lấy các bài post mà không có comments nào cả. Ta dùng doesntHaveorDoesntHave:

$posts = Post::doesntHave('comments')->get();

Tiếp theo, để có thể truy vấn với những ràng buộc mạnh mẽ hơn, chúng ta sử dụng whereDoesntHaveorWhereDoesntHave:

use Illuminate\Database\Eloquent\Builder;

// lấy ra các bài post không có comment có nội dung chứa từ foo
$posts = Post::whereDoesntHave('comments', function (Builder $query) {
    $query->where('content', 'like', 'foo%');
})->get();

Chúng ta có thể tạo những quan hệ lồng nhau. Ở $query ta hoàn toàn có thể tiếp tục sử dụng whereDoesntHaveorWhereDoesntHave để ràng buộc những quan hệ khác của thằng comments.

Truy vấn với Polymorphic Relationships

Để truy vấn với Polymorphic Relationships, ta dùng phương thức whereHasMorphwhereDoesntHasMorph và các phương thức tương ứng.

Lưu ý: chúng ta cần truyền tham số vào trong các hàm này.

use Illuminate\Database\Eloquent\Builder;

// Lấy ra các comment của post hoặc video có title chứa từ foo
$comments = Comment::whereHasMorph(
    'commentable',
    [Post::class, Video::class],
    function (Builder $query) {
        $query->where('title', 'like', 'foo%');
    }
)->get();

// Lấy ra các comment của post có title không chứa từ foo
$comments = App\Comment::whereDoesntHaveMorph(
    'commentable',
    Post::class,
    function (Builder $query) {
        $query->where('title', 'like', 'foo%');
    }
)->get();

Bạn cũng có thể dùng tham số $type để thêm các điều kiện với relationship.

use Illuminate\Database\Eloquent\Builder;

$comments = Comment::whereHasMorph(
    'commentable',
    [Post::class, Video::class],
    function (Builder $query, $type) {
        $query->where('title', 'like', 'foo%');

        if ($type === Post::class) {
            $query->orWhere('content', 'like', 'foo%');
        }
    }
)->get();

Để có thể truy vấn từ tất cả các kiểu model, thay vì truyền 1 mảng các model vào tham số thứ 2, ta thay bằng dấu *.

Nếu bạn cần đếm số lượng kết quả trả về từ 1 quan hệ và không cần load chúng thì có thể dùng phương thức withCount. Hàm này sẽ thêm cột {relation}_count vào trong kết quả, sau đó bạn có thể gọi ra như 1 Dynamic Properties.

$posts = Post::withCount('comments')->get();

foreach ($posts as $post) {
    echo $post->comments_count;
}

Nâng cao một xíu, ta có thể đếm nhiều quan hệ, kết hợp với ràng buộc:

$posts = Post::withCount(['votes', 'comments' => function (Builder $query) {
    $query->where('content', 'like', 'foo%');
}])->get();

Sau đó lấy kết quả như sau:

echo $posts[0]->votes_count;
echo $posts[0]->comments_count;

Đếm 1 quan hệ nhiều lần với những điều kiện khác nhau:

$posts = Post::withCount([
    'comments',
    'comments as pending_comments_count' => function (Builder $query) {
        $query->where('approved', false);
    },
])->get();

echo $posts[0]->comments_count;

echo $posts[0]->pending_comments_count;

Kết luận

Qua bày này mình giới thiệu tới các bạn cách để chúng ta truy vấn với ràng buộc relationship.

Nguồn tham khảo:

Rất mong được sự ủng hộ của mọi người để mình có động lực ra những bài viết tiếp theo.
{\__/}
( ~.~ )
/ > ♥️ I LOVE YOU 3000

JUST DO IT!


Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source Nguyễn Quang Đạt !
Comments
  TOC