Cosmetic: Fix attribute lines types and defaults
[catagits/Catalyst-Model-DBIC-Schema.git] / lib / Catalyst / Model / DBIC / Schema.pm
1 package Catalyst::Model::DBIC::Schema;
2
3 use Moose;
4 use mro 'c3';
5 extends 'Catalyst::Model';
6 with 'CatalystX::Component::Traits';
7
8 our $VERSION = '0.65';
9 $VERSION = eval $VERSION;
10
11 use namespace::autoclean;
12 use Carp::Clan '^Catalyst::Model::DBIC::Schema';
13 use Data::Dumper;
14 use DBIx::Class ();
15 use Module::Runtime qw/use_module/;
16
17 use Catalyst::Model::DBIC::Schema::Types
18     qw/ConnectInfo SchemaClass Schema/;
19
20 use MooseX::Types::Moose qw/Str Bool/;
21 use MooseX::Types::LoadableClass qw/LoadableClass/;
22
23 =head1 NAME
24
25 Catalyst::Model::DBIC::Schema - DBIx::Class::Schema Model Class
26
27 =head1 SYNOPSIS
28
29 First, prepare your database schema using L<DBIx::Class>, see
30 L<Catalyst::Helper::Model::DBIC::Schema> for how to generate a
31 L<DBIx::Class::Schema> from your database using the Helper script, and
32 L<DBIx::Class::Schema::Loader::Base>.
33
34 A typical usage of the helper script would be:
35
36     script/myapp_create.pl model FilmDB DBIC::Schema MyApp::Schema::FilmDB \
37         create=static dbi:mysql:filmdb dbusername dbpass \
38         quote_names=1
39
40 If you are unfamiliar with L<DBIx::Class>, see L<DBIx::Class::Manual::Intro>
41 first.
42
43 These examples assume that you already have a schema called
44 C<MyApp::Schema::FilmDB>, which defines some Result classes for tables in
45 C<MyApp::Schema::FilmDB::Result::Actor> and
46 C<MyApp::Schema::FilmDB::Result::Film>. Either created by the helper script (as
47 shown above) or manually.
48
49 The helper also creates a Model in C<lib/MyApp/Model/FilmDB.pm>, if you already
50 have a schema you can create just the Model using:
51
52     script/myapp_create.pl model FilmDB DBIC::Schema MyApp::Schema::FilmDB
53         dbi:mysql:filmdb dbusername dbpass
54
55 The connect_info is optional and will be hardcoded into the Model if provided.
56 It's better to configure it in your L<Catalyst> config file, which will also
57 override any hardcoded config, see L</connect_info> for examples.
58
59 Now you have a working Model which accesses your separate DBIC Schema. This can
60 be used/accessed in the normal Catalyst manner, via C<< $c->model() >>:
61
62   my $db_model = $c->model('FilmDB');         # a Catalyst::Model
63   my $dbic     = $c->model('FilmDB')->schema; # the actual DBIC object
64
65 There is also a shortcut, which returns a L<DBIx::Class::ResultSet> directly,
66 instead of a L<Catalyst::Model>:
67
68   my $rs = $c->model('FilmDB::Actor');
69
70 See L<DBIx::Class::ResultSet> to find out more about which methods can be
71 called on ResultSets.
72
73 You can also define your own ResultSet methods to encapsulate the
74 database/business logic of your applications. These go into, for example,
75 C<lib/MyApp/Schema/FilmDB/ResultSet/Actor.pm>. The class must inherit from
76 L<DBIx::Class::ResultSet> and is automatically loaded.
77
78 Then call your methods like any other L<DBIx::Class::ResultSet> method:
79
80     $c->model('FilmDB::Actor')->SAG_members
81
82 =head2 Some examples:
83
84   # to access schema methods directly:
85   $c->model('FilmDB')->schema->source(...);
86
87   # to access the source object, resultset, and class:
88   $c->model('FilmDB')->source(...);
89   $c->model('FilmDB')->resultset(...);
90   $c->model('FilmDB')->class(...);
91
92   # For resultsets, there's an even quicker shortcut:
93   $c->model('FilmDB::Actor')
94   # is the same as $c->model('FilmDB')->resultset('Actor')
95
96   # To get the composed schema for making new connections:
97   my $newconn = $c->model('FilmDB')->composed_schema->connect(...);
98
99   # Or the same thing via a convenience shortcut:
100   my $newconn = $c->model('FilmDB')->connect(...);
101
102   # or, if your schema works on different storage drivers:
103   my $newconn = $c->model('FilmDB')->composed_schema->clone();
104   $newconn->storage_type('::LDAP');
105   $newconn->connection(...);
106
107   # and again, a convenience shortcut
108   my $newconn = $c->model('FilmDB')->clone();
109   $newconn->storage_type('::LDAP');
110   $newconn->connection(...);
111
112 To set up authentication, see L</"Setting up DBIC authentication"> below.
113
114 =head1 DESCRIPTION
115
116 This is a Catalyst Model for L<DBIx::Class::Schema>-based Models.  See
117 the documentation for L<Catalyst::Helper::Model::DBIC::Schema> for
118 information on generating these Models via Helper scripts.
119
120 When your Catalyst app starts up, a thin Model layer is created as an interface
121 to your DBIC Schema. It should be clearly noted that the model object returned
122 by C<< $c->model('FilmDB') >> is NOT itself a DBIC schema or resultset object,
123 but merely a wrapper proving L<methods|/METHODS> to access the underlying
124 schema.
125
126 In addition to this model class, a shortcut class is generated for each 
127 source in the schema, allowing easy and direct access to a resultset of the 
128 corresponding type. These generated classes are even thinner than the model 
129 class, providing no public methods but simply hooking into Catalyst's 
130 model() accessor via the 
131 L<ACCEPT_CONTEXT|Catalyst::Component/ACCEPT_CONTEXT> mechanism. The complete 
132 contents of each generated class is roughly equivalent to the following:
133
134   package MyApp::Model::FilmDB::Actor
135   sub ACCEPT_CONTEXT {
136       my ($self, $c) = @_;
137       $c->model('FilmDB')->resultset('Actor');
138   }
139
140 In short, there are three techniques available for obtaining a DBIC 
141 resultset object: 
142
143   # the long way
144   my $rs = $c->model('FilmDB')->schema->resultset('Actor');
145
146   # using the shortcut method on the model object
147   my $rs = $c->model('FilmDB')->resultset('Actor');
148
149   # using the generated class directly
150   my $rs = $c->model('FilmDB::Actor');
151
152 In order to add methods to a DBIC resultset, you cannot simply add them to 
153 the source (row, table) definition class; you must define a separate custom 
154 resultset class. This is just a matter of making a
155 C<lib/MyApp/Schema/ResultSet/Actor.pm> class that inherits from
156 L<DBIx::Class::ResultSet>, if you are using
157 L<DBIx::Class::Schema/load_namespaces>, the default for helper script generated
158 schemas.
159
160 See L<DBIx::Class::Manual::Cookbook/"Predefined searches"> 
161 for information on definining your own L<DBIx::Class::ResultSet> classes for
162 use with L<DBIx::Class::Schema/load_classes>, the old default.
163
164 =head1 CONFIG PARAMETERS
165
166 =head2 schema_class
167
168 This is the classname of your L<DBIx::Class::Schema> Schema.  It needs
169 to be findable in C<@INC>, but it does not need to be inside the 
170 C<Catalyst::Model::> namespace.  This parameter is required.
171
172 =head2 connect_info
173
174 This is a hashref or arrayref of connection parameters, which are specific to
175 your C<storage_type> (see your storage type documentation for more details). If
176 you only need one parameter (e.g. the DSN), you can just pass a string.
177
178 This is not required if C<schema_class> already has connection information
179 defined inside itself (which isn't highly recommended, but can be done.)
180
181 For L<DBIx::Class::Storage::DBI>, which is the only supported
182 C<storage_type> in L<DBIx::Class> at the time of this writing, the
183 parameters are your dsn, username, password, and connect options hashref.
184
185 See L<DBIx::Class::Storage::DBI/connect_info> for a detailed explanation
186 of the arguments supported.
187
188 Examples:
189
190   connect_info => {
191     dsn => 'dbi:Pg:dbname=mypgdb',
192     user => 'postgres',
193     password => ''
194   }
195
196   connect_info => {
197     dsn => 'dbi:SQLite:dbname=foo.db',
198     on_connect_do => [
199       'PRAGMA synchronous = OFF',
200     ]
201   }
202
203   connect_info => {
204     dsn => 'dbi:Pg:dbname=mypgdb',
205     user => 'postgres',
206     password => '',
207     pg_enable_utf8 => 1,
208     on_connect_do => [
209       'some SQL statement',
210       'another SQL statement',
211     ],
212   }
213
214 Or using L<Config::General>:
215
216     <Model::FilmDB>
217         schema_class   MyApp::Schema::FilmDB
218         traits Caching
219         <connect_info>
220             dsn   dbi:Pg:dbname=mypgdb
221             user   postgres
222             password ""
223             auto_savepoint 1
224             quote_names 1
225             on_connect_do   some SQL statement
226             on_connect_do   another SQL statement
227         </connect_info>
228         user_defined_schema_accessor foo
229     </Model::FilmDB>
230
231 or
232
233     <Model::FilmDB>
234         schema_class   MyApp::Schema::FilmDB
235         connect_info   dbi:SQLite:dbname=foo.db
236     </Model::FilmDB>
237
238 Or using L<YAML>:
239
240   Model::MyDB:
241       schema_class: MyDB
242       traits: Caching
243       connect_info:
244           dsn: dbi:Oracle:mydb
245           user: mtfnpy
246           password: mypass
247           LongReadLen: 1000000
248           LongTruncOk: 1
249           on_connect_call: 'datetime_setup'
250           quote_names: 1
251
252 The old arrayref style with hashrefs for L<DBI> then L<DBIx::Class> options is also
253 supported:
254
255   connect_info => [
256     'dbi:Pg:dbname=mypgdb',
257     'postgres',
258     '',
259     {
260       pg_enable_utf8 => 1,
261     },
262     {
263       auto_savepoint => 1,
264       on_connect_do => [
265         'some SQL statement',
266         'another SQL statement',
267       ],
268     }
269   ]
270
271 =head2 traits
272
273 Array of Traits to apply to the instance. Traits are L<Moose::Role>s.
274
275 They are relative to the C<< MyApp::TraitFor::Model::DBIC::Schema:: >>, then
276 the C<< Catalyst::TraitFor::Model::DBIC::Schema:: >> namespaces, unless
277 prefixed with C<+> in which case they are taken to be a fully qualified name.
278 E.g.:
279
280     traits Caching
281     traits +MyApp::TraitFor::Model::Foo
282
283 A new instance is created at application time, so any consumed required
284 attributes, coercions and modifiers will work.
285
286 Traits are applied at L<Catalyst::Component/COMPONENT> time using
287 L<CatalystX::Component::Traits>.
288
289 C<ref $self> will be an anon class if any traits are applied, C<<
290 $self->_original_class_name >> will be the original class.
291
292 When writing a Trait, interesting points to modify are C<BUILD>, L</setup> and
293 L</ACCEPT_CONTEXT>.
294
295 Traits that come with the distribution:
296
297 =over 4
298
299 =item L<Catalyst::TraitFor::Model::DBIC::Schema::Caching>
300
301 =item L<Catalyst::TraitFor::Model::DBIC::Schema::Replicated>
302
303 =item L<Catalyst::TraitFor::Model::DBIC::Schema::SchemaProxy>
304
305 =item L<Catalyst::TraitFor::Model::DBIC::Schema::PerRequestSchema>
306
307 =back
308
309 =head2 compose_namespaces
310
311 This model calls L<DBIx::Class::Schema/compose_namespaces> by default to
312 install classes into the model namespaces. You can turn that off by
313 setting this attribute to false. Default is true.
314
315 =head2 install_model_shortcuts
316
317 If you don't want shortcut models so you can do e.g. C<< $c->model('DB::Book')
318 >> set this attribute to false, Default is true.
319
320 =head2 storage_type
321
322 Allows the use of a different C<storage_type> than what is set in your
323 C<schema_class> (which in turn defaults to C<::DBI> if not set in current
324 L<DBIx::Class>).  Completely optional, and probably unnecessary for most
325 people until other storage backends become available for L<DBIx::Class>.
326
327 =head1 ATTRIBUTES
328
329 The keys you pass in the model configuration are available as attributes.
330
331 Other attributes available:
332
333 =head2 connect_info
334
335 Your connect_info args normalized to hashref form (with dsn/user/password.) See
336 L<DBIx::Class::Storage::DBI/connect_info> for more info on the hashref form of
337 L</connect_info>.
338
339 =head2 model_name
340
341 The model name L<Catalyst> uses to resolve this model, the part after
342 C<::Model::> or C<::M::> in your class name. E.g. if your class name is
343 C<MyApp::Model::DB> the L</model_name> will be C<DB>.
344
345 =head2 _default_cursor_class
346
347 What to reset your L<DBIx::Class::Storage::DBI/cursor_class> to if a custom one
348 doesn't work out. Defaults to L<DBIx::Class::Storage::DBI::Cursor>.
349
350 =head1 ATTRIBUTES FROM L<MooseX::Traits::Pluggable>
351
352 =head2 _original_class_name
353
354 The class name of your model before any L</traits> are applied. E.g.
355 C<MyApp::Model::DB>.
356
357 =head2 _traits
358
359 Unresolved arrayref of traits passed in the config.
360
361 =head2 _resolved_traits
362
363 Traits you used resolved to full class names.
364
365 =head1 CONFIGURING YOUR SCHEMA AND RESULTSETS
366
367 See the documentation for
368 L<Catalyst::TraitFor::Model::DBIC::Schema::SchemaProxy> for instructions on how
369 to pass config values from your L<Catalyst> config to your
370 L<DBIx::Class::Schema> and/or L<DBIx::Class::ResultSet> classes.
371
372 =head1 METHODS
373
374 =head2 new
375
376 Instantiates the Model based on the above-documented ->config parameters.
377 The only required parameter is C<schema_class>.  C<connect_info> is
378 required in the case that C<schema_class> does not already have connection
379 information defined for it.
380
381 =head2 schema
382
383 Accessor which returns the connected schema being used by the this model.
384 There are direct shortcuts on the model class itself for
385 schema->resultset, schema->source, and schema->class.
386
387 =head2 composed_schema
388
389 Accessor which returns the composed schema, which has no connection info,
390 which was used in constructing the L</schema>. Useful for creating
391 new connections based on the same schema/model.  There are direct shortcuts
392 from the model object for composed_schema->clone and composed_schema->connect
393
394 If L</compose_namespaces> is not true, L</composed_schema> is equivalent to
395 C<< $model->schema_class->clone >>.
396
397 =head2 clone
398
399 Shortcut for ->composed_schema->clone
400
401 =head2 connect
402
403 Shortcut for ->composed_schema->connect
404
405 =head2 source
406
407 Shortcut for ->schema->source
408
409 =head2 class
410
411 Shortcut for ->schema->class
412
413 =head2 resultset
414
415 Shortcut for ->schema->resultset
416
417 =head2 txn_do
418
419 Shortcut for ->schema->txn_do
420
421 =head2 txn_scope_guard
422
423 Shortcut for ->schema->txn_scope_guard
424
425 =head2 storage
426
427 Provides an accessor for the connected schema's storage object.
428
429 See L<DBIx::Class::Storage> and L<DBIx::Class::Storage::DBI>.
430
431 =cut
432
433 has schema_class => (
434     is => 'ro',
435     isa => SchemaClass,
436     required => 1
437 );
438
439 has compose_namespaces => (is => 'ro', isa => Bool, default => 1 );
440
441 has install_model_shortcuts => (is => 'ro', isa => Bool, default => 1 );
442
443 has storage_type => (is => 'rw', isa => Str);
444
445 has connect_info => (is => 'rw', isa => ConnectInfo, coerce => 1);
446
447 has model_name => (
448     is => 'ro',
449     isa => Str,
450     required => 1,
451     lazy_build => 1,
452 );
453
454 has _default_cursor_class => (
455     is => 'ro',
456     isa => LoadableClass,
457     default => 'DBIx::Class::Storage::DBI::Cursor',
458 );
459
460 has schema => (is => 'rw', isa => Schema);
461
462 my $app_class;
463
464 before COMPONENT => sub {
465     $app_class = ref $_[1] || $_[1];
466 };
467
468 sub app_class { $app_class }
469
470 sub BUILD {
471     my ($self, $args) = @_;
472     my $class = $self->_original_class_name;
473     my $schema_class = $self->schema_class;
474
475     if( !$self->connect_info ) {
476         if($schema_class->storage && $schema_class->storage->connect_info) {
477             $self->connect_info($schema_class->storage->connect_info);
478         }
479         else {
480             die "Either ->config->{connect_info} must be defined for $class"
481                   . " or $schema_class must have connect info defined on it."
482                   . " Here's what we got:\n"
483                   . Dumper($args);
484         }
485     }
486
487     if (exists $self->connect_info->{cursor_class}) {
488         eval { use_module($self->connect_info->{cursor_class}) }
489             or croak "invalid connect_info: Cannot load your cursor_class"
490         . " ".$self->connect_info->{cursor_class}.": $@";
491     }
492
493     $self->setup($args);
494
495     my $is_installed = defined $self->composed_schema;
496
497     if (not $is_installed) {
498         $self->composed_schema($self->compose_namespaces ?
499             $schema_class->compose_namespace($class)
500             :
501             $schema_class->clone
502         );
503     }
504
505     $self->schema($self->composed_schema->clone)
506         unless $self->schema;
507
508     $self->schema->storage_type($self->storage_type)
509         if $self->storage_type;
510
511     $self->schema->connection($self->connect_info);
512
513     if ((not $is_installed) && $self->install_model_shortcuts) {
514         $self->_install_rs_models;
515     }
516 }
517
518 sub clone { shift->composed_schema->clone(@_); }
519
520 sub connect { shift->composed_schema->connect(@_); }
521
522 # some proxy methods, see also SchemaProxy
523
524 sub resultset { shift->schema->resultset(@_); }
525
526 sub txn_do { shift->schema->txn_do(@_); }
527
528 sub txn_scope_guard { shift->schema->txn_scope_guard(@_); }
529
530 sub storage { shift->schema->storage(@_); }
531
532 =head2 setup
533
534 Called at C<BUILD> time before configuration, but after L</connect_info> is
535 set. To do something after configuuration use C<< after BUILD => >>.
536
537 Receives a hashref of args passed to C<BUILD>.
538
539 =cut
540
541 sub setup { 1 }
542
543 =head2 ACCEPT_CONTEXT
544
545 Point of extension for doing things at C<< $c->model >> time with context,
546 returns the model instance, see L<Catalyst::Manual::Intro/ACCEPT_CONTEXT> for
547 more information.
548
549 =cut
550
551 sub ACCEPT_CONTEXT { shift }
552
553 sub _install_rs_models {
554     my $self  = shift;
555     my $class = $self->_original_class_name;
556
557     no strict 'refs';
558
559     my @sources = $self->schema->sources;
560
561     unless (@sources) {
562         warn <<'EOF' unless $ENV{CMDS_NO_SOURCES};
563 ******************************* WARNING ***************************************
564 * No sources found (did you forget to define your tables?)                    *
565 *                                                                             *
566 * To turn off this warning, set the CMDS_NO_SOURCES environment variable.     *
567 *******************************************************************************
568 EOF
569     }
570
571     foreach my $moniker (@sources) {
572         my $classname = "${class}::$moniker";
573         *{"${classname}::ACCEPT_CONTEXT"} = sub {
574             shift;
575             shift->model($self->model_name)->resultset($moniker);
576         }
577     }
578 }
579
580 sub _reset_cursor_class {
581     my $self = shift;
582
583     if ($self->storage->can('cursor_class')) {
584         $self->storage->cursor_class($self->_default_cursor_class)
585             if $self->storage->cursor_class ne $self->_default_cursor_class;
586     }
587 }
588
589 {
590     my %COMPOSED_CACHE;
591
592     sub composed_schema {
593         my $self = shift;
594         my $class = $self->_original_class_name;
595         my $store = \$COMPOSED_CACHE{$class}{$self->schema_class};
596
597         $$store = shift if @_;
598
599         return $$store
600     }
601 }
602
603 sub _build_model_name {
604     my $self  = shift;
605     my $class = $self->_original_class_name;
606     (my $model_name = $class) =~ s/^[\w:]+::(?:Model|M):://;
607
608     return $model_name;
609 }
610
611 __PACKAGE__->meta->make_immutable;
612
613 =head1 ENVIRONMENT
614
615 =over 4
616
617 =item CMDS_NO_SOURCES
618
619 Set this variable if you will be using schemas with no sources (Result classes)
620 to disable the warning. The warning is there because having no Result classes
621 is usually a mistake.
622
623 =back
624
625 =head1 Setting up DBIC authentication
626
627 You can set this up with 
628 L<Catalyst::Authentication::Store::DBIx::Class> in MyApp.pm:
629
630   package MyApp;
631
632   use Catalyst qw/... Authentication .../;
633
634   ...
635
636   __PACKAGE__->config('Plugin::Authentication' =>
637                 {
638                     default_realm => 'members',
639                     members => {
640                         credential => {
641                             class => 'Password',
642                             password_field => 'password',
643                             password_type => 'hashed'
644                             password_hash_type => 'SHA-256'
645                         },
646                         store => {
647                             class => 'DBIx::Class',
648                             user_model => 'DB::User',
649                             role_relation => 'roles',
650                             role_field => 'rolename',
651                         }
652                     }
653                 });
654
655 =head1 METHOD PROXYING
656
657 The automatic proxying to the underlying L<DBIx::Class::Schema> has been
658 removed as of version C<0.34>, to enable this feature add C<SchemaProxy> to
659 L</traits>.
660
661 See L<Catalyst::TraitFor::Model::DBIC::Schema::SchemaProxy>.
662
663 =head1 SEE ALSO
664
665 General Catalyst Stuff:
666
667 L<Catalyst::Manual>, L<Catalyst::Test>, L<Catalyst::Request>,
668 L<Catalyst::Response>, L<Catalyst::Helper>, L<Catalyst>,
669
670 Stuff related to DBIC and this Model style:
671
672 L<DBIx::Class>, L<DBIx::Class::Schema>,
673 L<DBIx::Class::Schema::Loader>, L<Catalyst::Helper::Model::DBIC::Schema>,
674 L<CatalystX::Component::Traits>, L<MooseX::Traits::Pluggable>
675
676 Traits:
677
678 L<Catalyst::TraitFor::Model::DBIC::Schema::Caching>,
679 L<Catalyst::TraitFor::Model::DBIC::Schema::Replicated>,
680 L<Catalyst::TraitFor::Model::DBIC::Schema::SchemaProxy>,
681 L<Catalyst::TraitFor::Model::DBIC::Schema::PerRequestSchema>,
682 L<Catalyst::TraitFor::Model::DBIC::Schema::QueryLog>
683
684 =head1 AUTHOR
685
686 Brandon L Black C<blblack at gmail.com>
687
688 =head1 CONTRIBUTORS
689
690 caelum: Rafael Kitover C<rkitover at cpan.org>
691
692 dandv: Dan Dascalescu C<dandv at cpan.org>
693
694 bluefeet: Aran Deltac C<bluefeet@cpan.org>
695
696 t0m: Tomas Doran C<bobtfish@bobtfish.net>
697
698 osfameron: C<osfameron@cpan.org>
699
700 ozum: Ozum Eldogan C<ozum@ozum.net>
701
702 Pavel I. Shaydo C<zwon@trinitum.org>
703
704 SineSwiper: Brendan Byrd <byrd.b@insightcom.com>
705
706 =head1 COPYRIGHT
707
708 Copyright (c) 2006 - 2010
709 the Catalyst::Model::DBIC::Schema L</AUTHOR> and L</CONTRIBUTORS>
710 as listed above.
711
712 =head1 LICENSE
713
714 This program is free software. You can redistribute it and/or modify it
715 under the same terms as Perl itself.
716
717 =cut
718
719 1;
720 # vim:sts=4 sw=4 et tw=80: