За замовчуванням Laravel автоматично відмовляє неавтентифікованим користувачам у будь-якому гейті чи методі політики: метод навіть не викликається, а результат - false. Це безпечне значення за замовчуванням.
Щоб гість міг пройти перевірку, параметр користувача оголошують необов'язковим:
class PostPolicy
{
public function view(?User $user, Post $post): bool
{
if ($post->is_published) {
return true; // опубліковане бачать усі, включно з гостями
}
return $user !== null && $user->id === $post->author_id; // чернетку - лише автор
}
}
Gate::define('view-pricing', fn (?User $user) => true);
Типові сценарії:
- публічний перегляд опублікованих матеріалів і приватний - чернеток;
- каталог для всіх, а ціни для партнерів - лише після входу;
- форма зворотного зв'язку для гостей з обмеженням частоти.
Пастки:
1. ?User і забута перевірка на null:
public function view(?User $user, Post $post): bool
{
return $user->id === $post->author_id || $post->is_published; // помилка для гостя
}
Для гостя $user->id - звернення до властивості null: у PHP 8 це помилка (Attempt to read property "id" on null), а не false. Завжди перевіряти null першим.
2. Гостьовий доступ, відкритий занадто широко. Додали ?User у view, щоб показувати опубліковані пости, - і забули, що той самий метод використовується для перегляду чернеток у прев'ю. Кожен метод з ?User варто переглядати окремо: які гілки повертають true для гостя.
3. Неявна довіра до параметрів: «опубліковане» має визначатися полем у базі (is_published), а не параметром запиту (?preview=1).
4. Авторизація ≠ автентифікація маршруту. Middleware auth на маршруті відсіює гостей ще до політики. Якщо маршрут має бути доступним гостям частково, auth на ньому не ставлять, а розрізнення робить політика.
Тести для гостьових сценаріїв: перевірити, що гість бачить лише дозволене ($this->get(...)->assertOk() для опублікованого, assertForbidden()/assertNotFound() для чернетки), - саме тут найчастіше випадково відкривають приватні дані.