File Coverage

blib/lib/Catalyst/Model/DBIC/Schema.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package Catalyst::Model::DBIC::Schema;
2              
3 3     3   93443 use Moose;
  0            
  0            
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/;
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', default => sub { 1 });
440              
441             has install_model_shortcuts => (is => 'ro', default => sub { 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: