In the latest lecture at my University, my professor is returning the View template from the routes.
Route::get('/list', function() {
return View::make('list');
}
I don't understand how this is even possible. I thought ONLY the controller can interact with the View? How are routes interacting with the view? Does this mean any class in our app can call View::make and control what View template is rendered?
This raises the even larger question of what is the routes returning to? In other words, when our Laravel server gets a request, something in our server pings the routes and our routes returns a template. What is routes returning the template too? I'm presuming it is index.php, I know there are a lot of bootstrapping methods in there. Can someone explain the internal structure of how Laravel works and why we can render views from routes?
Laravel intentionally makes this possible for the sake of flexibility. That said, you'll want to let a controller return a view response in most cases.
There are times where this flexibility comes in handy; say you want to quickly setup a route to output some debug data. Building a controller action and view template would be overkill in this case when you can just output it directly from your route definition. There are other cases too, but the route -> controller -> view method handles most use cases.
Actually, when you make a request to your Laravel application the request goes through route matching and if the matching route found then the framework invokes the handler/action registered for that route. So, it's actually the handler and a handler could be anything which is callable. So, when you declare a route like this:
Route::get('url', 'action');
The framework just searches for the requested route and if that is available in the Route Collection then it invokes/calls/runs that action/handler and the handler returns the response (View). So, if it's somethiong like this;
Route::get('url', 'myHandler');
function myHandler()
{
//...
return View::make(...);
}
In this case your execution scope is the function and if it's a class method then the scope is different, but in both cases the handler is taking the control to return the response, that's all. So, while it's possible to return a View from a named function then it's also possible to return a View from an anonymous function as well. If you can access/use something (Some other class such as a service class for something) from your execution scope then it's possible to be used. The difference between a function and a class method is the scope. So it's possible and easy and also useful in some cases (for a single page or a small application) but not recommended way and neither it's good practice. Try to use a class instead of an anonymous function.
Why does Laravel allow us to render views in routes?
It's a framework and in other words, it's a toolbox with so many tools and this is (the question you asked) just another tool which increases it's (the framework's) usefulness. It's not the toolbox's job to make a better application but your's and the toolbox is just a helper which saves your effort and time to be more productive. So, you have shortcuts but you need to chose the right thing wisely.
Request lifecycle is quite well explained in Laravel documentation:
http://laravel.com/docs/4.2/lifecycle
Short answer: routes can return response (i.e. string, view), or can load controller which can return response as well. It's probably not very good practise to return view directly from routes, but can be useful in some cases.
Related
In modern web frameworks (Laravel, Symfony, Silex, to name a few), there seems to be a pattern of using a routes.php file or similar to attach URIs to controllers. Laravel makes it a bit easier with an option to use PHP annotations.
But to me all this feels like a bit of a code repetition, and when you are creating/modifying controller logic, you have to keep routes file always at hand. Interestingly, there's a simpler way I saw in several old frameworks, and I used to use this in my old projects as well:
Controller. All classes in src/controllers folder (old way) or all classes in YourApp\Controllers namespace are being automatically mapped to the first part of the URL by adding "Controller" to it. Example: /auth gets mapped to AuthController, /product/... — to ProductController, and / — to default IndexController.
Action. Action is the second part of the URL, and it gets mapped to the method name. So, /auth/login will call AuthController::loginAction() method. If no second part provided, we try indexAction(). Don't want people to access some internal method? Don't make it public.
Parameters. Next parts of the URL are being mapped to the arguments of the method; if there are Application and/or Request type hintings in the argument list, they are skipped so they can be properly injected; we can access GET/POST variables as usual through Request.
Here's the full example, using all these features together:
URL: https://example.com/shop/category/computers?country=US&sort=brand
namespace MyApp\Controllers;
class ShopController extends BaseController {
public function categoryAction(Application $app, Request $req, $category, $subcategory = null) {
echo $category; // computers
echo $subcategory; // null, it's optional here
echo $req->get('country'); // US
echo $req->get('sort'); // brand
}
}
I'm sure it seems to lack some familiar features at first, but all features that I can think of could be easily added if needed — using attachable providers, connecting middlewares, branching controllers to subcontrollers, specifying HTTP methods, and even performing a bit of a pre-validation on the arguments. It's very flexible.
This approach would really speed up the routing creation and management. So besides having all the routes in one file (which is also not always true, considering various providers, using ->mount() in Silex or bundles in Symfony), what are the reasons modern frameworks seem to prefer this way of doing MVC routing over the simpler way I've described? What am I missing?
I'll speak here from Symfony/Silex perspective:
Decoupling. routes.php provide separation of URL mapping and controllers. Do you need to add or change URL? You go straight to routes.php. Very handy if you want to change a lot of them.
Flexibility. Generally, routes.php is a bit more flexible approach. SEO might go crazy and might require you to have route like /shop_{shopName}/{categoryName}/someStaticKeyword/{anotherVar}. With routes.php you can easily map this to your code, but this might become a problem if your routes are directly mapped to the code. Even more, you can have this only controller, you don't need to write controllers for each slash part. You can even have different controllers handling the same URL with different variable parts, for example /blog/[\d]+ or /blog/[a-z-]+ being handled by different controllers (one might generate redirect to another). You might never need to do something like this, but this is just a demonstration of flexibility of this approach - with it anything is possible.
Validation. Another thing to consider is that routes.php provide simple means of validation via ->assert method. That is, routes.php does not only map URLs to controller methods, but also ensure that these URLs match specific requirements, and you don't have to do this in code (which in most scenarios would take a bit more code to write). Also, you can create default asserts for some variables, for instance, you may ensure that {page} or {userId} are always \d+, single line in routes.php that would take care of all usages of {page} or {userId}.
URL Generation. One more feature of routes.php is url generation. You can assign any route any name (via ->bind() method) and then generate URLs based on that name, and providing variables for parts of URL that change. And once we have this system and use URL generator throughout our code we can change URLs as much as we want, but we won't have any need to edit anything but routes.php. And once again - these are flexible names, that you won't have to change everywhere throughout your project once URL has changed and you are not limited choosing the name. It might be much shorter than the URL, or a bit more verbose.
Maintainability. Say, you might want to change some urls (as in example above - from /blog/[\d+] to /blog/[a-z-]+. Also, you might want to keep both of them for some time, and make old one redirect to the new one). With routes.php you simply add a new line and add a todo memo to remove it in some time if you want to remove it later.
Sure, all of this might be achieved with any aproach. But would that be this simple, flexible, transparent and compact as this approach?
Just as note, the standard edition of Symfony is shipped with SensioFrameworkExtraBundle, that allows to use annotations instead of file-based declaration:
/**
* #Route("/")
*/
public function indexAction()
{
// ...
}
In the same manner, you can set the prefix for the whole controller file:
/**
* #Route("/blog")
*/
class PostController extends Controller
{
/**
* #Route("/{id}")
*/
public function showAction($id)
{
}
}
Still, we have to declare it. I think the main reason is that routing requirements are heavily influenced by SEO optimizations. For anything that faces the public you want some keyword-rich URL. The "public" logic organization may also be different, you may have only one controller to deliver all your static pages, still you want those to be /contact, /about...
In some domains, routes can be inferred with conventions. If you create a REST API using FosRestBundle, it will auto-generate routes based on your controllers/actions names on the ressource approach. In general I think the FosRestBundle got a lot of things in their approach, you can easily parse and validate query parameters at the same time:
class FooController extends Controller
{
/**
* This action route will be /foo/articles, GET method only
*
* #QueryParam(name="val", default="75 %%")
*/
public function getArticlesAction(ParamFetcher $paramFetcher)
{
...
}
I always prefer having the routes in a config file rather than using annotations because, firstly, I feel it's more maintainable and easier to have an oversight. It's easier to ensure that you don't have any conflicting routes when you can see them all together.
Additionally, it's theoretically faster. Annotations require reflection, where the application needs to scan the file system and parse each controller to collect the set of routes.
The reason is probably maintainability for the long term. It doesn't matter how the routes are set up but generally, Laravel as a framework focuses on the simplicity and the small things that make the framework great. As long as it is easy to use for the user (web developer in this case), the goal is reached.
You can use a different name for the routes.php file and also use multiple route files, if you have a lot of routes in your application.
You can try and rethink the whole concept to improve it but I don't think that Laravel's routes need really a big change to make it even easier. It's already simple. Maybe it will be changed for the better in the future but for now, I think it's fine.
I am attempting to create a controller that can detect if it is called from another controller in the application.
If it called directly via the URL, however, I need to know so I can perform some other actions.
i.e.
cheese/modulename calling potato/modulename is different to someone accessing site/cheese/modulename via URL - and I need to be able to pick up on this and act accordingly.
I am aware of:
$this->router->class
but it will not work as I may have the same named class in another module (HMVC pattern as an FYI) that may want to call this controller (cheese/modulename calling potato/modulename as an example would return 'modulename' - so I can't use that as a check to see if it was called by itself.)
I basically need a check for:
controller was called via another controller = true / false
can anyone tell me how (or if I am being thick!)
I am doing this in the __construct() just in case your solution will have a problem with that (can't see why but you never know!)
EDIT
Thank you to Mohammad Walid for his answer.
For clarity the structure is
CLIENTS
MODELS
CONTROLLERS
- Client
- Api
VIEWS
JOBS
MODELS
CONTROLLERS
- Jobs
- Api
VIEWS
I will be calling the API from Client - but may also call it from another API (possibly) That may be
In another Module
For Example the CLIENTS Api might get called from the JOBS Api Controller (I have no intention of doing this at present but it may be a possibility under different scenarios I haven't forseen and want to make it future-proof so I don't have a massive refactoring job in the future.)
You can try this:
function is_called_via_url($controller_object){
return $this->uri->segment(1) === get_class($controller_object);
}
and in your controller:
if(is_called_via_url($this)){
//do something
}
I'm not quite sure if passing $this as an argument in the constructor will work, but it worth try.
Reference and a hint from MonkeyZeus's comment.
From the comments there seems to be no way to do this without using debug_backtrace($options, $limit)
However the work-around I have ended up doing is to add a 'flag' within the authorisation module (which is called before all controllers)
The flag defaults to false.
If a controller from within my application calls the API page I turn this flag to true (is_application = true - I am currently just manually pasting this into the __construct of any controllers in my application that need to talk to my API)
I can then just do a simple
if(!is_application){
//this was called directly from the URL not from within the application
}
from within the API controller.
Hopefully this will be helpful for others who are attempting this sort of thing and once again thank you to the people who took the time to comment / answer.
I am trying to build a custom MVC framework in PHP. I'm just starting out with this MVC and framework stuff and I'm not very good.
I created all the general stuff. The library for the model, controller and view and I got a general app up and running.
I would like to now incorporate some error handling. Primarily on the user side for bad urls. I want to make Page Not Found or 404 Errors. So i need a way to check for bad controllers,actions and query strings. And then send the users to a 404 page.
What is the best practice for doing this in an MVC environment?
EDIT
This is a learning based project it is not for production.
First of all it is not recommended to develop a custom framework your own if you are planning it for production. There are many great frameworks that you can make use of, with good flexibility and more importantly with more performance.
Coming to the problem. First write a custom exception handler for managing 404. I recommend to call them in your system/core class where you create Object of the controller and invoke the action, if they dont exists.
Add an error handler in your bootstrap.
For example, in mine I have:
public function error(){
$this->view->msg = 'Page not found';
$this->view->render('error/index');
}
Add a check for the method in the bootstrap. Where depends on your structure. If the method or controller isn't found call the error() method.
Thats probably the simplest way.
you can use .htaccess (if using apache) to route all request to your index page. Usually the the controller (and action), view is associated with a file or function. so, if you want to show custom error message, just check for the file, class, or its methods.
for example:- index.php?controller=control1&action=action1.
if(!file_exists('control1.php'))
if(!method_exists($controller, 'action1'))
//route to error handler
Well, if you are learning like you said, you shouldn't use your framework for production code because one thing is breaking the app at home and another one is loosing money with angry clients :)
Back to the problem. I've built my own custom framework for PHP applications and I have in my framework several Routers. The idea is that each Router handles a specific pattern of Route, like this:
http://someapplication.org/services/awebservice/
This code would call the ServiceRouter, which handles Routing web services. Before calling any Router I check if the Router exists, and if it does, then I would call a method to check if the route is valid, something like this:
if ( RoutingManager::RouterExists($route) )
{
$router = RoutingManager::GetRouter($route);
if ( !$router->IsValid )
RoutingManager::RedirectTo404();
}
The idea is to locate your 404 handler to a class, and delegate the task of routing into different classes to allow a variety of Routers and make your application more changeable (scalable)
Hope I can help!
I've been using the CodeIgniter framework for PHP and am enjoying it, but I notice that it seems to require a controller for every view. I'm wondering if there is a way to call a specific model from the view itself, rather than route through a controller. I understand that use of a controller is best practice in most cases, especially where the data from the model needs to be modified in some way, but I have cases where I just need to do a strict data pull to the view (which is loaded via ajax), and setting up a controller for that seems superfluous.
Any thoughts? Thanks in advance!
You're fundamentally misunderstanding MVC, at least as implemented in CI.
All URLs on your site (at least those that utilize the CI framework) are mapped to functions (methods) within controllers.
http://myCIsite.com/controller/method[/var1][/var2]...
It doesn't matter whether the URL is accessed via regular HTTP or via AJAX. This is always a one to one mapping. Because of this, you should think of the controller/method combination as the "web page". Do not think of the view as the web page.
Models and views are subordinate to controllers. The controller delegates specific responsibilities to them - database interaction for models, and page output to views.
Because models and views only serve to perform delegated responsibilities, their use is not required in any given controller/method. Help pages, for example, generally have no need to interact with a database, so there is no model utilized by the controller/method combination that serves a given help page. Likewise, form handlers frequently redirect to another page upon completion of processing. As such, there is no view corresponding to the form handler (but there is (likely) a view called from the controller/method in the redirected to page).
Furthermore, models and views do not necessarily correspond on a one to one basis with individual controllers/methods. Any given model can be loaded and used from within several controllers. Similarly, a controller could have a single monolithic view that is used by all methods, or each method could be assigned its own view. (Or, as I just said, a given controller/method could utilize no view at all.)
Finally, CI does not enforce strict MVC separation. You can interact with the database and echo HTML all from within the controller and CI will not complain. Nevertheless, this separation and delegation of responsibility is followed because logically separating the responsibilities makes the code easier to read and helps you follow the DRY principle in your coding.
The fundamental Understanding is that the "web page" corresponds to the controller/method. The view and model, when used, handle delegated responsibilities for the controller/method.
I'm wondering if there is a way to
call a specific model from the view
itself, rather than route through a
controller.
That's not possible as of what I know, the main abstract class of the CI controller imposes restriction to use a controller otherwise you will get a fatal error.
And actually what you say will break the best practice of MVC design pattern. You got to go to model through a controller not view.
I'm a bit confused as to exactly what you're trying to achieve. The controller's value, aside from just being a clean way to handle incoming requests, is to manage the interaction between the models and the views and to determine which views to load. It's also entirely reasonable to load model data directly from your views, but how did you get to your view in the first place?
I guess I'm just having a hard time seeing the context here..
To run a query via Ajax you still need to provide a URL / path in the javascript call. You can not get around the fact that a controller function has to "catch" this call; you can not map a url directly to a model. All you need is 3-4 lines of code in your controller.
Via URI routing you can map a URL to a different controller, so you don't "require a controller for every view". I always create a controller called "ajax" to handle those requests.
A basic ajax call with jquery can be something like this
$('#prod_img').load( "http://domain.com/ajax/get_img", {'color': 'blue', 'url_title': 'bla' } )
You can echo stuff in your controller, so rather than trying to bypass the controller you should be looking into how to do away with the views. This will actually be easy, you can load the db class in the controller just as you can in a model.
But if you really don't want to use MVC perhaps Codeigniter is not the framework for you.
You should read more into the principles of MVC.
Views are strictly for presentation of data, the model shouldn't communicate with views directly.
But still if that's what you want then just pass $this->db from the controller to the view and use it in the view.
Then again, this is NOT a good practice.
I am learning the PHP MVC pattern for my backend implementation. Looking at this excellent example:
Implementing MVC in PHP: The Controller
http://onlamp.com/pub/a/php/2005/11/03/mvc_controller.html
I feel comfortable with the execution flow in a GET.
But there is no mentioning of what happens in a POST. What would the typical controller code for the POST do? I wonder if I am misunderstanding something obvious here, since I can't find similar situations in previous SO posts or Google.
For example: An app to manage persons,(name, last, age) wants to add a record to db when a POST hits the controller.
What happens next?
My guess is that the 'View' is not used at all, or maybe for confirmation?
Is there just a call from the controller to a model class that adds a record to db?
Or do I skip the controller altogether for a POST and go directly to an "add record" script?
Is there any available example?
Thanks in advance,
Ari
Well, POST is basically the same as GET, just some random chunks of info client sended to server. So you can treat it the same way.
I worked with CodeIgniter MVC framework in PHP. It uses GET URI to route to controller and it's methods. When the POST request comes, it treats its URI in the same way. The later actions are in the hand of the programmer, who accesses POST request data directly or through some wrapper, and he can also don't use it at all.
I need to say that you focus on the wrong parts. MVC is not the model of everything, and it doesn't say how to treat POST or GET requests. It's just a simple principle known many years before the name "MVC" became famous as the principle about splitting of logic, data and representation. And most of software(from old to new) actually do this splitting, because it is very hard not to do this in most cases. In some apps the borders are not so evident, some of them even haven't object model. The implementation of the app is always up to you, because MVC doesn't say you what to write but just gives some clues about highest level organization of you code.
P.S. Sorry for my bad English.
Typically, the controller would process the request (the controller processes ALL requests), then call into the model to actually manipulate data based on the request, and then either redirect to somewhere else (triggering a new GET request), or invoke a view to output a resulting page.
Well, if you are going to build your own MVC pattern solution, you could make one tricky thing. Since you're handling MVC you're supposed to have a really reliable routing manager. So after parsing your URL and defining what controller/method you are supposed to trigger, you could make something like:
<?php
...;
$method_name = (count($_POST) > 0) ? "post_".$route_result : $route_result;
...;
and later in your controller class you could do something like:
<?php
namespace Controllers;
class MyController extends \System\Controller {
function my_method($whatever = null){
...;
return $this->view($model_or_whatever); // supposed that you prepared view Class in routes
}
function post_my_method($whatever = null){
...;
return $this->view($model_or_whatever); // supposed that you prepared view Class in routes
}
}