I really wanted to challenge myself with this project from a backend perspective with this project. I have used popular MVC frameworks in the past, Django and Ruby on Rails, but I always struggled with fully grasping the model. So I decided to build one myself, without any 3rd party tools.
Custom Model-View-Controller Framework
Having had experience with the MVC design pattern in the past, I knew the basics behind it's structure. For any of you unaware, MVC stands for Model-View-Controller. Each of these terms identify the 3 separate types of components in a web app:
Model: The model handles the meat-and-potatoes of the product. It communicates with the database, interpreting and storing data as necessary.
View: Views manage the visual presentation, taking data from the models and inserting them into templates for the final page presentation.
Controller: All user requests go through the controller which routes the responses to the relevant view and model as needed.
How each of these traditionally coordinate together is best described by this graphic from wikipedia:
I found quickly found that the simplicity provided by the professional MVC frameworks had a lot more working behind the scenes than first meets the eye. For example, I found it easier, in my implementation, to create a separate Model Manager that would handle the creation, search of, and destruction of the Models themselves. This led to a slightly messier flow chart than the textbook one above.
First, the user will send a request to index.php via their browser which would immediately forward the request to Router.php. As of this writing, the framework only supports single page apps.
The Router.php was responsible for sending the user's request to the correct controller. It would do so with the following steps:
Check the CSRF token is valid.
Parse the requested URL for the specific page name.
If the parsed page name matches with an existing controller, forward the request to the correct Controller.php file.
The controller would determine the type of request, whether it was a GET or POST. I ended up splitting POST requests into either PUT, CREATE, or DELETE, depending on a hidden REQUEST variables included in the form's POST. For GET requests, the controller would directly initialize the relevant view. For any POST request, the controller would reach out to the model manager and then the view.
Models are split into two distinct parts: the model and a model manager. Models handle interaction with the database and represent individual objects (such as a User or Recipe). Model managers, on the other hand, manage the individual model objects. The managers create new model objects, search the database for any given specific object so it could be manipulated or provided to the view which wanted to use it.
Finally the views themselves would either take the information provided to it from the controller, or it would request information about model objects from the model manager. It would take that information and hand it to the template. The template would organize the code into the HTML, and the view would replace the index.php's code with the resulting templates.
As a side note, templates were broken up into multiple separate sub-templates. This allowed for easy extensibility and prevented me from needing to rewrite code.
Database Management
Very quickly, I realized that I was constantly rewriting SQL queries. In an effort to make my life easier, I spun off all queries into an internal DBManager interface.
This interface would manage all of the interactions with the database. It made the initial connection to the DB, would format the queries, and send it along. I opted to use PDO as the database manager for this tool.
In order to keep things as simple as possible, the DBManager would create a new instance of itself, generate a connection to the database, submit the query, and return the result. This method ended up being the most convenient pattern used across the site.
Takeaway
Developing the entire framework from scratch was a bit task to chew. I learned a lot doing it, and I would recommend others give it a try themselves.
Perhaps the largest mistake I made was developing this while I was building out a recipe website and the code became intertwined. While this helped me build only what I needed as I needed it, it meant it would be difficult to use this for any other project. In the future, I should separately design a framework and develop it in parallel, not apart of the same project.