ModelForm developments

October 20, 2008 – 9:39 am Tags: ,

I’ve been reworking the ModelForm class for ZF a bit. Earlier this year, I discussed porting it to use Zend_Db_Table with Matthew Weier O’Phinney, for using it with Zend Framework. I initially had done some checking on Zend_Db_Table, and some small code changes to modify the class to use it instead of Doctrine, but I ran into some issues. Now, I’ve had some time to think about it, I’ve reworked the class slightly and added basic Zend_Db_Table support, too…

The initial issues

While Zend_Db_Table supports relations, it doesn’t do them as “precisely” as Doctrine. With Doctrine, it’s very easy to determine what kind of a relation we’re dealing with, so it’s easy to generate the relation fields in the form correctly. With DbTable, this isn’t the case. There is no way to determine whether a relation is a “has one” or a “has many” without the user defining some additional properties.

Also, due to my own lack of reasoning at the time, I started porting the ModelForm class directly. I replaced some Doctrine-specific parts with Zend_Db_Table parts. Since I still wanted to support Doctrine as well, this wasn’t exactly a very good approach, and I’ve now scrapped most of this earlier code.

Solving the issues

I thought about the relations issue and the code a bit more, and I came to the conclusion that it would be best to use an adapter, similar to how Zend Framework uses adapters with certain other components.

By modifying the Doctrine-based modelform class to use an adapter, and then creating a Doctrine adapter and a Zend_Db_Table adapter, I could easily support using both of them. It would also allow anyone to add support for their favorite DB system, or even for models that aren’t directly using a DB.

As for adding parameters for the relations in Zend_Db_Table, I’ve thought about adding a new property to the modelform class, which would allow you to pass parameters to the adapter. This would allow you to tell the adapter which kind of relations it’s dealing with. Alternatively, I could modify the generator itself to accept the parameters.

I’ve already modified the modelform to use adapters, and when using the Doctrine adapter, it works exactly the same as the non-adapter version of the class. The Zend_Db_Table adapter I’m working on currently supports normal fields, but no relations.

The code can be found from the modelform-adapter branch in my svn. The code is still slightly experimental – for example, it still uses CU_Validate_DbRowExists which depends on Doctrine.

I’ve also created a proposal for adding this to Zend Framework. If you have an account on the Zend Framework wiki, I suggest you post comments about this to the proposal itself.

Share this:

RSS feed Subscribe to my RSS feed

About the author

Jani is a 15 year veteran of the software industry. He's currently available for consulting

  1. 11 Responses to “ModelForm developments”

  2. good idea, I will chekout and follow you in the maillist.

    By etng on Oct 20, 2008

  3. Just a little point: in the new relases of doctrine there are some changes to Doctrine_Relation::ONE_AGGREGATE -> Doctrine_Relation::ONE and Doctrine_Relation::MANY_AGGREGATE -> Doctrine_Relation::MANY

    By ftb on Oct 27, 2008

  4. Thanks for the reminder. I am going to modify the class to work with latest Doctrine versions after I’ve had time to confirm the adapter stuff isn’t breaking anything etc. =)

    By Jani Hartikainen on Oct 27, 2008

  5. Hi Jani,

    Great that you fixed the problem with your former CU_ModelForm (which tried to load 1:n relations by its alias and not the classname). But with the new version and the doctrine adapter I´m getting the following errors:

    Notice: Undefined variable: relation in E:\Projekte\www.schlaufux.de\workspace\zf_v162_doctrine_v103\library\codeutopia\CU\ModelForm.php on line 524

    Notice: Undefined variable: table in E:\Projekte\www.schlaufux.de\workspace\zf_v162_doctrine_v103\library\codeutopia\CU\ModelForm.php on line 367

    Besides you are still using Doctrine_Relation::MANY_AGGREGATE sometimes.

    Greats from Germany
    LeisureLarry

    By LeisureLarry on Nov 28, 2008

  6. Thanks for pointing that out. Must have forgotten to replace it with the builtin relation constants… the adapter based one version is a bit experimental still, as I haven’t really had much time to work on that. Hopefully I can eventually make it work and get the zend_db_table based adapter done as well.

    By Jani Hartikainen on Nov 28, 2008

  7. I think the most important should be to get the doctrine adapter working again ;-)

    Therefore I´ll post all problems I find.

    1. CU\ModelForm.php line 372
    You should use CU_ModelForm::RELATION_MANY.

    2. CU\ModelForm.php line 524
    The variable $relation doesn´t exist.

    I’ve fixed this bug with a new method in the doctrine adapter and the following change to the function:

    public function getRelationElementName($name)
    {
    $relation = $this->_adapter->getRelation($name);
    $elName = $this->_fieldPrefix . $relation['local'];

    return $elName;
    }

    public function getRelation($name)
    {
    $relations = $this->getRelations();
    return $relations[$name];
    }

    I´ve changed the content of $elName because its value should be for example ‘f_roles_id’ and ‘roles_id’ is the content of $relation['local'].

    3. Besides I´ve changed a method in the doctrine adapter:

    public function getRelations()
    {
    if (defined(‘Doctrine_Relation::ONE_AGGREGATE’)) {
    $doctrineRelation = Doctrine_Relation::ONE_AGGREGATE;
    } else {
    $doctrineRelation = Doctrine_Relation::ONE;
    }

    $rels = $this->_table->getRelations();
    $relations = array();

    foreach($rels as $rel)
    {
    $relation = array();

    if($rel->getType() == $doctrineRelation)

    This way you´ve a possibility to support both doctrine versions.

    4. CU\ModelForm line 367
    $table doesn´t exist

    By LeisureLarry on Dec 1, 2008

  8. I´ve fixed problem 4 for the moment by adding the following line in the doctrine adapter getRelations-method:

    $relation['table'] = $rel->getTable();

    and changing line 367 in CU\ModelForm.php

    $field->addValidator(new CU_Validate_DbRowExists($relation['table']));

    Greats from Germany
    LeisureLarry (interiete.net)

    By LeisureLarry on Dec 1, 2008

  9. Thanks for your efforts Larry. With any luck, I’ll have some time this week to incorporate your fixes and confirm that everything works at least with the Doctrine adapter.

    The latest revision in the modelform branch now contains some unit tests, and I’ve also renamed the class to CU_Form_Model as per the ZF proposal’s naming.

    By Jani Hartikainen on Dec 1, 2008

  10. Well, I wrote some unit tests that test relation support, and of course they failed pretty bad.

    Then I implemented Larry’s fixes, and it’s all fine now. Tests pass etc., so things should work. I didn’t test subform relations yet though, so that’ll be a job for tomorrow maybe.

    By Jani Hartikainen on Dec 1, 2008

  11. Today I tried saving of my test form and I’ve found another problem concerning ModelForm.php and its doctrine adapter. In ModelForm.php you´ve the method getRelationElementName which you are using in two ways. Normally you use the relation alias as parameter (which I think is the correct way) and in one case you use $relation as parameter.

    The wrong case is in the save method of ModelForm.php in the switch case RELATION_ONE. As for as I found out at the moment this switch case is faulty and completly useless, because with my former changes to the method getRelationElementName the select in the zend form replaces the input type text for the local key of the relation and therefore the foreach $this->_adapter->getColumns() would set the record value.

    Greats from Germany
    LeisureLarry (interiete.net)

    By LeisureLarry on Dec 2, 2008

  12. I have a problem with implementing a one to many relationship with this (my User class has a group_id field which, in your previous class created a select populated from a Groups class). While with a little modification, this worked fine in your Model_Form class, in Form_Model the relation generates two fields, a text field and a select. The duplicate (the text field generated from the relation’s column) has no bearing on the data submitted to the database, so I have at this point just added a style that uses css to hide the extraneous field. Any way I could get rid of it entirely?

    By Edward Prislac on Mar 4, 2010

Post a Comment

You can use some HTML (a, em, strong, etc.). If you want to post code, use <pre lang="PHP">code here</pre> (you can replace PHP with the language you are posting)