C::M::DBIC::Schema -- better defaults for create=static, with backcompat
[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.23';
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 which accesses your separate DBIC Schema. This 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 In order to add methods to a DBIC resultset, you cannot simply add them to 
179 the source (row, table) definition class; you must define a separate custom 
180 resultset class. See L<DBIx::Class::Manual::Cookbook/"Predefined searches"> 
181 for more info.
182
183 =head1 CONFIG PARAMETERS
184
185 =over 4
186
187 =item schema_class
188
189 This is the classname of your L<DBIx::Class::Schema> Schema.  It needs
190 to be findable in C<@INC>, but it does not need to be inside the 
191 C<Catalyst::Model::> namespace.  This parameter is required.
192
193 =item connect_info
194
195 This is an arrayref of connection parameters, which are specific to your
196 C<storage_type> (see your storage type documentation for more details). 
197 If you only need one parameter (e.g. the DSN), you can just pass a string 
198 instead of an arrayref.
199
200 This is not required if C<schema_class> already has connection information
201 defined inside itself (which isn't highly recommended, but can be done)
202
203 For L<DBIx::Class::Storage::DBI>, which is the only supported
204 C<storage_type> in L<DBIx::Class> at the time of this writing, the
205 parameters are your dsn, username, password, and connect options hashref.
206
207 See L<DBIx::Class::Storage::DBI/connect_info> for a detailed explanation
208 of the arguments supported.
209
210 Examples:
211
212   connect_info => [ 'dbi:Pg:dbname=mypgdb', 'postgres', '' ],
213
214   connect_info => [
215                     'dbi:SQLite:dbname=foo.db',
216                     {
217                       on_connect_do => [
218                         'PRAGMA synchronous = OFF',
219                       ],
220                     }
221                   ],
222
223   connect_info => [
224                     'dbi:Pg:dbname=mypgdb',
225                     'postgres',
226                     '',
227                     { AutoCommit => 0 },
228                     {
229                       on_connect_do => [
230                         'some SQL statement',
231                         'another SQL statement',
232                       ],
233                     }
234                   ],
235
236 Or using L<Config::General>:
237
238     <Model::FilmDB>
239         schema_class   MyApp::Schema::FilmDB
240         connect_info   dbi:Pg:dbname=mypgdb
241         connect_info   postgres
242         connect_info
243         <connect_info>
244             AutoCommit   0
245             on_connect_do   some SQL statement
246             on_connect_do   another SQL statement
247         </connect_info>
248     </Model::FilmDB>
249
250 or
251
252     <Model::FilmDB>
253         schema_class   MyApp::Schema::FilmDB
254         connect_info   dbi:SQLite:dbname=foo.db
255     </Model::FilmDB>
256
257
258 =item storage_type
259
260 Allows the use of a different C<storage_type> than what is set in your
261 C<schema_class> (which in turn defaults to C<::DBI> if not set in current
262 L<DBIx::Class>).  Completely optional, and probably unnecessary for most
263 people until other storage backends become available for L<DBIx::Class>.
264
265 =back
266
267 =head1 METHODS
268
269 =over 4
270
271 =item new
272
273 Instantiates the Model based on the above-documented ->config parameters.
274 The only required parameter is C<schema_class>.  C<connect_info> is
275 required in the case that C<schema_class> does not already have connection
276 information defined for it.
277
278 =item schema
279
280 Accessor which returns the connected schema being used by the this model.
281 There are direct shortcuts on the model class itself for
282 schema->resultset, schema->source, and schema->class.
283
284 =item composed_schema
285
286 Accessor which returns the composed schema, which has no connection info,
287 which was used in constructing the C<schema> above.  Useful for creating
288 new connections based on the same schema/model.  There are direct shortcuts
289 from the model object for composed_schema->clone and composed_schema->connect
290
291 =item clone
292
293 Shortcut for ->composed_schema->clone
294
295 =item connect
296
297 Shortcut for ->composed_schema->connect
298
299 =item source
300
301 Shortcut for ->schema->source
302
303 =item class
304
305 Shortcut for ->schema->class
306
307 =item resultset
308
309 Shortcut for ->schema->resultset
310
311 =item storage
312
313 Provides an accessor for the connected schema's storage object.
314 Used often for debugging and controlling transactions.
315
316 =back
317
318 =cut
319
320 sub new {
321     my $self = shift->NEXT::new(@_);
322     
323     my $class = ref($self);
324     my $model_name = $class;
325     $model_name =~ s/^[\w:]+::(?:Model|M):://;
326
327     croak "->config->{schema_class} must be defined for this model"
328         unless $self->{schema_class};
329
330     my $schema_class = $self->{schema_class};
331
332     $schema_class->require
333         or croak "Cannot load schema class '$schema_class': $@";
334
335     if( !$self->{connect_info} ) {
336         if($schema_class->storage && $schema_class->storage->connect_info) {
337             $self->{connect_info} = $schema_class->storage->connect_info;
338         }
339         else {
340             croak "Either ->config->{connect_info} must be defined for $class"
341                   . " or $schema_class must have connect info defined on it."
342                   . " Here's what we got:\n"
343                   . Dumper($self);
344         }
345     }
346
347     $self->composed_schema($schema_class->compose_namespace($class));
348     $self->schema($self->composed_schema->clone);
349
350     $self->schema->storage_type($self->{storage_type})
351         if $self->{storage_type};
352
353     $self->schema->connection( 
354         ref $self->{connect_info} eq 'ARRAY' ? 
355         @{$self->{connect_info}} : 
356         $self->{connect_info}
357     );
358     
359     no strict 'refs';
360     foreach my $moniker ($self->schema->sources) {
361         my $classname = "${class}::$moniker";
362         *{"${classname}::ACCEPT_CONTEXT"} = sub {
363             shift;
364             shift->model($model_name)->resultset($moniker);
365         }
366     }
367
368     return $self;
369 }
370
371 sub clone { shift->composed_schema->clone(@_); }
372
373 sub connect { shift->composed_schema->connect(@_); }
374
375 sub storage { shift->schema->storage(@_); }
376
377 =head1 SEE ALSO
378
379 General Catalyst Stuff:
380
381 L<Catalyst::Manual>, L<Catalyst::Test>, L<Catalyst::Request>,
382 L<Catalyst::Response>, L<Catalyst::Helper>, L<Catalyst>,
383
384 Stuff related to DBIC and this Model style:
385
386 L<DBIx::Class>, L<DBIx::Class::Schema>,
387 L<DBIx::Class::Schema::Loader>, L<Catalyst::Helper::Model::DBIC::Schema>
388
389 =head1 AUTHOR
390
391 Brandon L Black, C<blblack@gmail.com>
392
393 =head1 COPYRIGHT
394
395 This program is free software, you can redistribute it and/or modify it
396 under the same terms as Perl itself.
397
398 =cut
399
400 1;