im writing a fairly simple website for a school ... this website has news , articles , video clips ... etc
the way it works is in the home page we present visitor with some lessons like
>math
>geography
>chemistry
user selects 1 on these and website contents changes based on the user selection
for example if user selects math he will see news , article , videos about math and so on ... right now this is what im doing (pleas ignore syntax errors)
Route::group(['prefix'=>'math'], function () {
Route::get('/news', 'NewsController#index')->name('news_index');
Route::get('/article', 'ArticleController#index')->name('article_index');
});
Route::group(['prefix'=>'geography'], function () {
Route::get('/news', 'NewsController#index')->name('news_index');
Route::get('/article', 'ArticleController#index')->name('article_index');
});
Route::group(['prefix'=>'chemistry'], function () {
Route::get('/news', 'NewsController#index')->name('news_index');
Route::get('/article', 'ArticleController#index')->name('article_index');
});
basically repeating all links for each prefix .... but as the links grow it will become more and more unmanageable ... is there any better way to do this ? something like
Route::group(['prefix'=>['chemistry','math' , 'geography' ], function () {
Route::get('/news', 'NewsController#index')->name('news_index');
Route::get('/article', 'ArticleController#index')->name('article_index');
});
------------------------- update -------------
i've tried this
$myroutes = function () {
Route::get('/news', 'NewsController#index')->name('news_index');
Route::get('/article', 'ArticleController#index')->name('article_index');
};
Route::group(['prefix' => 'chemistry'], $myroutes);
Route::group(['prefix' => 'math'], $myroutes);
Route::group(['prefix' => 'geography'], $myroutes);
and it works fine , the problem is the last prefix gets attached to all the internal links
for example if i click on math
my links will be
site.com/math/news
but all the links on the loaded page like
link to article
look like
site.com/geography/article
basically link get the last mentioned prefix regardless of currently selected one
Why not do it this way:
$subjects = [
'chemistry', 'geography', 'math'
];
foreach ($subjects as $subject) {
Route::prefix($subject)->group(function () {
Route::get('news', 'NewsController#index')->name('news_index');
Route::get('article', 'ArticleController#index')->name('article_index');
});
}
I know this is an elementary way do to it. Yet you can easily add subjects, it is clear and effortless to understand.
Update
As pointed in the comments it could be convenient to name the route as per subject, here is how to do this:
$subjects = [
'chemistry', 'geography', 'math'
];
foreach ($subjects as $subject) {
Route::prefix($subject)->group(function () use ($subject) {
Route::get('news', 'NewsController#index')->name("{$subject}_news_index");
Route::get('article', 'ArticleController#index')->name("{$subject}_article_index");
});
}
I think it's better to do:
Route::get('/news/{group}', 'NewsController#index')->name('news_index')->where('group', 'math|geography|chemistry');
And then just put condition on the controller function whether it is geography/math/chemistry/etc.
Don't you think?
You can try following:
$myroutes = function () {
Route::get('/news', 'NewsController#index')->name('news_index');
Route::get('/article', 'ArticleController#index')->name('article_index');
};
Route::group(['prefix' => 'chemistry'], $myroutes);
Route::group(['prefix' => 'math'], $myroutes);
Route::group(['prefix' => 'geography'], $myroutes);
Use as following:
{!!URL::to('chemistry/news')!!}
{!!URL::to('geography/news')!!}
{!!URL::to('math/news')!!}
You could try to use the as option within your groups to tell the Router to prepend a string to every route name within that group. To do so try the following:
Route::group(['prefix' => 'chemistry', 'as' => 'chemistry.'], $myroutes);
Route::group(['prefix' => 'math', 'as' => 'math.'], $myroutes);
Route::group(['prefix' => 'geography', 'as' => 'geography.'], $myroutes);
So what you will be able to do should be:
link to article
link to article
link to article
Hope it helps.
There are several good answers here already, it is probably just a matter of personal preference or deeper project specifics which one suits. Here's another option for the pile.
I am not sure why #Shams answer was downvoted, it seems like the cleanest approach to me - but only if the prefixes are constrained so that only valid subjects are accepted. Something like:
// Only 1 place to update if you add subjects
$subjectRegex = 'math|geography|chemistry';
// Only 1 route per 'group'
Route::get('{subject}/news', 'NewsController#index')->name('news_index')->where('subject', $subjectRegex);
Route::get('{subject}/article', 'ArticleController#index')->name('article_index')->where('subject', $subjectRegex);
As a bonus you have $subject available in your Controller methods, which seems like it might be useful, for example you can use it to generate routes within the current subject:
route('article_index', ['subject' => $subject])
You can wildcard the route group and specify the preferred prefixes in your RouteServiceProvider
routes.php
Route::group(['prefix'=>'{slug}'],function (){
Route::get('/news', 'NewsController#index')->name('news_index');
Route::get('/article', 'ArticleController#index')->name('article_index');
});
RouteServiceProvider boot method
Route::bind('slug',function ($name){
$prefix = ["math","chemistry","geography"];
if(!in_array($name,$prefix))
{
//handle wrong prefixes
throw new \Exception("Something went wrong");
}
});
use named route
{{route('news_index',['slug'=>'math'])}}
Just for Curiosity sake I attempted optional parameter on prefix route grouping in laravel and it worked. Check it out:
Route::group(['prefix' => '{subject?}', 'as'=> 'subject.', where' => ['subject' => 'math|english|geo']],function (){
Route::get('news', function (){
return 'This is the news';
})->name('news');
});
Pretty sure this is the solution you dreamt of.
Well before this would be the correct answer, there might be a little issue. Calling route('subject.news') will give http://example.com/news. To make it happy, you have to pass the optional parameter to route() function i.e. route('subject.news','math'); for example; then you'll have http://example.com/math/news.
PS: This was done on Laravel 5.4.30 PHP 7.1
Instead of grouping you can use route parameters
Route::get('/{prefix}/news', 'NewsController#index')->name('news_index');
Route::get('/{prefix}/article', 'ArticleController#index')->name('article_index');
Related
I would like to use language selector in Laravel. I used this sollution: Laravel optional prefix routes with regexp.
It works fine. I store location in database eg.: en, de. I would like to use the prefix only if the site has multiple language set in database. So how can I prevent use 'prefix' => '{lang?}' if i have only one language.
Here you are my web.php (Route):
Route::group(['prefix' => '{lang?}', 'middleware' => 'locale', 'where' => ['lang' => "en|de"], function () {
Route::get('/', 'HomeController#index');
Route::get('article', 'ArticlesControllerController#index');
});
With 1 language:
/home
/article
With multi language:
/en/home
/de/home
/en/article
/de/article
Well in this situation, you can use the Closure function and if condition. Set the prefix based on the values as it is possible to declare a Closure with all your routes and add that once with the prefix and once without:
$languageList = 'fr|en';
$optionalLanguageRoutes = function() {
Route::get('/test', 'DashboardController#test');
};
// Add routes with lang-prefix
if ($languageList) {
Route::group(
['prefix' => '/{lang}/', 'where' => ['lang' => $languageList]],
$optionalLanguageRoutes
);
}
// Add routes without prefix
$optionalLanguageRoutes();
Declare empty languageList when you don't have languages.
I have already
a/{id}
api
Now I want
a/b
But it is not hitting a/b, it is hitting a/{id} and taking b as {id}
How can I create a/b assuming I am not allowed to change a/{id}?
Framework Laravel.
$apiRoutes = [
// Dev routes
'get_a_by_id' => ['get', 'a/{id}','AController#getA'],
// App routes
'fetch_all_b' => ['get','a/b', 'BController#getB'],
]
This is my code route.php
Even reordering also doesn't work.
What you can do is
First place a/b above a/{id} and add ->where(['id' => '[0-9]+'); this will make sure that the route a/{id} will trigger only if there is numeric value.
You can change regex based on your needs.
Route::get('a/b', function () {
//code
});
Route::get('a/{id}', function ($id) {
//code
})->where(['id' => '[0-9]+');
It depends in which order you define them, you need to first create the specific route a/b then below the wildcard one.
Route::get('a/b', function () {
dd('testing b');
});
Route::get('a/{id}', function ($id) {
dd('testing', $id);
});
The short answer is to define a/b before a/{id}
Here's my example:
Route::get('/v1/smsportal/search/{type?}/{search?}', 'SMSPortals#search');
Route::get('/v1/smsportal/{id?}', 'SMSPortals#get');
Route::post('/v1/smsportal', 'SMSPortals#save');
Route::post('/v1/smsportal/{id?}', 'SMSPortals#update');
Route::delete('/v1/smsportal/{id?}', 'SMSPortals#delete');
/v1/smsportal/search will be called before /v1/smspotal/{id} if I call for search.
I'm trying to create a route resource in a route group:
Route::group(['middleware' => ['web', 'auth'], 'prefix' => 'template'], function() {
Route::resource('template', 'TemplateController');
Route::get('{template}/assign', 'blabla');
Route::post('{template}/assign', 'blabla');
Route::get('{template}/clone', 'blabla');
Route::get('{template}/customizer', 'blabla');
});
But now I get routes like this: template.template.index, template.template.create, etc.
And when I change the name from Route::resource('template', 'TemplateController'); to Route::resource('/', 'TemplateController');, the route look like this: template..index, template..create, etc.
I do have a solution but then I need to create another route group without prefix.
(TL;DR;) I was wondering how to make the route resource work in a route group with prefix and have the correct route name (template.index, template.create, etc.).
You can use as to define the route name. You can also use it in groups:
Route::group(['as' => 'admin::'], function () {
Route::get('dashboard', ['as' => 'dashboard', function () {
// Route named "admin::dashboard"
}]);
});
I assume you are using Laravel 5.1+ seeing as you are using group prefixes. You can read more in the route section of Laravel:
https://laravel.com/docs/5.1/routing#named-routes
https://laravel.com/docs/5.2/routing#named-routes
Not sure how you should do it in your case (my best guess would be to leave the as empty in the group), so you're going to have to fiddle around. But the principle should be the same.
(My best guess):
Route::group(['middleware' => ['web', 'auth'], 'prefix' => 'template', 'as' => ''], function() {
// stuff
});
If naming the group does not work in your case, you may have to set the as for each route.
In my routes.php I have the following group:
Route::group(array('before' => 'checkSomething', 'prefix' => '{lang}'), function() {
echo "1";
Route::get('/', array('as' => 'home', 'uses' => 'HomeController#home'));
});
And the filter attached to it:
Route::filter('checkSomething', function(){
echo "2";
if(!somethingNotRight($something)){
return Redirect::route('index', array()));
}
});
My question is, why is the route inside the route group getting called first, and after that the filter? If I execute this code, I get the following result:
21
But what I want is:
12
Pretty simple isn't it? You are aplying your filter BEFORE the app hits the controller of the route, if you want to apply filter after just change:
Route::group(array('after' => 'checkSomething', 'prefix' => '{lang}')
The code inside the Route::group closure isn't called at the moment the route is executed. It's called very early to register all the routes. The filter is working correctly, but to test that you'd need to put the echo inside the controller
I have this defined in my routes.php file
Route::post('gestionAdministrador', array('as' => 'Loguearse', 'uses' => 'AdministradorController#Login'));
Route::post('gestionAdministrador', array('as' => 'RegistrarAdministrador', 'uses' => 'AdministradorController#RegistrarAdministrador'));
And in my login.blade.php file, the form starts as this
{{ Form::open(array('route'=>'Loguearse'))}}
I dont know why when i submit the form takes the second route instead the first one, even though I am pointing to the first one.
There must be a way to go to the same url from two different forms, that is what I want.
If you have two routes with the exact same URI and same method:
Route::post('gestionAdministrador', array('as' => 'Loguearse', 'uses' => 'AdministradorController#Login'));
Route::post('gestionAdministrador', array('as' => 'RegistrarAdministrador', 'uses' => 'AdministradorController#RegistrarAdministrador'));
How can Laravel know the difference between them when something hit /gestionAdministrador?
It will always assume the first one.
The name you set 'as' => 'RegistrarAdministrador' will be used to create URLs based on that route name, only, when something (browser, curl...) hit the URL the only ways to differentiate them is by
1) URL
2) URL parameters (which is basically number 1 plus parameters)
3) Method (GET, POST)
So you could change them to something like:
Route::post('gestionAdministrador/loguearse', array('as' => 'Loguearse', 'uses' => 'AdministradorController#Login'));
Route::post('gestionAdministrador/registrar', array('as' => 'RegistrarAdministrador', 'uses' => 'AdministradorController#RegistrarAdministrador'));
EDIT 2
What you really need to understand is that the name you give to a route ('as' => 'name') will not be part of your url, so this is not something that Laravel can use to differentiate your two URls, this is for internal use only, to identify your routes during the creation of URLs. So, those instructions:
$loguearse = URL::route('Loguearse');
$registrar = URL::route('RegistrarAdministrador');
Would generate exactly the same URL:
http://yourserver.dev/gestionAdministrador
EDIT 1 - TO ANSWER A COMMENT
Redirecting in Laravel is easy, in your controller, after processing your form, in any of your methods you can just:
return Redirect::to('/');
or
return Redirect::route('home');
Having a route like this one:
Route::get('/', array('as' => 'home', 'uses' => 'HomeController#index'));
So, your controller would look like this:
class AdministradorController extends Controller {
public function RegistrarAdministrador()
{
...
return Redirect::route('home');
}
public function Login()
{
...
return Redirect::route('home');
}
}
Actually you have only one route in your route collection, because:
You have following routes declared:
Route::post('gestionAdministrador', array('as' => 'Loguearse', 'uses' => 'AdministradorController#Login'));
Route::post('gestionAdministrador', array('as' => 'RegistrarAdministrador', 'uses' => 'AdministradorController#RegistrarAdministrador'));
Both of these used post method and this is post method:
public function post($uri, $action)
{
return $this->addRoute('POST', $uri, $action);
}
It calls addRoute and here it is:
protected function addRoute($methods, $uri, $action)
{
return $this->routes->add($this->createRoute($methods, $uri, $action));
}
Here $this->routes->add means Illuminate\Routing\RouteCollection::add() and the add() method calls addToCollections() and it is as follows:
protected function addToCollections($route)
{
foreach ($route->methods() as $method)
{
$this->routes[$method][$route->domain().$route->getUri()] = $route;
}
$this->allRoutes[$method.$route->domain().$route->getUri()] = $route;
}
The $routes is an array (protected $routes = array();) and it's obvious that routes are grouped by methods (GET/POST etc) and in each method only one unique URL could be available because it's something like this:
$routes['post']['someUrl'] = 'a route';
$routes['post']['someUrl'] = 'a route';
So, in your case, the last one is replacing the first one and in this case you may use different methods to declare two routes using same URL so it would be in different array, something like this:
$routes['post']['someUrl'] = 'a route';
$routes['put']['someUrl'] = 'a route'; // Route::put(...)
There must be a way to go to the same url from two different forms
Yes, there is a way and it's simply that you have to use the same route as the action of your form and therefore, you don't need to declare it twice.
What you want to do is a bad idea, you shouldn't be logging in and registering from the same route. With that said what you are saying isn't really possible. Routing in Laravel is first come first served. Basically it checks the route until the URI matches one and then calls that method on the controller or executes the callback. Your routes have to be the other way in your routes file. This will be fixed by changing the url.