Search code examples
phplaravelsolid-principles

Laravel 8: controller refactoring, consolidate store() and create() functions?


I have a function create() which returns the view of the insert form:

    /**
     ** Add new Record Page.
     **/
    public function create()
    {
        $locationList = Locations::all(); // This is fetching data from another table to use in a <select>
        return view('pages.dataEntry.lostFound._addForm', compact('locationList'));
    }

And another function store() for creating the new record. I created a request file to shorten the store() in the controller:

    /**
     ** Add new Record.
     **/
    public function store(LostFoundRequest $request)
    {
        $lostFound = LostFound::create( $request->validated() );

        $request->store();
        return redirect()->route('data-entry.lost-and-found.index')
            ->with('success', 'LF Added Successfully');

    }

I want to get rid of the create(). Is it possible to return view of the form? $request->store then after submitting return redirect() in one function?

Is there a better/cleaner way for doing this?
Or am I following the SOLID principle correctly? "S: Single responsibility" per function?


Solution

  • You can simplify controllers for simple crud by passing a new (unsaved) model into the other controller methods.

    eg;

    public function create()
    {
        return $this->edit(new Post());
    }
    
    public function store(PostForm $request)
    {
        return $this->update($request, new Post());
    }
    
    public function edit(Post $post)
    {
        return view('post.edit', compact('post'));
    }
    
    public function update(postForm $request, post $post)
    {
        //store post
    
        return redirect(route('posts.index'));
    }
    

    When you couple this with route model binding, you get a very simple controller. One view for the form for both create and update. The create method uses the edit method, passing it an empty model, and store uses the update method, again passing it an empty model.

    The form opening tag needs to be different which can be solved with a simple blade @if

    @if($post->exists)
        <form class="flex flex-col w-full" method="POST" action="{{ route('posts.update',$post) }}">
            @method('put')
    @else
        <form class="flex flex-col w-full" method="POST" action="{{ route('posts.store') }}">
    @endif
    

    The ->exists returns a boolean to tell us if the model has been persisted. If not then this is a create form, otherwise its an edit form.

    From my article https://talltips.novate.co.uk/laravel/simplify-laravel-crud-controllers