1 package Catalyst::Model::DBIC::Schema;
6 extends 'Catalyst::Model';
10 use Carp::Clan '^Catalyst::Model::DBIC::Schema';
15 use Catalyst::Model::DBIC::Schema::Types
16 qw/ConnectInfo SchemaClass CursorClass/;
18 use MooseX::Types::Moose qw/ArrayRef Str ClassName Undef/;
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
248 on_connect_do some SQL statement
249 on_connect_do another SQL statement
256 schema_class MyApp::Schema::FilmDB
257 connect_info dbi:SQLite:dbname=foo.db
270 on_connect_do: [ "alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS'" ]
271 cursor_class: 'DBIx::Class::Cursor::Cached'
274 The old arrayref style with hashrefs for L<DBI> then L<DBIx::Class> options is also
278 'dbi:Pg:dbname=mypgdb',
287 'some SQL statement',
288 'another SQL statement',
295 Array of Traits to apply to the instance. Traits are L<Moose::Role>s.
297 They are relative to the C<< MyApp::Model::DB::Trait:: >>, then the C<<
298 Catalyst::Model::DBIC::Schema::Trait:: >> namespaces, unless prefixed with C<+>
299 in which case they are taken to be a fully qualified name. E.g.:
302 traits +MyApp::DB::Trait::Foo
304 A new instance is created at application time, so any consumed required
305 attributes, coercions and modifiers will work.
307 Traits are applied at L<Catalyst::Component/COMPONENT> time using L<MooseX::Traits>.
309 C<ref $self> will be an anon class if any traits are applied, C<<
310 $self->_original_class_name >> will be the original class.
312 When writing a Trait, interesting points to modify are C<BUILD>, L</setup> and
315 Traits that come with the distribution:
319 =item L<Catalyst::Model::DBIC::Schema::Trait::Caching>
321 =item L<Catalyst::Model::DBIC::Schema::Trait::Replicated>
327 Allows the use of a different C<storage_type> than what is set in your
328 C<schema_class> (which in turn defaults to C<::DBI> if not set in current
329 L<DBIx::Class>). Completely optional, and probably unnecessary for most
330 people until other storage backends become available for L<DBIx::Class>.
334 The keys you pass in the model configuration are available as attributes.
336 Other attributes available:
340 Your connect_info args normalized to hashref form (with dsn/user/password.) See
341 L<DBIx::Class::Storage::DBI/connect_info> for more info on the hashref form of
346 The model name L<Catalyst> uses to resolve this model, the part after
347 C<::Model::> or C<::M::> in your class name. E.g. if your class name is
348 C<MyApp::Model::DB> the L</model_name> will be C<DB>.
350 =head2 _original_class_name
352 The class name of your model before any L</traits> are applied. E.g.
355 =head2 _default_cursor_class
357 What to rest your L<DBIx::Class::Storage::DBI/cursor_class> if a custom one
358 doesn't work out. Defaults to L<DBIx::Class::Storage::DBI::Cursor>.
362 Unresolved arrayref of traits passed at C<COMPONENT> time.
364 =head2 _resolved_traits
366 Traits you used resolved to full class names.
372 Instantiates the Model based on the above-documented ->config parameters.
373 The only required parameter is C<schema_class>. C<connect_info> is
374 required in the case that C<schema_class> does not already have connection
375 information defined for it.
379 Accessor which returns the connected schema being used by the this model.
380 There are direct shortcuts on the model class itself for
381 schema->resultset, schema->source, and schema->class.
383 =head2 composed_schema
385 Accessor which returns the composed schema, which has no connection info,
386 which was used in constructing the C<schema> above. Useful for creating
387 new connections based on the same schema/model. There are direct shortcuts
388 from the model object for composed_schema->clone and composed_schema->connect
392 Shortcut for ->composed_schema->clone
396 Shortcut for ->composed_schema->connect
400 Shortcut for ->schema->source
404 Shortcut for ->schema->class
408 Shortcut for ->schema->resultset
412 Provides an accessor for the connected schema's storage object.
413 Used often for debugging and controlling transactions.
417 has schema => (is => 'rw', isa => 'DBIx::Class::Schema');
419 has schema_class => (
426 has storage_type => (is => 'rw', isa => Str);
428 has connect_info => (is => 'ro', isa => ConnectInfo, coerce => 1);
437 has _traits => (is => 'ro', isa => ArrayRef);
438 has _resolved_traits => (is => 'ro', isa => ArrayRef);
440 has _default_cursor_class => (
443 default => 'DBIx::Class::Storage::DBI::Cursor',
447 has _original_class_name => (
451 default => sub { blessed $_[0] },
455 my ($class, $app, $args) = @_;
457 $args = $class->merge_config_hashes($class->config, $args);
459 if (my $traits = delete $args->{traits}) {
460 my @traits = $class->_resolve_traits($traits->flatten);
461 return $class->new_with_traits(
463 _original_class_name => $class,
465 _resolved_traits => \@traits,
470 return $class->new(%$args);
473 # we override Catalyst::Component::BUILDARGS, which merges configs, because we
474 # merge configs ourselves in COMPONENT. We also don't pass $app to ->new, so
475 # Moose::Object::BUILDARGS works perfectly.
477 goto &Moose::Object::BUILDARGS;
482 my $class = ref $self;
483 my $schema_class = $self->schema_class;
485 if( !$self->connect_info ) {
486 if($schema_class->storage && $schema_class->storage->connect_info) {
487 $self->connect_info($schema_class->storage->connect_info);
490 die "Either ->config->{connect_info} must be defined for $class"
491 . " or $schema_class must have connect info defined on it."
492 . " Here's what we got:\n"
497 if (exists $self->connect_info->{cursor_class}) {
498 eval { Class::MOP::load_class($self->connect_info->{cursor_class}) }
499 or croak "invalid connect_info: Cannot load your cursor_class"
500 . " ".$self->connect_info->{cursor_class}.": $@";
505 $self->composed_schema($schema_class->compose_namespace($class));
507 $self->schema($self->composed_schema->clone);
509 $self->schema->storage_type($self->storage_type)
510 if $self->storage_type;
512 $self->schema->connection($self->connect_info);
514 $self->_install_rs_models;
517 sub clone { shift->composed_schema->clone(@_); }
519 sub connect { shift->composed_schema->connect(@_); }
521 sub storage { shift->schema->storage(@_); }
523 sub resultset { shift->schema->resultset(@_); }
527 Called at C<BUILD>> time before configuration, but after L</connect_info> is
528 set. To do something after configuuration use C<< after BUILD => >>.
534 =head2 ACCEPT_CONTEXT
536 Point of extension for doing things at C<< $c->model >> time, returns the model
537 instance, see L<Catalyst::Manual::Intro> for more information.
541 sub ACCEPT_CONTEXT { shift }
543 sub _install_rs_models {
545 my $class = $self->_original_class_name;
549 my @sources = $self->schema->sources;
551 die "No sources found (did you forget to define your tables?)"
554 foreach my $moniker (@sources) {
555 my $classname = "${class}::$moniker";
556 *{"${classname}::ACCEPT_CONTEXT"} = sub {
558 shift->model($self->model_name)->resultset($moniker);
563 sub _reset_cursor_class {
566 if ($self->storage->can('cursor_class')) {
567 $self->storage->cursor_class($self->_default_cursor_class)
568 if $self->storage->cursor_class ne $self->_default_cursor_class;
575 sub composed_schema {
577 my $class = $self->_original_class_name;
578 my $store = \$COMPOSED_CACHE{$class}{$self->schema_class};
580 $$store = shift if @_;
586 sub _resolve_traits {
587 my ($class, @names) = @_;
590 my @search_ns = grep !/^(?:Moose|Class::MOP)::/,
591 $class->meta->class_precedence_list;
595 OUTER: for my $name (@names) {
596 if ($name =~ /^\+(.*)/) {
600 for my $ns (@search_ns) {
601 my $full = "${ns}::${base}::${name}";
602 if (eval { Class::MOP::load_class($full) }) {
612 sub _build_model_name {
614 my $class = $self->_original_class_name;
615 (my $model_name = $class) =~ s/^[\w:]+::(?:Model|M):://;
620 __PACKAGE__->meta->make_immutable;
624 General Catalyst Stuff:
626 L<Catalyst::Manual>, L<Catalyst::Test>, L<Catalyst::Request>,
627 L<Catalyst::Response>, L<Catalyst::Helper>, L<Catalyst>,
629 Stuff related to DBIC and this Model style:
631 L<DBIx::Class>, L<DBIx::Class::Schema>,
632 L<DBIx::Class::Schema::Loader>, L<Catalyst::Helper::Model::DBIC::Schema>,
633 L<MooseX::Object::Pluggable>
637 L<Catalyst::Model::DBIC::Schema::Trait::Caching>,
638 L<Catalyst::Model::DBIC::Schema::Trait::Replicated>
642 Brandon L Black, C<blblack at gmail.com>
646 Rafael Kitover, C<rkitover at cpan.org>
650 This program is free software, you can redistribute it and/or modify it
651 under the same terms as Perl itself.