1 package Catalyst::Model::DBIC::Schema;
5 extends 'Catalyst::Model';
6 with 'MooseX::Object::Pluggable';
10 use Carp::Clan '^Catalyst::Model::DBIC::Schema';
13 use Scalar::Util 'reftype';
14 use MooseX::ClassAttribute;
17 use Catalyst::Model::DBIC::Schema::Types qw/ConnectInfo SchemaClass/;
18 use MooseX::Types::Moose qw/ArrayRef Str ClassName/;
20 use namespace::clean -except => 'meta';
24 Catalyst::Model::DBIC::Schema - DBIx::Class::Schema Model Class
28 Manual creation of a DBIx::Class::Schema and a Catalyst::Model::DBIC::Schema:
34 Create the DBIx:Class schema in MyApp/Schema/FilmDB.pm:
36 package MyApp::Schema::FilmDB;
37 use base qw/DBIx::Class::Schema/;
39 __PACKAGE__->load_classes(qw/Actor Role/);
43 Create some classes for the tables in the database, for example an
44 Actor in MyApp/Schema/FilmDB/Actor.pm:
46 package MyApp::Schema::FilmDB::Actor;
47 use base qw/DBIx::Class/
49 __PACKAGE__->load_components(qw/Core/);
50 __PACKAGE__->table('actor');
54 and a Role in MyApp/Schema/FilmDB/Role.pm:
56 package MyApp::Schema::FilmDB::Role;
57 use base qw/DBIx::Class/
59 __PACKAGE__->load_components(qw/Core/);
60 __PACKAGE__->table('role');
64 Notice that the schema is in MyApp::Schema, not in MyApp::Model. This way it's
65 usable as a standalone module and you can test/run it without Catalyst.
69 To expose it to Catalyst as a model, you should create a DBIC Model in
70 MyApp/Model/FilmDB.pm:
72 package MyApp::Model::FilmDB;
73 use base qw/Catalyst::Model::DBIC::Schema/;
76 schema_class => 'MyApp::Schema::FilmDB',
80 password => "password",
84 See below for a full list of the possible config parameters.
88 Now you have a working Model which accesses your separate DBIC Schema. This can
89 be used/accessed in the normal Catalyst manner, via $c->model():
91 my $actor = $c->model('FilmDB::Actor')->find(1);
93 You can also use it to set up DBIC authentication with
94 Authentication::Store::DBIC in MyApp.pm:
98 use Catalyst qw/... Authentication::Store::DBIC/;
102 __PACKAGE__->config->{authentication}{dbic} = {
103 user_class => 'FilmDB::Actor',
104 user_field => 'name',
105 password_field => 'password'
108 C<< $c->model('Schema::Source') >> returns a L<DBIx::Class::ResultSet> for
109 the source name parameter passed. To find out more about which methods can
110 be called on a ResultSet, or how to add your own methods to it, please see
111 the ResultSet documentation in the L<DBIx::Class> distribution.
113 Some examples are given below:
115 # to access schema methods directly:
116 $c->model('FilmDB')->schema->source(...);
118 # to access the source object, resultset, and class:
119 $c->model('FilmDB')->source(...);
120 $c->model('FilmDB')->resultset(...);
121 $c->model('FilmDB')->class(...);
123 # For resultsets, there's an even quicker shortcut:
124 $c->model('FilmDB::Actor')
125 # is the same as $c->model('FilmDB')->resultset('Actor')
127 # To get the composed schema for making new connections:
128 my $newconn = $c->model('FilmDB')->composed_schema->connect(...);
130 # Or the same thing via a convenience shortcut:
131 my $newconn = $c->model('FilmDB')->connect(...);
133 # or, if your schema works on different storage drivers:
134 my $newconn = $c->model('FilmDB')->composed_schema->clone();
135 $newconn->storage_type('::LDAP');
136 $newconn->connection(...);
138 # and again, a convenience shortcut
139 my $newconn = $c->model('FilmDB')->clone();
140 $newconn->storage_type('::LDAP');
141 $newconn->connection(...);
145 This is a Catalyst Model for L<DBIx::Class::Schema>-based Models. See
146 the documentation for L<Catalyst::Helper::Model::DBIC::Schema> for
147 information on generating these Models via Helper scripts.
149 When your Catalyst app starts up, a thin Model layer is created as an
150 interface to your DBIC Schema. It should be clearly noted that the model
151 object returned by C<< $c->model('FilmDB') >> is NOT itself a DBIC schema or
152 resultset object, but merely a wrapper proving L<methods|/METHODS> to access
153 the underlying schema.
155 In addition to this model class, a shortcut class is generated for each
156 source in the schema, allowing easy and direct access to a resultset of the
157 corresponding type. These generated classes are even thinner than the model
158 class, providing no public methods but simply hooking into Catalyst's
159 model() accessor via the
160 L<ACCEPT_CONTEXT|Catalyst::Component/ACCEPT_CONTEXT> mechanism. The complete
161 contents of each generated class is roughly equivalent to the following:
163 package MyApp::Model::FilmDB::Actor
166 $c->model('FilmDB')->resultset('Actor');
169 In short, there are three techniques available for obtaining a DBIC
173 my $rs = $c->model('FilmDB')->schema->resultset('Actor');
175 # using the shortcut method on the model object
176 my $rs = $c->model('FilmDB')->resultset('Actor');
178 # using the generated class directly
179 my $rs = $c->model('FilmDB::Actor');
181 In order to add methods to a DBIC resultset, you cannot simply add them to
182 the source (row, table) definition class; you must define a separate custom
183 resultset class. See L<DBIx::Class::Manual::Cookbook/"Predefined searches">
186 =head1 CONFIG PARAMETERS
190 This is the classname of your L<DBIx::Class::Schema> Schema. It needs
191 to be findable in C<@INC>, but it does not need to be inside the
192 C<Catalyst::Model::> namespace. This parameter is required.
196 This is an arrayref of connection parameters, which are specific to your
197 C<storage_type> (see your storage type documentation for more details).
198 If you only need one parameter (e.g. the DSN), you can just pass a string
199 instead of an arrayref.
201 This is not required if C<schema_class> already has connection information
202 defined inside itself (which isn't highly recommended, but can be done)
204 For L<DBIx::Class::Storage::DBI>, which is the only supported
205 C<storage_type> in L<DBIx::Class> at the time of this writing, the
206 parameters are your dsn, username, password, and connect options hashref.
208 See L<DBIx::Class::Storage::DBI/connect_info> for a detailed explanation
209 of the arguments supported.
214 dsn => 'dbi:Pg:dbname=mypgdb',
220 dsn => 'dbi:SQLite:dbname=foo.db',
222 'PRAGMA synchronous = OFF',
227 dsn => 'dbi:Pg:dbname=mypgdb',
232 'some SQL statement',
233 'another SQL statement',
237 Or using L<Config::General>:
240 schema_class MyApp::Schema::FilmDB
243 dsn dbi:Pg:dbname=mypgdb
247 on_connect_do some SQL statement
248 on_connect_do another SQL statement
255 schema_class MyApp::Schema::FilmDB
256 connect_info dbi:SQLite:dbname=foo.db
269 on_connect_do: [ "alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS'" ]
270 cursor_class: 'DBIx::Class::Cursor::Cached'
272 The old arrayref style with hashrefs for L<DBI> then L<DBIx::Class> options is also
276 'dbi:Pg:dbname=mypgdb',
285 'some SQL statement',
286 'another SQL statement',
293 Array of Roles to apply at BUILD time. Roles are relative to the
294 C<<MyApp::Model::DB::Role::> then C<<Catalyst::Model::DBIC::Schema::Role::>>
295 namespaces, unless prefixed with C<+> in which case they are taken to be a
296 fully qualified name. E.g.:
299 roles +MyApp::DB::Role::Foo
301 This is done using L<MooseX::Object::Pluggable>.
303 A new instance is created at application time, so any consumed required
304 attributes, coercions and modifiers will work.
306 Roles are applied before setup, schema and connection are set.
308 C<ref $self> will be an anon class if any roles are applied.
310 You cannot modify C<new> or C<BUILD>, modify C<setup> instead.
312 L</ACCEPT_CONTEXT> and L</finalize> can also be modified.
314 Roles that come with the distribution:
318 =item L<Catalyst::Model::DBIC::Schema::Role::Caching>
320 =item L<Catalyst::Model::DBIC::Schema::Role::Replicated>
326 Allows the use of a different C<storage_type> than what is set in your
327 C<schema_class> (which in turn defaults to C<::DBI> if not set in current
328 L<DBIx::Class>). Completely optional, and probably unnecessary for most
329 people until other storage backends become available for L<DBIx::Class>.
335 Instantiates the Model based on the above-documented ->config parameters.
336 The only required parameter is C<schema_class>. C<connect_info> is
337 required in the case that C<schema_class> does not already have connection
338 information defined for it.
342 Accessor which returns the connected schema being used by the this model.
343 There are direct shortcuts on the model class itself for
344 schema->resultset, schema->source, and schema->class.
346 =head2 composed_schema
348 Accessor which returns the composed schema, which has no connection info,
349 which was used in constructing the C<schema> above. Useful for creating
350 new connections based on the same schema/model. There are direct shortcuts
351 from the model object for composed_schema->clone and composed_schema->connect
355 Shortcut for ->composed_schema->clone
359 Shortcut for ->composed_schema->connect
363 Shortcut for ->schema->source
367 Shortcut for ->schema->class
371 Shortcut for ->schema->resultset
375 Provides an accessor for the connected schema's storage object.
376 Used often for debugging and controlling transactions.
380 class_has 'composed_schema' => (is => 'rw', isa => 'DBIx::Class::Schema');
382 has 'schema' => (is => 'rw', isa => 'DBIx::Class::Schema');
384 has 'schema_class' => (
391 has 'storage_type' => (is => 'rw', isa => Str);
393 has 'connect_info' => (is => 'ro', isa => ConnectInfo, coerce => 1);
395 # ref $self changes to anon after roles are applied, and _original_class_name is
396 # broken in MX::O::P 0.0009
397 has '_class_name' => (is => 'ro', isa => ClassName, default => sub {
401 has 'model_name' => (is => 'ro', isa => Str, default => sub {
404 my $class = ref $self;
405 (my $model_name = $class) =~ s/^[\w:]+::(?:Model|M):://;
410 has 'roles' => (is => 'ro', isa => ArrayRef|Str);
414 my $class = ref $self;
415 my $schema_class = $self->schema_class;
417 if( !$self->connect_info ) {
418 if($schema_class->storage && $schema_class->storage->connect_info) {
419 $self->connect_info($schema_class->storage->connect_info);
422 die "Either ->config->{connect_info} must be defined for $class"
423 . " or $schema_class must have connect info defined on it."
424 . " Here's what we got:\n"
429 if (exists $self->connect_info->{cursor_class}) {
430 eval { Class::MOP::load_class($self->connect_info->{cursor_class}) }
431 or croak "invalid connect_info: Cannot load your cursor_class"
432 . " ".$self->connect_info->{cursor_class}.": $@";
435 $self->_plugin_ns('Role');
437 $self->load_plugins($self->roles->flatten) if $self->roles;
441 $self->composed_schema($schema_class->compose_namespace($class));
443 $self->schema($self->composed_schema->clone);
445 $self->schema->storage_type($self->storage_type)
446 if $self->storage_type;
448 $self->schema->connection($self->connect_info);
450 $self->_install_rs_models;
455 sub clone { shift->composed_schema->clone(@_); }
457 sub connect { shift->composed_schema->connect(@_); }
459 sub storage { shift->schema->storage(@_); }
463 Called at C<<BUILD>> time before configuration.
471 Called at the end of C<BUILD> after everything has been configured.
477 =head2 ACCEPT_CONTEXT
479 Point of extension for doing things at C<<$c->model>> time, returns the model
480 instance, see L<Catalyst::Manual::Intro> for more information.
484 sub ACCEPT_CONTEXT { shift }
486 sub _install_rs_models {
488 my $class = $self->_class_name;
492 my @sources = $self->schema->sources;
494 die "No sources found (did you forget to define your tables?)"
497 foreach my $moniker (@sources) {
498 my $classname = "${class}::$moniker";
499 *{"${classname}::ACCEPT_CONTEXT"} = sub {
501 shift->model($self->model_name)->resultset($moniker);
506 __PACKAGE__->meta->make_immutable;
510 General Catalyst Stuff:
512 L<Catalyst::Manual>, L<Catalyst::Test>, L<Catalyst::Request>,
513 L<Catalyst::Response>, L<Catalyst::Helper>, L<Catalyst>,
515 Stuff related to DBIC and this Model style:
517 L<DBIx::Class>, L<DBIx::Class::Schema>,
518 L<DBIx::Class::Schema::Loader>, L<Catalyst::Helper::Model::DBIC::Schema>,
519 L<MooseX::Object::Pluggable>
523 L<Catalyst::Model::DBIC::Schema::Role::Caching>,
524 L<Catalyst::Model::DBIC::Schema::Role::Replicated>
528 Brandon L Black, C<blblack@gmail.com>
532 Rafael Kitover, C<<rkitover at cpan.org>>
536 This program is free software, you can redistribute it and/or modify it
537 under the same terms as Perl itself.