[Catalyst] Re: Using model layers between Catalyst and DBIC

Jason Galea lists at eightdegrees.com.au
Mon Jan 9 14:16:43 GMT 2012


On Mon, Jan 9, 2012 at 3:14 PM, Bill Moseley <moseley at hank.org> wrote:

>
>
> On Monday, January 2, 2012, Jason Galea wrote:
>
>>
>> I think I've added another layer but I'm not sure where you draw the
>> line.. I have a model layer over DBIC pulling together related result
>> classes under a single model class. Then the app? layer uses the model
>> layer to get things done. So I'd probably have one "distribution" that is
>> our DBIC wrapped in a model layer layer and use that in a number of apps=
..
>> 8) Each app can then be used as the single model in a Catalyst app or
>> script or whatever.. (I think I need more names for the parts..)
>>
>
> Yes, where to draw the line is difficult to know.   I've only had a few
> hours to work on this but already I feel like I'm reinventing Catalyst --
> mostly because my model layer is pulling in much of the components that my
> Catalyst app would normally do -- DBIC, caching, even some concept of the
> "current user".   Access control is another topic.
>

The problem parts for me are DBIC and TT. I thought I could just set up the
components as usual, then load my app with them but it get's tricky calling
one component from another at setup time, although it all works fine if you
instantiate the app per request. So now I'm connecting/creating those
myself.

For other things provided by plugins I'm working more with Catalyst so for
caching I will probably have my app accept a cache object at construction
and pass in the Catalyst cache. For Authentication I've created my own
store and user for the Catalyst Authentication plugin and they use my app
to do what they have to. I've also created a store for the session plugin
which uses my app, so all-in-all my app can see/touch everything that
Catalyst is doing, and I can still make use of all the Catalyst stuff
available (hopefully).


>
>> I have "Sets" in lu of ResultSets and "Models" for Results. Although in
>> most instances a Model will actually cover the usage of multiple Results.
>> Each Set gets the dbic schema object and knows it's resultset name. Each
>> model has a data attribute which contains a dbic row object and "handles"
>> any methods I don't need to override via the Moose "handles" attribute
>> attribute!?
>>
>> Set->create($hash) creates the dbic object and stuffs it into a model
>> class and returns that.
>>
>
> So you are mirroring DBICs class structure a bit.  I need to consider that
> approach more as currently my model layer returns the DBIC row object
> directly.  So, I have something like this:
>
> my $user_model =3D Model::User->new;
> my $new_user =3D $user->new_user( $user_data );
>

> Not as flexible as your approach but my goal currently is to just abstract
> out the ORM so that Model::User can hide the specifics of the database.
> Actually, it's not that hard to do directly with DBIC, either.
>

yeh, I decided a while back that DBIx::Class is complicated enough and I'm
too lazy to keep trying to work out complicated solutions in the DBIC
classes to do things I know I can do quickly and easily with regular Moose
classes.. and I like having nice clean DBIC classes..


>
>
>> Each result class that has a model class overrides it's inflate_result
>> method which again stuffs the dbic row object into the model object so
>> searches on the related dbic resultsets return my model objects.
>>
>
> Can you show a code example of that?  I'm not sure I'm following why you
> use that approach instead of having your layer on top of DBIC do that.
>

 and the exception to the rule.. I did have my Set classes (which I now
refer to as Model Controllers) grabbing search results and looping through,
inflating them all into my Model Instances but then I couldn't just grab a
resultset if I needed to limit/restrict/whatever, and any search or find
had to be put through that ringer. With inflate_result I know that no
matter how I get the results they'll be instances of my model. create is
the only thing that doesn't work for so my controller does the wrapping
there.

package Lecstor::Schema::Result::Person;
use base qw/DBIx::Class/;
__PACKAGE__->load_components(qw/ Core /);
__PACKAGE__->table('person');
__PACKAGE__->add_columns('id' ,'firstname','surname');
__PACKAGE__->set_primary_key('id');

sub inflate_result {
    my $self =3D shift;
    my $ret =3D $self->next::method(@_);
    return unless $ret;
    return Lecstor::Model::Instance::Person->new( _record =3D> $ret );
}

1;


>
>>
>> Each model class has a validation class based on.. Validation::Class and
>> create & update run their input through that. If there are errors I stuff
>> the errors into a very basic exception object and return that. This way I
>> can return the same exception object no matter where the error comes fro=
m,
>> eg a dbic exception..
>>
>
> Yes, I'm doing something very similar where validation happens before the
> method in the model and on validation errors and exception is thrown (if
> you are on the Moose list you may have seen my example).
>
> Thanks for the feedback and the ideas,
>

no worries at all, happy to be able to provide it.

cheers,

J


>
>
>
> --
> Bill Moseley
> moseley at hank.org
>
> _______________________________________________
> List: Catalyst at lists.scsys.co.uk
> Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
> Searchable archive:
> http://www.mail-archive.com/catalyst@lists.scsys.co.uk/
> Dev site: http://dev.catalyst.perl.org/
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.scsys.co.uk/pipermail/catalyst/attachments/20120110/ccdf1=
03e/attachment.htm


More information about the Catalyst mailing list