In reality, building a class responsible for creating and handling view objects is a pretty straightforward process; there are many ways to accomplish this without much trouble. In this case, however, there's an additional challenge: as I explained before, the class must be able to parse all of the variables embedded into its associated template file, including closures. So, is this somewhat difficult to achieve? Not really. The class below, which implements the previous "ViewInterface" interface, performs this task. Check it out: (MyApplication/View.php) <?php namespace MyApplication; class View implements ViewInterface The above "View" class implements some simple mutators/getters, which allow you to assign, remove and retrieve fields by means of some PHP magic methods. On the other hand, its "render()" method does exactly what it name suggests: it renders the associated template by using a bit of output buffering. Of course, if you're anything like me, you're wondering which part of the class actually parses any closures added to the corresponding template. Well, if you look closely at the "__get()" method, you'll realize that it first checks to see if the requested field is actually callable. If this is true, then it calls the closure (or any other callable function) and passes to it an instance of the view. This simple yet effective trick allows you to embed variables into a template file, including closures, which can be used as traditional view helpers. Got it? Good. Naturally, the best way to see if the class can actually treat closures like helpers is by means of a concrete example. Below I created a basic HTML5 template; it contains some interspersed view fields. Have a look at it: <!doctype html> While the structure of this template is pretty simple, it makes reference to a view field called "clientIp," which echoes to screen the IP address from which the user is accessing the web page. Since this data is essentially dynamic, it could be retrieved through a view helper. Thanks to the functionality of the earlier "View" class, however, the same task can be performed by a closure, as shown below (as usual, the autoloader has been omitted for the sake of brevity): <?php use MyApplicationView; // include the autoloader and create an instance of it // create a view object and assign some properties to it $view->clientIp = function() { // render the view template As you can see from the above script, once a new view object has been spawned, it gets assigned some values to its "header," "content," "footer" and "clientIp" fields. Naturally, the most interesting aspect of this process is the use of a closure, which performs a basic check on some server variables to determine the actual IP address of the client. Leaving apart the limited efficiency of the closure itself (it'll fail hard when the user is behind an anonymous proxy), the example shows how to encapsulate a specific task inside a closure, which can then be parsed by the view's "render()" method. Moreover, if you want to see the output generated by the previous script, the following screen capture will hopefully be quite illustrative:
The script worked decently well. But, what's the point in using a closure to find out the IP of the client, when the same could be done through a method of a helper object? Since the task is fairly straightforward, a closure does the trick, even at the expense of sacrificing the advantages brought by Polymorphism. For more complex logic, an OO helper would unquestionably be the right option. However, there's a hidden benefit in using a closure as a view field: as you may have noticed, it gets called only when the view's template is parsed and rendered accordingly. This means that it's possible to use this approach to perform some expensive operations on request, such a lazy-loading data from a database. That's precisely the use case that I plan to cover in depth in the second part of this tutorial. Final Thoughts In this first chapter of a two-part tutorial, I went through the development of a basic -- yet extendable -- template system, which could parse any type of closure assigned as a property of its view object(s). In the sample application that you just saw, I used this ability to determine the IP addresses of users accessing the application. This was pretty easy to accomplish. As I explained before, however, it's dead simple to use this closure-based approach to perform more complex tasks. To give you proof of my claim, in the final installment, I'll be showing you how to lazy-load data from a simple text file and embed the corresponding contents into the previous template file. Best of all, I won't need to amend a single chunk of the entire system. Don't miss the last part!
blog comments powered by Disqus |
|
|
|
|
|
|
|