Search code examples
laravellaravel-bladelaravel-6laravel-controllerlaravel-route

How to send variable from blade to controller without changing the url


In blade I have a list of books. I want to choose a specific book to show its information. And to do so I want to send with href the id of the book to my controller passing through route.

For example i have

 <div class="body text-center">
 <a href="{{HERE!}}"><h6><b>{{($book->getName())}}</b></h6></a>
 </div> 

In href I want to add $bookId = $book->id and the route name so I can call the route with the specific name which calls a method in a controller which can use the variable $bookId

 Route::get('/infromation','Books\BookController@index')->name('info');

Solution

  • Here's two propositions:

    • The first one is to use spatie/laravel-sluggable to have the book name in the URL
    • The second one is to access the book without changing the URL with a POST request

    Using spatie/laravel-sluggable

    The slug will be generated automatically from name when the book is created.

    your-migration.php

     Schema::create('books', function (Blueprint $table) {
        $table->bigIncrements('id');
        $table->string('slug')->unique()->index();
        $table->string('name');
        // ...
        $table->timestamps();
    });
    

    web.php

    // Change the URIs as you want. `{book}` is mandatory to retrieve the book though.
    Route::get('/books','Books\BookController@index')->name('book.index');
    Route::get('/books/{book}','Books\BookController@show')->name('book.show');
    

    Book.php

    use Spatie\Sluggable\HasSlug;
    use Spatie\Sluggable\SlugOptions;
    
    class Book extends Model
    {
        use HasSlug;
    
        protected $guarded = [];
    
        public function getSlugOptions()
        {
            // Adapt with what you want
            return SlugOptions::create()
                ->generateSlugsFrom('name')
                ->saveSlugsTo('slug')
                ->doNotGenerateSlugsOnUpdate();
        }
    
        public function getRouteKeyName()
        {
            return 'slug';
        }
    
    }
    

    BookController.php

    class BookController extends Controller
    {
        public function index()
        {
            return view('book.index');
        }
    
        public function show(Book $book)
        {
            // $book is retrieving using Model Binding: https://laravel.com/docs/5.8/routing#route-model-binding 
            return view('book.show', compact('book'));
        }
    }
    

    index.blade.php

    <div class="body text-center">
        <a href="{{ route('book.show', $book) }}">
            <h6><b>{{ $book->getName() }}</b></h6>
        </a>
    </div> 
    

    Using POST request (URI does not change) and without SLUG

    I wouldn't recommend using this for the user experience.

    • The user cannot bookmark the book or share the link with someone else
    • When refreshing the page, it will prompt to the user if he want to re-submit the form request

    web.php

    Route::get('/books','Books\BookController@index')->name('book.index');
    Route::post('/books','Books\BookController@show')->name('book.show');
    

    BookController.php

    class BookController extends Controller
    {
        public function index()
        {
            return view('book.index');
        }
    
        public function show()
        {
            $book = Book::findOrFail(request('book_id'));
            return view('book.show', compact('book'));
        }
    }
    

    index.blade.php

    <div class="body text-center">
        <form action="{{ route('book.show') }}" method="POST">
            @csrf
            <input type="hidden" value="{{ $book->id }}" name="book_id">
            <h6>
                <button type="submit"> 
                    <b>{{ $book->getName() }}</b>
                </button>
            </h6>
        </form>
    </div> 
    

    You can remove the default button style to make it looks like a link https://stackoverflow.com/a/45890842/8068675