I'm developing a web application in PHP using an MVC-approach (not using any framework, pure PHP). As is often the case with MVC, every request arrives at a front controller which routes it to the corresponding controller, and executes the requested action. The URL structure looks like this:
www.site.com/controller/action
Let's say I'm building an e-commerce site which has products in different categories. Possible URLs could be:
www.site.com/sofas/overview
www.site.com/video-games/overview
For the first URL, the "sofas" controller is loaded, and the overview() method of it is executed. This all works well until we have to nest these products within parent catagories. I'll take the two previous URLs to demonstrate what I mean:
www.site.com/furniture/sofas/overview
www.site.com/electronics/video-games/overview
Now, the "video-games" controller is nested within the "electronics" controller. However, with the current 'load controller -> execute action' structure this won't work.
A possible solution would be to create a method within the parent controller ("electronics") which gets executed in case an unexisting action is requested ("video-games"). This method checks whether the requested action exists as a controller. If so, the controller gets loaded and the action ("overview") of it gets executed.
I fruitlessly searched for solutions to this limitation of the standard front controller pattern, including here on SO. I think my implementation of MVC is now correct, but the front controller still brings limitations.
I think the thought that you have to have a different "controller" for each different product type may where you are running into problems.
Unless you are going to have drastically different views for each product type, I would think the controller would be linked to a concept such as "catalog" (or "product" or whatever you want to call it). So for example your URL structure might look like
www.site.com/catalog/furniture/sofas/overview
www.site.com/catalog/electronics/video-games/overview
With the catalog portion of the URI determining the controller and the additional URI segments in essence being parameters passed to the controller indicating the specifics of the request.
This would work nicely with an OOP inheritance structure in that you could have a root 'product' class, and then extend that class with subclasses for furniture, electronics, etc. which would each have their own properties specific to the category. You would the further sub-class for sofas, video games, etc.
All your controller would have to do is evaluate the request URI to determine which class to load for the request. So something like:
// assume URI has been parsed to get value such as "sofas", "video-games", etc. into a variable called $class_to_load
$product = new $class_to_load;
$product->overview();
You are confusing MVC structure with routing issues.
The controller should be probably something like a product controller, or a category controller. Group controllers by functionality.
Now routing deals with the structure of the request and where this gets sent in the application.
You should have a routing layer that knows (for example) to send /<category>/<subcategory>/<action> to the appropriate controller (say products controller) with appropriate arguments (i.e category and subcategory) so it can construct a response.
Only directly mapping the url to controller and action (i.e enforcing /<controller>/<action>) is a very limited way of constructing the architecture of your application.
Related
I am creating an MVC inspired PHP framework, mainly for learning purposes.
I basically have the framework created and am building an app on it and improving the framework as i go along.
I am still confused/not sure about certain aspects of this sort of architecture, and at the moment i am questioning my implementation of the View part.
How i have set up my framework:
Its a very simple set up, for example: you go to the url /post/post_id, this will load index.php which will instantiate the router. The router will then check the url and instantiate the correct controller and method based on the url. In this case it would be PostController, and the method would be a default method that would use the post_id to get the posts data from the relevant model. Next the controller would set up a "data" variable that will hold the data to pass on to the View, and this is where i am confused - should it send to its own View object (a view class file dedicated to the PostController), or to a generally used View class that is used by all controllers to load an html file?
At the moment my controller is sending data to the View class, this data includes what template file should be included/shown, and the actual data for the page (what we got from the Model through the controller).
My question is this:
Should this type of system have one View object that renders all of the views (html files) based on what data is given to the "render" method, or, should each controller that eventually sends data to the View have its own View object/class?
Meaning, should PostController send a request to the general view class, the same one that is used by all controllers to renders pages, or should the PostController send to a dedicated View Class (call it PostView if it makes it clearer), and this class will then render the specific html file?
Basically if it should be one View class for all controllers that will render what ever html file the controller tells it to, or if there should be many View classes, one for each page load.
NOTE:
I know a lot of questions have already been asked about MVC in PHP, but i could not find an answer to my question in any of the answers.
A bit about MVC:
In the original MVC pattern (presented by Trygve Reenskaug in 1979), the controller updates the model, the model notifies the view about the changes, and the view pulls its data from it. Though the pattern was thought for desktop applications - each M-V-C "triad" beeing related to a single control in a window (a button, a textbox, a checkbox, etc). So, each control on the screen had an MVC "attached" to it.
Since, in web applications, the model-to-view notification step is not (or can not be) present, the original pattern can not be applied as is to them.
But a most similar approach can still be relatively easily implemented: the controller updates the model, the view pulls data from it (irrespective of the controller). I think it's called "Web MVC Model 2".
There is a multitude of web MVC variations. You are using one in which the controller takes the role of an intermediary between the model and the view.
So the controller is the only one component communicating with the model.
The view's responsibility:
The responsibility of the view component is the presentation logic - which should not be assumed by the controller at all. Beside loading and rendering template files, this kind of logic involves the preparation of the data fetched from the model for displaying purposes. The result of the preparation should, preferably, be a list of values of primitive types (strings, booleans, integers, arrays, etc) which can be easily "injected" into the template files during the load-and-render process.
Examples of presentation logic:
Example #1: If you would fetch the value 123.45 (from the column amount of a table revenues) from the model, the presentation logic would consist of formatting it to the string 123.45 USD, in order to be displayed in a template file.
Example #2: Formatting a fetched date value of 28/05/2019 to 2019-05-28 by using a code snippet like this:
$fetchedDateFromModel = '28/05/2019';
$time = strtotime($fetchedDateFromModel);
$formattedDate = date('Y-m-d', $time);
The value of $formattedDate would then be "injected" into a template file.
Example #3: Setting a boolean value based on some model data, in order to be used in a template file for deciding if a button ("Get Bonus") should be active or not.
$paidAmount = 512.79; /* model data */
$isActiveGetBonusButton = false;
if ($paidAmount > 500) {
$isActiveGetBonusButton = true;
}
The answer (in respect of your chosen MVC approach):
By using a View instance in all controllers, you would be forced to perform specific presentation logic in each controller - before passing its result (e.g. the list of the prepared values) to the used View instance, in order to further just be "injected" in a specific template file.
Whereas, if you are implementing a dedicated view class (like PostView - which, preferably, inherit a base class View containing the render() method) for a controller class (like PostController) - so a 1:1 relationship, but see it as a loose one! - you can pass the data fetched from the model, in an unprepared form, from the controller to the view. The view class would then correctly take the responsibility of preparing the data for displaying prior to actually load and render a specific template file. E.g. of performing the whole specific presentation logic.
Note: In "Web MVC Model 2" - where, ideally, the controller has no knowledge of the view component - the above argument is more obvious:
the PostController just updates the model (when an update step is required);
the PostView fetches data from model, prepares it for display, and displays it (by loading & rendering a template file like posts.html.twig, for example). In other words, the view component performs the whole presentation logic by itself.
The best way, IMO, is to have this View or Template class do all the work related to views with just one simple method: render(string $templateName, array $context = []).
This allows for easy extension or creation of adapters. You should make your controllers use this method. If you use a DI Container in your framework, you could do a TemplatingAwareInterface and implement that with a trait that allows setter injection. That will inject the templating service on fetching from the service container. That way, you can use $this->templating->render() in your controller, without having to either make it global, nor constructing the templating service inside the controller, nor injecting the container into the controller.
Having one view class for each type of controller is cumbersome, harder to maintain and I don't really see a reason for it.
I'm currently putting together a little mvc framework for practice, I have a bit of laravel experience so it is loosely based on that. I've made a router that simply returns a specified view for the url that you set.
Now I've also made controller that splits the url and uses the first part after the base url as controller and second part as action. This loads a file corresponding with the controller and a method within that file corresponding with the action.
So if the url is something like: url.com/users/index it will load a UsersController.php file and look for the index() method within that file.
Now I'm wondering with is the exact difference between a controller and a router? It it like a specified? Wherein a router is just a little bit simpler and just reacts to an exact given url and a router chops it up and has a little more depth?
What I currently have seems to overlap quite a bit.
(Presuming that router does not refer to network hardware or wood working tool!)
The router takes the request and decides which controller/controller methods will handle the request.
The controller accepts the requests and handles it!
Now I've also made controller that splits the url and uses the first part after the base url as controller and second part as action. This loads a file corresponding with the controller and a method within that file corresponding with the action.
This isn't really a controller (as far as MVC is concerned) it is part of the routing.
For example take the [GET] uri: example.com/article/view/123
The MVC router will parse the uri and find the following segments
article
view
123
By default most routers would now instantiate the articleController and call its view method passing in 123 as a parameter. (You could alternatively have some getUriSegment(segmentIdx) method, that's a design choice for your framework.)
The ArticleController would have a view method with an $articleId parameter. This method would probably do something like: get the specified article (from a db via a model for example) and then display it (probably by returning a view which has been given the article returned by the model)
The controller sits between the model and the view. It communicates with both to make a response to a request.
The router chooses which controller (and which method on that controller) handles the request. For example it might decide the request 'product/view/123' should call the ProductController's View methods passing 123 as a parameter.
A router might also convert urls. Instead of using, like you said, method and action in the url. You could also have www.example.com/members. And the router would convert it to UsersController's Index method. This allows the user of 'pretty' urls to map to nice logically named controllers.
The router points a request to a controller.
I use php and usually structure my application into model-view-controller so its always accessed via index.php with class and method attributes. Class attribute passed as part of URL specifies controller class and method simply method to be called. This seems to be pretty common, but then I'm always having trouble in figuring out what controllers shall I create. What is the best, easiest and most applicable way to decide on what controllers should be created? I understand it depends on web application itself but must be some general way of thinking to get this process started.
I've found that building controllers based on your application's objects works well, and can take care of most actions you'll want for your app.
Take a look at SO -- there's URLs starting with /questions, /tags, /users, etc. I'd suggest a design which starts by creating a different controller for each object. /questions (or /questions/list) returns a list of all the questions. /questions/[0-9]+ returns the details of a particular question with that id number. /questions/ask returns the Ask Question interface.
As you continue building your app, you might find that the controller-based-on-objects method doesn't meet all your needs. For example, on my site (http://www.wysiap.com), I eventually made a /list controller to simplify my Grails URL mapping. But in most cases I did use this method and it's easy to figure out which controller should be doing different actions.
I recommend to think about the pages you'll need in your applications to accomplish all the requested tasks. You'll group similar tasks on the same page and create as many pages as you need. A page can be sliced in different views for specific actions.
With this in mind you could have one controller per page. Each view of the page can have its own method (action) in the controller. And inside the method of each view you can have a switch() that will enable you to have several tasks for the view. Example:
index.php (Dashboard controller)
/question-list (QuestionList controller, action index, display the whole page)
/question-list/add (QuestionList controller, action add, manage the "add" view with tasks like show-form, validate-form, insert-question)
/profile (Profile controller, action index)
/profile/edit (Profile controller, action edit, manage all the tasks requested for your profile)
...
I design most of my web applications this way and using Zend-Framework
I'm wanting to be sure that I am setting things up correctly. On a typical website with 10 pages, would each page have it's own controller with it's own IndexAction and it's own View folder with it's own index.phtml as a view?
Or do you have one controller with multiple Page1Action, Page2Action, etc and have multiple differently named view.phtml pages within view/index folder?
I'm leaning toward the former because then I can have a cleaner controller for each page...
Is there a standard, or is it subjective?
Your sitemap would play a major role in this question. But, in lieu of that, here's a few examples.
Example 1. Flat
/foo
/bar
/baz
You'll probably want to use separate controllers: Foo/IndexController.php, Bar/IndexController.php, and Baz/IndexController.php with each having an indexAction() method to pass information to your view (once again separate).
Example 2. A Little Bit Lower Now
/foo/bar
/baz
You'll only need two controllers: Foo/BarController and Baz/IndexController. If /foo needs a landing page you'll have to throw in a Foo/IndexController.php to be safe. Your actions are still indexAction(). Because you've not gone deep enough to hit that third level, your views are still index.phtml.
Example 3. Straight Line
/foo/bar/baz
You're down to onc controller: Foo/BarController.php. If you need landing pages for /foo and /foo/bar you'll need another controller for /foo (Foo/IndexController) and an indexAction() for both. With /foo/bar/baz you're actually down to a slightly different action now too - bazAction() (inside Foo/BarController.php). Your view is now baz.phtml.
Summary.
The wider the sitemap the more controllers you have and fewer actions. The more narrow the sitemap the fewer the controllers and more actions.
Postscript.
I should also state, this is also contingent on using default routing patterns. If you do something a little more sophisticated in routing patterns, this is all shot out the window. Sometimes we use routes to keep the number of classes manageable. When we have a wide sitemap it's possible to create some custom routes and use __call() within a controller to hand-off view data appropriately. Just another way to skin this cat.
Typically you would create one Controller for a related group of Actions. What related means is subjective.
Very roughly speaking, a group of related Actions operates on the same Model. At least that's a good starting point, but it rarely works out that simply, because few real-world applications consist solely of CRUD operations on each Model.
If you decouple the Model from being simply a data access component, you can more sensibly define a logical grouping of Controller Actions for a Model. A Model is where the majority of your code exists for business logic. Database persistence is just an (optional) internal detail of the Model, to preserve state from request to request. But a Model doesn't necessarily use a database. It could be standalone, or it could be an aggregation of other Model objects.
By default, each Action has its own View script. But this is also just a starting point, because you could use Layouts to make many View scripts share some if their markup in common, and you could use View Helpers and Partials and so on.
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.