[Catalyst] Making Controllers thin and Models thick

Kee Hinckley nazgul at somewhere.com
Wed Jul 18 18:24:51 GMT 2007


On Jul 18, 2007, at 8:07 AM, Bill Moseley wrote:
> I do wonder if that's not coupling the ORM and the validation too
> closely.  Not that it happens very often, but if you decided to change
> ORMs you would would still want the same validation of input data.
>
> I tend to have a separate layer between the controller and the model
> for validation.

That makes a certain amount of sense.  When it comes down to it,  
there are three validation layers

1. UI
Dealing with things that are specific to user interactions.  E.g. Ask  
for the password twice.

2. Biz-logic
Things you've specified for you application.  E.g. We need an email  
address for all new accounts

3. DB
Constraints specific to the database and object model.  E.g. Field  
length, required fields.

By putting #2 in the ResultSet I'm assuming that the structure of the  
underlying database will always be the same, and you're right, that's  
not always a safe bet.

I am planning on automating #3.  After going to all the trouble of  
specifying the database structure in my Perl code, I'm certainly not  
going to turn around and write code to check field lengths and types  
by hand.  Oddly I haven't seen a DBIx::Class::Validate library anywhere.

>
>> In short the CRUD controller actions using it can be as simple as:
>>
>> sub update : Local {
>>    my ( $self, $c, $id ) = @_;
>>    if ( $c->request->method eq 'POST' ){
>>        my $update_result = $c->model( 'DB::Table' )->build_update(
>> $id, $c->request->params() );
>>        if ( $update_result->{status} eq 'OK' ){
>>            $c->res->redirect( $c->uri_for( "table/view", $id") );
>>        }else{
>>            $c->stash( update_result => $update_result );
>>       }
>>    }else{
>>        $c->stash( item => $c->model( 'DB::Table' )->find( $id ) );
>>    }
>> }
>
>
> I suspect you write that enough times and you will notice the common
> code.
>
> My Create/Update controllers tend to look like this:
>
>     sub edit : Local {
>         my ( $self, $c, $id ) = @_;
>
>         $c->post_redirect( 'list', 'Foo was updated' )
>             if $c->update_from_from( $id );
>
>     }
>
> Because update_from_form knows the action path and can therefore find
> the associated form.  It can also determine what the concept of a
> posted form is in a single place.  And the associated form knows what
> model class to update or create a row in.  And the model class knows
> how to validate ids.  And the form knows how to look at the model
> class and determine lookup values for one-to-many and many-to-many
> relationships.

That's a lot of info crammed into one paragraph.  Let me expand it  
out here just to make sure I've got it.

First of all, what's "$id"?  The object being updated?  And if so,  
where did it come from?  I can think of a couple options, but the  
fact that "edit" is "Local" makes them unlikely.

So, first update_from_form looks at the action path to determine  
which form is being submitted. Then it dispatches the validation and  
update task to that form.  That implies you have (at least logically)  
an object model for forms?  Which means that forms can easily share  
common validation and other objects; which is very nice.  How is that  
set up, and how does the form know what model it is managing?

> that might be considered part of the validation and the form module
> can validate that the user can access $id.  But, I kind of consider
> that more of a controller responsibility since an invalid $id in the
> path for a given user is an invalid path (and thus maybe a 404).

Agreed.




More information about the Catalyst mailing list