Mẹo Laravel - Một số lưu ý cơ bản cho một dự án Laravel

Chào các bạn, hôm nay mình xin chia sẻ một số mẹo nhỏ cho một dự án Laravel, có thể nó sẽ áp dụng tốt trong một số trường hợp nhất định (vì mình không nghĩ mọi thứ sẽ là tuyệt đối).

Thông báo tùy chỉnh khi sử dụng Laravel Gate với database

Giải thích trong trường hợp này hơi phức tạp một tí, hiện tại mình có một table user_role có Model name là UserRole với các cột như sau:

key_name key_desc key_message
show-message Xem tin nhắn Bạn không có quyền xem tin nhắn
delete-message Xóa tin nhắn Bạn không có quyền xóa tin nhắn

Khi đó trong Controller, có thể bạn sẽ sử dụng như sau (Trường hợp 1 - TH1):

$this->authorize('show-message');

Và khi bạn muốn tùy chỉnh lại câu thông báo thì bạn có thể làm như sau (Trường hợp 2 - TH2):

abort_if(auth()->user()->can('show-message'), 403, 'Bạn không có quyền xem tin nhắn');

Nhưng trong trường hợp trên, bạn vẫn có thể sử dụng lại Trường hợp 1 mà không cần phải đặt câu thông báo tùy chỉnh, và bạn sử dụng câu thông báo trong table bên trên.

Vậy bằng cách nào?

Trong đoạn code này, bạn đang sử dụng hành động check quyền xem tin nhắn bằng auth()->user()->can(), và đây là một Gate được khai báo trong AuthServiceProvider, trong một số trường hợp phân quyền như thế này, thì trong AuthServiceProvider của bạn có thể được viết như sau:

File app/Providers/AuthServiceProvider.php

public function boot()
{
  $this->registerPolicies();

  // ... Your other code

  try {
    UserRole::get(['key_name', 'key_message'])->map(function ($permission) {
      Gate::define($permission->key_name, function($user) use($permission) {
        return $user->hasRole($permission->key_name) 
          ? Response::allow() 
          : Response::deny($permission->key_message);
      });
    });
  } catch (\Exception $e) {
    Return [];
  }
}

Khi sử dụng như thế này, bạn có thể tiết kiệm được kha khá dòng code cho mình, mà vẫn có thể trả về với những câu thông báo mong muốn cho mỗi khóa phân quyền của bạn như ở trường hợp 1 mà không cần phải trả về câu thông báo tùy chỉnh khá lằng nhằng như ở trường hợp thứ 2.

Đặt tên cho khóa phân quyền thật rõ nghĩa

Như ở ví dụ trên, mình dùng một cái khóa phân quyền là show-message, nếu bạn xem các khóa khác, như delete-message, edit-message, viewAny-message, restore-message chắc bạn sẽ hiểu rằng mình đang làm gì, như xóa, chỉnh sửa, xem tất cả hoặc khôi phục các rows đã xóa, vậy còn show-message? Là show cái what? Có vẻ nó hơi khó hiểu một tí, có thể bạn sẽ đặt lại là view-message sẽ dễ hiểu hơn. Nếu bạn thuộc hệ solo thì bạn có để show-message cũng không ảnh hưởng lắm, nhưng nếu bạn làm việc với một team, hoặc có thành viên mới tham gia vào team, việc để show-message có thể sẽ gây khó hiểu/nhầm lẫn cho người mới, đến lúc đó lại tốn công giải thích lắm nhé.

Đừng nhầm lẫn giữa Scope và Attribute trong Model

Bạn đã dùng Scope bao giờ chưa? (Mình thì không dùng, vì mình dùng Abstract/Interface). Vậy bạn có bao giờ dùng Scope giống như dưới đây chưa?

/** Ví dụ trong model Contact */
// Scope 1: Kiểm tra trạng thái đã đọc liên hệ chưa
public function scopeIsRead() {
  return $this->is_read ? 'Đã xem' : 'Chưa xem';
}
// Scope 2: Trả về thông tin ngày giờ
public function scopeCreatedAt() {
  return $this->created_at->format('Y-m-d');
}

Và bạn sử dụng chúng trong Controller như sau:

echo $model->isRead();
echo $model->createdAt();

Vậy có sự nhầm lẫn nào trong cách sử dụng Scope trên hay không? À dĩ nhiên là có một tí xíu, Scope không dùng để định dạng cho các trường, mà Scope cơ bản sinh ra là để sử dụng cho việc tạo thêm các query nhanh cho Laravel trong Model. Bởi vậy, 2 cái scope ví dụ phía trên phải là Attribute. Vậy nên, hai function phía trên cần phải chỉnh sửa lại một ít:

/** Ví dụ trong model Contact */
// Scope 1: Kiểm tra trạng thái đã đọc liên hệ chưa
public function getIsReadAttribute() {
  return $this->is_read ? 'Đã xem' : 'Chưa xem';
}
// Scope 2: Trả về thông tin ngày giờ
public function getCreatedAtAttribute() {
  return $this->created_at->format('Y-m-d');
}

Đến lúc này, bạn có thể sử dụng chúng tại Controller hoặc View:

echo $model->is_read;
echo $model->created_at;

Nhưng, tại sao chúng ta lại phải ghi đè giá trị mặc định của cột created_at như vậy? Trong khi chúng ta có thể sử dụng như sau:

echo $model->created_at->format('Y-m-d');

Khi sử dụng như thế này, bạn cũng không cần thiết phải dùng đến Scope 2, mà vẫn có thể giữ nguyên được giá trị gốc của cột created_at trong Model.

Đến đây thì một mớ mẹo nữa đã kết thúc, rất cảm ơn bạn đã chịu khó đọc đến đây, nếu có chỗ nào khó hiểu, bạn có thể để lại bình luận, mình sẽ trả lời lại cho các bạn trong sự hiểu biết của mình nhé. Hẹn gặp lại bạn trong mẹo tiếp theo.

Chuyên đề: Mẹo Lập trình Laravel
Bài trước Bài tiếp theo
Lưu ý: Hệ thống bài học theo Chuyên Đề đang được thử nghiệm.
Mẹo Laravel - Một số lưu ý cơ bản cho một dự án Laravel

Danh sách bài học

Cùng chuyên mục

Xem nhiều hôm nay