suedunnell wrote: Hi Again - I should add my name to comment #1 above and ask that if anyone has questions, they can either post them here or ask me directly:
Sue Dunnell
PowerBuilder Product Manager
978 287 1752
sue.dunnell@sybase.com
Before I get into any sort of detail, I think it's important to say that there is more than one way to approach an MVC. Go ahead, ask a few seasoned developers how they would hack together a project like this and each one will give you a completely different answer. I'm going to do my best to simplify the process as much as I possibly can without damaging the stability, security, and scalability of the framework.
In my previous article, I announced that I would be documenting the process of developing a simple MVC framework. In this one I will go into a little more detail about each of the specific components of our MVC and will discuss the series of events which occur each time a page loads, otherwise known as the page load scenario.
We’re going to start by outlining a typical page load scenario, and then how to write code around it so we can gain more control over each part. By the end of this post we’ll have a solid understanding of what code needs to be written, and how it all will fit together.
A 10,000 Foot View
User navigates to /index.php/catalog/
The front controller initializes and sets up the environment
The front controller instantiates the request object.
Using data in the request, the route object is instantiated.
Using data in the route, the view object is instantiated.
Using data in the route, the application controller object is instantiated.
As the controller is being constructed, it creates instances to models and other resources it will need during execution.
The front controller calls the appropriate method in the application controller.
The front controller instructs the view to render.
How URIs Work
As the acronym would suggest, a URI identifies a resource. A typical web application may use a URI like: /viewCategory.php?id=123&output=json. This system is tried and true and always works — but it can still be improved upon. This URI could be rewritten as: /category/123.json.
There are a few key things to note about this URI structure:
The resource context is identified by the first URI component
The resource identifier is identified by the second URI component (if it exists)
The request method will define the method of execution upon the resource
The output type will be identified by the last set of characters following a period in the URI (defaulting to HTML if none is provided)
A Note On mod_rewrite
URI restructuring has become a common practice with most frameworks today. Apache users will use mod_rewrite, IIS users will use ISAPI Rewrite, and there are other solutions for other server platforms. When implemented properly, a well rewritten URI can prove to be a valuable site component which brings an added layer of usability and search engine optimization. But still, we can’t rely upon an external technology as a core part of the framework — it should be as “out of the box” as possible.
It’s also important to mention that the query string should not be modified by the framework in any way. This leaves it entirely up to the developer whether or not to use the query string to pass data into the application and not an assumption made by the framework. Instead of passing the information through the query string, it is advised that the extra data be passed as a path directly after the index.php in the file name; for example: /index.php/some/information.
The Front Controller
During the page load scenario, a single, central object oversees the entire execution — this is known as the front controller. The front controller loads all necessary components of the application and provides an API to access them. In this particular case, it loads the route, the request, the view, and the controller (the application controller).
If anything were to go wrong anywhere throughout the process, the front controller knows about it and handles the problem gracefully.
The Request Object
After the front controller is loaded, the next object to be instantiated during the page load scenario is the request object. This most likely will be a singleton instance and will contain all request data (_GET, _POST, and _FILE). Additional information about the request will also be stored in the request object. This includes the request method, actual URI, and evaluated URI (which is used when creating the route object).
It’s quite obvious that most of this information is already found in PHP’s superglobals, so at first glance it may not seem like it makes sense to duplicate it here; however, it affords us the ability to filter the data as it comes in and as it goes out.
The Route Object
The route represents the extra data from the URI mentioned above. Routes provides valuable information about a resource and how it is to be accessed. In order for a route to be instantiated properly, a request object will need to exist.
Routes are more than just path strings. They are dynamic and constantly changing. For example, imagine a scenario where no extra data is provided in the URI, but you still wish to load a default application controller. In this case, the route object would need to be injected with the default data. The only reasonable solution is to write an API for it which allows developers to add and remove path components at will. Really, routes should extend a base Path class… but we’ll worry about that later.
The View Object
Views represent the information presented to the client who requested the data. Since clients may vary, various output formats are required. This fact alone makes the view, quite possibly, the most flexible component in our MVC. For example, when it comes to web browsers, the most common output format will be HTML. If a JavaScript application were to request the same resource, JSON should be served up. The possibilities are endless so special attention to the design of this package is critical.
Some MVC implementations suggest allowing full access to models; this one however, will not. The view will have an API allowing data to be manipulated before it is sent to the client as a response.
Although a view could stand on its own if instantiated directly, the problem arises as to which view to create. This job, will be assigned to the route as it knows all of the information it needs to create an instance of the correct view.
Controllers
Controllers (more specifically, application controllers) are classes designated to handle requests to resources which are managed by the MVC. They also act as a liaison between models and the view.
Traditionally, MVC applications use the first two components of the route path to identify the controller class and the method (also known as an action) to call. For example, the route /blog/view/1234 would load the Blog class, call the view method with a parameter of “1234“. There is nothing wrong with this approach, but it still could be optimized further. Ideally, all controllers would act as a REST service whereby the action could be omitted.
Controllers for REST Services
REST-friendly controllers are simple: whenever a GET request comes in, the get method is called on the controller. Likewise, if a POST, PUT, or DELETE request comes in, the post, put or delete method is called respective to the request method. According to most REST services, GET requests are used to locate data, POST request are used to create data, PUT requests are used to update data, and DELETE requests are used to destroy data.
There is one problem though: web browsers don’t support PUT or DELETE based requests. This can be overcome by using POST requests in place of PUT and DELETE, but by adding an additional field to the posted data which correctly identifies the request.
Models
Models are classes designed to represent data. Most web applications are powered by a relational database of some kind so it’s not uncommon to find that models relate to a specific database table and/or row. A lot of frameworks take that to another level and allow their model classes to implement an ActiveRecord style interface with convenience functions to find, save, and delete data that the model represents.
For this MVC, models will much more simple. If models just represent data, and nothing more, that means they can represent any kind of data and thus maximizing component reuse. If you’re worried about loosing all the convenience that ActiveRecord offers, simple inherit from the ActiveRecord class and code as you normally would.
Conclusion
So there you have it. Everything you just read sums up the core framework components for our MVC. In future articles, I will examine revisit each component in detail and provide code and unit tests. After all, you were really here for the code anyway right?
I encourage everyone to offer their feedback on any level. This is an open source project so you have as much say in it as I do.
About Michael Girouard Mike Girouard is a front-end web developer living in New York City. As the Sr. Developer at the creative agency Magnani Caruso Dutton, he takes pride in his ability to introduce web standards and beautiful code to industry giants such as Discover and AT&T. In his offtime, Girouard goes right back to his editor and codes toward his latest open-source baby, Panda PHP Components. You can read more about him and his other projects on his blog, http://www.lovemikeg.com/blog.
Reader Feedback: Page 1 of 1
Subscribe to the World's Most Powerful Newsletters
Subscribe to Our Rss Feeds & Get Your SYS-CON News Live!
Click to Add our RSS Feeds to the Service of Your Choice: