soyhuce/laravel-fluent-policy

Write fluent policies in Laravel

1.7.0 2024-12-17 12:42 UTC

README

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status GitHub PHPStan Action Status Total Downloads

Write your policies fluently in Laravel.

<?php

class PostPolicy extends FluentPolicy
{
    public function delete(User $user, Post $post): Response
    {
        return $this->denyWhen($post->user_id !== $user->id)
            ->denyWhen($post->published_at !== null)
            ->allow();
    }
}

Installation

You can install the package via composer:

composer require soyhuce/laravel-fluent-policy

Usage

The goal of this package is to write your policies more easily in a clean syntax.

For exemple, the following policy:

<?php
use Illuminate\Auth\Access\HandlesAuthorization;

class PostPolicy
{
    use HandlesAuthorization;

    public function delete(User $user, Post $post): bool
    {
        if ($post->user_id !== $user->id) {
            return false;
        }
        
        if ($post->published_at !== null) {
            return false;
        }
    
        return true;
    }
}

can be re-written as:

<?php
use Illuminate\Auth\Access\Response;
use Soyhuce\FluentPolicy\FluentPolicy;

class PostPolicy extends FluentPolicy
{
    public function delete(User $user, Post $post): Response
    {
         return $this->denyWhen($post->user_id !== $user->id)
            ->denyWhen($post->published_at !== null)
            ->allow();
    }
}

You can customise the response if needed :

return $this->denyWhen($post->published_at !== null, 'You cannot delete a published post')
    ->allow();

You can also call another policy or gate this way :

return $this->authorize($user, 'update', $post)
    ->allowWhen($post->published_at === null)
    ->deny();

Custom Http status

You can deny the policy returning a custom http status code :

return $this->denyWithStatusWhen($post->user_id !== $user->id, 404)
        ->allow();
// or $this->>allowWhen(...)->denyWithStatus(404);

In the case of 404 status code, you can use the shortcut

return $this->denyAsNotFoundWhen($post->user_id !== $user->id)
        ->allow();
// or $this->>allowWhen(...)->denyAsNotFound();

Lazy evaluation

The different branches allowWhen and denyWhen are evaluated lazily which mean that the following code is completely correct :

<?php
use Illuminate\Auth\Access\Response;
use Soyhuce\FluentPolicy\FluentPolicy;

class PostPolicy extends FluentPolicy
{
    public function delete(User $user, Post $post): Response
    {
         // Here, $post->published_at is Carbon or null
    
         return $this->denyWhen($post->user_id !== $user->id)
            ->allowWhen($post->published_at === null) // 1
            ->allowWhen($post->published_at->isFuture()) // 2
            ->deny();
    }
}

2 will only be called if previous branches are all false. We are sure that here $post->published_at is not null thanks to 1.

PHPStan

When running PHPStan on

public function delete(User $user, Post $post): Response
{
     return $this->denyWhen($post->user_id !== $user->id)
        ->allowWhen($post->published_at === null) // 1
        ->allowWhen($post->published_at->isFuture()) // 2
        ->deny();
}

an error is raised on 2 (Cannot call method isFuture on Carbon|null).

An extension is available to fix this issue and should be included in your phpstan.neon file.

includes:
    - vendor/bin/soyhuce/laravel-fluent-policies/extension.neon

Unfortunately, due to a PHPStan limitation, you still have to rewrite your policy a little bit :

public function delete(User $user, Post $post): Response
{
    $this->denyWhen($post->user_id !== $user->id)
        ->allowWhen($post->published_at === null);
    // From here, PHPStan understands that $post->published_at is not null
    
    return $this->allowWhen($post->published_at->isFuture())
        ->deny();
}

Testing

composer test

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

Credits

License

The MIT License (MIT). Please see License File for more information.