In this article I plan on addressing CodeIgniter’s shortfalls as a framework
for validating objects and introduce a method for improving the validator
classes re-usability.
When To Validate?
The answer to this question is simple: whenever we are dealing with input. The
(incorrect) assumption that CodeIgniter and many web-applications make is that
user input comes in the form of GET and POST variables and a considerable
amount of effort goes into validating inputs via these routes. However, GET
and POST are not the only sources for user input. User input can come via
external sources such as tying into a remote API, an RSS feed, or from the
database itself. From each of these sources we could get an invalid state. In
the case of the remote API or RSS feed this is easy to understand. The API
could change, or the RSS feed could be malformed. In the case of the database
the issue typically appears when data is stored into the database under one
context but is then accessed with a different expectation.
Take for example a database with the following table:
Now say that we inserted a person with name “Bob” and birthdate
“1975-01-01.” This passes the validator going into the database, but later on
we pull this row from the database and use it to construct a plain PHP object
with properties id, name, and birthdate which we pass onto the view and
attempt to output the birthdate with the following line:
This is going to cause an error. Why? Because the date function is expecting
the second parameter to be a UNIX timestamp, but birthdate is already a
formatted date string. Now, we could solve this by changing the schema of the
database or changing the schema of the person object, but it is important to
note that even if we did fix the disparity between the two we would still not
fix the issue that it is possible for the person object to exist in an
invalid state.
So my answer is to when should validation occur is during object instantiation
and setting. The properties of the object should not be able to be set to a
value that the object cannot accept. This places validation clearly into the
realm of the “M” in “MVC.”
Form Validation in CodeIgniter
CodeIgniter’s
documentation
offers a form validation class that makes the above mistake very clearly. It
can only validate the POST super global and doesn’t really offer much of a
solution towards validation of objects themselves. Furthermore, their example
controller oddly mixes the issue of object validation, and thus business logic,
inside the controller which tends to create in many CI application fairly
bloated controllers:
I cannot offer a solution towards adapting the validation class to be fully
object operating without a heavy rewrite of the class, but we can move this
obtuse validation into a distinct model that encapsulates this behavior away
from the controller.
Introducing the Abstract Validator Class
We can get the validation logic out of the controller by moving it into a
Validator class. We begin with an abstract base class since each form will
need their own validator classes:
We take advantage of the fact that the CI god to access the form_validation
object inside the Validator instance to create the validate method which
merely sets the validation rules and then runs them. The Validator has two
properties $rules and $fields which we will use in sub-classes to provide
the CI_Validator rules and fields strings. We can transform the above
controller into the following subclass:
Here we can see how the rules and fields are used as well as how we can extend
the Validator class to add additional unique callback validations. This
simplifies the controller significantly:
The business logic is now gone and the controller is back to focusing on what
it’s supposed to be doing – load resources and running paths.
I have been keeping my own personal accounts for some time in a progressively
growing spreadsheet that after one decade of use, multiple files, and dozens of
worksheets. The entire thing is quite a mess. My solution? Build an app for it!
Pecunia will be a simple budgeting application designed from the ground up for
keeping track of monthly budgets, annual budgets, and keeping a ledger of
individual expenses. With a little bit of work, I should be able to turn it into
a multi-user application to launch as an extension on Kynda.net for public use
as well as an open source repository on Bitbucket.
This also gives me an excuse for a long series of posts going through the steps
necessary to take a spread sheet, abstract it’s logic into models, and implement
it’s functionality into a useful application.
Resources
Pecunia will be built using the following resources:
PHP 5.4
Apache 2.2
MySQL 5.5
Silex
Laraval 4
Update: January 22, 2014
After some consideration, I am opting away from Silex towards using Laraval 4.
It is not that I have suddenly found a dislike for Silex, rather I love working
with it, but that I would like to try my hands at the “latest and greatest” to
see what the big deal is about and to add another tool to my retinue.
I have worked with Code Igniter almost exclusively for the last nine months. In
that time, I have found it to be a massive step ahead over working with some of
the major CMS systems on the market (WordPress, I am looking at you).
Nevertheless, there remains some major architectural and blind spots that exist
in CodeIgniter as a framework. Some of these issues are resolvable
(CodeIgniter’s presumption that you would only ever want to validate the POST
superglobal), while others are inherent in it’s design. In this series I hope to
look at some of these issues that I have found with CodeIgniter, showcase
work-arounds where I can, or simply rant where no good solution exists. Today’s
topic will be of the latter variety.
The God Object AntiPattern
Lets dip over to WikiPedia for the definition of a God
Object:
In object-oriented programming, a god object is an object that knows too much
or does too much… a program’s overall functionality is coded into a single
“all-knowing” object, which maintains most of the information about the entire
program and provides most of the methods for manipulating this data. Because
this object holds so much data and requires so many methods, its role in the
program becomes god-like (all-encompassing). Instead of program objects
communicating amongst themselves directly, the other objects within the
program rely on the god object for most of their information and interaction.
The God Object in CodeIgniter
CodeIgniter started as an early MVC framework that has maintained backwards
compatibility with PHP5.2. It’s maintainers have insisted on maintaining this
compatibility which has limited CI from taking advantage the advances that
PHP5.3, 5.4, and 5.5 introduced to the language.
There remains nothing truly wrong with PHP5.2. While 5.3+ offers us many great
advantages, a SOLID framework is still possible using the older version. CI’s
architectural issues do not stem necessarily from it’s usage of the older
version but rather the violation of SOLID principles in archetyping it’s
interpretation of MVC.
In CI we have the CI super class (the idea of a super class alone should be a
code smell) that is globally available via the get_instance() function. This
returns an instance of CI_Controller, our main application controller handling
the current request. This instance is our elusive beast. The God Object itself.
We’ll call this object CI from here on out.
In any one request there can be only one instance of CI – it is essentially a
singleton responsible for:
Loading models
Processing the request
Returning the response
Overloaded Models
Here is where we get into the meat and potatoes.
The CI object begins its life by loading resources, that is it begins by loading
various models and libraries and maintaining links to each of them like so:
This code instantiates an instance of the news model and assigns a reference to
news. It then instantiates an instance of events. In this manner every model
that comes into existence during request process is held as a reference by the
CI object and can be access latter on in the request, e.g.
Once more, something very peculiar is done during this process. CI not only
instantiates an instance of the given model but it also copies these
references to every subsequently loaded model.
Thus every object that is loaded in this manner becomes aware of every object
that had been loaded up-to that point regardless of whether that object really
needed access to the behaviors of those objects. The model becomes unnecessarily
bloated and the difficulty of debugging the behaviors of a given model
increases. Unintended behaviors might be caused not by the model itself but by
the combination of that particular model and the order or selection of
previously loaded models.
Examine a Model’s State? No way.
Take for example the simple act of using var_dump to see the state of an
object in memory. If we were to var_dump our instance of news we might as
well call it a day as news contains a reference to everything that has been
loaded into memory for our request. The server will proceed to dump the entirety
of our application to the screen for us to wade through!
No Public Property is Safe
A larger issue is the assigning of the references themselves. Since the first
act of initiating the model object is to copy CI’s massive registry of
references to the model any properties or references set in the model’s
constructor is at the mercy of the controller overwriting the model. Take for
example, the events model. Let’s say the following was in the constructor:
Following substantiation of the events object the Events object CI will
immediately overwrite the news property with it’s own instance of the news
property. Thus the events model would either need to make the news property
private or protected which would generate an error when CI attempts to
access it or we would always need to take care to keep our model properties from
existing in the same namespace as CI.
I actually ran into a horrible bug where this very thing happened. I had a class
named Validator that I loaded in with the controller. I also intended each of
my models to load their own instances of the Validator class and to initialize
their instances with own unique validation parameters. However, since the
controller had already loaded an instance of Validator it immediately
overwrote each of my model’s Validator’s forcing them all to use the same
instance of the class. The resolution to this problem was to have to name each
instance of Validator something different, thus we had EventValidator,
NewsValidator, etc.