[html-formfu] HTML::FormFu::Elements::Repeatable and DBIx::Class

Ascii King tech at swattermatter.com
Thu Nov 26 15:08:24 GMT 2009


Benjamin Martin wrote:
> hello,
>
> I have been trying to get the HTML::FormFu::Elements::Repeatable to work
> with a 'has_many' (DBIx::Class) relationship without success. I am yet
> to find complete working example, just snippets from perldoc .... which
> I am yet to get going :(
>
> Can anyone point me towards a working example?? (or any resource of
> help/documentation)
>
> Many thanks for any help you can give me :)
>
>

I was having a huge problem with the FormFu repeatable element in a 
has_many as well. This is the example I came up with to show how it is 
supposed to work and why it doesn't seem to do what we expect. I posted 
this before, but as a reply to an old post, so I feel it's OK to post it 
again.

If this example is wrong, please let me know.

As near as I can tell, FormFu wants the id field in a  repeatable 
element to be a Hidden element. So, you create a new id field in your 
intermediary table that can be the hidden id field and you set your 
repeatable nested_name to the has_many relationship, not the 
many-to-many. You use the many-to-many relationship when you want to 
display it.

Here's my example:

Each hero can have many skills. Each hero's skill has a rank for that 
hero only.
Batman can jump over a wall. Skill -> Jump with Rank -> 2
Superman can leap tall buildings. Skill -> Jump with Rank -> 5

 package My::Hero;
 use base 'DBIx::Class';
 __PACKAGE__->load_components('Core');
 __PACKAGE__->table('hero');
 __PACKAGE__->add_columns(qw/hero_id hero_name/);
 __PACKAGE__->set_primary_key('hero_id');

 __PACKAGE__->has_many('hero_skill' => 'My::Result::HeroSkill', 'hero_id' );
 __PACKAGE__->many_to_many('skills' => 'hero_skill', 'skill_id');


Here is the intermediary table. It includes Rank because that is 
particulair to each hero. Notice I've created a new primary key rather 
than using the hero_id, skill_id combination key. This is important.

 package My::HeroSkill;
 use base 'DBIx::Class';
 __PACKAGE__->load_components('Core');
 __PACKAGE__->table('hero_skill');
 __PACKAGE__->add_columns(qw/hero_skill_id hero_id skill_id rank/);
 __PACKAGE__->set_primary_key('hero_skill_id');

 __PACKAGE__->belongs_to('hero_id' => 'My::Result::Hero');
 __PACKAGE__->belongs_to('skill_id' => 'My::Result::Skill');


 package My::Skill;
 use base 'DBIx::Class';
 __PACKAGE__->load_components('Core');
 __PACKAGE__->table('skill');
 __PACKAGE__->add_columns(qw/skill_id skill_name/);
 __PACKAGE__->set_primary_key('skill_id');

 __PACKAGE__->has_many('hero_skill' => 'My::Result::HeroSkill', 'skill_id');
 __PACKAGE__->many_to_many('heroes' => 'hero_skill', 'hero_id');


When you are building your form's config file, just remember to use the 
has_many relationship as the nested_name.
Since the skills are going to come from a pick list, I have set the 
Select element resultset to "Skill" so it uses the Skill table.

hero_form.yml
------------------------
---
indicator: submitted
elements:

# HERO_NAME --
   - type: Text
     name: hero_name
     label: Name

# THECOUNT --
   - type: Hidden
     name: skill_count

# ** REPEAT SKILLS --
   - type: Repeatable
     nested_name: hero_skill
     counter_name: skill_count
     model_config:
       empty_rows: 1
       new_rows_max: 100
     elements:

# HERO_SKILL_ID --
     - type: Hidden
       name: hero_skill_id

# SKILL --
     - type: Select
       name: skill_id
       label: Skill
       empty_first: 1
       model_config:
         resultset: Skill
         id_column: skill_id
         label_column: skill_name
         attributes:
             order_by: skill_name

# RANK --
     - type: Text
       name: rank
       label: Rank

# DELETE HERO SKILL --
     - type: Checkbox
       name: remove_skill
       label: Delete
       model_config:
         delete_if_true: 1

# SUBMIT --
   - type: Submit
     name: submitted
     value: Submit


This will create a form that allows you to create Heroes with multiple 
skills where each skill has a unique rank.
To display your hero, you then use the many-to-many relationship as normal.

list_hero.tt2
---------------------
<table>
<thead>
   <th>Name</th>
 <th>Skills</th>
</thead>

[% FOREACH hero IN object -%]
 </tr>
   <td>[% hero.hero_name %]</a></td>
   <td>
     [% FOREACH unique_skill = hero.hero_skill %]
         [[% unique_skill.rank %]] [% unique_skill.skill_id.skill_name 
%]<br />
     [% END %]
   </td>
 </tr>
[% END -%]
</table>

Please notice the odd relationship to get the rank to display. [% 
unique_skill.skill_id.skill_name %]
If there is some data-integrity reason why I can't do it this way, 
please tell me. I hope this helps someone.



More information about the HTML-FormFu mailing list