[Catalyst] Catalyst and Moose with "has" many_to_many SOLVED
will trillich
will.trillich at serensoft.com
Thu Nov 25 22:36:15 GMT 2010
many-to-many interface SOLVED:
Here's how we do the many-to-many interface, made brain-dead simple thanks
to Moose, DBIC and FormHandler. All the heavy lifting is handled backstage,
we don't need to lift a finger. We thought we'd have to do lots of
mechanical drudgery, but it's all handled for me!
Here's the relationship-definition between the main record (incident) and
two of its linking tables (one for agencies, one for contractors):
package *Spill::Schema::DB::Result::Incident*;
#...
__PACKAGE__->has_many( map_incident_agency =3D>
'Spill::Schema::DB::Result::IncidentAgency' =3D> 'incident' );
__PACKAGE__->many_to_many( *agencies* =3D> 'map_incident_agency',
'agency' );
__PACKAGE__->has_many( map_incident_contractor =3D>
'Spill::Schema::DB::Result::IncidentContractor' =3D> 'incident' );
__PACKAGE__->many_to_many( *contractors* =3D> 'map_incident_contractor',
'contractor' );
meanwhile in the form...
package *Spill::Form::IncidentForm*;
use HTML::FormHandler::Moose;
extends 'HTML::FormHandler::Model::DBIC';
has '+item_class' =3D> ( default =3D> 'Incident' ); # ties it to DBIC
#...
has_field '*agencies*' =3D> ( type =3D> 'Multiple' );
has_field '*contractors*' =3D> ( type =3D> 'Multiple' );
then in the controller...
package Spill::Controller::Spill;
use Moose;
use namespace::autoclean;
BEGIN { extends 'Catalyst::Controller'; }
#...
has '*form*' =3D> (
isa =3D> 'Spill::Form::IncidentForm',
is =3D> 'rw',
lazy =3D> 1,
default =3D> sub { Spill::Form::IncidentForm->new },
);
#...
sub my_action : Action {
#..
my $form =3D $c->*form*;
my $obj =3D $c->model('blah')->find_or_new({id=3D>$id});
return unless process( # *MAGIC!*
item =3D> $obj, # take this database object
params =3D> $c->req->params, # and update it if everything validates
);
#..
}
With those definitions in place, your [% form.render %] (or [%
form.render_field('agencies') %] if you're hand-rolling your form) will do
the right thing, no problem!
If you need to filter the items that populate the multi-select list, no
problem. There's another link between incident and, this time, person --
only we want to limit the person-list to just those related to the "org" in
the incident itself:
# back in package *Spill::Form::IncidentForm*
sub options_persons {
my $self =3D shift;
return unless $self->schema;
my $persons =3D $self->schema->resultset('Person');
my $org =3D $self->field('org')->init_value;
$persons =3D $persons->search(
{ $org ? (org =3D> $org) : () },
{order_by =3D> ['lname','fname']},
);
my @persons;
while ( my $person =3D $persons->next ) {
push @persons, {
value =3D> $person->id,
label =3D> $person->name,
#active =3D> $person->active,
};
}
return \@persons;
}
The active label seems to be ignored when we do this, unfortunately. And
really we should pull "org" from $c->user instead of the form.
But whether you roll your own list or let FormHandler/DBIC pull them all --
in your browser you get a multi-select list of all the items in each
many-to-many relationship, and users can click to turn them on or off. Click
"submit", and the linking-table gets updated accordingly. Very, very nice!
Catalyst, DBIx::Class, HTML::FormHandler, Moose -- woo hoo! Kudos, big fat
thumbs up!
-- =
Failure is not important. How you overcome it, is.
-- Nick Vujicic
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.scsys.co.uk/pipermail/catalyst/attachments/20101125/857f6=
2d5/attachment.htm
More information about the Catalyst
mailing list