Posting this here for feedback and ideas.
-------------------------------
Simple Example :
Lets say you have a database structure that goes something like this (Simple 1-n relationship)
Table 1: Car Manufacturer
structure = make_id, name, etc
data = (1,Porsche), (2,Mercedes), (3,BMW), (4,Audi)
Table 2: Car Model
structure = make_id, model_name, power, top_speed, price, etc, etc ...
data = (1,911), (1,Boxster), (2,500SEL), (2,190E), (3,318i), (3,M5), (4,A4),(4,TT)
So there are many car models in the database, and each one belongs to exactly 1 car make. So far so good.
--------------------------
Screen Layout / Application Workflow
I am now going to have 2 layouts in the example application.
The first one is 'Car Manufacturer', which is a simple CRUD on Table 1 (Car Manufacturer) above.
List screen displays a list of Car Manufacturers, which you can add, modify, delete data from. All standard so far.
However, on the Edit screen for the Car Manufacturer, I want to display an extra list below the edit form to show all Car Models belonging to this Car Manufacturer.
ie - when editing record 1 = Porsche, the details for Porsche are displayed, but below the edit form a new CRUD list appears, with a filter applied : "select from car_model where maker_id=1" .
This attached CRUD form will then allow the user to Add/Modify/Delete car models that are related to the manufacturer we are currently viewing.
------------------------------------------------------------
Screen Layout for CarModel
Basic CRUD for all car models (regardless of make)
On the edit form, display the specific CarModel selected, and below this display a second CRUD for all other models of car from the same manufacturer. This second CRUD list will have unset_operations() called, so its only there as a reference list. Simple.
------------------------------------------------------------
Extending the concept :
Lets say, for example, that we add another table to the database of "Number of Sales by CarModel, for each year"
Each record in that new table is a 1-n relationship with the CarModel table. ie - each CarModel has a number of sales records for each year sold, and each sale record points back to exactly 1 car model.
So in our chained CRUD form example, we now have :
Edit form for CarManufacturer
Edit form for CarModels belonging to the CarManufacturer
CRUD form for sales records related to the specific CarModel displayed in the edit form above.
That is the basic concept anyway. I know there are several different ways of handling this type of pattern, but the solution of having multiple forms chained together in this way is also quite common.
-----------------------------------------------------------
Proposed solution :
For the developer API on this, I would expect that the programmer defines the chaining of a number of CRUD forms inside the controller.
Simple psuedo code example for the CarManufacturer screen layout :
class CarManufacturer extends CI_Controller {
function index () {
// Create a standard GC list on the CarManufacturer table
$form1 = new grocery_CRUD();
$form1->set_table('car_manufacturer');
......
// Create a second CRUD on CarModel
$form2 = new grocery_CRUD();
$form2->set_table('car_model');
......
// Connect the forms together !
$form2->chain_to($form1, 'make_id');
// AFTER all CRUDs are created and chained, call
// render on the top CRUD, and all of them get rendered by magic !
$content = $form1->render();
$this->template->display_content ("Car Manufacters", $content);
}
}
The chain_to($parent_form, $key_field) function performs the magic trick of linking 2 CRUD forms together.
Parameters passed :
$parent_form is a pointer to a grocery_CRUD object that this new form is chained to. This tells the parent form that it has another form underneath it.
$key_field is the name of the field in the 2nd table that relates this back to the parent table. So in our example, $form2 gets an automatic where clause added to the SQL list, that filters by make_id = primary key of whatever data is being edited in the form above.
The final call to grocery_CRUD->render(); then does all the magic needed to work out whether secondary lists should be displayed or not, and if so, passes down key information for filtering, etc.
-------------------------------------------------
Implementation :
Ah - now that's the hard part
I dont want to implement this in such a way that it limits the level of nesting available. May as well write the implementation so that it will support infinite levels of nesting. (Whether that makes the application unusable is another issue !!)
The only real hard part is hacking the operation of the grocery_states class inside the core of grocery_CRUD. This is the part that calculates what state a given CRUD form is at, based on the details in the URL passed to the application.
From grocery_CRUD's point of view, it will start to see multiple states and multiple primary keys happening at the same time : eg[list=1]
[*]CarManufacturer is in the list state, so no other forms displayed.
[*]CarManufacturer is in the edit state, CarModel is in the list state
[*]CarManufacturer is in the edit state, CarModel is in the edit state, CarSales is in the list state
[*]All 3 tables are in the edit state.
[/list]
Whilst this all sounds horribly complicated, I think it may be quite simple to pull off without a huge amount of code.
By storing a couple of extra pointers on the grocery_crud object - $parent_crud, $child_crud ... this creates a doubly linked list of grocery_crud objects. When a grocery_crud object is told to render() itself, it can easily traverse the tree either way, and work out where it sits in the hierarchy of related forms, and therefore calculate which segment of the URL it should get its state information from.
I think that will work without major dramas. It is essentially a simple enough problem, but very easy to make a complete mess of it !
There are plenty of alternate approaches as well, comments welcome.
Using flashdata might be a viable option for this. Havent tried that approach before though, so anyone know what the likely downsides are ? Hows the portability of using flashdata ?
-------------------------------------------------
I will have a play with this over the next coding session, and see how we go. All comments, feedback, peer review, other suggestions .. highly appreciated.
Once I get it working, I will post a patch in the contributors forum here for people to play with.
Cheers
Steve