How to create slugs in Laravel? - laravel

I'm trying to develop an e-com website in Laravel 5 (Since it required lot of customization, I decided not to use Magento or OpenCart).
The problem I'm facing is to creating category and product URLs from slugs.
For example, one URL can be:
http://somewebsite.com/products/lenovo-yoga-500
But I want to make URL like:
http://somewebsite.com/lenovo-yoga-500
I tried to create a route like:
Route::get('{slug}', 'BaseController#route');
and in the route() method, I tried:
function route($route){
$product = Product::where(array('slug' => $slug))->first();
if(isset($product))
.....
else
.....
but first of all, the value is not getting passed to variable '$slug' and secondly I want to redirect the request to another route after checking the condition.

You might want to go with the first URL (product/{slug}) because the way you have it setup with the slug as the first parameter, every route you make for any page will be interpreted as the slug.
Either way, fix your controller method as such
function route($slug){
$product = Product::where(array('slug' => $slug))->first();
if(isset($product))
.....
else
.....
The documentation covers everything you are asking for here, so please read through about redirecting to routes.

To achieve it like Magento does, you need to add a new product-routes.php file that will be rebuilt.
First instruct app/Providers/RouteServiceProvider.php to load your dynamic route file thus:
protected function mapWebRoutes(Router $router)
{
$router->group([
'namespace' => $this->namespace,
], function ($router) {
require app_path('Http/routes.php');
require base_path('resources/routes/product-routes.php');
});
}
Next: Rebuild product-routes.php automatically on save or when the user requests it in the backend to include all product slug routes and to load the product by slug.
Hope that helps.

in Product-model:
public function getRouteKeyName() {
return 'slug';
}
in routes:
Route::get('{product}', 'ProductController#product');
in ProductController:
public function product(Product $product) {
$product->doSomething();
}
Note that the route should be last in the routes-file so any other route has chance to match before that.

You must use this Route
Route::get('/sample/show/{sample : slug}',[SampleController::class]);
Enter the model of the controller and set the value of getRouteKeyName equal to slug such as:
public function getRouteKeyName(){return 'slug';}
Custom Keys & Scoping
When implicitly binding multiple Eloquent models in a single route definition, you may wish to scope the second Eloquent model such that it must be a child of the previous Eloquent model. For example, consider this route definition that retrieves a blog post by slug for a specific user:
use App\Models\Post;
use App\Models\User;
Route::get('/users/{user}/posts/{post:slug}', function (User $user, Post $post) { return $post;});

Related

Laravel - how to bind soft deleted route and model

I'm using soft delete in model Article, but in model Comment not use soft delete. I'm also customize the key using slug column in model Article. If the article is deleted, I want still show the comment. But when article is deleted, show method always return 404.
public function show(Article $article, Comment $comment)
{
if ($article->id != $comment->article_id)
throw new NotFoundHttpException('Record Not Found.');
return $this->success(['comment => $comment']);
}
How to fix this?
Your question statement is not defining the problem you should ask how to bind soft deleted route and model.
Laravel provide ->withTrashed() method for this so it also bind soft deleted models in route.
web.php
user App/Http/Controller/ArticleController;
Route::get('article/{article}', [ArticleController::class, 'show'])->name('article.show')->withTrashed();
But this method added in Laravel 8.55 If you have older version so you can simply find model in controller without route model binding.
ArticleController.php
public function show($article, App/Comment $comment)
{
$article = App/Article::withTrashed()->findOrFail($article);
if ($article->id != $comment->article_id) {
throw new NotFoundHttpException('Record Not Found.');
}
return $this->success(['comment => $comment']);
}
Or you can also use Explicit Binding for specific model in RouteServiceProvider.
public function boot()
{
parent::boot();
Route::bind('article', function ($id) {
return App\Article::withTrashed()->find($id) ?? abort(404);
});
}
And you can also use onlyTrashed() method in explicit binding in case you use separate route for trashed models.
If you want to get deleted records as well, use the method withTrashed
Your code should look something like this:
Article::withTrashed()->find($id);
Hope it help u and happy coding !

Resolving a model using multiple columns in a Laravel route

I'm trying to register a route to fetch a single model automatically based on multiple columns. For example:
Route:get( 'sample/{myModel:status}/{myModel:slug}', [ SomeController:class, 'show' );
Let's access this URI:
www.example.com/sample/publish/this-is-a-slug
Now what I expect to get in my controller is a single myModel instance, which has a status column equal to publish and a slug column equal to this-is-a-slug. Is this possible?
Trying to register the above route will result in a reference error:
Route pattern "/sample/{myModel}/{myModel}" cannot reference variable name "myModel" more than once.
Now I can just drop the model name and do the query in my controller, but I wonder if it's possible to inject a model into a controller using more than 1 column. The slug column is not unique, but the combination of slug and status is.
You may define the route like this,
Route::get('sample/{model_by_status}/{slug}', [ SomeController:class, 'show'] );
Then you can add route binding in the boot() method of RouteServiceProvider.php like this,
Route::bind('model_by_status', function($value){
$slug = request()->route('slug');
return myModel::where([
'status' => $value,
'slug' => $slug,
])->firstOrFail();
});
And in your controller method you need to declare the show method like this
public function show(myModel $model_by_status){
}
Note that the naming is important here. model_by_status (whatever in your case) must be same in all those three places

Laravel controller return model instance instead data content

i have this route:
Route::group(['prefix' => 'comments', 'middleware' => ['auth:api']], function(){
Route::delete('/delete/{id}', [CommentController::class, 'destroy'])->name('comment.delete');
});
in my controller i have
public function destroy(Comments $comments)
{
dd($comments);
}
normally i must have the data of respective of the id right? but instead i got empty instance of the model
To work you need to change route parameter from id to comments
Laravel automatically resolves Eloquent models defined in routes or
controller actions whose type-hinted variable names match a route
segment name.
Route::delete('/delete/{comments}', [CommentController::class, 'destroy'])->name('comment.delete');
Read here : https://laravel.com/docs/8.x/routing#implicit-binding.

My form requires additional data, where should I put my routes and controller methods to get it?

I'm learning how to create web application with laravel and I don't know correct way to create struct for this example form:
I have simple controller named i.e. PostController with methods:
index(), create(), store(), show(), edit(), update(), destroy()
created via make:controller --resource.
And I have routes for it in my web.php file created via Route::resource() method.
Now, my create() method in PostController display a form for create new post, but this form required select an option from list.
I can't display whole list because it's thousands of options do I want create search field which use ajax to get available options from my database. Then user just select an option and can send form.
It's ofcourse just example but in that situation where should I put my method controller and route for search options?
Should I crate additional method in my PostController ( i.e. searchOption( $string ) ) or should it be new controller?
If it should be new controller then do you have any suggestion for folder/name structure for it?
And where should I put my routes for it? Still in web.php? or api.php?
And again, how should I name this route?
Sinse you have another entity you can make another controller. Let's say you want filtered City in your Post's create view.
// Controllers/Api/CityController.php
public function index(Request $request)
{
$query = City::query();
if ($request->has('search')) {
$query->where('name', 'LIKE', $request->input('search') . '%');
}
$cities = $query->paginate();
return response()->json(['cities' => $cities]);
}

Route Parameters laravel issue

I simply made this but /maps/{category}/{map} does not work correctly.
putting anything on {category} showing same result.
/maps/php/1
/maps/laravel/1
I want to show a result when category's name and map's id exactly matches otherwise redirect to homepage.
My route
Route::get('/maps/{category}', 'MapsController#index');
Route::get('/maps/{category}/{map}', 'MapsController#show');
My controller
public function show(Category $category, Map $map)
{
return view('maps.show', compact('map'));
}
My blade template
{{ $map->title }}
Swap over the routes in your routes file so that the more specific one is first.
Route::get('/maps/{category}/{map}', 'MapsController#show');
Route::get('/maps/{category}', 'MapsController#index');
Firstly, there is no need to swap order of routes if you are using 5.4, since I started with 5.4 to use laravel i cannot say anything for previous versions.
If you you want to filter asked map if it matches given category, you can use "whereHas" method with eloquent if you defined relationship between category and map.
"One To Many (Inverse)" relationship is what you need to use, check here: https://laravel.com/docs/5.4/eloquent-relationships#one-to-many-inverse
And querying relationship is what you need to know, check here: https://laravel.com/docs/5.4/eloquent-relationships#querying-relationship-existence
How your relationship should look like in Map model:
/**
* Get the category that owns the map.
*/
public function category()
{
return $this->belongsTo('App\Category');
}
An example code:
public function show($category, $map)
{
$map = Map::whereHas('category',function($query) use($category){
$query->where('id', $category);
});
return view('maps.show', compact('map'));
}
Route::get('/maps/{category}', 'MapsController#index');
is a more generalized route so it matches more than
Route::get('/maps/{category}/{map}', 'MapsController#show');
You should list the show route first.
To match an exact model attribute (i.e. name), you should customize the resolution logic the router uses in the route service provider boot method. For example:
Route::bind('category', function ($value) {
return App\Category::where('name', $value)->first();
});
Route::bind('map', function ($value) {
return App\Map::where('id', $value)->first();
});
public function show($category,$map)
{
$category = App\Category::where('category_name',$category);
$map_id =App\Map::where('map_id',$map);
if(!empty($category) && !empty($map_id))
{
return view('maps.show', compact('map'));
}
else
{
return view('homepage', compact('map'));
}
}

Resources