Forum Moderators: coopster
Here's the idea: Imagine a page that displays a set of widgets. These widgets are (usually) charts that are based off of data contained in several separate tables in the database. I use a running example of page that reports on a student's performance.
**High Level Requirements**
- a page that displays a set of (**HTML only**) widgets.
- widget's data will be based off a database query.
- the page can be used to view separate datasets containing similarly laid out data. For example, a single page will display widgets that report on various aspects of a single student's performance.
- want to see another student's performance, pull up another page. Displaying different widgets for different students is not needed (though it may be nice to have later).
- There may be many students, but data contained in the database is similarly laid out for all students.
- the way a widget is displayed may be changed easily (say changing a widget from displaying as a pie chart to display as a bar chart).
- widgets should be able to be created quickly.
**Low Level Requirements**
- Currently data does not change so widgets will not need to automatically update themselves.
- Widgets may represent a ratio of two things (eg. a ratio of failed tests to successful tests as a pie chart), a series of points, or sometimes a single numeric value.
- Development of new widgets should be a breeze, existing code need not be modified.
- Framework to be used: Zend Framework, based on MVC.
There are (minimally) three things to define a widget: the dataset to report on (in the example above, the student ID), the query that describes the metric being reported, and a render mode (barchart, timeseries etc).
Here is a pass at breaking down the responsibilities of each layer of the MVC:
*View:* Zend views are HTML templates with PHP injected. They will contain one of several types of widgets. Widgets are of various forms including: static JPEG images (loaded from a remote site ie: `<img src="http://widgetssite.com?x=2&y=3"/>`, JSON based javascript widgets, or charts of various kinds (piechart, bar chart etc.)
*Controller:* Creates the widgets, assigns them to the view afterwards. The set of widgets that is to be displayed on a page needs to be maintained somewhere. Since I can't think of a good way to do this in the view, I'll add this to the controllers responsibilities for now. If there's a better place for this please shout. The controller will also have to handle any other input parameters and passing them to the widget. For example, the data_set id which may be passed at the url line as `http:/.../report/?student_id=42`
*Model*: The model, in the Zend Framework, is responsible for pulling the data and as such will most likely contain a class for each widget for accessing the database.
Some points:
1. The model here, represents the data for a particular widget. So necessarily, it will need to know what the query is going to be, in order to pull together the tables necessary to fetch that data.
2. There's an additional processing step that will most likely be necessary before the widget can present the data. This is dependant upon which renderer will be used. Sometimes it may require forming a url out of the data returned. Other times, a JSON array. Other times perhaps creating some markup. This can go either in the model or the controller or the view. Unless anyone can think of a good reason to move it to the controller or view, it is probably best to let this live in the model and keep the view and controller thin.
3. Likewise, a widget will be made up of 3 things, its parameters, its data, and its renderer.
One big part of the question is: **What's a good way to represent the widget in an Object Oriented design?** I already asked this once, couldn't get an answer. **Is there a design pattern that can be applied to the Widgets that makes the most sense for this project?**
Here's a first pass at a rather simple class for the Widget:
class Widget{
//method called by the view
render() {//output the markup based on the widget Type and interleaved the processed data}
//methods called by the controller:
public function __construct() {//recieve arguments for widget type (query and renderer), call create()}
public function create() {//tell the widget to build the query, execute it, and filter the data}
public function process_data() {//transform into JSON, an html entity etc}
//methods called by the model:
public function build_query() {...};
public function execute_query() {...};
public function filter_data() {...};
}
4. For example, it is straightforward to pass the widget that was created in the controller to the View to render.
But when it comes to implementing the model it seems not so straight forward. Table Gateway Pattern is simpler to implement than ORM. But since table gateway pattern has one class for each model/table it doesn't seem to fit the bill. I could create a model for a particular table, and then within that, instantiate any other models needed. But that doesn't seem so to fit the Table Gateway Pattern, but rather ORM pattern. **Can Table Gateway Pattern be implemented with multiple tables? What alternatives are there? Does it make sense that the controller creates the widget and the widget creates the Model?**
5. Another issue that arises is that this design does not enable ease of widget creation. ie. Say I wanted to create a PiechartWidget, how much code can be reused? Would it not make more sense to use some OO ideas such as an interface or abstract classes/methods and inheritance?
Let's say I abstract the Widget class so only the shared methods are defined concretely, and the rest are declared as abstract methods. Revising the Widget class to make it abstract (second pass):
abstract class Widget{
private $_type;
private $_renderer;
//methods called by the controller:
//receive arguments for widget type (query and renderer),
protected function __construct($type, $renderer) {
$this->_type = $type;
$this->_render = $renderer;
$this->create();
}
//tell the widget to build the query, execute it, and filter the data
private function create() {
$this->build_query();
$this->execute_query();
$this->filter_data();
}
//methods called by the model:
abstract protected function build_query();
protected function execute_query() {
//common method
}
abstract protected function filter_data();
//method called by controller to tranform data for view
//transform into JSON, an html entity etc
abstract protected function process_data();
//method called by the view
//output the markup based on the widget Type and interleave the processed data
abstract protected function render();
}
6. I assume writing a new widget will require at least some new code to build the query, and maybe filter the data, but it should be able to use preexisting code for almost all of the rest of its functionality, including the renderers that already exist.
I am hoping anyone could provide at least some feedback on this design. Validate it?
Tear it apart. Call me an idiot. That's fine too. I could use any forward traction.
A few specific questions:
Q1. What's the best way to implement the renderers, as part of the Widget class or as a separate class? 1a. If separate, how would it interact with the widget class(es)?
Q2. How could I improve this design to simplify creation of new kinds of widgets?
Q3. And lastly, I feel like I am missing something here with regards to data encapsulation. How does data encapsulation relate to the requirements and play out in this scenario?
Q4. How best could it be harnessed this project?
Thanks in advance.
Reading your post... It's amazing how much free time I have at work lately.
I imagine you have experience in formal programming? Your commitment to "a standard" is highly unusual in most PHP programming. You seem to have put A LOT of thought into the planning, which is great. But you seem to be overly worried over particulars that may not be of much consequence. For example, turning what can be a basic class or two into interfaces and abstract classes is perhaps entirely unnecessary and more complex than needed. And perhaps you have these specific requirements from an employer, client, or perhaps even school work?
If I understand you right, you'll have many different students with all sorts of information, and you want to "build/display" graphs for this data for each student when viewing a student.
Something I have learned is make your programming as generic as possible when appropriately possible. There is no better feeling than being able to pull a class, function, etc from one project and be able to use it in-whole on an entirely different project. From your thorough planning you seem to be heading in this direction.
Something I would consider... each student page will have multiple widgets on it. Each widget is basically just a graph of some kind. Correct? I would use two classes here: 'widget' and 'graph' (or perhaps a better name). 'widget' to handle all the widget stuff (get the data, handling the data, etc; all the major points you note). And 'graph' to handle solely the RENDERING aspect.
In other words, 'widget' handles making the widget you need, but when it comes to turning that data/info into a graph, call upon 'graph'. I think your first major plan (under your #3) is a good start (for 'widget'). What I would do is use the 'widget' method 'render' as a wrapper, to call upon the 'graph' object.
Then, make your graph class as generic as possible. Set it up to handle data in a predefined format, and then have methods for different tasks. For example, you could have a method for making a pie chart, a method for a bar chart, etc. Then you could theoretically use this graphing ability in other places and for other projects. Just setup your widget class to process its internal data into the format needed by the graph object.
Hopefully this can give you some "traction".
Given the relative simplicity of what you're doing, I would put less emphasis on ensuring it is "perfect" in a design-sense, and more emphasis on working on the code and being sure your logic and general design are sound --- which you seem to have a grasp of. :)
Unfortunately I just don't feel capable of answering all of your questions. They are questions you'd get the best answers to by consulting some sort of mentor who is programming literate and perhaps familiar with your project.