Laravel has really nice features of routing which made our life so comfortable. I am so curious about this Route::group() wrapper. How does it work and how does Roue class inside group wrapper get the information at group parameters. Is it maintain any global variable?
Route::group([
'prefix' => 'audiobook',
'middleware' => 'auth:api',
'namespace' => 'Api\Audiobook'
], function () {
Route::get('latest', 'AudioController#LatestAudioBook');
});
Here's the code that actually does it!
It's not modifying a "global" variable, or at least whether it's global or not is only incidentally relevant in that it's a design decision.
The routes are stored in a Router object, and you can read how the code works at the link above.
Related
I'm using Laravel 5.5 and wonder if I can not only group routes by name prefixes and route prefixes but also controllers since they all use the same controller. The documentation didn't provide anything the like. In Laravel 4 there were implicit controllers available by using Route::controller() but that's not what I'm looking for since this won't be implicit. To cut a long story short, here's what I currently have:
Route::group([ 'prefix' => 'my-route', 'as' => 'myRoute.' ] , function () {
Route::get('/{viewMode?}', 'MyRouteController#index')->name('index')->where('viewMode', '[a-z]+');
Route::get('/ajax', 'MyRouteController#ajax')->name('ajax');
});
And it should look something like that:
Route::group([ 'prefix' => 'my-route', 'as' => 'myRoute.', 'controller' => 'MyRouteController' ] , function () {
Route::get('/{viewMode?}', 'index')->name('index')->where('viewMode', '[a-z]+');
Route::get('/ajax', 'ajax')->name('ajax');
});
Is there a way to achieve this kind of behavior?
Thanks in advance!
So building a few pages on the same template and loading the content via AJAX. Most of the content are forms. Views are defined by step number (1,2,3,4,5....32)
Here is how I built my route:
Route::get('onboarding/', [
'as' => 'get-onboarding-start',
'uses' => 'OnboardingController#getStart'
]);
Route::get('onboarding/{i}', [
'as' => 'get-onboarding-step',
'uses' => 'OnboardingController#getNextStep'
]);
Route::post('onboarding/{i}', [
'as' => 'post-onboarding-step',
'uses' => 'OnboardingController#postStepForm'
]);
Now one method in the controller cannot handle all the work. Meaning I will need to redirect to another method based on the $i (step number).
I am afraid that it is not simple to read if I put a big blog of switch case $i = 1,2,3...
At the same time I don't want to write 32 different routes.
What would you propose?
Hard code all the routes meaning: 'onboarding/username' then
'onboarding/email' etc... etc... The good point is that it is super
simple to read in the views and you know exactly what the next step
is... no need to check what the number corresponds to.
Catch all as coded now and redirect to different methods in the controller
Something better, super easy to read and with little lines of
code... which is .... ??
If these steps are going to remain as they are without many changes in the future, I'd go for the first option (having 32 get & 32 post routes). This will keep your application simple, if you'd want to apply parameters or middleware to them you can use route groups. Below I've posted a small code example from the laravel documentation
Route::group(['middleware' => 'auth'], function () {
Route::get('/', function () {
// Uses Auth Middleware
});
Route::get('user/profile', function () {
// Uses Auth Middleware
});
});
My code is below in Routes.php
Route::group([
'middleware' => 'auth',
], function() {
Route::get('/Categories-List', 'Category_Controller#index');
Route::get('/Create-Category', 'Category_Controller#create');
Route::post('/SaveCategory', 'Category_Controller#store')->middleware(['isAdmin']);
Route::post('/UpdateCategory', 'Category_Controller#update')->middleware(['isAdmin']);
});
What's the problem ?
There are still other 100s of routes defined which contains many belongs to admin.
Is there any clean way to isolate the admin routes ?
You can nest route groups:
Route::group([
'middleware' => 'auth',
], function() {
Route::get('/Categories-List', 'Category_Controller#index');
Route::get('/Create-Category', 'Category_Controller#create');
Route::group([
'middleware' => 'isAdmin',
], function() {
Route::post('/SaveCategory', 'Category_Controller#store');
Route::post('/UpdateCategory', 'Category_Controller#update');
});
});
You could also put the admin routes in an entirely separate file via app/Providers/RouteServiceProvider.php (add another line like the existing require app_path('Http/routes.php');).
I bumped into this last week and starred it on github. You can use this Laravel package (Laravel-context) to separate your admin context all together.
Let's say you have 2 contexts in your application: an Administration Panel and a RESTful WebService. This are certainly two completely different contexts as in one context you'll maybe want to get all resources (i.e. including trashed) and in the other one you want only the active ones.
This is when Service Providers come in really handy, the only problem is that Laravel doesn't come with an out of the box solution for loading different Service Providers for different contexts.
This package gives you the possibility to register your different repositories to a single interface and bind them through your Context's Service Provider, using Laravel's amazing IoC Container to resolve which concrete implementation we need to bind depending on which context we are on.
Thanks,
Karmendra
I try to learn Laravel, and I'd like to verify the user is logged in before calling a controller to do stuff.
There seems to be at least 3 different ways to accomplish this, and I'd like to know what is the difference between these.
Route::get('/main', 'StuffController#doStuff')->before('auth');
Route::get('/main', array('before' => 'auth', 'uses' => 'StuffController#doStuff'));
Or in the controllers constructor:
public function __construct() {
$this->beforeFilter('auth');
}
There are no differences. Laravel is the Framework that allow you to accomplish many tasks in many ways.
I prefer to add filters in routes grouping them, for example:
// logged users paths
Route::group(
['before' => 'auth'],
function () {
Route::get('/dashboard', [
'as' => 'dashboard',
'uses' => 'DashboardController#mainPage'
]);
}
);
// paths only for admin
Route::group(
['before' => 'admin'],
function () {
Route::get('/admin',
['as' => 'admin_dashboard',
'uses' => 'AdminDashBoardController#mainPage'
]);
Route::get('/categories/{page?}',
['as' => 'categories',
'uses' => 'CategoryController#displayList'
])->where('page', '[1-9]+[0-9]*');
}
);
There is one benefit of such use - its' much easier to look if all routes have correct filters.
Assume you want to display some content only for logged users and you need to use auth filter. You have many controllers to display content for logged users.
If you use beforeFilter directly in those controllers or in parent controllers constructor the following things can happen:
you may forget to put beforeFilter in all your controller constructors
you may forget in your controller constructor to run parent constructor (where you have beforeFilter)
you may extend not the class you wanted (for example you extend BaseController and you have beforeFilter defined in AuthController and in one or some classes you extend BaseController)
Those situations can cause that you display content for unlogged users because you need to remember about auth filter it in each controller and if you want to make sure you did everything right, you need to look at code of all your controllers.
Using route grouping (as I showed above) you can easily look at one file (of course assuming you use one file for routing) and you will see which routes use auth filter and which don't.
Of course I assume many people will have their own opinion on that thing but that's me personal preference to use filters in routes.
Your two ways have no difference, just different syntax style.
I prefer to put the auth filter in a BaseController, then extends all controllers I want to be authed from BaseController. Just write once, used everywhere. Btw, you can also put your csrf filter here.
class BaseController extends Controller {
public function __construct() {
$this->beforeFilter('auth');
$this->beforeFilter('csrf', array('on' => 'post'));
}
}
I have the following in my routes.php
Route::resource('g', 'GameController');
I link to these generated routes via HTML::linkRoute('g.index', 'Title', $slug) which produces a link to http://domain/g/demo-slug
What I am looking to do is verify if it is possible to have the prefix be declared in one place so I'm not hunting for links if a URL structure were to change.
For example, I would want to change http://domain/g/demo-slug to http://domain/video-games/demo-slug
I was hoping to use the similar functionality with the standard routes, but that does not seem to be available to resource routes.
Route::get('/', array('as' => 'home', 'uses' => 'HomeController#getUpdated'));
Route::group() takes a 'prefix'. If you put the Route::resource() inside, that should work.
Tangent, I find this reads better:
Route::get('/', array('uses' => 'HomeController#getUpdated', 'as' => 'home'));
As far as I know it's true you can't have named routes for a resource controllers (sitation needed) but you can contain them in a common space using Route::group() with a prefix. You can even supply a namespace, meaning you can swap out an entire api with another quickly.
Route::group(array(
'prefix' => 'video-games',
'before' => 'auth|otherFilters',
'namespace' => '' // Namespace of all classes used in closure
), function() {
Route::resource('g', 'GameController');
});
Update
It looks like resource controllers are given names internally, which would make sense as they are referred to internally by names not urls. (php artisan routes and you'll see the names given to resource routes).
This would explain why you can't name or as it turns out is actually the case, rename resource routes.
I guess you're probably not looking for this but Route:group is your best bet to keep collections of resources together with a common shared prefix, however your urls will need to remain hard coded.
You can give custom names to resource routes using the following syntax
Resource::route('g', 'GameController', ['names' => [
'index' => 'games.index',
'create' => 'games.create',
...
]])
This means you can use {!! route('games.index') !!} in your views even if you decided to change the URL pattern to something else.
Documented here under Named resource routes