Laravel RESTful Controller - Params Before Action Name - laravel

I have UsersController which is RESTful controller, there are some functions within it.
inside UsersController.php:
function postOutletVisit($id){
// some code
}
inside routes.php :
Route::controller('users', 'UsersController');
with this route I can access postOutletVisit action like this:
[POST] mydomain.com/users/outlet-visit/{id}
But I'm wondering if it's possible to convert that link to:
[POST] mydomain.com/users/{id}/outlet-visit
I know that I can do this by defining routes for every action like:
Route::post('users/{id}/outlet-visit', 'UsersController#outletVisit')
But this is not suitable for me because there are plenty of actions inside UsersConroller and I will loose the greate naming convenient for actions (first part of action name determines the method used in it, instead of defining the methods separately in routes.php file)

Outlet Visit is a specific resource, so it doesn't belong in your UserController and should have its own controller.
class OutletController extends BaseController
{
public function postStore($userId) // Controller for storing Outlet Visits
{
...
}
}
Then you define your routes for OutletController, specifying that you need to attach it to a specific user :
Route::controller('users/{id}/outlet', 'OutletController');
If needed you can add more actions for this Controller, such as listing, forms for adding / editing, etc.

First off, I think you mean to use Route::resource('users', 'UsersController').
Secondly, of course you can. Just overload the other route method underneath:
// This defines the predefined Laravel routing
Route::resource('users', 'UsersController')
Route::post('users/{id}/outlet-visit', 'UsersController#outletVisit');

There are no ways to change the urls created by the method Route::controller unless you extend it.

Related

Separate Laravel controller for each URL route?

I am building a REST API server (in Lumen, actually, rather than Laravel) with multiple endpoints that allow various operations to be performed on resources such as Users, Accounts, and Products. For example, here are the routes I have defined for the User resource:
GET /v1.0/user
POST /v1.0/user
GET /v1.0/user/{username}
PUT /v1.0/user/{username}
DELETE /v1.0/user/{username}
I currently have all of these API routes for a particular resource defined in a single controller. For example, here are my routes for the User resource:
$router->get('/v1.0/user', 'UserController#listAll');
$router->post('/v1.0/user', 'UserController#createUser');
$router->get('/v1.0/user/{username}', 'UserController#getUser');
$router->put('/v1.0/user/{username}', 'UserController#updateUser');
$router->delete('/v1.0/user/{username}', 'UserController#deleteUser');
Some of the controller logic is getting pretty complex, and I am now finding that my controller files are getting really, really long. I am now thinking that I should use a separate controller file for each route, to make the code more maintainable.
My question is whether there is any idiom or convention I should follow with regard to file/folder naming or structure. Should I create a subfolder under Controllers for each resource (ex: Controllers/User/UserCreateController.php)? Or is this entirely a matter of personal choice?
You should check out Single Action Controller which take only the __invoke() method and can handle one single route.
By the way, what I see usually is that when a controller logic is getting complex, is time to refactor and move that complexity outside the controller.
You do not need to create sub folders or multiple controllers. Use a single controller User Controller which only contains entry points of each route. Move the business logic outside of the controller by creating a class or group of classes that take care of the process.
For example : You can create another directory Libraries under app folder and create a class User under Libraries which contains all functions of User resource.
app/Libraries/User.php
namespace App\Libraries;
class User {}
Now, you can access this class and functions using the namespace within your User Controller
namespace App\Http\Controllers;
use App\Libraries\User;
class UserController extends Controller {}

Controllers design pattern

I have a design question about MVC and controllers
I have this two routes
Route::get('/foo/{id}', FooController#show)
Route::get('/bar/{id}', BarController#show)
But know, I would like to add another route for the index like this
Route::get('/', ???)
In this route, I need some information about the Foo and Bar models.
The question is, should I create a new controller for that route? like MainController?
In general, controllers are meant to respond to requests associated with a specific resource (model). Thereby, given your concrete example, two distinct scenarios apply.
The Foo and Bar models are required on the landing page (/ route): In this case, a dedicated controller would be a perfectly fine thing to do. Also, the use of a view model would be beneficial.
No information about application-specific models is needed on the landing page: You can still use a dedicated controller class to return your view, while another possibility would be the use of a lambda function:
Route::get('/', function () {
return view('landing');
});
It has always worked best for me to have a controller per resource type.
You have the resource 'foo' with FooController, and 'bar' with BarController. So for the root context resource, you aught to have a root controller.
This helps with separation of concern and keeps your code modularized.

Should all controllers have only the basic CRUD methods?

In Laravel, are all controllers only supposed to have the basic CRUD methods, as shown in the link below?
https://laravel.com/docs/5.3/controllers#resource-controllers
That is, should the only methods in a controller be:
index()
create()
store()
show()
edit()
update()
destroy()
Thanks.
No.
A controller can have methods named however you want!
If you are creating a RESTful controller, then the names of the methods make sense.
When you create a Resource Controller, then Laravel will save you the pain of writing the routes (you can use Route::resource)
For example: you can do this in YourController.php
function tada() {
return "Tadaaaa";
}
and then in your routes.php, you define a route like
Route::get('tada', 'YourController#tada');
And visiting that route will present you with the string Tadaaaa
Have fun!
No, You can have your own functions too. This is just a boilerplate that Laravel provides you to start with.

Call controller within another controller - CodeIgniter

I need to call a controller say 'faq_view' inside admin controller as the URL structure admin/faq_view like this how I can do this?
e.g:
site.com/maincontroller/function
site.com/maincontroller/othercontroller/function
Then, just redirect the page. Else if you want to just call the function, call it via AJAX.
It depends what you exactly want to do. If you want to just invoke the function, its not the right way. Controller as it defines itself controls the flow of the pages that comes on sequence. Controller is responsible to send commands to its associated view to change the view's presentation of the model.
So, if you are saying you want to call controller within another controller, that should mean you are about to redirect to another page.
Updated answer:
Just assume you have new_function on maincontroller that calls the function from othercontroller. The function does not need to be defined on othercontroller.
Add the following line on routes.php.
$routes['maincontroller/new_function'] = 'othercontroller/new_function';
Now, you can call the function of othercontroller as maincontroller/new_function.
You can always call a controller inside another controller, but this only works for calling one controller as far as I have tried. Let's say you are trying to load a controller inside a controller. You can try this:
$this->load->library('../controllers/myothercontroller');
Then do this:
$this->myothercontroller->function_name();
That's it! You can now access any function inside myothercontroller (controller) in your current controller. I hope this helps too.
Your controllers are part of the presentation layer and should not contain application logic. That means you should never need to call a controller from another controller, instead refactor your application and move the domain logic to the model layer.
Now if you have a method that you need in multiple controllers, say for example you need a template method that automatically adds your header and footer views.
If that is the case, create a base class that your controllers extend.
If you are talking about just a routing issue, then just use the routes file for that. I don't like the CI automatic routing and it should be avoided as it will result in duplicate URLs for the same resource.

How to set a custom URL path for a controller without creating new routes?

I wonder if there is attribute (built-in or some open source) for me to tag my controllers with the specific URL segment I want it to use, as in:
[MagicUrlRoute("status")]
public class InternalNameNotToBeRevealed : Controller
{
public ActionResult Show()
{
...
}
}
This way, instead of "/InternalNameNotToBeRevealed/Show" being what the user sees, it will be "/status/Show". This might be nit-picking, but it bothers that I have to use the controller class name as the official URL path.
Now, I do understand I could create a custom-route on global.asax, but that will be a lot of work for hundreds of controllers.
I found this very handy library to do exactly that, but only for actions:
http://maproutes.codeplex.com/releases/view/39888
I appreciate any suggestions.
You could have a listing of the mappings and just call MapRoute in a loop to register all the custom mappings. The mappings could be a dictionary, or you could even scan all your controllers once on App_Start, collect a custom attribute value and then use those to build the mappings. However, I'm not sure how well that would perform for a large number of mappings.
If you wanted a higher-performance mechanism, you'd have to create your own Route. You should be able to do this by inheriting from System.Web.Routing.RouteBase and overloading GetRouteData and GetVirtualPath to do the mapping. When constructing RouteData, you can just provide the existing System.Web.Mvc.MvcRouteHandler as the route handler, and as long as your route data contains 'controller' and 'action' values, it should continue down the MVC pipeline. Then just use the Add method on RouteCollection to add your route. You can take a look at MapRoute in System.Web.Mvc.RouteCollectionExtensions for some insight on how MVC adds it's route.

Resources