[Catalyst] CatalystX::CRUD::Controller::RHTMLO: related database tables and nested forms

John Siracusa siracusa at gmail.com
Wed Jan 13 16:30:52 GMT 2010


On Wed, Jan 13, 2010 at 10:33 AM, Adam Mackler <nabble at mackler.org> wrote:
> First, his solution won't work with CatalystX::CRUD.  He has his
> method called "person_from_form" that returns a Rose::DB::Object that
> was created from the Rose::HTML::Form, and that DB object contains the
> references to the related objects created from the related, nested
> forms.  That's just what I want!  The problem is that his
> person_from_form() method takes no arguments (besides the caller),
> whereas I am using CatalystX::CRUD::Controller::RHTMLO, which calls
> person_to_form() and expects to be able to pass to a it Person object
> in order to update that object.  So his solution, which doessn't do
> such updating, does't work with CatalystX::CRUD::Controller::RHTMLO.

Ignoring Catalyst for a moment, here are some general patterns for
CRUD with RHTMLO (error handling omitted):

* Show empty creation form:

    # Get form object:

    # Either create a new one:
    $form = MyForm->new;
    # or grab an existing one ans reset it:
    #$form = GetMyForm();
    #$form->reset();

    # Initialize form with query parameters (if any)
    $form->params(GetQueryParamsSomehow());
    $form->init_fields();

    # Show the page with the form on it
    ShowCreationPageWithForm($form);

* Handle a "create" form submission:

    # Get form object (same as before)
    $form = ...

    # Initialize form with query parameters (same as before)
    ...

    # Get object form form
    $object = $form->my_whatever_from_form();

    # Save object;
    $object->save;

    # Redirect to success page
    ...

* Show an edit form:

    # Get object to be edited
    $id = IdFromQueryParameters();
    $object = MyObject->new(id => $id)->load;

    # Get form object (same as before)
    $form = ...

    # Initialize form with object
    $form->init_with_my_whatever($object);

    # Show the edit page with the form on it
    ShowEditPageWithForm($form);

* Handle a "update" form submission:

    # Get object to be updated (same as before)
    $object = ...

    # Get form object (same as before)
    $form = ...

    # Initialize form with query parameters (same as before)
    ...

    # Get the updated object from form:
    # (Note that the existing object is passed as an arg.)
    $object = $form->my_whatever_from_form($object);

    # Save the updated object
    $object->save;

    # Redirect to success page
    ...

In all cases, the $object could be a tree of related objects.  In
those cases, the load() calls to get an existing $object should use
the load(with => ...) form (or perhaps a Manager call) to get a fully
populated tree of objects.

As you can see, there's a lot of common stuff that can be factored
out.  For example, in my web apps, I usually have a set of pre-created
form objects stashed away by name (e.g., create_form, edit_form, etc.)
and a prepare_form() method that does all the form reset()ing and
initialization with params:

    # Get form, reset(), and initialized with the current query params
    $form = $mywebapp->prepare_form('create_form');

> Second, how hard really would it be to create the functionality I
> want?  Can I make my "person_to_form()" method (in my case
> debtor_to_form) go through all its subforms and add the related
> objects?  It doesn't seem to me as if it would be too hard (for
> someone competent).  I envision something like this in my Debtor Form:
>
> sub debtor_from_form (
>    my ($self) = shift;
>    my (@args) = @_;
>
> #   get the main object for the form
>    my $debtor = $self->object_from_form(@args);
>
> #   get related object from the related forms
>    foreach my $subname ($self->form_names) {
>       my $subform = $self->form($subname);
>       my $method = $subform->init_object;
>       my $sub_object = $subform->$method;
>       $debtor->$subname($sub_object);
>    }
>    return $debtor;
> }

That's the basic idea: delegate sub-object handling to the sub-forms
that know how to handle them.  The init_with_db_object() and
db_object_from_form() methods linked to earlier[1] automate this
process as long as you match up the sub-form names with the
relationship/fk names in your RDBO classes.

> But even if that works, there's still the issue of populating the form
> fields when making changes to existing database rows.  I wouldn't know
> where to start on that one.

Pass a fully populated object tree to init_with_db_object() and it'll
do this for you by matching up relationship/fk names with sub-form
names.  Step through the code to see how it works, and how you could
manually do the same thing.

-John

1. http://www.mail-archive.com/rose-db-object@lists.sourceforge.net/msg01443.html
   and bug fix:
http://www.mail-archive.com/rose-db-object@lists.sourceforge.net/msg01464.html



More information about the Catalyst mailing list