8b63475315f0343d6740d70a8b8325503543f2f8
[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.64';
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/;
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 storage_type
310
311 Allows the use of a different C<storage_type> than what is set in your
312 C<schema_class> (which in turn defaults to C<::DBI> if not set in current
313 L<DBIx::Class>).  Completely optional, and probably unnecessary for most
314 people until other storage backends become available for L<DBIx::Class>.
315
316 =head1 ATTRIBUTES
317
318 The keys you pass in the model configuration are available as attributes.
319
320 Other attributes available:
321
322 =head2 connect_info
323
324 Your connect_info args normalized to hashref form (with dsn/user/password.) See
325 L<DBIx::Class::Storage::DBI/connect_info> for more info on the hashref form of
326 L</connect_info>.
327
328 =head2 model_name
329
330 The model name L<Catalyst> uses to resolve this model, the part after
331 C<::Model::> or C<::M::> in your class name. E.g. if your class name is
332 C<MyApp::Model::DB> the L</model_name> will be C<DB>.
333
334 =head2 _default_cursor_class
335
336 What to reset your L<DBIx::Class::Storage::DBI/cursor_class> to if a custom one
337 doesn't work out. Defaults to L<DBIx::Class::Storage::DBI::Cursor>.
338
339 =head1 ATTRIBUTES FROM L<MooseX::Traits::Pluggable>
340
341 =head2 _original_class_name
342
343 The class name of your model before any L</traits> are applied. E.g.
344 C<MyApp::Model::DB>.
345
346 =head2 _traits
347
348 Unresolved arrayref of traits passed in the config.
349
350 =head2 _resolved_traits
351
352 Traits you used resolved to full class names.
353
354 =head1 CONFIGURING YOUR SCHEMA AND RESULTSETS
355
356 See the documentation for
357 L<Catalyst::TraitFor::Model::DBIC::Schema::SchemaProxy> for instructions on how
358 to pass config values from your L<Catalyst> config to your
359 L<DBIx::Class::Schema> and/or L<DBIx::Class::ResultSet> classes.
360
361 =head1 METHODS
362
363 =head2 new
364
365 Instantiates the Model based on the above-documented ->config parameters.
366 The only required parameter is C<schema_class>.  C<connect_info> is
367 required in the case that C<schema_class> does not already have connection
368 information defined for it.
369
370 =head2 schema
371
372 Accessor which returns the connected schema being used by the this model.
373 There are direct shortcuts on the model class itself for
374 schema->resultset, schema->source, and schema->class.
375
376 =head2 composed_schema
377
378 Accessor which returns the composed schema, which has no connection info,
379 which was used in constructing the C<schema> above.  Useful for creating
380 new connections based on the same schema/model.  There are direct shortcuts
381 from the model object for composed_schema->clone and composed_schema->connect
382
383 =head2 clone
384
385 Shortcut for ->composed_schema->clone
386
387 =head2 connect
388
389 Shortcut for ->composed_schema->connect
390
391 =head2 source
392
393 Shortcut for ->schema->source
394
395 =head2 class
396
397 Shortcut for ->schema->class
398
399 =head2 resultset
400
401 Shortcut for ->schema->resultset
402
403 =head2 txn_do
404
405 Shortcut for ->schema->txn_do
406
407 =head2 txn_scope_guard
408
409 Shortcut for ->schema->txn_scope_guard
410
411 =head2 storage
412
413 Provides an accessor for the connected schema's storage object.
414
415 See L<DBIx::Class::Storage> and L<DBIx::Class::Storage::DBI>.
416
417 =cut
418
419 has schema_class => (
420     is => 'ro',
421     isa => SchemaClass,
422     required => 1
423 );
424
425 has storage_type => (is => 'rw', isa => Str);
426
427 has connect_info => (is => 'rw', isa => ConnectInfo, coerce => 1);
428
429 has model_name => (
430     is => 'ro',
431     isa => Str,
432     required => 1,
433     lazy_build => 1,
434 );
435
436 has _default_cursor_class => (
437     is => 'ro',
438     isa => LoadableClass,
439     default => 'DBIx::Class::Storage::DBI::Cursor',
440 );
441
442 has schema => (is => 'rw', isa => Schema);
443
444 my $app_class;
445
446 before COMPONENT => sub {
447     $app_class = ref $_[1] || $_[1];
448 };
449
450 sub app_class { $app_class }
451
452 sub BUILD {
453     my ($self, $args) = @_;
454     my $class = $self->_original_class_name;
455     my $schema_class = $self->schema_class;
456
457     if( !$self->connect_info ) {
458         if($schema_class->storage && $schema_class->storage->connect_info) {
459             $self->connect_info($schema_class->storage->connect_info);
460         }
461         else {
462             die "Either ->config->{connect_info} must be defined for $class"
463                   . " or $schema_class must have connect info defined on it."
464                   . " Here's what we got:\n"
465                   . Dumper($args);
466         }
467     }
468
469     if (exists $self->connect_info->{cursor_class}) {
470         eval { use_module($self->connect_info->{cursor_class}) }
471             or croak "invalid connect_info: Cannot load your cursor_class"
472         . " ".$self->connect_info->{cursor_class}.": $@";
473     }
474
475     $self->setup($args);
476
477     my $is_installed = defined $self->composed_schema;
478
479     $self->composed_schema($schema_class->compose_namespace($class))
480         unless $is_installed;
481
482     $self->schema($self->composed_schema->clone)
483         unless $self->schema;
484
485     $self->schema->storage_type($self->storage_type)
486         if $self->storage_type;
487
488     $self->schema->connection($self->connect_info);
489
490     $self->_install_rs_models unless $is_installed;
491 }
492
493 sub clone { shift->composed_schema->clone(@_); }
494
495 sub connect { shift->composed_schema->connect(@_); }
496
497 # some proxy methods, see also SchemaProxy
498
499 sub resultset { shift->schema->resultset(@_); }
500
501 sub txn_do { shift->schema->txn_do(@_); }
502
503 sub txn_scope_guard { shift->schema->txn_scope_guard(@_); }
504
505 sub storage { shift->schema->storage(@_); }
506
507 =head2 setup
508
509 Called at C<BUILD> time before configuration, but after L</connect_info> is
510 set. To do something after configuuration use C<< after BUILD => >>.
511
512 Receives a hashref of args passed to C<BUILD>.
513
514 =cut
515
516 sub setup { 1 }
517
518 =head2 ACCEPT_CONTEXT
519
520 Point of extension for doing things at C<< $c->model >> time with context,
521 returns the model instance, see L<Catalyst::Manual::Intro/ACCEPT_CONTEXT> for
522 more information.
523
524 =cut
525
526 sub ACCEPT_CONTEXT { shift }
527
528 sub _install_rs_models {
529     my $self  = shift;
530     my $class = $self->_original_class_name;
531
532     no strict 'refs';
533
534     my @sources = $self->schema->sources;
535
536     unless (@sources) {
537         warn <<'EOF' unless $ENV{CMDS_NO_SOURCES};
538 ******************************* WARNING ***************************************
539 * No sources found (did you forget to define your tables?)                    *
540 *                                                                             *
541 * To turn off this warning, set the CMDS_NO_SOURCES environment variable.     *
542 *******************************************************************************
543 EOF
544     }
545
546     foreach my $moniker (@sources) {
547         my $classname = "${class}::$moniker";
548         *{"${classname}::ACCEPT_CONTEXT"} = sub {
549             shift;
550             shift->model($self->model_name)->resultset($moniker);
551         }
552     }
553 }
554
555 sub _reset_cursor_class {
556     my $self = shift;
557
558     if ($self->storage->can('cursor_class')) {
559         $self->storage->cursor_class($self->_default_cursor_class)
560             if $self->storage->cursor_class ne $self->_default_cursor_class;
561     }
562 }
563
564 {
565     my %COMPOSED_CACHE;
566
567     sub composed_schema {
568         my $self = shift;
569         my $class = $self->_original_class_name;
570         my $store = \$COMPOSED_CACHE{$class}{$self->schema_class};
571
572         $$store = shift if @_;
573
574         return $$store
575     }
576 }
577
578 sub _build_model_name {
579     my $self  = shift;
580     my $class = $self->_original_class_name;
581     (my $model_name = $class) =~ s/^[\w:]+::(?:Model|M):://;
582
583     return $model_name;
584 }
585
586 __PACKAGE__->meta->make_immutable;
587
588 =head1 ENVIRONMENT
589
590 =over 4
591
592 =item CMDS_NO_SOURCES
593
594 Set this variable if you will be using schemas with no sources (Result classes)
595 to disable the warning. The warning is there because having no Result classes
596 is usually a mistake.
597
598 =back
599
600 =head1 Setting up DBIC authentication
601
602 You can set this up with 
603 L<Catalyst::Authentication::Store::DBIx::Class> in MyApp.pm:
604
605   package MyApp;
606
607   use Catalyst qw/... Authentication .../;
608
609   ...
610
611   __PACKAGE__->config('Plugin::Authentication' =>
612                 {
613                     default_realm => 'members',
614                     members => {
615                         credential => {
616                             class => 'Password',
617                             password_field => 'password',
618                             password_type => 'hashed'
619                             password_hash_type => 'SHA-256'
620                         },
621                         store => {
622                             class => 'DBIx::Class',
623                             user_model => 'DB::User',
624                             role_relation => 'roles',
625                             role_field => 'rolename',
626                         }
627                     }
628                 });
629
630 =head1 METHOD PROXYING
631
632 The automatic proxying to the underlying L<DBIx::Class::Schema> has been
633 removed as of version C<0.34>, to enable this feature add C<SchemaProxy> to
634 L</traits>.
635
636 See L<Catalyst::TraitFor::Model::DBIC::Schema::SchemaProxy>.
637
638 =head1 SEE ALSO
639
640 General Catalyst Stuff:
641
642 L<Catalyst::Manual>, L<Catalyst::Test>, L<Catalyst::Request>,
643 L<Catalyst::Response>, L<Catalyst::Helper>, L<Catalyst>,
644
645 Stuff related to DBIC and this Model style:
646
647 L<DBIx::Class>, L<DBIx::Class::Schema>,
648 L<DBIx::Class::Schema::Loader>, L<Catalyst::Helper::Model::DBIC::Schema>,
649 L<CatalystX::Component::Traits>, L<MooseX::Traits::Pluggable>
650
651 Traits:
652
653 L<Catalyst::TraitFor::Model::DBIC::Schema::Caching>,
654 L<Catalyst::TraitFor::Model::DBIC::Schema::Replicated>,
655 L<Catalyst::TraitFor::Model::DBIC::Schema::SchemaProxy>,
656 L<Catalyst::TraitFor::Model::DBIC::Schema::PerRequestSchema>,
657 L<Catalyst::TraitFor::Model::DBIC::Schema::QueryLog>
658
659 =head1 AUTHOR
660
661 Brandon L Black C<blblack at gmail.com>
662
663 =head1 CONTRIBUTORS
664
665 caelum: Rafael Kitover C<rkitover at cpan.org>
666
667 dandv: Dan Dascalescu C<dandv at cpan.org>
668
669 bluefeet: Aran Deltac C<bluefeet@cpan.org>
670
671 t0m: Tomas Doran C<bobtfish@bobtfish.net>
672
673 osfameron: C<osfameron@cpan.org>
674
675 ozum: Ozum Eldogan C<ozum@ozum.net>
676
677 Pavel I. Shaydo C<zwon@trinitum.org>
678
679 SineSwiper: Brendan Byrd <byrd.b@insightcom.com>
680
681 =head1 COPYRIGHT
682
683 Copyright (c) 2006 - 2010
684 the Catalyst::Model::DBIC::Schema L</AUTHOR> and L</CONTRIBUTORS>
685 as listed above.
686
687 =head1 LICENSE
688
689 This program is free software. You can redistribute it and/or modify it
690 under the same terms as Perl itself.
691
692 =cut
693
694 1;
695 # vim:sts=4 sw=4 et tw=80: