Using plain classes as Catalyst models
A common pitfall when designing models is the tendency to tie application logic to Catalyst. The problem with this approach is that model classes become difficult to reuse outside of Catalyst, which is a common requirement for most applications. A better design approach would be to write and test a plain class outside of your main web application, and then painlessly glue it to Catalyst with a couple lines of code.
A review of Catalyst components
The COMPONENT() method
Catalyst gives you a chance to instantiate all your components (Models, Views and Controllers) during load time by calling the COMPONENT() method on each component class. This means you can implement it and return whatever you want from it. For models this boils down to:
package MyApp::Model::SomeClass; use warnings; use strict; use base qw/Catalyst::Model/; use SomeClass; sub COMPONENT { my($self, $c, @config) = @_; return SomeClass->new(@config); } 1;
This particular implementation passes the configuration for this model to the
external class on the call to new(). Of course, you could choose to instance
the class any way you want to. Later on, $c->model('SomeClass')
will get
you the SomeClass
instance, not MyApp::Model::SomeClass
. Notice that
the returned object is the very same instance that was created initially when
your app was loaded.
The ACCEPT_CONTEXT() method
Every time $c->model
is called, Catalyst gives you a chance to run custom
logic by attempting to run ACCEPT_CONTEXT on the model, whatever is returned
by this method is what $c->model
returns as well. So, if you need a
new instance on every call, your model becomes something like:
package MyApp::Model::SomeClass; use warnings; use strict; use base qw/Catalyst::Model/; use SomeClass; sub ACCEPT_CONTEXT { my($self, $c, @args) = @_; return SomeClass->new(@args); } 1;
So when you call $c->model('SomeClass')
, you'll get a fresh instance of
SomeClass
not MyApp::Model::SomeClass
.
The Catalyst::Model::Adaptor
way
Instead of implementing your own glue code, you can use the generic
implementation provided by the Catalyst::Model::Adaptor
module, which
provides several different ways of building your external class instances.
If you need a single application-wide instance of your external class, you can
inherit from Catalyst::Model::Adaptor
:
package MyApp::Model::Foo; use warnings; use strict; use base qw/Catalyst::Model::Adaptor/; __PACKAGE__->config( class => 'SomeClass', args => { foo => 'bar' } ); 1;
Of course, you can also configure your class via myapp.yaml
Model::Foo: class: SomeClass args: foo: bar
This gives you more flexibility when you decide to change your implementation,
just replace SomeClass
with whatever class you wish to use, without even
touching your code.
Alternate object instancing approaches
For instancing objects on every call to $c->model
, just inherit from
Catalyst::Model::Factory
instead. And for instancing objects on a
per-request basis, inherit from Catalyst::Model::Factory::PerRequest
.
The Catalyst::Model::Adaptor
documentation provides information on how to
further customize your models to address your specific needs.
For the incredibly lazy
You can easily create glue models by using the helpers provided with
Catalyst::Model::Adaptor
:
script/myapp_create.pl model SomeClass <type> MyApp::Model::SomeClass
Where <type> can be Adaptor
, Factory
or Factory::PerRequest
.
ACKNOWLEDGEMENTS
Jonathan Rockway, for writing Catalyst::Model::Adaptor
.
AUTHOR
edenc - Eden Cardim - edencardim@gmail.com
- Previous
- Next