[Catalyst] Mapping Schema to Model in Catalyst?

John Napiorkowski jjn1056 at yahoo.com
Sun May 14 04:43:24 CEST 2006


Dennis,

I think your problem is how you setup the Schema. 
Based on your code example I think if you replaced the
following:

> $c->stash->{quotes} =
> [$c->model('CatapultDB::Quotes')->find($id)];

with 

> $c->stash->{quotes} =
> [$c->model('Quotes')->find($id)];

I think it would work.  The reason why may not be so
clear.  I also struggled with this at first. 
Basically the emerging best practice is to create your
DBIx schemas in a "Schema" directory rather that
directly in the Model directory, since that way you
can use it for things other than Catalyst.  Let me
show you what I did and I hope it helps.

First I created a directory called Schema at the same
level as Controller, Model, View.  I choose to make my
DBIx classes 'by hand' rather than use the table
loader, since I am learning and I felt this was the
best way to learn.  Inside that directory I created a
file called 'db.pm' which looks like this:

package myApp::Schema::db;

use warnings;
use strict;

use base qw/DBIx::Class::Schema/;

my $dsn      = '';
my $user     = '';
my $password = '';
	
my $options	= { 

	RaiseError => 1, 
	PrintError => 0, 
	ShowErrorStatement => 1, 
	TraceLevel => 0,

	};
	
__PACKAGE__->connection( $dsn, $user, $password,
$options);

__PACKAGE__->load_classes();

I also added some stuff to auto inflate my date
columns to DateTime objects, but I'll leave that out
for now.

I choose to put my connection info here since as I
said that way DBIx is available to other things, like
my cron jobs, etc.

In the same folder I created a file called "base.pm"
which will serve as a base class to each of my
handwritten DBIx classes.  This has also seemed to
become a best pratices method, since that way you can
put all your commonly use plugins and so forth in one
location.  Here's my base.pm file.

package myApp::Schema::base;

use warnings;
use strict;

use base qw/DBIx::Class/;

__PACKAGE__->load_components(qw/ PK::Auto HTMLWidget
UUIDColumns Core /);
__PACKAGE__->uuid_class('::Data::UUID');


I use the UUID stuff in my tables a lot, so I preload
it here, as well as the PK stuff to properly support
my serial postgresql column.  HTMLWidget is something
I am playing with, but you can safely leave it out.

Then I created classes for each of my database tables.
 Since I like to arrange my tables into schema by
function I end up creating extra directories then you
would if you just put all your tables into a single
schema.  Here's my directory structure:

Schema
  --db
    -- bookmarks
    -- members
         members.pm
         countries.pm
         etc.
    -- sessions
    -- etc.

Nothing goes into the db directory, it's just there
for Catalyst to hook later on.  Let me show you my
members class, since you mentioned you are trying to
get authentication working:

--------------------------------------------------

package myApp::Schema::db::members::members;

use warnings;
use strict;

use base qw/talentspace_portal::Schema::base/;

__PACKAGE__->table('members.members');

__PACKAGE__->add_columns(qw/

	member_id
	given_name
	family_name	
	email
	nickname
	password	
	karma 	
	note
	
	nickname
	password
	karma
	date_of_birth
/);

__PACKAGE__->set_primary_key('member_id');

--------------------------------------------------

Again I removed some of my foreign key stuff to make
it more clear.

Now I have to hook this up to Catalyst.  This is easy.
  In my Model directory I create the following file
"db.pm":

package myApp::Model::db;

use strict;
use warnings;

use base 'Catalyst::Model::DBIC::Schema';

__PACKAGE__->config(
	schema_class => 'myApp::Schema::db',
);

return 1; 

Now you can see why I created that empty 'db'
directory in the Schema tree.  This will act as a
'gateway' to my DBIx classes.

Assuming your database is setup and working properly
(no small assumption) you can assess the DBIx classes
from any controller:

my $members = $c->model("members::members");

I have to say "members::members" because the members
table is inside a schema called members.  Otherwise
think of this db.pm file almost like a link to
everything under the 'db' directory in your Schema
area.  So I could access the countries tables with
$c->model("members::countries")

When you call the model method it automatically calls
the resultset method.  if you do this in scalar
context you'll get an iterator, but if you want to get
all the rows at once (bad if the DB is large) you can
call it in array context.  If you call it in array
context it will do an implict ->all for you :)  Yes, I
also found a lot of this magic confusing but there you
go.

If you want to put this stuff into the stash so you
can use them in a template toolkit template you can do
this:

my $members = $c->model("members::members");
$c->stash->{members} = $members;

then in the TT you can:

[% WHILE (member= members.next) %]
[% member.given_name %]
[% END %]

I find this is the easiest way for me to make this
work.  I'm not sure the easiest way to directly assign
something from a DBIx model to the stash.  I think you
have to be careful here because of all the magic the
DBIx does for you.  I found the above way working,
even if it leads to me declaring more variables.  I
doubt this impact performance since these are all
interator classes.

Hope this helps and doesn't make it more confusing :)

BTW, you can skip setting the $c->stash->{template} if
you are willing to let TT use it's default of looking
for the file in the root directory under the same
directory structure as you have in Controller. 
Personally I think this should be considered best
practice since the default behavior is sane and that
way we all can save some keystrokes.

So if you have an action called "hello: Local" at the
following location "/Controller/test.pm" the default
TT  will look for a template at "/Root/test/hello".


--- dennis <ddaupert at sbcglobal.net> wrote:

> Hello,
> 
> I'm trying to figure out how to get the DBIx::Class
> schema object into my 
> controllers in Catalyst.
> 
> Basically, the documentation says:
> 1) Create a schema class
> 2) Create a table class for each table.
> 3) Then you can use these classes in your
> application's code; eg,
> 
> my $schema = DB::Main->connect($dbi_dsn, $user,
> $pass, \%dbi_params);
> And then you call methods on the schema object, and
> everything's hunkie dorey.
> 
> The example is based on a cgi application. But in
> Catalyst, the connection 
> method in all the examples I've seen is separated
> from the controller code. 
> So how do I get the schema object into my
> controllers? Use stash? FedEX? 
> Email?
> 
> I'm trying to follow Kennedy's excellent
> Authentication And Authorization 
> Tutorial, and I've gotten partway along (see code at
> bottom)
> 
> In my controller class, this works to grab a list of
> records in quotes table:
> 
>   $c->stash->{quotes} =
> [$c->model('CatapultDB::Quotes')->all];
> 
> (but I don't see how this maps to the schema stuff;
> I probably should mention 
> that I have OO dyslexia.)
> 
> I've tried to retrieve one record using similar
> code, but I'm gonna lose my 
> hair. This (one of many, many such silly attempts)
> certainly doesn't work:
> 
> $c->stash->{quotes} =
> [$c->model('CatapultDB::Quotes')->find($id)];
> 
> Then after retrieving the record, I want to edit it
> and update the db, and 
> then go on and do some other tricks, and so on and
> so on.
> 
> /dennis
> 
> -----------------------------------------------
> I have this schema class:
> -----------------------------------------------
> package Catapult::Model::CatapultDB;
> use strict;
> use base 'Catalyst::Model::DBIC::Schema';
> __PACKAGE__->config(
>     schema_class => 'CatapultDB',
>     connect_info => [
>         'dbi:Pg:dbname=catapult',
>         'catapult',
>         '',
>         { AutoCommit => 1 },        
>     ],
> );
> -----------------------------------------------
> Quotes class:
> -----------------------------------------------
> package CatapultDB::Quotes;
> use base qw/DBIx::Class/;
> # Load required DBIC stuff
> __PACKAGE__->load_components(qw/PK::Auto::Pg Core/);
> 
> # Set the table name
> __PACKAGE__->table('quotes');
> # Set columns in table
> __PACKAGE__->add_columns(qw/id quote author/);
> # Set the primary key for the table
> __PACKAGE__->set_primary_key(qw/id/);
> -----------------------------------------------
> Controller class:
> -----------------------------------------------
> package Catapult::Controller::Text::Quote;
> sub list : Path('/text/quote/list_quote') {
>   my ($self, $c) = @_;
>   $c->stash->{quotes} =
> [$c->model('CatapultDB::Quotes')->all];
>   $c->stash->{template} =
> 'text/quote/list_quote.tt';
> }
> sub edit : Path('/text/quote/edit_quote') {
>   my ( $self, $c ) = @_;
> 
> <buncha stuff>
> # make sure the record is there
> my $id = $c->request->params->{id};
> $c->stash->{quotes} =
> [$c->model('CatapultDB::Quotes')->find($id)];
> <more stuff>
> # update the record, by some mysterious means...
> }
> -----------------------------------------------
> 
> _______________________________________________
> Catalyst mailing list
> Catalyst at lists.rawmode.org
> http://lists.rawmode.org/mailman/listinfo/catalyst
> 


__________________________________________________
Do You Yahoo!?
Tired of spam?  Yahoo! Mail has the best spam protection around 
http://mail.yahoo.com 



More information about the Catalyst mailing list