SeanKimball's avatar

Scoped bindings on controllers using traits

OK - I have an application that has several models that can relate to several other types of models example Resources(files) can be related to an index, an account, a user etc. anything that is "resourceable" That becomes problematic in the respect that adding all the resource crud methods to every controller for a resourceable model is a lot of problematic duplication. So I created a controller trait to handle all the resource methods (great!!) However the problem arises that I need to use route binding to ensure the model relationship chain i.e.:

Route::post('projects/{project}/file-indexes/{fileIndex}/resources', [FileIndexController::class, 'storeResource']);
Route::post('projects/{project}/accounts/{account}/resources', [FileIndexController::class, 'storeResource']);
Route::post('projects/{project}/users/{user}/resources', [FileIndexController::class, 'storeResource']);

We can't pass the type hints on to the Trait (ManagesResourcesTrait) = route/model bindings will not work So, I can pass on the models I want to work with to the ManagesResourcesTrait easily enough:

class FileIndexController extends Controller
{
    use ManagesResources;

    protected ?FileIndex $resourcableModel = null;
    protected ?Resource $resource = null;

    public function __construct(private readonly FileIndexServiceInterface $fileIndexService)
    {
        $this->authorizeResource(FileIndex::class, 'file_index');

        // Resolve the FileIndex model from route parameters for trait access
        $resourcableModelId = request()->route()->parameter('fileIndex');
        $this->resourcableModel = FileIndex::find($resourcableModelId) ?? null;

        $resourceId = request()->route()->parameter('resource');
        $this->resource = Resource::find($resourceId) ?? null;

    }
}

That makes the trait pretty easy to manage:

/**
     * Display a specific resource.
     */
    public function showResource(): ResourceResource
    {
        return new ResourceResource($this->resource);
    }

But I still need to enforce the model relations. resource belongs to an index that belongs to a project.

Is a middleware the best place to do this? I have experimented with route binding in the AppServiceProvider but it seems very messy and unmanageable.

0 likes
1 reply
SeanKimball's avatar

This is my middleware

Please or to participate in this conversation.