Laravel routing access category and show method - laravel

To display the blog list i have using the following route
// Blog List
Route::name('blog')->get('blog', 'Front\BlogController#index');
Ex: http://www.mypropstore.com/blog/
To display the blog category,
Route::name('category')->get('blog/{category}', 'Front\PostController#category');
Ex: http://www.mypropstore.com/blog/buy-sell
To display the blog details, comments and tag details, we have using "posts" middleware
// Posts and comments
Route::prefix('posts')->namespace('Front')->group(function () {
Route::name('posts.display')->get('{slug}', 'PostController#show');
Route::name('posts.tag')->get('tag/{tag}', 'PostController#tag');
Route::name('posts.search')->get('', 'PostController#search');
Route::name('posts.comments.store')->post('{post}/comments', 'CommentController#store');
Route::name('posts.comments.comments.store')->post('{post}/comments/{comment}/comments', 'CommentController#store');
Route::name('posts.comments')->get('{post}/comments/{page}', 'CommentController#comments');
});
Ex: http://www.mypropstore.com/posts/apartment-vs-villa-which-is-the-right-choice-for-you
Now i want to change the blog details url page to
http://www.mypropstore.com/blog/apartment-vs-villa-which-is-the-right-choice-for-you-{{blogid}}
Ex: http://www.mypropstore.com/blog/apartment-vs-villa-which-is-the-right-choice-for-you-54
If i change that above format, it conflict category page. Any body knows how to set the routing for blog details page(middleware "posts")

Assuming the blogid part, at the end of your suggested route...
http://www.mypropstore.com/blog/apartment-vs-villa-which-is-the-right-choice-for-you-{{blogid}}
...is numeric, you could do something like this:
For your route definition for your post details page, use the following:
Route::name('posts.display')
->get('blog/{slug}-{id}', 'PostController#show')
->where('id', '[0-9]+');
What this does is ensures that this route is only matched by paths that follow the pattern blog/{slug}-{id} but constrains that the id part of your route must be numeric i.e. consist only of one or more numbers.
You will need to ensure that this route appears before the one matching your category route or else the category route will take precedence.
Your controller should have a show method like this:
class PostController extends Controller
{
public function show($slug, $id)
{
// $id will contain the number at the end of the route
// $slug will contain the slug before the number (without the hyphen)
// You should be able to do this to get your post.
$post = Post::findOrFail($id);
dd($post);
}
}

Since your categories aren't numbers you could solve the conflict specifying that id will always be a number like this:
Route::get('/blog/{id}', 'BlogController#show')->where('id', '[0-9]+');

Related

How do I pass a value in my Route to the Controller to be used in the View in Laravel?

I have 2 entities called Match and Roster.
My Match routes are like this
http://localhost:8888/app/public/matches (index)
http://localhost:8888/app/public/matches/14 (show)
In order to view/create the teams for each specific match I added the routes for the match roster like this:
Route::get('/matches/'.'{id}'.'/roster/', [App\Http\Controllers\RosterController::class, 'index']);
Now I need that {id} i have in my URL to pass it to the Controller here:
public function index()
{
return view('roster.index');
}
I need that for a couple of things. First I need to do a search on the Roster table filtering by a column with that value, so I can display only the players that belong to that match.
Second, I need to pass it on to the view so I can use it on my store and update forms. I want to add or remove players from the roster from that same index view.
How can I do that?
#1 You can get the route parameter defined on ur routes via request()->route('parameter_name').
public function index()
{
// get {id} from the route (/matches/{id}/roster)
$id = request()->route('id');
}
#2 You can pass the data object via using return view(file_name, object)
public function index()
{
// get {id} from the route (/matches/{id}/roster)
$id = request()->route('id');
// query what u want to show
// dunno ur models specific things, so just simple example.
$rosters = Roster::where('match_id', '=', $id);
// return view & data
return view('roster.index', $rosters);
}
#3 It can be done not only index but also others (create, store, edit, update)
In addition, STRONGLY RECOMMEND learn Official Tutorial with simple example first.
Like a Blog, Board, etc..
You need to know essentials to build Laravel App.
Most of the time, I prefer named routes.
Route::get('{bundle}/edit', [BundleController::class, 'edit'])->name('bundle.edit');
In controller
public function edit(Bundle $bundle): Response
{
// do your magic here
}
You can call the route by,
route('bundle.edit', $bundle);

Laravel route difference between {id} vs {tag}

I am new in Laravel pardon me if question is silly. I have seen a doc where they used
For get request
Route::get("tags/{id}","TagsController#show");
For put request
Route::put("tags/{tag}","TagsController#update");
What is the difference and benefit between this ? I understood 1st one, confusion on put route.
There’s no real difference as it’s just a parameter name, but you’d need some way to differential parameters if you had more than one in a route, i.e. a nested resource controller:
Route::get('articles/{article}/comments/{comment}', 'ArticleCommentController#show');
Obviously you couldn’t use just {id} for both the article and comment parameters. For this reason, it’s best to use the “slug” version of a model for a parameter name, even if there’s just one in your route:
Route::get('articles/{article}', 'ArticleController#show');
You can also use route model binding. If you add a type-hint to your controller action for the parameter name, Laravel will attempt to look up an instance of the given class with the primary key in the URL.
Given the route in the second code example, if you had a controller that looked like this…
class ArticleController extends Controller
{
public function show(Article $article)
{
//
}
}
…and you requested /articles/123, then Laravel would attempt to look for an Article instance with the primary key of 123.
Route model binding is great as it removes a lot of find / findOrFail method calls in your controller. In most instances, you can reduce your controller actions to be one-liners:
class ArticleController extends Controller
{
public function show(Article $article)
{
return view('article.show', compact('article'));
}
}
Generally there's no practical difference unless you define a custom binding for a route parameter. Typically these bindings are defined in RouteServiceProvider as shown in the example in the docs
public function boot()
{
parent::boot();
Route::model('tag', App\Tag::class);
}
When you bind tag this way then your controller action can use the variable via model resultion:
public function update(Tag $tag) {
// $tag is resolved based on the identifier passed in the url
}
Usually models are automatically bound so doing it manually doesn't really need to be done however you can customise resolution logic if you do it manually
Normal way
Route::get("tags/{id}","TagsController#show");
function($id)
{
$tag = Tag::find($id);
dd($tag); // tag
}
With route model bindings
Route::put("tags/{tag}","TagsController#update");
function(Tag $tag) // Tag model binding
{
dd($tag); // tags
}
ref link https://laravel.com/docs/5.8/routing#implicit-binding
It's just a convention. You can call it all you want. Usually, and {id} refers to the id in your table. A tag, or similarly, a slug, is a string value. A tag could be 'entertainment' for video categories, while 'my-trip-to-spain' is a slug for the description of a video.
You have to chose the words what you are comfortable with. The value will be used to find in your database what record is needed to show the correct request in the view. Likewise you can use video/view/{id}/{slug} or any combination thereof.
Just make sure your URLs don't get too long. Because search engines won't show your website nicely in search results if you do. Find the balance between the unambiguous (for your database) and logic (for your visitors).
Check this out: Route model bindings
Use id, Laravel will get the id from route, and it will be the tag's id, it is integer.
function show($id) {
$tag = Tag::find($id);
}
Use tag, Laravel automatically resolves Eloquent models defined in routes or controller actions whose type-hinted variable names match a route segment name.
In URL, your tag parameter is integer, however in your controller action $tag will be a model object:
function action(Tag $tag) {
$tag->name;
}
So you don't need to get the $tag by eloquent in your controller action. You just need to specify it is From model Tag $tag
It will do it automatically.

Laravel Routing - Best way to differentiate routes with same format

Say I have a database of items, each belonging to a country, county and a city. I want to have routes to list all of the items within a singular country, county or a city. Each country/county/city has a slug, for example france for France that is to be used in URL.
I want all the routes to have the same format:
/items-in-{slug}, so for example /items-in-france or /items-in-paris.
However, the slug can be a slug of one of multiple Models. What is the best set up for this sort of situation? I can think of 3 main options:
A single route that will catch all matching URLs, which will run a specialised RoutingController or similar, which will then in turn check which Model slug represents and propogate to the correct controller method (for example, viewInCountry($slug) or viewInCity($slug))
One route for each type of Model, and putting restrictions on each route that would only accept one of the existing slugs (i.e. fetch all of the slugs and generate a regex that will only accept one of the existing slugs)
Fetch all Models (countries/cities/counties) and generate a Route for each one
All options seem a little hacky and I am wondering if there is a more elegant solution to this.
I would go for the second option, except don't use a regex to accept existing slugs. Instead you could write your own model route binding resolution logic as mentioned here under 'Customizing The Resolution Logic'. Something like this could probably do the trick:
// Put this in your RouteServiceProvider.php
public function boot()
{
parent::boot();
Route::bind('slug', function ($value) {
$country = App\Country::where('slug', $value)->first();
if ($country !== null) {
return $country;
}
$city = App\City::where('slug', $value)->first();
if ($city !== null) {
return $city;
}
// Repeat for each model.
// 404 in case no model has been matched.
abort(404).
});
}
Alternatively if you are willing to adjust the url a bit, then you could create a route and a controller per model. That would require you to have urls like /items/france or items/paris etc.

Want to show name instead of id in the URL field in Laravel

I don't want to show /route_name/{id} in the URL field of my Laravel project. Instead of that I want to show /route_name/{name} but pass the id in the back-end to the controller.
Suppose I have a route named departments and pass an id 3 named knee_pain as a parameter. And it is like /departments/3
But I want to to show /departments/knee_pain in my url and as well as want to pass the id 3 in my controller without showing the id in the url.
How to do that ?
In your model you can use the getRouteKeyName method to bind to another attribute than the default id in your routes :
public function getRouteKeyName()
{
return 'slug'; // Default is 'id'.
}
Rather than using the name attribute, that you could use elsewhere in your application for displaying the name of the entry, I recommend using an attribute made url friendly. You could use Str::slug() for that.
public function setNameAttribute($value) {
$this->name = $value;
$this->slug = \Str::slug($value);
}
It will 'slugify' your string, for example : \Str::slug('Knee pain') => 'knee-pain'.
Note : in Laravel 5.5, use the str_slug() helper.
You should also make sure this string is unique in your database.
First you have to garantee that the name is unique, if don't you will have more than one Id in your controller. For that i recommend you to use Purifier to remove spaces and make it URL friendly:
Purifier
Second, probably the best way to have clean controllers is creating a middleware that understand what kind of name is (what table should middleware look for). You can validate that by route name and send the correct id to controller.
Middleware docs

show name instead of id in a url using laravel routing

I have defined a route in laravel 4 that looks like so :
Route::get('/books/{id}', 'HomeController#showBook');
in the url It shows /books/1 for example , now i'm asking is there a way to show the name of the book instead but to keep also the id as a parameter in the route for SEO purposes
thanks in advance
You could also do something like this:
Route::get('books/{name}', function($name){
$url = explode("-", $name);
$id = $url[0];
return "Book #$id";
});
So you can get book by id if you pass an url like: http://website.url/books/1-book-name
if your using laravel 8, this may be helpfull.
In your Controller add this
public function show(Blog $blog)
{
return view('dashboard.Blog.show',compact('blog'));
}
In your web.php add this
Route::get('blog/{blog}', [\App\Http\Controllers\BlogController::class,'show'])->name('show');
Then add this to your model (am using Blog as my Model)
public function getRouteKeyName()
{
return 'title'; // db column name you would like to appear in the url.
}
Note: Please let your column name be unique(good practice).
Result: http://127.0.0.1:8000/blog/HelloWorld .....url for a single blog
So no more http://127.0.0.1:8000/blog/1
You are welcome.
You can add as many parameters to the url as you like, like this:
Route::get('/books/{id}/{name}', 'HomeController#showBook');
Now when you want to create an url to this page you can do the following:
URL::action('HomeController#showBook', ['id' => 1, 'name' => 'My awesome book']);
Update:
If you are certain that there will never be two books with the same title, you can just use the name of the book in the url. You just need to do this:
Route::get('/books/{name}', 'HomeControllers#showBook');
In your showBook function you need to get the book from the database using the name instead of the id. I do strongly encourage to use both the id and the name though because otherwise you can get in trouble because I don't think the book name will always be unique.
You can also use model binding check more on laravel docs
For example
Route::get('book/{book:name}',[BookController::class,'getBook'])->name('book');
The name attribute in "book/{book:name}" should be unique.

Resources