You are viewing revision #3 of this wiki article.
This version may not be up to date with the latest version.
You may want to view the differences to the latest version.
Please consider the following Dear software engineers imagine I have a simple ISA hierarchy: parent A with children B and C. So let's say we have the corresponding tables named "a", "b", "c" and (created with the help of the gii model creator) the classes A, B, C The following is a screenshot from MySQL workbench:
The tables b and c have a primary key which ALSO plays the role of the foreign key to the primary key of table a (as expected)
I have an attribute which are common among the children classes B and C .. BUT for other reasons (as you can see other foreign keys) I cannot move these properties to the parent class A.
Initially I names these attributes with different names. This proved to be bad design! So I got back to the database and named them with the same name "protocol"
Now to access these attributes from the parent class A you have to create a custom property inside class A.
A custom property is not a variable but a combination of get/set methods to read and write to a variable. If you have getAttr and setAttr then the attribute is named "attr".
What I did was to create a getChild method (but no setChild method was necessary) Remember the parent class (created from gii) has already access to its children from the public function relations. So exploit this to get the child object.
Note: that this solution works only if row of A has ONLY ONE child, either B or C
The goal is to create a common form to fill the database tables a and b or c So you need an attribute to differentiate the category. Is model A a parent of B or a parent of C? Once more you use custom properties. I used getCateg and setCateg methods. In my case if model A is a newRecord then the category is null but you may want to use a different solution
//...
-I create with the help of CHtml a drop down list so user can select B or C This drop down list has values (0, 1) which correspond to class B or C Therefore I have added to class A an array which remembers this correspondace from value => class The textfield of the dropdown list is taken from an explanation function inside class B and class C
So ok the user has chosen the class. Now I would like to have a textbox to fill the attrB and attrC in a COMMON way. I use the "activeForm" widget so I would like to keep all the functionality
Of course if I use a textbox for the model B I would have to add a SECOND textbox for the model C Above solution is not at all intuitive for the user. We would not like to have two textboxes but a SINGLE textbox.
I added an attribute named let's say "attr" inside class A to play the role for both attrB and attrC I also edited the "rules" method of class A. Now I could create a textbox based on model A. When the user submits the form the controller distinguishes from the dropdown list whether we deal with B and C. I created a protected attribute to remember the choice of B or C In that effect I created a new public method for class A where I could set this protected attribute and not only:
I use the relations to instantiate a new model B or C (for example $modelA->modelB = new modelB;)
I set the primary key of the model B (or C) according to the primary key of model A
and return the model B (or C) for further processing by the controller
So currently I use the controller to call explicitly $modelB->save() or $modelC->save(). There was an idea to override save() function in class A to automagically save the child model (since in object oriented terms the class/model A is an abstract class) Please let me know if that works better for you.
My Solution
Would be to create a FormModel class that presents a common interface for dealing with models B and C. This form has an attribute about which class type we are saving and add in a save() method that then deals with the two different interfaces transparently to the client form etc. Do a search on the Proxy Design Pattern.
Proxy Design Pattern
Say_Ten suggests this solution: http://en.wikipedia.org/wiki/Proxy_pattern
which is also a very nice presentation of php interfaces
If implementation would start from the beginning probably a new class would be best. I currently simulated the solution on A class. I have added an attributes which stores (by calculation) which is the category (B or C) of the A class object. I will transfer this addition inside wiki as soon as possible.
Although I was not aware of the proxy pattern I indeed tried to create a new class that would handle the B and C.
BUT I didn't want to deviate away from the CActiveForm and the way that form elements are created. The error handler and all this stuff (this was just a choice, not sure it is the right one)
When I created the new class in order to use CActiveForm I extended it from CActiveRecord but then of course an exception was thrown since the name of this class was not found as a table inside the database.
Probably I should extend it from some other of the Yii's base classes or use another solution.
In case someone has explored Yii's classes more thoroughly would know the answer.
Thank you!
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.