1 package Catalyst::Model::DBIC::Schema;
5 no warnings 'uninitialized';
9 use parent qw/Catalyst::Model Class::Accessor::Fast Class::Data::Accessor/;
12 use UNIVERSAL::require;
16 use Scalar::Util 'reftype';
17 use namespace::clean -except => 'meta';
19 __PACKAGE__->mk_classaccessor('composed_schema');
20 __PACKAGE__->mk_accessors(qw/
21 schema connect_info schema_class storage_type caching model_name
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
194 This is the classname of your L<DBIx::Class::Schema> Schema. It needs
195 to be findable in C<@INC>, but it does not need to be inside the
196 C<Catalyst::Model::> namespace. This parameter is required.
200 This is an arrayref of connection parameters, which are specific to your
201 C<storage_type> (see your storage type documentation for more details).
202 If you only need one parameter (e.g. the DSN), you can just pass a string
203 instead of an arrayref.
205 This is not required if C<schema_class> already has connection information
206 defined inside itself (which isn't highly recommended, but can be done)
208 For L<DBIx::Class::Storage::DBI>, which is the only supported
209 C<storage_type> in L<DBIx::Class> at the time of this writing, the
210 parameters are your dsn, username, password, and connect options hashref.
212 See L<DBIx::Class::Storage::DBI/connect_info> for a detailed explanation
213 of the arguments supported.
218 dsn => 'dbi:Pg:dbname=mypgdb',
224 dsn => 'dbi:SQLite:dbname=foo.db',
226 'PRAGMA synchronous = OFF',
231 dsn => 'dbi:Pg:dbname=mypgdb',
236 'some SQL statement',
237 'another SQL statement',
241 Or using L<Config::General>:
244 schema_class MyApp::Schema::FilmDB
246 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'
275 The old arrayref style with hashrefs for L<DBI> then L<DBIx::Class> options is also
279 'dbi:Pg:dbname=mypgdb',
287 'some SQL statement',
288 'another SQL statement',
295 Whether or not to enable caching support using L<DBIx::Class::Cursor::Cached>
296 and L<Catalyst::Plugin::Cache>. Enabled by default.
298 In order for this to work, L<Catalyst::Plugin::Cache> must be configured and
299 loaded. A possible configuration would look like this:
303 class Cache::FastMmap
308 Then in your queries, set the C<cache_for> ResultSet attribute to the number of
309 seconds you want the query results to be cached for, eg.:
311 $c->model('DB::Table')->search({ foo => 'bar' }, { cache_for => 18000 });
315 Allows the use of a different C<storage_type> than what is set in your
316 C<schema_class> (which in turn defaults to C<::DBI> if not set in current
317 L<DBIx::Class>). Completely optional, and probably unnecessary for most
318 people until other storage backends become available for L<DBIx::Class>.
328 Instantiates the Model based on the above-documented ->config parameters.
329 The only required parameter is C<schema_class>. C<connect_info> is
330 required in the case that C<schema_class> does not already have connection
331 information defined for it.
335 Accessor which returns the connected schema being used by the this model.
336 There are direct shortcuts on the model class itself for
337 schema->resultset, schema->source, and schema->class.
339 =item composed_schema
341 Accessor which returns the composed schema, which has no connection info,
342 which was used in constructing the C<schema> above. Useful for creating
343 new connections based on the same schema/model. There are direct shortcuts
344 from the model object for composed_schema->clone and composed_schema->connect
348 Shortcut for ->composed_schema->clone
352 Shortcut for ->composed_schema->connect
356 Shortcut for ->schema->source
360 Shortcut for ->schema->class
364 Shortcut for ->schema->resultset
368 Provides an accessor for the connected schema's storage object.
369 Used often for debugging and controlling transactions.
374 my $self = shift->next::method(@_);
376 my $class = ref $self;
378 $self->_build_model_name;
380 croak "->config->{schema_class} must be defined for this model"
381 unless $self->schema_class;
383 my $schema_class = $self->schema_class;
385 $schema_class->require
386 or croak "Cannot load schema class '$schema_class': $@";
388 if( !$self->connect_info ) {
389 if($schema_class->storage && $schema_class->storage->connect_info) {
390 $self->connect_info($schema_class->storage->connect_info);
393 croak "Either ->config->{connect_info} must be defined for $class"
394 . " or $schema_class must have connect info defined on it."
395 . " Here's what we got:\n"
400 $self->composed_schema($schema_class->compose_namespace($class));
402 $self->schema($self->composed_schema->clone);
404 $self->schema->storage_type($self->storage_type)
405 if $self->storage_type;
407 $self->_normalize_connect_info;
409 $self->_setup_caching;
411 $self->schema->connection($self->connect_info);
413 $self->_install_rs_models;
418 sub clone { shift->composed_schema->clone(@_); }
420 sub connect { shift->composed_schema->connect(@_); }
422 sub storage { shift->schema->storage(@_); }
426 Sets up runtime cache support on $c->model invocation.
436 unless ($c->can('cache') && ref $c->cache) {
437 $c->log->debug("DBIx::Class cursor caching disabled, you don't seem to"
438 . " have a working Cache plugin.");
440 $self->_reset_cursor_class;
444 if (ref $self->schema->default_resultset_attributes) {
445 $self->schema->default_resultset_attributes->{cache_object} =
448 $self->schema->default_resultset_attributes({
449 cache_object => $c->cache
456 sub _normalize_connect_info {
459 my $connect_info = $self->connect_info;
461 my @connect_info = reftype $connect_info eq 'ARRAY' ?
462 @$connect_info : $connect_info;
466 if (!ref $connect_info[0]) { # array style
467 @connect_info{qw/dsn user password/} =
468 splice @connect_info, 0, 3;
471 my $extra = shift @connect_info;
473 croak "invalid connect_info" unless reftype $extra eq 'HASH';
475 %connect_info = (%connect_info, %$extra);
478 croak "invalid connect_info" if @connect_info;
479 } elsif (@connect_info == 1 && reftype $connect_info[0] eq 'HASH') {
480 %connect_info = %{ $connect_info[0] };
481 } elsif (reftype $connect_info eq 'HASH') {
482 %connect_info = %$connect_info;
484 croak "invalid connect_info";
487 if (exists $connect_info{cursor_class}) {
488 $connect_info{cursor_class}->require
489 or croak "invalid connect_info: Cannot load your cursor_class"
490 . " $connect_info{cursor_class}: $@";
493 $self->connect_info(\%connect_info);
496 sub _install_rs_models {
498 my $class = ref $self;
501 foreach my $moniker ($self->schema->sources) {
502 my $classname = "${class}::$moniker";
503 *{"${classname}::ACCEPT_CONTEXT"} = sub {
505 shift->model($self->model_name)->resultset($moniker);
510 sub _build_model_name {
513 my $class = ref $self;
514 my $model_name = $class;
515 $model_name =~ s/^[\w:]+::(?:Model|M):://;
517 $self->model_name($model_name);
523 return if defined $self->caching && !$self->caching;
527 if (my $cursor_class = $self->connect_info->{cursor_class}) {
528 unless ($cursor_class->can('clear_cache')) {
529 carp "Caching disabled, cursor_class $cursor_class does not"
534 my $cursor_class = 'DBIx::Class::Cursor::Cached';
536 unless ($cursor_class->require) {
537 carp "Caching disabled, cannot load $cursor_class: $@";
541 $self->connect_info->{cursor_class} = $cursor_class;
549 sub _reset_cursor_class {
552 if ($self->connect_info->{cursor_class} eq 'DBIx::Class::Cursor::Cached') {
553 $self->storage->cursor_class('DBIx::Class::Storage::DBI::Cursor');
563 General Catalyst Stuff:
565 L<Catalyst::Manual>, L<Catalyst::Test>, L<Catalyst::Request>,
566 L<Catalyst::Response>, L<Catalyst::Helper>, L<Catalyst>,
568 Stuff related to DBIC and this Model style:
570 L<DBIx::Class>, L<DBIx::Class::Schema>,
571 L<DBIx::Class::Schema::Loader>, L<Catalyst::Helper::Model::DBIC::Schema>
575 Brandon L Black, C<blblack@gmail.com>
579 This program is free software, you can redistribute it and/or modify it
580 under the same terms as Perl itself.