1 package Catalyst::Model::DBIC::Schema;
5 extends 'Catalyst::Model';
9 use Carp::Clan '^Catalyst::Model::DBIC::Schema';
12 use Scalar::Util 'reftype';
13 use MooseX::ClassAttribute;
17 use Catalyst::Model::DBIC::Schema::Types
18 qw/ConnectInfo SchemaClass CursorClass/;
20 use MooseX::Types::Moose qw/ArrayRef Str ClassName Undef/;
22 use namespace::clean -except => 'meta';
26 Catalyst::Model::DBIC::Schema - DBIx::Class::Schema Model Class
30 Manual creation of a DBIx::Class::Schema and a Catalyst::Model::DBIC::Schema:
36 Create the DBIx:Class schema in MyApp/Schema/FilmDB.pm:
38 package MyApp::Schema::FilmDB;
39 use base qw/DBIx::Class::Schema/;
41 __PACKAGE__->load_classes(qw/Actor Role/);
45 Create some classes for the tables in the database, for example an
46 Actor in MyApp/Schema/FilmDB/Actor.pm:
48 package MyApp::Schema::FilmDB::Actor;
49 use base qw/DBIx::Class/
51 __PACKAGE__->load_components(qw/Core/);
52 __PACKAGE__->table('actor');
56 and a Role in MyApp/Schema/FilmDB/Role.pm:
58 package MyApp::Schema::FilmDB::Role;
59 use base qw/DBIx::Class/
61 __PACKAGE__->load_components(qw/Core/);
62 __PACKAGE__->table('role');
66 Notice that the schema is in MyApp::Schema, not in MyApp::Model. This way it's
67 usable as a standalone module and you can test/run it without Catalyst.
71 To expose it to Catalyst as a model, you should create a DBIC Model in
72 MyApp/Model/FilmDB.pm:
74 package MyApp::Model::FilmDB;
75 use base qw/Catalyst::Model::DBIC::Schema/;
78 schema_class => 'MyApp::Schema::FilmDB',
82 password => "password",
86 See below for a full list of the possible config parameters.
90 Now you have a working Model which accesses your separate DBIC Schema. This can
91 be used/accessed in the normal Catalyst manner, via $c->model():
93 my $actor = $c->model('FilmDB::Actor')->find(1);
95 You can also use it to set up DBIC authentication with
96 Authentication::Store::DBIC in MyApp.pm:
100 use Catalyst qw/... Authentication::Store::DBIC/;
104 __PACKAGE__->config->{authentication}{dbic} = {
105 user_class => 'FilmDB::Actor',
106 user_field => 'name',
107 password_field => 'password'
110 C<< $c->model('Schema::Source') >> returns a L<DBIx::Class::ResultSet> for
111 the source name parameter passed. To find out more about which methods can
112 be called on a ResultSet, or how to add your own methods to it, please see
113 the ResultSet documentation in the L<DBIx::Class> distribution.
115 Some examples are given below:
117 # to access schema methods directly:
118 $c->model('FilmDB')->schema->source(...);
120 # to access the source object, resultset, and class:
121 $c->model('FilmDB')->source(...);
122 $c->model('FilmDB')->resultset(...);
123 $c->model('FilmDB')->class(...);
125 # For resultsets, there's an even quicker shortcut:
126 $c->model('FilmDB::Actor')
127 # is the same as $c->model('FilmDB')->resultset('Actor')
129 # To get the composed schema for making new connections:
130 my $newconn = $c->model('FilmDB')->composed_schema->connect(...);
132 # Or the same thing via a convenience shortcut:
133 my $newconn = $c->model('FilmDB')->connect(...);
135 # or, if your schema works on different storage drivers:
136 my $newconn = $c->model('FilmDB')->composed_schema->clone();
137 $newconn->storage_type('::LDAP');
138 $newconn->connection(...);
140 # and again, a convenience shortcut
141 my $newconn = $c->model('FilmDB')->clone();
142 $newconn->storage_type('::LDAP');
143 $newconn->connection(...);
147 This is a Catalyst Model for L<DBIx::Class::Schema>-based Models. See
148 the documentation for L<Catalyst::Helper::Model::DBIC::Schema> for
149 information on generating these Models via Helper scripts.
151 When your Catalyst app starts up, a thin Model layer is created as an
152 interface to your DBIC Schema. It should be clearly noted that the model
153 object returned by C<< $c->model('FilmDB') >> is NOT itself a DBIC schema or
154 resultset object, but merely a wrapper proving L<methods|/METHODS> to access
155 the underlying schema.
157 In addition to this model class, a shortcut class is generated for each
158 source in the schema, allowing easy and direct access to a resultset of the
159 corresponding type. These generated classes are even thinner than the model
160 class, providing no public methods but simply hooking into Catalyst's
161 model() accessor via the
162 L<ACCEPT_CONTEXT|Catalyst::Component/ACCEPT_CONTEXT> mechanism. The complete
163 contents of each generated class is roughly equivalent to the following:
165 package MyApp::Model::FilmDB::Actor
168 $c->model('FilmDB')->resultset('Actor');
171 In short, there are three techniques available for obtaining a DBIC
175 my $rs = $c->model('FilmDB')->schema->resultset('Actor');
177 # using the shortcut method on the model object
178 my $rs = $c->model('FilmDB')->resultset('Actor');
180 # using the generated class directly
181 my $rs = $c->model('FilmDB::Actor');
183 In order to add methods to a DBIC resultset, you cannot simply add them to
184 the source (row, table) definition class; you must define a separate custom
185 resultset class. See L<DBIx::Class::Manual::Cookbook/"Predefined searches">
188 =head1 CONFIG PARAMETERS
192 This is the classname of your L<DBIx::Class::Schema> Schema. It needs
193 to be findable in C<@INC>, but it does not need to be inside the
194 C<Catalyst::Model::> namespace. This parameter is required.
198 This is an arrayref of connection parameters, which are specific to your
199 C<storage_type> (see your storage type documentation for more details).
200 If you only need one parameter (e.g. the DSN), you can just pass a string
201 instead of an arrayref.
203 This is not required if C<schema_class> already has connection information
204 defined inside itself (which isn't highly recommended, but can be done)
206 For L<DBIx::Class::Storage::DBI>, which is the only supported
207 C<storage_type> in L<DBIx::Class> at the time of this writing, the
208 parameters are your dsn, username, password, and connect options hashref.
210 See L<DBIx::Class::Storage::DBI/connect_info> for a detailed explanation
211 of the arguments supported.
216 dsn => 'dbi:Pg:dbname=mypgdb',
222 dsn => 'dbi:SQLite:dbname=foo.db',
224 'PRAGMA synchronous = OFF',
229 dsn => 'dbi:Pg:dbname=mypgdb',
234 'some SQL statement',
235 'another SQL statement',
239 Or using L<Config::General>:
242 schema_class MyApp::Schema::FilmDB
245 dsn dbi:Pg:dbname=mypgdb
250 on_connect_do some SQL statement
251 on_connect_do another SQL statement
258 schema_class MyApp::Schema::FilmDB
259 connect_info dbi:SQLite:dbname=foo.db
272 on_connect_do: [ "alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS'" ]
273 cursor_class: 'DBIx::Class::Cursor::Cached'
276 The old arrayref style with hashrefs for L<DBI> then L<DBIx::Class> options is also
280 'dbi:Pg:dbname=mypgdb',
289 'some SQL statement',
290 'another SQL statement',
297 Array of Traits to apply to the instance. Traits are L<Moose::Role>s.
299 They are relative to the C<< MyApp::Model::DB::Trait:: >>, then the C<<
300 Catalyst::Model::DBIC::Schema::Trait:: >> namespaces, unless prefixed with C<+>
301 in which case they are taken to be a fully qualified name. E.g.:
304 traits +MyApp::DB::Trait::Foo
306 A new instance is created at application time, so any consumed required
307 attributes, coercions and modifiers will work.
309 Traits are applied before setup, schema and connection are set.
311 C<ref $self> will be an anon class if any traits are applied, C<<
312 $self->_original_class_name >> will be the original class.
314 You cannot modify C<BUILD> in a trait, as that is when traits are applied,
315 modify L</setup> instead.
317 Traits that come with the distribution:
321 =item L<Catalyst::Model::DBIC::Schema::Trait::Caching>
323 =item L<Catalyst::Model::DBIC::Schema::Trait::Replicated>
329 Allows the use of a different C<storage_type> than what is set in your
330 C<schema_class> (which in turn defaults to C<::DBI> if not set in current
331 L<DBIx::Class>). Completely optional, and probably unnecessary for most
332 people until other storage backends become available for L<DBIx::Class>.
338 Instantiates the Model based on the above-documented ->config parameters.
339 The only required parameter is C<schema_class>. C<connect_info> is
340 required in the case that C<schema_class> does not already have connection
341 information defined for it.
345 Accessor which returns the connected schema being used by the this model.
346 There are direct shortcuts on the model class itself for
347 schema->resultset, schema->source, and schema->class.
349 =head2 composed_schema
351 Accessor which returns the composed schema, which has no connection info,
352 which was used in constructing the C<schema> above. Useful for creating
353 new connections based on the same schema/model. There are direct shortcuts
354 from the model object for composed_schema->clone and composed_schema->connect
358 Shortcut for ->composed_schema->clone
362 Shortcut for ->composed_schema->connect
366 Shortcut for ->schema->source
370 Shortcut for ->schema->class
374 Shortcut for ->schema->resultset
378 Provides an accessor for the connected schema's storage object.
379 Used often for debugging and controlling transactions.
383 has schema => (is => 'rw', isa => 'DBIx::Class::Schema');
385 has schema_class => (
392 has storage_type => (is => 'rw', isa => Str);
394 has connect_info => (is => 'ro', isa => ConnectInfo, coerce => 1);
396 has model_name => (is => 'ro', isa => Str, default => sub {
399 my $class = ref $self;
400 (my $model_name = $class) =~ s/^[\w:]+::(?:Model|M):://;
405 has traits => (is => 'ro', isa => ArrayRef|Str);
407 has _trait_fqns => (is => 'ro', isa => ArrayRef|Undef, lazy_build => 1);
409 has _default_cursor_class => (
412 default => 'DBIx::Class::Storage::DBI::Cursor',
416 has _original_class_name => (
420 default => sub { blessed $_[0] },
425 my $class = ref $self;
426 my $schema_class = $self->schema_class;
428 if( !$self->connect_info ) {
429 if($schema_class->storage && $schema_class->storage->connect_info) {
430 $self->connect_info($schema_class->storage->connect_info);
433 die "Either ->config->{connect_info} must be defined for $class"
434 . " or $schema_class must have connect info defined on it."
435 . " Here's what we got:\n"
440 if (exists $self->connect_info->{cursor_class}) {
441 eval { Class::MOP::load_class($self->connect_info->{cursor_class}) }
442 or croak "invalid connect_info: Cannot load your cursor_class"
443 . " ".$self->connect_info->{cursor_class}.": $@";
446 Moose::Util::apply_all_roles($self, $self->_trait_fqns->flatten)
447 if $self->_trait_fqns;
451 $self->composed_schema($schema_class->compose_namespace($class));
453 $self->schema($self->composed_schema->clone);
455 $self->schema->storage_type($self->storage_type)
456 if $self->storage_type;
458 $self->schema->connection($self->connect_info);
460 $self->_install_rs_models;
465 sub clone { shift->composed_schema->clone(@_); }
467 sub connect { shift->composed_schema->connect(@_); }
469 sub storage { shift->schema->storage(@_); }
471 sub resultset { shift->schema->resultset(@_); }
475 Called at C<BUILD>> time before configuration.
483 Called at the end of C<BUILD> after everything has been configured.
489 =head2 ACCEPT_CONTEXT
491 Point of extension for doing things at C<< $c->model >> time, returns the model
492 instance, see L<Catalyst::Manual::Intro> for more information.
496 sub ACCEPT_CONTEXT { shift }
498 sub _install_rs_models {
500 my $class = $self->_original_class_name;
504 my @sources = $self->schema->sources;
506 die "No sources found (did you forget to define your tables?)"
509 foreach my $moniker (@sources) {
510 my $classname = "${class}::$moniker";
511 *{"${classname}::ACCEPT_CONTEXT"} = sub {
513 shift->model($self->model_name)->resultset($moniker);
518 sub _reset_cursor_class {
521 if ($self->storage->can('cursor_class')) {
522 $self->storage->cursor_class($self->_default_cursor_class)
523 if $self->storage->cursor_class ne $self->_default_cursor_class;
530 sub composed_schema {
532 my $class = $self->_original_class_name;
533 my $store = \$COMPOSED_CACHE{$class}{$self->schema_class};
535 $$store = shift if @_;
541 sub _build__trait_fqns {
543 my $class = $self->_original_class_name;
546 my @names = $self->traits->flatten if $self->traits;
547 return unless @names;
549 my @search_ns = grep !/^(?:Moose|Class::MOP)::/,
550 $class->meta->class_precedence_list;
554 OUTER: for my $name (@names) {
555 if ($name =~ /^\+(.*)/) {
559 for my $ns (@search_ns) {
560 my $full = "${ns}::${base}::${name}";
561 if (eval { Class::MOP::load_class($full) }) {
568 return @traits ? \@traits : undef;
571 __PACKAGE__->meta->make_immutable;
575 General Catalyst Stuff:
577 L<Catalyst::Manual>, L<Catalyst::Test>, L<Catalyst::Request>,
578 L<Catalyst::Response>, L<Catalyst::Helper>, L<Catalyst>,
580 Stuff related to DBIC and this Model style:
582 L<DBIx::Class>, L<DBIx::Class::Schema>,
583 L<DBIx::Class::Schema::Loader>, L<Catalyst::Helper::Model::DBIC::Schema>,
584 L<MooseX::Object::Pluggable>
588 L<Catalyst::Model::DBIC::Schema::Trait::Caching>,
589 L<Catalyst::Model::DBIC::Schema::Trait::Replicated>
593 Brandon L Black, C<blblack@gmail.com>
597 Rafael Kitover, C<rkitover at cpan.org>
601 This program is free software, you can redistribute it and/or modify it
602 under the same terms as Perl itself.