Home > Software design >  Bind model on Request in store method Laravel
Bind model on Request in store method Laravel

Time:11-09

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

CodePudding user response:

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" ?

CodePudding user response:

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.

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.

  • Related