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