[html-formfu] HTML::FormFu::Model::DBIC and Many-to-many

Ascii King tech at swattermatter.com
Sun Sep 27 22:12:04 GMT 2009

Carl Franks wrote:
> I don't think it's currently possible in a single step, to both select
> which one's to relate, and edit them.
> It's either a multi-value element to select them, or a Repeatable
> block to edit the already related rows.
> Carl
This is something I have been struggling with for over a year now. It is 
possible to do exactly what Nigel is asking. As near as I can tell, the 
reason this does not seem to work is because 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__->add_columns(qw/hero_id hero_name/);

  __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__->add_columns(qw/hero_skill_id hero_id skill_id rank/);

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

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

  __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.

indicator: submitted

    - type: Text
      name: hero_name
      label: Name

    - type: Hidden
      name: skill_count

    - type: Repeatable
      nested_name: hero_skill
      counter_name: skill_count
        empty_rows: 1
        new_rows_max: 100

      - type: Hidden
        name: hero_skill_id

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

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

      - type: Checkbox
        name: remove_skill
        label: Delete
          delete_if_true: 1

    - 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.


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

Please notice the odd relationship to get the rank to display. [% 
unique_skill.hero_skill.rank %]
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