Search code examples
phplaraveltraits

Dynamic model binding in trait function


I have multiple Policy classes.

and these policies' update, delete, restore functions have the same logic evaluation which is to check if the authenticated user owns the resource.

For example, I have a Post and a Comment model.

Then for PostPolicy and CommentPolicy, both of their update, delete, restore functions will all have:

public function update(User $user, Post $post)
{
    return $user->id == $post->user_id;
}

public function delete(User $user, Post $post)
{
    return $user->id == $post->user_id;
}

public function restore(User $user, Post $post)
{
    return $user->id == $post->user_id;
}

// Also the same with CommentPolicy

With that, I might as well have a trait like this:

trait AuthorizableTrait
{
    public function authorize(User $user, Resource $resource)
    {
        return $user->id == $resource->user_id;
    }

}

So, my question is, is it possible to inject a dynamic instance of the current model inside the trait, for example, Post and Comment models now will become Resource? if so, how?


Solution

  • There's a couple other ways to handle this too. In your trait, you can simply replace Resource $resource with Model $model. As long as your Post.php, Comment.php and Resource.php Models have are class ... extends Model { ... }, then it'll accept it:

    <?php 
    namespace App\Models\Traits;
    
    use Illuminate\Database\Eloquent\Model;
    
    trait AuthorizableTrait {
      public function authorize(User $user, Model $model) {
        return $user->id == $model->user_id;
      }
    }
    

    If that is too "loose", and you don't want this available for all Models (i.e. in the possible case that not all Models have a user_id column), then you can use Union Types (assuming you are on a compatible PHP version):

    <?php 
    namespace App\Models\Traits;
    
    use App\Models\Post;
    use App\Models\Comment;
    use App\Models\Resource;
    
    trait AuthorizableTrait {
      public function authorize(User $user, Post|Comment|Resource $model) {
        return $user->id == $model->user_id;
      }
    }