Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

bwrigley's avatar

Overriding methods with different parameters

I'm not sure if what I'm trying to achieve in my application is a php and/or Laravel no no, or perhaps just poor design, but I would be grateful for your thoughts.

In my forum application, users can create a Post, they can also create things like Advert,Poll,Question etc which are all types of Post, just with subtly different implementations on the front end and a few other variations.

It seemed sensible to me to make a generic Post model and controller and then extend those to reduce code duplication and then override methods that do require something a bit different.

I was just starting with my PostController::store() method:

class PostController extends Controller
{

    protected $postableType;

    public function __construct()
    {
        $this->postableType = PostType::GENERIC;
    }


    public function store(PostStoreRequest $request): RedirectResponse
    {
        $validated = $request->validated();

        $post = new Post();
        $post->message = $validated['message'];
        $post->user_id = $request->user()->id;

        $post->postable_type = $this->postableType;

        $post->save();

        return Redirect::route('feed');
    }

}

And then began my Advert::store() method which needs slightly different validation rules so I created a different FormRequest :

class AdvertController extends PostController
{

    protected $postableType;

    public function __construct()
    {
        $this->postableType = PostType::ADVERT;
    }


    public function store(AdvertStoreRequest $request): RedirectResponse
    {
        $validated = $request->validated();

        $advert = new Advert();
        $advert ->message = $validated['message'];
        $advert ->user_id = $request->user()->id;

        $advert ->postable_type = $this->postableType;

        $advert ->save();

        return Redirect::route('feed');
    }

}

When I run my unit test to make sure this simple logic works I get a PHP error:

 Declaration of App\Http\Controllers\AdvertController::store(App\Http\Requests\AdvertStoreRequest $request): Illuminate\Http\RedirectResponse must be compatible with App\Http\Controllers\PostController::store(App\Http\Requ   
  ests\PostStoreRequest $request): Illuminate\Http\RedirectResponse      

Both AdvertStoreRequest and PostStoreRequest extend FormRequest

Any thoughts would be really helpful!

0 likes
16 replies
krisi_gjika's avatar

@bwrigley AdvertStoreRequest needs to extend PostStoreRequest, even tho both AdvertStoreRequest and PostStoreRequest extend FormRequest there is no indication that AdvertStoreRequest is a type of PostStoreRequest

martinbean's avatar

@bwrigley This is a PHP limitation. You can’t do method overloading like that. The method signatures need to match, even in child classes.

1 like
bwrigley's avatar

@martinbean ah! That would explain it then :) Thanks!

So can I ask you how you would solve this instance? Is mine just a poor design, or should I maybe manage the form request inside the method? Or something better?

martinbean's avatar

@bwrigley I sorely wish PHP supported method overloading for a use case spookily similar to yours.

I primarily use resource controllers in my applications, and I’d love to be able to define an abstract ResourceController and then have various child controllers extend it with more specific types. Unfortunately, I’ve not really found a “nice” way to achieve this.

1 like
bwrigley's avatar

@martinbean I want to use resource controllers too as they are straightforward and make sense to me. This hiccup leaves me with three options I can think of, but none of which I like!

  1. Remove the overloading altogether and just duplicate code for each model.
  2. instead of overloading methods, have methods with different names in children e.g. _store()
  3. In this particular case, instantiate the form request inside the method, but how long before I find myself with another method that needs different parameters?
    public function store(): RedirectResponse
    {
        $request = app('App\Http\Requests\AdvertStoreRequest');

        $validated = $request->validated();
		 //
   }
kokoshneta's avatar

There are two primary options here:

One is what @krisi_gjika says: either let AdvertStoreRequest extend PostStoreRequest or let both of them extend a generic StoreRequest

The other would be to create an interface for your store request classes to implement.

Since it doesn’t look like you’re really adding anything new on top of what the basic FormRequest comes with in terms of functionality, I would go with the former – an interface doesn’t really make that much sense if there’s no functionality whose presence you want to ensure.

1 like
kokoshneta's avatar

@bwrigley Have you made sure that the actual signatures are also compatible? If you make a generic StoreRequest class and extend that in your PostStoreRequest and AdvertStoreRequest, then your store method should typehint for a StoreRequest – the parent class, not the child classes. You can then pass either of the child classes as the actual parameter, and they will be accepted since they both extend StoreRequest.

Although, as far as your snippet shows, the method does the same thing in both controllers, so there’s little need to define it more than once.

I would define the PostController like this:

class PostController extends Controller {
	protected $postableType = PostType::GENERIC;
	protected $modelType = Post::class;

	public function store(StoreRequest $request): RedirectResponse {
		$validated = $request->validated();
		$post = new $this->modelType;
		$post->message = $validated['message'];
		$post->user_id = $request->user()->id;
		$post->postable_type = $this->postableType;
		$post->save();
		return Redirect::route('feed');
	}
}

And the AdvertController would just need this:

class AdvertController extends PostController {
	protected $postableType = PostType::ADVERT;
	protected $modelType = Advert::class;
}
krisi_gjika's avatar

@bwrigley if AdvertStoreRequest extends PostStoreRequest and you are still getting an error you have left out some important information

bwrigley's avatar

@kokoshneta Thanks again for taking the time on this.

I'm not sure I've fully understood how this works.

You are right that my two store methods are the same, the only reason I have overridden one is because I want to use a different FormRequest for each store method as the validation rules are slightly different between the Post and the Advert models.

In your example I can see that PostController::store() will know to use the PostStoreRequest form request to validate input, but how will AdvertController::store() know to use the AdvertStoreRequest form request?

Sorry if I'm being dumb:

My PostStoreRequest:

class PostStoreRequest extends FormRequest
{

//

    public function rules(): array
    {
        return [
            'message' => 'required',
            'parent' => 'null'
        ];
    }

//

}

My AdvertFormRequest:

class AdvertStoreRequest extends FormRequest
{

//

    public function rules(): array
    {
        return [
            'message' => 'required|max:255',
            'parent' => 'null'
        ];
    }

//

}
bwrigley's avatar

@krisi_gjika hmm I'm not sure what that could be? Important information in the FormRequests you mean? Or in the Controllers?

martinbean's avatar

@krisi_gjika They haven’t “left something out”. Method signatures—including parameters—need to match exactly when extending classes. PHP has always behaved like this.

kokoshneta's avatar

@bwrigley Oops, sorry – I had neglected to edit a crucial part. The signature in the store() method should call for a StoreRequest, not a PostStoreRequest! I’ve fixed that in the comment above now.

This is assuming that PostStoreRequest and AdvertStoreRequest both extend StoreRequest. Those two child classes will then not need to define the store() method at all, because type-hinting for StoreRequest automatically, implicitly also allows any classes that extend StoreRequest.

So when you store a generic post in your PostController, you pass a PostStoreRequest object as the $request parameter; when you store an advert in your AdvertController, you pass an AdvertStoreRequest object.

Although, come to think of it, if you’re relying on the service container to dependency-inject the request, then it will need to know the final class, not the parent class. You could get around this by instantiating the request inside the method (as you mentioned above), based on information available in the controller; for example:

class PostController extends Controller {
	protected $postableType = PostType::GENERIC;
	protected $modelName = 'Post';

	protected function getModelInstance() {
		$class = "\\App\\Models\\" . $this->modelName;
		return new $class;
	}

	protected function getRequestInstance($type) {
		$class = "\\App\\Http\\Requests\\" . $this->modelName . $type . "Request";
		return new $class;
	}

	public function store(): RedirectResponse {
		$request = $this->getRequestInstance('Store');
		$validated = $request->validated();
		$post = new $this->getModelInstance();
		$post->message = $validated['message'];
		$post->user_id = $request->user()->id;
		$post->postable_type = $this->postableType;
		$post->save();
		return Redirect::route('feed');
	}
}
class AdvertController extends PostController {
	protected $postableType = PostType::ADVERT;
	protected $modelName = 'Advert';
}
bwrigley's avatar

@kokoshneta Ah yes thanks. That's similar to where I had got to with my options to @martinbean above. I think it's probably the way I'll have to go. Thanks again!

s4muel's avatar

This may not be the answer you’re looking for, but in such simple scenarios, I always end up wishing I had chosen the simple solution with separate models or form requests (...), even if it involves some duplication. Not true in every case, but close 😅

1 like

Please or to participate in this conversation.