Home  >  Q&A  >  body text

Laravel PHP overridden method with different parameters

Sorry if this question has been asked several times before and this is simply not possible in Laravel/PHP.

In my Laravel application, I have a PostController that uses Laravel's rich naming convention. I also have a CommentController that mostly copies PostController in various ways, so I decided CommentController should extend PostController.

The only difference so far is that the store() method needs to accept a different FormRequest object since they have different validation rules. Therefore, I overridden the store() method to expect CommentFormRequest instead of PostFormRequest (both extend FormRequest) .

This will raise an error that the overridden method parameters need to match the base method.

Is this expected? Is there a reasonable solution for what I want to do?

Edit from here

I just started designing this project where users can create posts, questions, polls, ads, etc. and comment on any of them.

All of these are Post types. Some have relationships with other models, for example Poll may have a relationship with the PredefinedAnswer model, while the generic Post may have no relationship.

Some may require different validation logic, for example a user may decide whether to allow comments on a generic Post, but may never allow comments on an Advert.

In my database, I think these could all be stored in the post table, but with different postable_type.

In my controller, I think most of the CRUD logic is the same between these different types. In some cases, differences may exist and methods may need to be overridden.

So, in my PostController I currently have a very simple 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');
    }
}

Let's say my AdvertController has the same logic but different validation rules I think:

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');
    }

P粉863295057P粉863295057251 days ago407

reply all(1)I'll reply

  • P粉807471604

    P粉8074716042024-01-17 16:43:49

    Instead of implying the specific implementation, you will get more benefits by implying the interface, for example:

    interface StoreRequestInterface {
      public function validated(): RedirectResponse;
      public function user();
      // etc
    }
    
    class PostStoreRequest implements StoreRequestInterface {/* ... */}
    class AdvertStoreRequest  implements StoreRequestInterface {/* ... */}
    
    abstract class Controller {
        protected $postableType;
    
        public function store(StoreRequestInterface $request): RedirectResponse
        {
            // ...
        }
    }
    
    class PostController extends Controller
    {
        public function __construct()
        {
            $this->postableType = PostType::GENERIC;
        }
    }
    
    class AdvertController extends PostController
    {
        public function __construct()
        {
            $this->postableType = PostType::ADVERT;
        }
    }
    

    This way you:

    • It is not necessary to redefine the same method body with slightly different parameter hints.
    • Parent::store($request) can be called in situations where some special handling is required, such as type/sanity checks, but the rest of the method remains the same.
    • You can avoid the pitfall of defining a "god" class, whose lineage must be traced by a large number of application classes. You can define standalone drop-in replacements by simply implementing the expected interface.
    You can further connect other classes referenced here [for example:

    ControllerInterface, RedirectInterface, etc.] and further simplify your code.

    reply
    0
  • Cancelreply