# Component Guide: Policies & Authorization Gates Policies kiểm soát quyền hạn truy cập của người dùng đối với từng hành động trên Model (view, create, update, delete). --- ## 1. Bản Chất Kiểm Thử Policy - Policy methods là các hàm thuần túy nhận `User` và `Model` (hoặc chỉ `User`) và trả về `bool` (hoặc `Response::allow()` / `Response::deny()`). - **Ưu điểm lớn**: Policy có thể được kiểm thử đơn vị **hoàn toàn 100% trong bộ nhớ (In-Memory)** mà không cần kết nối database hay framework gate pipeline. --- ## 2. Mã Mẫu Chuẩn: Policy Unit Test ### Policy Nguồn: `App\Policies\ArticlePolicy` ```php namespace App\Policies; use App\Models\Article; use App\Models\User; class ArticlePolicy { public function update(User $user, Article $article): bool { // Admin được sửa tất cả bài viết if ($user->is_admin) { return true; } // Tác giả chỉ được sửa bài của mình khi bài chưa publish return $user->id === $article->user_id && !$article->is_published; } public function delete(User $user, Article $article): bool { // Chỉ admin hoặc tác giả mới được xóa return $user->is_admin || $user->id === $article->user_id; } } ``` ### Test Chuẩn: PHPUnit 10+ ```php namespace Tests\Unit\Policies; use PHPUnit\Framework\TestCase; use PHPUnit\Framework\Attributes\Test; use App\Policies\ArticlePolicy; use App\Models\Article; use App\Models\User; class ArticlePolicyTest extends TestCase { private ArticlePolicy $policy; protected function setUp(): void { parent::setUp(); $this->policy = new ArticlePolicy(); } #[Test] public function admin_can_update_any_article_even_if_published(): void { $admin = new User(['id' => 99, 'is_admin' => true]); $article = new Article(['user_id' => 1, 'is_published' => true]); $this->assertTrue($this->policy->update($admin, $article)); } #[Test] public function author_can_update_own_article_if_not_published(): void { $author = new User(['id' => 10, 'is_admin' => false]); $article = new Article(['user_id' => 10, 'is_published' => false]); $this->assertTrue($this->policy->update($author, $article)); } #[Test] public function author_cannot_update_own_article_once_published(): void { $author = new User(['id' => 10, 'is_admin' => false]); $article = new Article(['user_id' => 10, 'is_published' => true]); $this->assertFalse($this->policy->update($author, $article)); } #[Test] public function non_author_cannot_update_others_article(): void { $otherUser = new User(['id' => 20, 'is_admin' => false]); $article = new Article(['user_id' => 10, 'is_published' => false]); $this->assertFalse($this->policy->update($otherUser, $article)); } } ```