phplaravel

Making a CommonController in Laravel


Most of my controllers in Laravel look the same.

Example:

class CourseController extends Controller
{
    public function index(Request $request)
    {
        $courses = Course::query();
        $courses = $this->commonIndex($courses, $request);
        return CourseResource::collection($courses);
    }
    public function store(StoreCourseRequest $request)
    {
        $course = Course::create($request->validated);
        return CourseResource::make($course);
    }
    public function show(Request $request, Course $course)
    {
        return CourseResource::make($course);
    }
    public function update(UpdateCourseRequest $request, Course $course)
    {
        $course->update($request->validated());
        return CourseResource::make($course);
    }
    public function destroy(Course $course)
    {
        $course->delete();
        return response()->noContent();
    }
}

I would like to create a common controller which is extended by all simple controllers and passes the Model, FormRequest, and Resource as public variables something like this:

class CommonController extends BaseController
{
    use AuthorizesRequests, ValidatesRequests;
    public $model;
    public $resource;
    public $storeFormRequest;
    public $updateFormRequest;
    public function index(Request $request)
    {
        $model = new $this->model;
        $resource = new $this->resource($request);
        $data = $model->query();
        return $resource->collection($data);
    }
    public function show(Request $request, string $id)
    {
        $model = new $this->model;
        $resource = new $this->resource($request);
        $data = $model->find($id);
        return $resource->make($data->load($loadWith));
    }
    public function store(Request $request)
    {
        $model = new $this->model;
        $resource = new $this->resource($request);
        $request = new $this->storeFormRequest($request->toArray());
        $validated = $request->validated();
        $data = $model->create($validated);
        return $resource->make($data);

    }
}

I don't know what's the best approach to passing the public variables to the constructor and evaluating them.

Also I'm facing an issue where new $this->storeFormRequest($request->toArray()) is evaluating to null.

The $storeFormRequest is the standard Laravel FormRequest:

class StoreCourseRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true;
    }

    public function rules(): array
    {
        return [
            'name' => 'required|string',
            'description' => 'required|string',
            'language_id' => 'required|exists:languages,id',
            'image' => 'array',
        ];
    }
}

This is how I'm using the CommonController:

class CourseController extends CommonController
{
    public $model = Course::class;
    public $resource = CourseResource::class;
    public $storeFormRequest = StoreCourseRequest::class;
    public $updateFormRequest = StoreCourseRequest::class;
}

Solution

  • If it were me, this is what I would do: First I would define CommonController as an abstract class, and subsequently add four abstract methods for getting the corresponding processing class name

    abstract protected function GetModel(): string;
    abstract protected function GetResource(): string;
    abstract protected function GetStoreFormRequest(): string;
    abstract protected function GetUpdateFormRequest(): string;
    

    Then I go and implement four methods in the subclass for returning the class name.

    protected function GetModel(): string
    {
        return Course::class;
    }
    

    Why? Using abstract methods constrains every subclass to implement them. This is just my personal opinion, I don't think there is a right answer to this question