0

In Laravel 11, I'm trying to make a single interface for all of the CRUD resource controllers in my API. Something like this (showing for store() method only):

<?php
namespace App\Interfaces;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Illuminate\Foundation\Http\FormRequest;
interface ResourceInterface
{
 /**
 * Store a newly created resource in storage.
 *
 * @param T $request
 * @return JsonResponse
 */
 public function store($request): JsonResponse;

And then in the UserController@store:

<?php
namespace App\Http\Controllers\Api\V1;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use App\Interfaces\ResourceInterface;
use App\Http\Requests\StoreUserRequest;
use App\Http\Controllers\Api\ApiController;
class UserController extends ApiController implements ResourceInterface
{
 public function __construct(private AuthController $authController)
 {
 }
 public function store(StoreUserRequest $request): JsonResponse
 {
 $user = $this->authController->register($request);
 return $this->createdResponse($user, "User {$request->name} registered successfully");
 }

As you can see, the UserController@store method uses a custom request class, which will be the case for all of my other resource controller methods. However, I have not been able to make the generic interface work.

I have tried using the FormRequest class in the controller method, as such:

 public function store(FormRequest $request): JsonResponse
 {
 if (!$request instanceof StoreUserRequest) {
 return $this->errorResponse("Invalid request type");
 }
 $request->validate();
 $user = $this->authController->register($request);
 return $this->createdResponse($user, "User $request->name registered succesfully");
 }

Together with replacing the generic interface with one that uses FromRequest, since my StoreUserRequest class extends FormRequest, but this did not work as the custom validation was not being applied.

And using PHPDoc comments as such:

 public function store(FormRequest $request): JsonResponse
 {
 /** @var StoreUserRequest $request */
 $user = $this->authController->register($request);
 return $this->createdResponse($user, "User $request->name registered succesfully");
 }

But none of these seem to work. What am I dowing wrong?,

6
  • "However, I have not been able to make the generic interface work." what does this mean? PHP has never had support for generics, and nothing you put in doc blocks is going to stop your code from working. Commented Sep 26, 2024 at 17:18
  • "I have tried using the FormRequest class in the controller method" that won't work, the controller method needs to be type hinted with the class you want injected. Commented Sep 26, 2024 at 17:20
  • This means that what I am trying to do is not working. I know that PHP does not natively support generics, but I imagine that there must be a way to make a single interface reusable around multiple classes that implement its methods but with different parameter types. This might be with some method that I am not aware of, might be using union types which seems ugly, etc. Commented Sep 26, 2024 at 17:29
  • Not without violating contravariance; you can't make method parameters more restrictive than ones declared in the interface. php.net/manual/en/language.oop5.variance.php Commented Sep 26, 2024 at 17:41
  • See this answer for a good explanation. Commented Sep 26, 2024 at 17:43

2 Answers 2

0

Let try Laravel' service container: https://laravel.com/docs/11.x/container#contextual-binding

Your case:

$this->app->when(UserController::class)
 ->needs(FormRequest::class)
 ->give(YourExtendClassOfFormRequest);

Add this code in boot function of AppServiceProvider

answered Sep 27, 2024 at 15:06
Sign up to request clarification or add additional context in comments.

Comments

-2

Seems like this is not possible or at least evil since it violates contravariance, and the way forward is to rethink the structure around my controller methods and my usage of interfaces.

answered Sep 26, 2024 at 17:47

1 Comment

As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.