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