skip to Main Content

I want to store books, with title, year and already existing Author. An author is saved in a table authors that have a one to many relationship with books table. To create a book i have a from with two text inputs and one select. Select is filled from database.
Now i want to store them and attach the author immediately.

I can’t pass the author in the route because it’s dynamically due the select-input.
Is there a possibility to do it like the call below?

Route:

Route::post('/store', [BookController::class, 'store'])->name('book.store');

Controller:

public function store(Request $request,Author $author_id)
{
    $validated = $request->validate([
        'title' => 'required',
        'year' => 'required',
        'book_id' => 'required'
    ]);
    Book::create($request->all());
    return redirect()->route('book.index');
}

2

Answers


  1. Second parameter must be sent in order to get Model from authors table.
    Unless you modify your route to accept params and add JS listener to form on submit, change action URL with selected author, it is not possible per my knowledge..

    Since you are not doing anything with Author model, maybe you can just add validation for author_id => "required,exists:authors" ?

    Login or Signup to reply.
  2. If I understand correctly, you want to use route model binding for your Author, but that value is chosen with a <select> on the form.

    Here’s a solution to do that; if you don’t have to use route model binding, it is much simpler, see the second solution below.

    Route model binding solution

    For route model binding, you’ll need a route like this:

    Route::post('/store/{author}', [BookController::class, 'store'])->name('book.store');
    

    So you need to know the author ID to generate the form action URI – which you can’t do at page load time because the user has not chosen an author yet. One way to solve this is with Javascript – every time the select chages, find the selected author, and update the form’s action.

    Here’s a working HTML/JS snippet which does that, though you’ll need to look at the form in your browser’s developer tools to see the action changing.

    let form = document.querySelector('form');
    let defaultAction = form.dataset.action;
    let select = document.querySelector('select');
    let debugDisplay = document.querySelector('p');
    let action;
     
    select.addEventListener('change', () => {
        action = defaultAction + select.value;
        form.setAttribute('action', action);
        debugDisplay.innerHTML = 'Form action is now "' + action + '"';
    });
    <form action="" method="post" data-action="/store/">
        <select name="author_id">
            <option>Choose an author</option>
            <option value="1">William Gibson</option>
            <option value="2">Iain M Banks</option>
        </select>
    </form>
    
    <p></p>

    And now, assuming you use the route, form, and Javascript shown above, and that you have your Author <-> Book relationships set up, your controller can do:

    public function store (Request $request, Author $author) {
    
        // Your validation ...
    
        // Create the book for this author
        $book = $author->books()->create($request->all());
    
        return redirect()->route('book.index');
    }
    

    Note you should probably include some kind of front end Javascript validation that checks if an author is selected, otherwise the action is not set and the form will post to the current URL, which will probably result in a 404.

    Simpler solution

    If you can live without route model binding, this is trivially simple. Your existing route is fine:

    Route::post('/store', [BookController::class, 'store'])->name('book.store');
    

    Your form should include the author_id, exactly as in the example above.

    And the controller just needs 1 extra line to find the selected author:

    public function store (Request $request) {
    
        // Your validation ...
    
        // Find the author
        $author = Author::find($request->author_id);
    
        // Create the book for this author
        $book = $author->books()->create($request->all());
    
        return redirect()->route('book.index');
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search