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   91572 use Moose;
  0            
  0            
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: