e44324e875bd4cd4b7668506a2acbd557e22dafc
[catagits/Catalyst-Model-DBIC-Schema.git] / lib / Catalyst / Model / DBIC / Schema.pm
1 package Catalyst::Model::DBIC::Schema;
2
3 use strict;
4 use warnings;
5
6 our $VERSION = '0.22';
7
8 use base qw/Catalyst::Model Class::Accessor::Fast Class::Data::Accessor/;
9 use NEXT;
10 use UNIVERSAL::require;
11 use Carp;
12 use Data::Dumper;
13 require DBIx::Class;
14
15 __PACKAGE__->mk_classaccessor('composed_schema');
16 __PACKAGE__->mk_accessors('schema');
17
18 =head1 NAME
19
20 Catalyst::Model::DBIC::Schema - DBIx::Class::Schema Model Class
21
22 =head1 SYNOPSIS
23
24 Manual creation of a DBIx::Class::Schema and a Catalyst::Model::DBIC::Schema:
25
26 =over
27
28 =item 1.
29
30 Create the DBIx:Class schema in MyApp/Schema/FilmDB.pm:
31
32   package MyApp::Schema::FilmDB;
33   use base qw/DBIx::Class::Schema/;
34
35   __PACKAGE__->load_classes(qw/Actor Role/);
36
37 =item 2.
38
39 Create some classes for the tables in the database, for example an 
40 Actor in MyApp/Schema/FilmDB/Actor.pm:
41
42   package MyApp::Schema::FilmDB::Actor;
43   use base qw/DBIx::Class/
44
45   __PACKAGE__->load_components(qw/Core/);
46   __PACKAGE__->table('actor');
47
48   ...
49
50 and a Role in MyApp/Schema/FilmDB/Role.pm:
51
52   package MyApp::Schema::FilmDB::Role;
53   use base qw/DBIx::Class/
54
55   __PACKAGE__->load_components(qw/Core/);
56   __PACKAGE__->table('role');
57
58   ...    
59
60 Notice that the schema is in MyApp::Schema, not in MyApp::Model. This way it's 
61 usable as a standalone module and you can test/run it without Catalyst. 
62
63 =item 3.
64
65 To expose it to Catalyst as a model, you should create a DBIC Model in
66 MyApp/Model/FilmDB.pm:
67
68   package MyApp::Model::FilmDB;
69   use base qw/Catalyst::Model::DBIC::Schema/;
70
71   __PACKAGE__->config(
72       schema_class => 'MyApp::Schema::FilmDB',
73       connect_info => [
74                         "DBI:...",
75                         "username",
76                         "password",
77                         {AutoCommit => 1}
78                       ]
79   );
80
81 See below for a full list of the possible config parameters.
82
83 =back
84
85 Now you have a working Model, accessing your separate DBIC Schema. Which can
86 be used/accessed in the normal Catalyst manner, via $c->model():
87
88   my $actor = $c->model('FilmDB::Actor')->find(1);
89
90 You can also use it to set up DBIC authentication with 
91 Authentication::Store::DBIC in MyApp.pm:
92
93   package MyApp;
94
95   use Catalyst qw/... Authentication::Store::DBIC/;
96
97   ...
98
99   __PACKAGE__->config->{authentication}{dbic} = {
100       user_class      => 'FilmDB::Actor',
101       user_field      => 'name',
102       password_field  => 'password'
103   }
104
105 C<< $c->model('Schema::Source') >> returns a L<DBIx::Class::ResultSet> for 
106 the source name parameter passed. To find out more about which methods can 
107 be called on a ResultSet, or how to add your own methods to it, please see 
108 the ResultSet documentation in the L<DBIx::Class> distribution.
109
110 Some examples are given below:
111
112   # to access schema methods directly:
113   $c->model('FilmDB')->schema->source(...);
114
115   # to access the source object, resultset, and class:
116   $c->model('FilmDB')->source(...);
117   $c->model('FilmDB')->resultset(...);
118   $c->model('FilmDB')->class(...);
119
120   # For resultsets, there's an even quicker shortcut:
121   $c->model('FilmDB::Actor')
122   # is the same as $c->model('FilmDB')->resultset('Actor')
123
124   # To get the composed schema for making new connections:
125   my $newconn = $c->model('FilmDB')->composed_schema->connect(...);
126
127   # Or the same thing via a convenience shortcut:
128   my $newconn = $c->model('FilmDB')->connect(...);
129
130   # or, if your schema works on different storage drivers:
131   my $newconn = $c->model('FilmDB')->composed_schema->clone();
132   $newconn->storage_type('::LDAP');
133   $newconn->connection(...);
134
135   # and again, a convenience shortcut
136   my $newconn = $c->model('FilmDB')->clone();
137   $newconn->storage_type('::LDAP');
138   $newconn->connection(...);
139
140 =head1 DESCRIPTION
141
142 This is a Catalyst Model for L<DBIx::Class::Schema>-based Models.  See
143 the documentation for L<Catalyst::Helper::Model::DBIC::Schema> for
144 information on generating these Models via Helper scripts.
145
146 When your Catalyst app starts up, a thin Model layer is created as an 
147 interface to your DBIC Schema. It should be clearly noted that the model 
148 object returned by C<< $c->model('FilmDB') >> is NOT itself a DBIC schema or 
149 resultset object, but merely a wrapper proving L<methods|/METHODS> to access 
150 the underlying schema. 
151
152 In addition to this model class, a shortcut class is generated for each 
153 source in the schema, allowing easy and direct access to a resultset of the 
154 corresponding type. These generated classes are even thinner than the model 
155 class, providing no public methods but simply hooking into Catalyst's 
156 model() accessor via the 
157 L<ACCEPT_CONTEXT|Catalyst::Component/ACCEPT_CONTEXT> mechanism. The complete 
158 contents of each generated class is roughly equivalent to the following:
159
160   package MyApp::Model::FilmDB::Actor
161   sub ACCEPT_CONTEXT {
162       my ($self, $c) = @_;
163       $c->model('FilmDB')->resultset('Actor');
164   }
165
166 In short, there are three techniques available for obtaining a DBIC 
167 resultset object: 
168
169   # the long way
170   my $rs = $c->model('FilmDB')->schema->resultset('Actor');
171
172   # using the shortcut method on the model object
173   my $rs = $c->model('FilmDB')->resultset('Actor');
174
175   # using the generated class directly
176   my $rs = $c->model('FilmDB::Actor');
177
178 =head1 CONFIG PARAMETERS
179
180 =over 4
181
182 =item schema_class
183
184 This is the classname of your L<DBIx::Class::Schema> Schema.  It needs
185 to be findable in C<@INC>, but it does not need to be inside the 
186 C<Catalyst::Model::> namespace.  This parameter is required.
187
188 =item connect_info
189
190 This is an arrayref of connection parameters, which are specific to your
191 C<storage_type> (see your storage type documentation for more details). 
192 If you only need one parameter (e.g. the DSN), you can just pass a string 
193 instead of an arrayref.
194
195 This is not required if C<schema_class> already has connection information
196 defined inside itself (which isn't highly recommended, but can be done)
197
198 For L<DBIx::Class::Storage::DBI>, which is the only supported
199 C<storage_type> in L<DBIx::Class> at the time of this writing, the
200 parameters are your dsn, username, password, and connect options hashref.
201
202 See L<DBIx::Class::Storage::DBI/connect_info> for a detailed explanation
203 of the arguments supported.
204
205 Examples:
206
207   connect_info => [ 'dbi:Pg:dbname=mypgdb', 'postgres', '' ],
208
209   connect_info => [
210                     'dbi:SQLite:dbname=foo.db',
211                     {
212                       on_connect_do => [
213                         'PRAGMA synchronous = OFF',
214                       ],
215                     }
216                   ],
217
218   connect_info => [
219                     'dbi:Pg:dbname=mypgdb',
220                     'postgres',
221                     '',
222                     { AutoCommit => 0 },
223                     {
224                       on_connect_do => [
225                         'some SQL statement',
226                         'another SQL statement',
227                       ],
228                     }
229                   ],
230
231 Or using L<Config::General>:
232
233     <Model::FilmDB>
234         schema_class   MyApp::Schema::FilmDB
235         connect_info   dbi:Pg:dbname=mypgdb
236         connect_info   postgres
237         connect_info
238         <connect_info>
239             AutoCommit   0
240             on_connect_do   some SQL statement
241             on_connect_do   another SQL statement
242         </connect_info>
243     </Model::FilmDB>
244
245 or
246
247     <Model::FilmDB>
248         schema_class   MyApp::Schema::FilmDB
249         connect_info   dbi:SQLite:dbname=foo.db
250     </Model::FilmDB>
251
252
253 =item storage_type
254
255 Allows the use of a different C<storage_type> than what is set in your
256 C<schema_class> (which in turn defaults to C<::DBI> if not set in current
257 L<DBIx::Class>).  Completely optional, and probably unnecessary for most
258 people until other storage backends become available for L<DBIx::Class>.
259
260 =back
261
262 =head1 METHODS
263
264 =over 4
265
266 =item new
267
268 Instantiates the Model based on the above-documented ->config parameters.
269 The only required parameter is C<schema_class>.  C<connect_info> is
270 required in the case that C<schema_class> does not already have connection
271 information defined for it.
272
273 =item schema
274
275 Accessor which returns the connected schema being used by the this model.
276 There are direct shortcuts on the model class itself for
277 schema->resultset, schema->source, and schema->class.
278
279 =item composed_schema
280
281 Accessor which returns the composed schema, which has no connection info,
282 which was used in constructing the C<schema> above.  Useful for creating
283 new connections based on the same schema/model.  There are direct shortcuts
284 from the model object for composed_schema->clone and composed_schema->connect
285
286 =item clone
287
288 Shortcut for ->composed_schema->clone
289
290 =item connect
291
292 Shortcut for ->composed_schema->connect
293
294 =item source
295
296 Shortcut for ->schema->source
297
298 =item class
299
300 Shortcut for ->schema->class
301
302 =item resultset
303
304 Shortcut for ->schema->resultset
305
306 =item storage
307
308 Provides an accessor for the connected schema's storage object.
309 Used often for debugging and controlling transactions.
310
311 =back
312
313 =cut
314
315 sub new {
316     my $self = shift->NEXT::new(@_);
317     
318     my $class = ref($self);
319     my $model_name = $class;
320     $model_name =~ s/^[\w:]+::(?:Model|M):://;
321
322     croak "->config->{schema_class} must be defined for this model"
323         unless $self->{schema_class};
324
325     my $schema_class = $self->{schema_class};
326
327     $schema_class->require
328         or croak "Cannot load schema class '$schema_class': $@";
329
330     if( !$self->{connect_info} ) {
331         if($schema_class->storage && $schema_class->storage->connect_info) {
332             $self->{connect_info} = $schema_class->storage->connect_info;
333         }
334         else {
335             croak "Either ->config->{connect_info} must be defined for $class"
336                   . " or $schema_class must have connect info defined on it."
337                   . " Here's what we got:\n"
338                   . Dumper($self);
339         }
340     }
341
342     $self->composed_schema($schema_class->compose_namespace($class));
343     $self->schema($self->composed_schema->clone);
344
345     $self->schema->storage_type($self->{storage_type})
346         if $self->{storage_type};
347
348     $self->schema->connection( 
349         ref $self->{connect_info} eq 'ARRAY' ? 
350         @{$self->{connect_info}} : 
351         $self->{connect_info}
352     );
353     
354     no strict 'refs';
355     foreach my $moniker ($self->schema->sources) {
356         my $classname = "${class}::$moniker";
357         *{"${classname}::ACCEPT_CONTEXT"} = sub {
358             shift;
359             shift->model($model_name)->resultset($moniker);
360         }
361     }
362
363     return $self;
364 }
365
366 sub clone { shift->composed_schema->clone(@_); }
367
368 sub connect { shift->composed_schema->connect(@_); }
369
370 sub storage { shift->schema->storage(@_); }
371
372 =head1 SEE ALSO
373
374 General Catalyst Stuff:
375
376 L<Catalyst::Manual>, L<Catalyst::Test>, L<Catalyst::Request>,
377 L<Catalyst::Response>, L<Catalyst::Helper>, L<Catalyst>,
378
379 Stuff related to DBIC and this Model style:
380
381 L<DBIx::Class>, L<DBIx::Class::Schema>,
382 L<DBIx::Class::Schema::Loader>, L<Catalyst::Helper::Model::DBIC::Schema>
383
384 =head1 AUTHOR
385
386 Brandon L Black, C<blblack@gmail.com>
387
388 =head1 COPYRIGHT
389
390 This program is free software, you can redistribute it and/or modify it
391 under the same terms as Perl itself.
392
393 =cut
394
395 1;