Laravel route variables with multiple "-" - laravel

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

Related

Disabling the calling of the two routes

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

Routing & pretty URL

I am looking to create prettier URLs, and I'm having issues creating a valid route:
Let's say I have the following page http://localhost/app/account/5/edit.
This works fine with Route::get('account/{account}', 'AccountController#edit');
If I modify the Account Model and modify getRouteKeyName to return 'name', I am able to (with the same Route from above) access the following link: http://localhost/app/account/randomName/edit
The thing is, I am interested in a slightly different route, which is: http://localhost/app/account/randomName-5/edit
If I create a route Route::get('/accounts/{ignore}-{account}/edit', 'AccountController#edit'), it will fail as the first argument sent to edit is string and not an instance of Account. This can easily be fixed by modifying edit(Account $ac) to edit($ignored, Account $ac);... but it feels like cheating.
Is there a way to to get the route to ignore the first {block}?
According to the docs, you can customize your resolution logic for route model binding.
In this scenario, you can do something like this in App\Providers\RouteServiceProvider:
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
parent::boot();
Route::bind('accountNameWithId', function ($value) {
list($accountName, $accountId) = explode('-', $value);
return App\Account::whereKey($accountId)
->where('name', $accountName)
->firstOrFail();
});
}
Then you can redefine your route like this:
Route::get('account/{accountNameWithId}', 'AccountController#edit');

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

Override URL validation rule to tolerate whitespaces at ends of URL

I would like to override the standard URL validation rule to make it more tolerant of a whitespace character before or after the URL. Basically use the trim() function on the url before passing it to the standard URL validation handler.
I know I need to override that rule but I'm not exactly where and how I need to do it.
(Plus, the CakePHP API and book documentation are currently offline. Upgrades, I know...)
You can add custom validation rules in your Model classes, your Behavior classes, or in the AppModel class:
http://book.cakephp.org/view/150/Custom-Validation-Rules#Adding-your-own-Validation-Methods-152
Since you want to override an existing method, just give it the same name and signature as the original. Something like this might do the trick:
function url($check, $strict = false) {
return Validation::url(trim($check), $strict);
}
Why would you wanna do that?
Simply make sure all posted data is always trimmed.
Thats cleaner and more secure, anyway.
I have a component doing that in beforeFilter:
/** DATA PREPARATION **/
if (!empty($controller->data) && !Configure::read('DataPreparation.notrim')) {
$controller->data = $this->trimDeep($controller->data);
}
The trimDeep method:
/**
* #static
*/
function trimDeep($value) {
$value = is_array($value) ? array_map(array(&$this, 'trimDeep'), $value) : trim($value);
return $value;
}

Resources