Disabling the calling of the two routes - laravel

For my project, I need to have dynamic routes, because {slug} in URL can point to multiple resources.
/shoes - poinst to category
/black-slippers - points to product
Beside the wildcard route, I have also a few (50) static routes (all defined before wildcard route in routes/web.php)
But now, when is called static route, the wildcard route is performed also, e.g.:
Route::get('/profile', [\App\Http\Controllers\Frontend\UserProfileController::class, 'show'])->name('profile.show');
Route::get('{address}', [\App\Http\Controllers\Core\WebaddressController::class, 'resolveAddress'])->where('address', '.*');
In the browser is displayed Profile page (correctly), but in SQL Queries I see, that the query which is called in WebaddressController#resolveAddress is performed also.
If I comment wildcard Route, the query disappears.
What can I do to not perform wildcard route? Thanks
Please do not suggest changing the route style, I cant, this is the requested form.

You can exclude some keywords from the wildcard route with regex in the where statement:
Route::get(
'{address}',
[\App\Http\Controllers\Core\WebaddressController::class, 'resolveAddress']
)
->where('address', '^(?!profile|other-static-route)$');
The list of keywords doesn't have to be hardcoded. You could create a list yourself, or parse keywords from the routes you defined, like this:
use Illuminate\Support\Str;
$keywords = collect(Route::getRoutes())
->map(function ($route) {
return Str::afterLast($route->uri(), '/');
})
->filter(function ($keyword) {
return !Str::endsWith($keyword, '}');
})
->implode('|');
Add them to the where statement like this:
->where('address', '^(?!' . $keywords . ')$');

I am not sure is that a best practice, but you can make a custom middleware for:
Route::get('{address}', [\App\Http\Controllers\Core\WebaddressController::class, 'resolveAddress'])->where('address', '.*')
->middleware('is_slug_route');
And in your handle method of freshly created middleware you can check is provided url an actual slug.
public function handle($request, Closure $next) {
$possibleSlug = $request->getPathInfo();
if (Address::where('slug',$possibleSlug)->exists()) {
return $next($request);
}
}
Something like that

Related

Add username as prefix in route URI in Laravel 9

I am building an application where users are registered and want to be redirected to their individual dashboard like this .
http://localhost/project/{username}/dashboard,
Now, its happening like
localhost/project/vendors/dashboard (here all users are accessing same URL)
but I want to make it like :
http://localhost/project/{username1}/dashboard, http://localhost/project/{username2}/dashboard
Googled lot but none of them are explained well and working.
Please assist with complete flow.
I want to declare the value of {username} globally and use it in route as prefix.
I dont want to use it before each name route. will use it as prefix and group with all vendors routes
I have made this, and its working as
localhost/project/vendors/dashboard
Route::prefix('vendors')->group(function () { Route::middleware(['auth:vendor'])->group(function () { Route::get('/dashboard', [VendorController::class, 'dashboard'])->name('vendor.dashboard'); });
});
You can specify route parameters in brackets like so {parameter}, change your code into this.
Route::get('project/{username}/dashboard', [UserDashboardController::class, 'dashboard'])
->name('user.dashboard');
In your controller you could access it like this.
class UserDashboardController
{
public function dashboard(string $username)
{
User::where('username', $username)->firstOrFail();
// something else
}
}
Seems like in your routes your are mixing vendor prefix logic in with something that in your specifications of what your urls should look like does not match. Which i think is making up some of the confusion on this case.
You can use route prefix like this
Route::prefix('{username}')->group(function () {
Route::middleware(['auth'])->group(function () {
Route::get('/dashboard', [UserController::class, 'dashboard'])->name('user.dashboard');
});
});

Laravel 8 - friendly url that call multiple controllers depending on match (products, categories, pages) - How to design it?

i would like to build a route that catch clean seo friendly url and call correct controller to display page. Examples:
https://mypage.com/some-friendly-url-separated-with-dashes [PageController]
https://mypage.com/some-cool-eletronic-ipod [ProductController]
https://mypage.com/some-furniture-drawers [CategoryController]
So I have in app route:
Route::get('/{friendlyUrl}', 'RouteController#index');
Each friendly url is a unique url(string) so there is no duplicate between pages/products/categories. There is also no pattern between urls - they could be any string used in seo(only text plus dashes/ sometimes params).
Is it wise to build one db table that keeps all urls in on place with info what to call ( url | controller_name | action_name) - as an example.
Another question is - how to call different controllers depending on url used? (for above example -> RouteController catch friendly urls -finds match in db table -> then calls correct controller)
Many thanks for any help.
Have a nice day
Mark
There's two approaches you can take to this.
Proactive:
In web.php
$slugs = Product::pluck('slug');
foreach ($slugs as $slug) {
Route::get($slug, 'ProductController#index');
}
$slugs = Category::pluck('slug');
foreach ($slugs as $slug) {
Route::get($slug, 'CategoryController#index');
}
$slugs = Page::pluck('slug');
foreach ($slugs as $slug) {
Route::get($slug, 'PagesController#index');
}
Then you can determine the product in the appropriate controler via e.g.
$actualItem = Product::where('slug', request()->path())->first();
The downside to this approach is that all routes are registered on every request even if they are not used meaning you hit the database on every request to populate them. Also, routes can't be cached when using this approach.
Reactive:
In this approach you use the fallback route:
In web.php:
Route::fallback(function (Request $request) {
if (Page::where('slug', $request->path())->exists()) {
return app()->call([ PageController::class, 'index' ]);
}
if (Category::where('slug', $request->path())->exists()) {
return app()->call([ CategoryController::class, 'index' ]);
}
if (Product::where('slug', $request->path())->exists()) {
return app()->call([ ProductController::class, 'index' ]);
}
abort(404);
});
You need create a table call slugs.
Then create a unique slug (can be auto generated or specified) for each page, product, category.
slug records also have columns to get Controller and params, ex: type and id
It'd be better if you just use a prefix for each type like this:
https://mypage.com/pages/some-friendly-url-separated-with-dashes [PageController]
https://mypage.com/products/some-cool-eletronic-ipod [ProductController]
https://mypage.com/category/some-furniture-drawers [CategoryController]
Then for achieving this, create three routes like this
Route::get('pages/{friendlyUrl}', 'PageController#index');
Route::get('products/{friendlyUrl}', 'ProductController#index');
Route::get('category/{friendlyUrl}', 'CategoryController#index');
These URLs would be SEO friendly

Laravel route variables with multiple "-"

So I've got a category route in my laravel application looking like this:
Route::get('all-{category}-listings', 'CategoryController#index')->name('category');
When I go to the following URL localhost:8000/all-test-listings, it works fine,
but when a category also has a hyphen in it's name it gives me a 404, for example localhost:8000/all-test-test-listings
Does anyone know a way to solve this?
You can use "Regular Expression Constraints" on your route to enable categories with a dash:
Route::get('all-{category}-listings', 'CategoryController#index')
->where('category', '[A-Za-z0-9-]+')
->name('category');
https://laravel.com/docs/7.x/routing#parameters-regular-expression-constraints
If you would like a route parameter to always be constrained by a
given regular expression, you may use the pattern method. You should
define these patterns in the boot method of your
RouteServiceProvider:
/**
* Define your route model bindings, pattern filters, etc.
*
* #return void
*/
public function boot()
{
Route::pattern('category', '[a-z0-9-]+');
parent::boot();
}
Once the pattern has been defined, it is automatically applied to all
routes using that parameter name:
Route::get('all-{category}-listings', function ($category) {
// {category} has to be alpha numeric (lowercase), but can include a dash
});

Laravel - Add Route Wildcard to every Route

I have created a multilanguage application in laravel and for every route (because i want to see in the url what my language is) i need
www.example.com/{locale}/home
for example, whereas {locale} is the set language and home well, is home. but for every route i need to declare that locale wildcard. is there any way to get this done with middleware or something, to add this before route is executed?
Thanks!
You can use prefix for it.
Route::group(['prefix' => '{locale}'], function () {
Route::get('home','Controller#method');
Route::get('otherurl','Controller#method');
});
And here how you can access it now.
www.example.com/{locale}/home
www.example.com/{locale}/otherurl
For more info.
https://laravel.com/docs/5.8/routing#route-group-prefixes
Not sure if I am understanding your request right, but I believe this is the scope you are looking for:
A generalized route which can receive the "locale" based on which you can serve the page in the appropriate language.
If that's the case, I would define a route like this:
Route::get({locale}/home, 'HomeController#index');
and then in your HomeController#index, you will have $locale variable based on which you can implement your language logic:
class HomeController extends Controller
{
/**
* Show the application homepage.
*
* #return mixed (View or Redirect)
*/
public function index(Request $request, $locale)
{
switch ($locale) {
case 'en':
//do english logic
break;
so on...
}
}
I hope it helps

Laravel 5.1 optional route params in the middle of the URL

Is there any way I can use optional route params in the middle of the URL in Laravel 5. Here is what I want
Route::get('api/{locale?}/my-url', 'MyController#myAction');
You can't have optional route parameters in the middle of the route path, because they make the definition ambiguous if they are omitted, and the route won't be matched.
You could have two route definitions one with and one without (as you've suggested in your comment):
Route::get('api/{locale}/my-url', 'MyController#myAction');
Route::get('api//my-url', 'MyController#myAction');
But if you have lots of routes you'll have a lot of duplicates just for this.
You could just leave one definition with the locale, since there's no big deal of passing the default locale as part of the URL path. So if your default locale is en it just gets passed via the path as other locales:
http://example.com/api/en/my-url
However, since I'm guessing the locale is used for language appropriate responses and is only used for GET/HEAD requests, the best solution that I see here and it makes the most sense, is to just pass the locale as a parameter, because it's essentially an option:
http://example.com/api/my-url?locale=en
That way the Laravel route definition doesn't need to worry about it. Then you can use a middleware to change the locale if it is passed along in the query string. Here's an example of a middleware class that sets the locale and checks if it's an allowed locale:
namespace App\Http\Middleware;
use Closure;
class SetLocale
{
public function handle($request, Closure $next)
{
if ($request->has('locale') && $this->isValidLocale()) {
app()->setLocale($request->input('locale'));
}
return $next($request);
}
protected function isValidLocale()
{
return in_array(request()->input('locale'), ['en', 'es', 'fr', 'de']);
}
}
Now in your controller action you can just use:
app()->getLocale();
And it will be set to the value passed in the query string.
If I understand well and you want the url to work both as api/my-url and api/en/my-url you can simply setup two routes:
Route::get('api/{locale}/my-url', 'MyController#myAction');
Route::get('api/my-url', 'MyController#myAction');

Resources