When we write web apps, we often have to create some interface for Creating, Reading, Updating, or Deleting data. In some cases, we do it for every model, so we want to have a way of generating those interfaces automatically.
Most frameworks have some kind of scaffolding, which comes in two forms:
- Code generation, which generates code for controllers and views.
- Dynamic scaffolding, which outputs some predefined view.
In other words, both methods are pretty much useless for bigger projects, where you have many models and most of them require small customizations.
For the last two years, I've used the latter one. The template for listing rows evolved into an ugly combinations of conditionals.
How? One controller wants to have it's list sortable and searchable, other might want to disable searching, and another wants pagination.
You'd think, But I can set some flags to turn features on/off. Well, it works. At least until you have to add something for one and only one controller.
But how to make it more flexible? Because if I want to rewrite part of the view (in the "html code" sense), I have to rewrite the whole, right? Right? Not really, as long as your view isn't just a blob of html code with occasional php echos. Or is it? Because in dynamic scaffolding, that's how the view looks like. A huge html file filled with templating directives, be it php, smarty, or whatever templating system you prefer.
What we'll do here, I'll call Flexible scaffolding. So how it works? It's simple, a class that generates html code. Ok, so you can wrap your template in a class and be happy, but that won't really help. True flexibility can be achieved, when you split the code in small, easily replaceable methods. Let me show you how:
class ListView { protected $data; function __construct($data) { $this->data = $data; } function fetch() { return '<h1>A list.</h1>' . $this->table(); } function table() { return '<table>' . '<thead>' . $this->header() . '</thead>' . '<tbody>' . $this->rows() . '</tbody>' . '</table>'; } function header() { return '<tr><th>Each</th> <th>field</th> <th>name</th> <th>here</th></tr>'; } function rows() { $result = ''; foreach($data as $row) { $result .= '<tr>' . $this->row($row) . '</td>'; } return $result; } function row($row) { /* <td>field</td> etc. */ } }
The class is longer than a simple template, but it's way easier to modify by inheritance. For example, suppose that we want to swap tbody with thead:
class SwappedListView extends ListView { function table() { return '<table>' . '<tbody>' . $this->rows() . '</tbody>' . '<thead>' . $this->header() . '</thead>' . '</table>'; } }
Now do try and do that with a typical template. Adding to the comparison:
ControllerNameActionView?)Of course, I have only shown how to make flexible views — because most of the time, controllers don't need to be flexible. If you think otherwise, post an example in a comment, and I'll tell you how to solve it with model and view, if possible.