M::DBIC::Schema -- Caching role
[catagits/Catalyst-Model-DBIC-Schema.git] / lib / Catalyst / Model / DBIC / Schema.pm
CommitLineData
ad91060a 1package Catalyst::Model::DBIC::Schema;
2
0fbbc8d5 3use Moose;
2201c2e4 4no warnings 'uninitialized';
018eb0e2 5
2201c2e4 6our $VERSION = '0.24';
018eb0e2 7
0f3de2c4 8use mro 'c3';
0fbbc8d5 9extends 'Catalyst::Model';
10with 'MooseX::Object::Pluggable';
11
12use Carp::Clan '^Catalyst::Model::DBIC::Schema';
bfcd6e3d 13use Data::Dumper;
2201c2e4 14use DBIx::Class ();
15use Scalar::Util 'reftype';
0fbbc8d5 16use MooseX::ClassAttribute;
17use Moose::Autobox;
ad91060a 18
0fbbc8d5 19use Catalyst::Model::DBIC::Schema::Types qw/ConnectInfo SchemaClass/;
20
21use namespace::clean -except => 'meta';
f1613faa 22
ad91060a 23=head1 NAME
24
25Catalyst::Model::DBIC::Schema - DBIx::Class::Schema Model Class
26
27=head1 SYNOPSIS
28
aabc1d75 29Manual creation of a DBIx::Class::Schema and a Catalyst::Model::DBIC::Schema:
30
31=over
32
33=item 1.
34
35Create the DBIx:Class schema in MyApp/Schema/FilmDB.pm:
36
37 package MyApp::Schema::FilmDB;
38 use base qw/DBIx::Class::Schema/;
39
40 __PACKAGE__->load_classes(qw/Actor Role/);
41
42=item 2.
43
44Create some classes for the tables in the database, for example an
45Actor in MyApp/Schema/FilmDB/Actor.pm:
46
47 package MyApp::Schema::FilmDB::Actor;
48 use base qw/DBIx::Class/
07edc53e 49
aabc1d75 50 __PACKAGE__->load_components(qw/Core/);
51 __PACKAGE__->table('actor');
07edc53e 52
aabc1d75 53 ...
54
c8ae74f8 55and a Role in MyApp/Schema/FilmDB/Role.pm:
aabc1d75 56
57 package MyApp::Schema::FilmDB::Role;
58 use base qw/DBIx::Class/
07edc53e 59
aabc1d75 60 __PACKAGE__->load_components(qw/Core/);
4a3e80e9 61 __PACKAGE__->table('role');
07edc53e 62
aabc1d75 63 ...
64
65Notice that the schema is in MyApp::Schema, not in MyApp::Model. This way it's
66usable as a standalone module and you can test/run it without Catalyst.
67
68=item 3.
69
70To expose it to Catalyst as a model, you should create a DBIC Model in
71MyApp/Model/FilmDB.pm:
72
73 package MyApp::Model::FilmDB;
74 use base qw/Catalyst::Model::DBIC::Schema/;
07edc53e 75
aabc1d75 76 __PACKAGE__->config(
77 schema_class => 'MyApp::Schema::FilmDB',
2201c2e4 78 connect_info => {
79 dsn => "DBI:...",
80 user => "username",
81 password => "password",
82 }
aabc1d75 83 );
84
85See below for a full list of the possible config parameters.
86
87=back
88
6dec11b7 89Now you have a working Model which accesses your separate DBIC Schema. This can
aabc1d75 90be used/accessed in the normal Catalyst manner, via $c->model():
91
92 my $actor = $c->model('FilmDB::Actor')->find(1);
93
94You can also use it to set up DBIC authentication with
95Authentication::Store::DBIC in MyApp.pm:
96
97 package MyApp;
07edc53e 98
aabc1d75 99 use Catalyst qw/... Authentication::Store::DBIC/;
07edc53e 100
aabc1d75 101 ...
07edc53e 102
aabc1d75 103 __PACKAGE__->config->{authentication}{dbic} = {
104 user_class => 'FilmDB::Actor',
105 user_field => 'name',
106 password_field => 'password'
107 }
108
d52bc376 109C<< $c->model('Schema::Source') >> returns a L<DBIx::Class::ResultSet> for
110the source name parameter passed. To find out more about which methods can
111be called on a ResultSet, or how to add your own methods to it, please see
112the ResultSet documentation in the L<DBIx::Class> distribution.
aabc1d75 113
114Some examples are given below:
115
f1613faa 116 # to access schema methods directly:
117 $c->model('FilmDB')->schema->source(...);
118
119 # to access the source object, resultset, and class:
07edc53e 120 $c->model('FilmDB')->source(...);
121 $c->model('FilmDB')->resultset(...);
122 $c->model('FilmDB')->class(...);
c12b7310 123
07edc53e 124 # For resultsets, there's an even quicker shortcut:
125 $c->model('FilmDB::Actor')
126 # is the same as $c->model('FilmDB')->resultset('Actor')
ad91060a 127
f1613faa 128 # To get the composed schema for making new connections:
129 my $newconn = $c->model('FilmDB')->composed_schema->connect(...);
130
131 # Or the same thing via a convenience shortcut:
132 my $newconn = $c->model('FilmDB')->connect(...);
133
134 # or, if your schema works on different storage drivers:
135 my $newconn = $c->model('FilmDB')->composed_schema->clone();
136 $newconn->storage_type('::LDAP');
137 $newconn->connection(...);
138
139 # and again, a convenience shortcut
140 my $newconn = $c->model('FilmDB')->clone();
141 $newconn->storage_type('::LDAP');
142 $newconn->connection(...);
143
ad91060a 144=head1 DESCRIPTION
145
7b39f3f0 146This is a Catalyst Model for L<DBIx::Class::Schema>-based Models. See
ef91bcf9 147the documentation for L<Catalyst::Helper::Model::DBIC::Schema> for
148information on generating these Models via Helper scripts.
ad91060a 149
d52bc376 150When your Catalyst app starts up, a thin Model layer is created as an
151interface to your DBIC Schema. It should be clearly noted that the model
152object returned by C<< $c->model('FilmDB') >> is NOT itself a DBIC schema or
153resultset object, but merely a wrapper proving L<methods|/METHODS> to access
154the underlying schema.
155
156In addition to this model class, a shortcut class is generated for each
157source in the schema, allowing easy and direct access to a resultset of the
158corresponding type. These generated classes are even thinner than the model
159class, providing no public methods but simply hooking into Catalyst's
160model() accessor via the
161L<ACCEPT_CONTEXT|Catalyst::Component/ACCEPT_CONTEXT> mechanism. The complete
162contents of each generated class is roughly equivalent to the following:
163
164 package MyApp::Model::FilmDB::Actor
165 sub ACCEPT_CONTEXT {
166 my ($self, $c) = @_;
167 $c->model('FilmDB')->resultset('Actor');
168 }
169
170In short, there are three techniques available for obtaining a DBIC
171resultset object:
172
173 # the long way
174 my $rs = $c->model('FilmDB')->schema->resultset('Actor');
175
176 # using the shortcut method on the model object
177 my $rs = $c->model('FilmDB')->resultset('Actor');
178
179 # using the generated class directly
180 my $rs = $c->model('FilmDB::Actor');
181
c082639a 182In order to add methods to a DBIC resultset, you cannot simply add them to
183the source (row, table) definition class; you must define a separate custom
184resultset class. See L<DBIx::Class::Manual::Cookbook/"Predefined searches">
185for more info.
186
ad91060a 187=head1 CONFIG PARAMETERS
188
189=over 4
190
191=item schema_class
192
193This is the classname of your L<DBIx::Class::Schema> Schema. It needs
aabc1d75 194to be findable in C<@INC>, but it does not need to be inside the
195C<Catalyst::Model::> namespace. This parameter is required.
ad91060a 196
197=item connect_info
198
199This is an arrayref of connection parameters, which are specific to your
b9a72351 200C<storage_type> (see your storage type documentation for more details).
201If you only need one parameter (e.g. the DSN), you can just pass a string
202instead of an arrayref.
ad91060a 203
0f2fd2c0 204This is not required if C<schema_class> already has connection information
d89e6c8a 205defined inside itself (which isn't highly recommended, but can be done)
0f2fd2c0 206
7db6da78 207For L<DBIx::Class::Storage::DBI>, which is the only supported
208C<storage_type> in L<DBIx::Class> at the time of this writing, the
209parameters are your dsn, username, password, and connect options hashref.
210
018eb0e2 211See L<DBIx::Class::Storage::DBI/connect_info> for a detailed explanation
212of the arguments supported.
7db6da78 213
214Examples:
215
2201c2e4 216 connect_info => {
217 dsn => 'dbi:Pg:dbname=mypgdb',
218 user => 'postgres',
219 password => ''
220 }
07edc53e 221
2201c2e4 222 connect_info => {
223 dsn => 'dbi:SQLite:dbname=foo.db',
224 on_connect_do => [
225 'PRAGMA synchronous = OFF',
226 ]
227 }
07edc53e 228
2201c2e4 229 connect_info => {
230 dsn => 'dbi:Pg:dbname=mypgdb',
231 user => 'postgres',
232 password => '',
233 pg_enable_utf8 => 1,
234 on_connect_do => [
235 'some SQL statement',
236 'another SQL statement',
237 ],
238 }
7db6da78 239
8281c933 240Or using L<Config::General>:
241
242 <Model::FilmDB>
243 schema_class MyApp::Schema::FilmDB
8281c933 244 <connect_info>
2201c2e4 245 dsn dbi:Pg:dbname=mypgdb
246 user postgres
247 password ''
248 auto_savepoint 1
8281c933 249 on_connect_do some SQL statement
250 on_connect_do another SQL statement
251 </connect_info>
252 </Model::FilmDB>
253
254or
255
256 <Model::FilmDB>
257 schema_class MyApp::Schema::FilmDB
258 connect_info dbi:SQLite:dbname=foo.db
259 </Model::FilmDB>
260
2201c2e4 261Or using L<YAML>:
262
263 Model::MyDB:
264 schema_class: MyDB
265 connect_info:
266 dsn: dbi:Oracle:mydb
267 user: mtfnpy
268 password: mypass
269 LongReadLen: 1000000
270 LongTruncOk: 1
271 on_connect_do: [ "alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS'" ]
272 cursor_class: 'DBIx::Class::Cursor::Cached'
273
274The old arrayref style with hashrefs for L<DBI> then L<DBIx::Class> options is also
275supported:
276
277 connect_info => [
278 'dbi:Pg:dbname=mypgdb',
279 'postgres',
280 '',
281 {
282 pg_enable_utf8 => 1,
283 },
284 {
0fbbc8d5 285 auto_savepoint => 1,
2201c2e4 286 on_connect_do => [
287 'some SQL statement',
288 'another SQL statement',
289 ],
290 }
291 ]
292
0fbbc8d5 293=item roles
294
295Array of Roles to apply at BUILD time. Roles are relative to the
296C<<MyApp::Model::DB::Role::> then C<<Catalyst::Model::DBIC::Schema::Role::>>
297namespaces, unless prefixed with C<+> in which case they are taken to be a
298fully qualified name. E.g.:
2201c2e4 299
0fbbc8d5 300 roles Caching
301 roles +MyApp::DB::Role::Foo
2201c2e4 302
0fbbc8d5 303This is done using L<MooseX::Object::Pluggable>.
2201c2e4 304
0fbbc8d5 305A new instance is created at application time, so any consumed required
306attributes, coercions and modifiers will work.
2201c2e4 307
0fbbc8d5 308Roles are applied before setup, schema and connection are set, and have a chance
309to modify C<connect_info>.
310
311C<ref $self> will not be what you expect.
312
313WARNING: you cannot modify C<new>, modify C<setup> instead.
314
315Roles that come with the distribution:
316
317=over 4
2201c2e4 318
0fbbc8d5 319=item L<Catalyst::Model::DBIC::Schema::Role::Caching>
320
321=back
8281c933 322
ad91060a 323=item storage_type
324
325Allows the use of a different C<storage_type> than what is set in your
326C<schema_class> (which in turn defaults to C<::DBI> if not set in current
f1613faa 327L<DBIx::Class>). Completely optional, and probably unnecessary for most
328people until other storage backends become available for L<DBIx::Class>.
ad91060a 329
330=back
331
332=head1 METHODS
333
334=over 4
335
336=item new
337
338Instantiates the Model based on the above-documented ->config parameters.
0f2fd2c0 339The only required parameter is C<schema_class>. C<connect_info> is
340required in the case that C<schema_class> does not already have connection
341information defined for it.
ad91060a 342
f1613faa 343=item schema
344
345Accessor which returns the connected schema being used by the this model.
346There are direct shortcuts on the model class itself for
347schema->resultset, schema->source, and schema->class.
348
349=item composed_schema
350
351Accessor which returns the composed schema, which has no connection info,
352which was used in constructing the C<schema> above. Useful for creating
353new connections based on the same schema/model. There are direct shortcuts
354from the model object for composed_schema->clone and composed_schema->connect
355
356=item clone
357
358Shortcut for ->composed_schema->clone
359
360=item connect
361
362Shortcut for ->composed_schema->connect
363
364=item source
c12b7310 365
f1613faa 366Shortcut for ->schema->source
367
368=item class
369
370Shortcut for ->schema->class
371
372=item resultset
373
374Shortcut for ->schema->resultset
375
376=item storage
377
378Provides an accessor for the connected schema's storage object.
379Used often for debugging and controlling transactions.
b8427e0b 380
ad91060a 381=cut
382
0fbbc8d5 383class_has 'composed_schema' => (is => 'rw');
384
385has 'schema' => (is => 'rw');
386
387has 'schema_class' => (
388 is => 'ro',
389 isa => SchemaClass,
390 coerce => 1,
391 required => 1
392);
393
394has 'storage_type' => (is => 'ro');
395
396has 'connect_info' => (is => 'ro', isa => ConnectInfo, coerce => 1);
397
398# ref $self changes to anon after roles are applied, and _original_class_name is
399# broken in MX::O::P
400has '_class_name' => (is => 'ro', isa => 'ClassName', default => sub {
401 ref shift
402});
403
404has 'model_name' => (is => 'ro', default => sub {
405 my $self = shift;
406
2201c2e4 407 my $class = ref $self;
0fbbc8d5 408 (my $model_name = $class) =~ s/^[\w:]+::(?:Model|M):://;
2201c2e4 409
0fbbc8d5 410 $model_name
411});
ad91060a 412
0fbbc8d5 413has 'roles' => (is => 'ro', isa => 'ArrayRef|Str');
ad91060a 414
0fbbc8d5 415sub BUILD {
416 my $self = shift;
417 my $class = ref $self;
2201c2e4 418 my $schema_class = $self->schema_class;
ad91060a 419
2201c2e4 420 if( !$self->connect_info ) {
f1613faa 421 if($schema_class->storage && $schema_class->storage->connect_info) {
2201c2e4 422 $self->connect_info($schema_class->storage->connect_info);
f1613faa 423 }
424 else {
425 croak "Either ->config->{connect_info} must be defined for $class"
460e3ac8 426 . " or $schema_class must have connect info defined on it."
427 . " Here's what we got:\n"
f1613faa 428 . Dumper($self);
429 }
7db6da78 430 }
431
0fbbc8d5 432 if (exists $self->connect_info->{cursor_class}) {
433 eval { Class::MOP::load_class($self->connect_info->{cursor_class}) }
434 or croak "invalid connect_info: Cannot load your cursor_class"
435 . " ".$self->connect_info->{cursor_class}.": $@";
436 }
437
438 $self->_plugin_ns('Role');
439
440 $self->load_plugins($self->roles->flatten) if $self->roles;
441
442 $self->setup;
443
f1613faa 444 $self->composed_schema($schema_class->compose_namespace($class));
2201c2e4 445
f1613faa 446 $self->schema($self->composed_schema->clone);
447
2201c2e4 448 $self->schema->storage_type($self->storage_type)
449 if $self->storage_type;
7db6da78 450
2201c2e4 451 $self->schema->connection($self->connect_info);
452
453 $self->_install_rs_models;
2201c2e4 454}
455
456sub clone { shift->composed_schema->clone(@_); }
457
458sub connect { shift->composed_schema->connect(@_); }
459
460sub storage { shift->schema->storage(@_); }
461
0fbbc8d5 462=item setup
2201c2e4 463
0fbbc8d5 464Called at C<<BUILD>> time, for modifying in roles/subclasses.
2201c2e4 465
466=cut
467
0fbbc8d5 468sub setup { 1 }
2201c2e4 469
0fbbc8d5 470=item ACCEPT_CONTEXT
2201c2e4 471
0fbbc8d5 472Point of extension for doing things at C<<$c->model>> time, returns the model
473instance, see L<Catalyst::Manual::Intro> for more information.
2201c2e4 474
0fbbc8d5 475=cut
2201c2e4 476
0fbbc8d5 477sub ACCEPT_CONTEXT { shift }
2201c2e4 478
479sub _install_rs_models {
480 my $self = shift;
0fbbc8d5 481 my $class = $self->_class_name;
2201c2e4 482
ad91060a 483 no strict 'refs';
484 foreach my $moniker ($self->schema->sources) {
0b2a7108 485 my $classname = "${class}::$moniker";
7db6da78 486 *{"${classname}::ACCEPT_CONTEXT"} = sub {
ad91060a 487 shift;
2201c2e4 488 shift->model($self->model_name)->resultset($moniker);
ad91060a 489 }
490 }
2201c2e4 491}
ad91060a 492
0fbbc8d5 493__PACKAGE__->meta->make_immutable;
2201c2e4 494
495=back
b8427e0b 496
ad91060a 497=head1 SEE ALSO
498
7b39f3f0 499General Catalyst Stuff:
500
501L<Catalyst::Manual>, L<Catalyst::Test>, L<Catalyst::Request>,
502L<Catalyst::Response>, L<Catalyst::Helper>, L<Catalyst>,
503
504Stuff related to DBIC and this Model style:
505
506L<DBIx::Class>, L<DBIx::Class::Schema>,
ef91bcf9 507L<DBIx::Class::Schema::Loader>, L<Catalyst::Helper::Model::DBIC::Schema>
ad91060a 508
509=head1 AUTHOR
510
511Brandon L Black, C<blblack@gmail.com>
512
513=head1 COPYRIGHT
514
515This program is free software, you can redistribute it and/or modify it
516under the same terms as Perl itself.
517
518=cut
519
5201;