Gantry

 view release on metacpan or  search on metacpan

lib/Gantry/Docs/Cookbook.pod  view on Meta::CPAN

            \               /
          +-------------------+
          |    author_book    |
          +-------------------+

Here the author_book table has only two (or three fields if you give it
an id).  Both are foriegn keys to the tables on the end of the relationship.

=head1 How do I work with a simple three way join?

You need two things for easy use of a three way structure, once your
SQL is in place.  First, you need your model to understand it.  Second,
you need an easy way to perform CRUD on the join table rows.

=head2 Making and using the model directly

Making the model understand your three way preferences is easy.  Start
with the two tables on the ends of the relationship as normal.  Then add
this to the bigtop file:

    join_table author_book {
        joins author => book;
    }

You can do this with kickstart syntax when you make or augment the
bigtop file:

    bigtop -n BookStore 'author(name)<->book(title,year:int4)'

That will make the two regular tables and the joining table.

=head2 The Threeway utils module

Once you have a three way structure, you can use L<Gantry::Utils::Threeway>
to manage the rows in the joining table (the one in the middle).  You
can do this from the controller for the table on either end of the
many-to-many relationship, or for both of them.

To show this, I'll pull code from L<Gantry::Samples::User>.

First, use the module:

    use Gantry::Utils::Threeway;

Then provide a do_ method.  The one in the user example manages group
membership for users.  In kickstart syntax, the relationship is
user<->groups.  The full method is:

 #-----------------------------------------------------------------
 # $self->do_groups(  )
 #-----------------------------------------------------------------
 sub do_groups {
     my ( $self, $user_id ) = @_;

     my $threeway = Gantry::Utils::Threeway->new( {
         self            => $self,
         primary_id      => $user_id,
         primary_table   => 'user',
         join_table      => 'user_user_group',
         secondary_table => 'user_group',
         legend          => 'Assign Groups to User'        
     } );

     $threeway->process();

 } # END do_groups

All you have to do is construct the three way object and call process
on it.  This displays a form with a check box for each group.  The current
memberships are already checked.  Clicking in the boxes and submitting
the form updates them.

The keys needed by C<new> are:

=over 4

=item self

The Gantry site object.

=item primary_id

The value of the foreign key in the joining table that points to this
controller.

=item primary_table

The name of the controller's table.

=item join_table

The name of the joining table.

=item secondary_table

The name of the table on the other end of the many-to-many.

=item legend

HTML fieldset legend around the form where new joining table rows
are created from check box values.

=back

=head3 Using the three way manually

If you want to access rows from the table on the other end of the
many-to-many relationship, use the C<many_to_many> relationship in the model:

    my @groups = $user->user_groups();

That will return an array of groups to which the current user belongs.
You can turn that around through a group row:

    my @members = $group->users();

If you need the rows from the joining table, use the C<has_many>
relationship from the model:

    my @joining_rows = $user->user_user_groups();

=head3 Making a three way manually

By far, the easiest way to create a three way joining structure is
with a bigtop C<join_table> block as shown above.  But you can do
it yourself.  In your author model, add calls like these:

    __PACKAGE__->has_many(
        author_books => 'YourApp::Model::author_book',
        'author' # your table name
    );
    __PACKAGE__->many_to_many(
        books => 'author_books',  # value matches the has many above
        'book' # the other table name
    );

Then do the same in the book model.  Finally, make sure you have a model
for the joining rows with a normal foreign key C<belongs_to> for each of
the end point tables:

    __PACKAGE__->belongs_to( user => 'YourApp::Model::author' );
    __PACKAGE__->belongs_to( user => 'YourApp::Model::book' );

=head1 How do I make my Gantry app respond to SOAP requests?

There are two types or 'styles' of SOAP requests.  Gantry can help with
either, but it is better at the document style, so that is what I'll
discuss here.

To see a sample of this approach, run the samples app.server in one
shell and samples/bin/soap_client in another.  Give the client a
Farenheit temperature on the command line.  You should see a SOAP
request packet.  The client will send that packet to the server immediately
after printing it for you.  Then, you should see a SOAP response packet
with the temperature in Celcius.

Here's what you need to do to make your own server.

Make a controller.  For instance, you could add this to your bigtop file:



( run in 2.665 seconds using v1.01-cache-2.11-cpan-75ffa21a3d4 )