draft work on new C::M::DBIC::Schema, untested, and still needs some compat work
[catagits/Catalyst-Model-DBIC-Schema.git] / lib / Catalyst / Model / DBIC / Schema.pm
1 package Catalyst::Model::DBIC::Schema;
2
3 use strict;
4 use base qw/Catalyst::Model/;
5 use NEXT;
6 use UNIVERSAL::require;
7 use Carp;
8 use Data::Dumper;
9 require DBIx::Class;
10
11 our $VERSION = '0.17_01';
12
13 =head1 NAME
14
15 Catalyst::Model::DBIC::Schema - DBIx::Class::Schema Model Class
16
17 =head1 SYNOPSIS
18
19 Manual creation of a DBIx::Class::Schema and a Catalyst::Model::DBIC::Schema:
20
21 =over
22
23 =item 1.
24
25 Create the DBIx:Class schema in MyApp/Schema/FilmDB.pm:
26
27   package MyApp::Schema::FilmDB;
28   use base qw/DBIx::Class::Schema/;
29
30   __PACKAGE__->load_classes(qw/Actor Role/);
31
32 =item 2.
33
34 Create some classes for the tables in the database, for example an 
35 Actor in MyApp/Schema/FilmDB/Actor.pm:
36
37   package MyApp::Schema::FilmDB::Actor;
38   use base qw/DBIx::Class/
39
40   __PACKAGE__->load_components(qw/Core/);
41   __PACKAGE__->table('actor');
42
43   ...
44
45 and a Role in MyApp/Schema/Role.pm:
46
47   package MyApp::Schema::FilmDB::Role;
48   use base qw/DBIx::Class/
49
50   __PACKAGE__->load_components(qw/Core/);
51   __PACKAGE__->table('role');
52
53   ...    
54
55 Notice that the schema is in MyApp::Schema, not in MyApp::Model. This way it's 
56 usable as a standalone module and you can test/run it without Catalyst. 
57
58 =item 3.
59
60 To expose it to Catalyst as a model, you should create a DBIC Model in
61 MyApp/Model/FilmDB.pm:
62
63   package MyApp::Model::FilmDB;
64   use base qw/Catalyst::Model::DBIC::Schema/;
65
66   __PACKAGE__->config(
67       schema_class => 'MyApp::Schema::FilmDB',
68       connect_info => [
69                         "DBI:...",
70                         "username",
71                         "password",
72                         {AutoCommit => 1}
73                       ]
74   );
75
76 See below for a full list of the possible config parameters.
77
78 =back
79
80 Now you have a working Model, accessing your separate DBIC Schema. Which can
81 be used/accessed in the normal Catalyst manner, via $c->model():
82
83   my $actor = $c->model('FilmDB::Actor')->find(1);
84
85 You can also use it to set up DBIC authentication with 
86 Authentication::Store::DBIC in MyApp.pm:
87
88   package MyApp;
89
90   use Catalyst qw/... Authentication::Store::DBIC/;
91
92   ...
93
94   __PACKAGE__->config->{authentication}{dbic} = {
95       user_class      => 'FilmDB::Actor',
96       user_field      => 'name',
97       password_field  => 'password'
98   }
99
100 C<< $c->model() >> returns a L<DBIx::Class::ResultSet> for the source name
101 parameter passed. To find out more about which methods can be called on a
102 ResultSet, or how to add your own methods to it, please see the ResultSet
103 documentation in the L<DBIx::Class> distribution.
104
105 Some examples are given below:
106
107   # You can access schema-level methods directly from the top-level model:
108   $c->model('FilmDB')->source(...);
109   $c->model('FilmDB')->resultset(...);
110   $c->model('FilmDB')->class(...);
111   $c->model('FilmDB')->any_other_schema_method(...);
112
113   # For resultsets, there's an even quicker shortcut:
114   $c->model('FilmDB::Actor')
115   # is the same as $c->model('FilmDB')->resultset('Actor')
116
117 =head1 DESCRIPTION
118
119 This is a Catalyst Model for L<DBIx::Class::Schema>-based Models.  See
120 the documentation for L<Catalyst::Helper::Model::DBIC::Schema> for
121 information on generating these Models via Helper scripts.
122
123 =head1 CONFIG PARAMETERS
124
125 =over 4
126
127 =item schema_class
128
129 This is the classname of your L<DBIx::Class::Schema> Schema.  It needs
130 to be findable in C<@INC>, but it does not need to be inside the 
131 C<Catalyst::Model::> namespace.  This parameter is required.
132
133 =item connect_info
134
135 This is an arrayref of connection parameters, which are specific to your
136 C<storage_type> (see your storage type documentation for more details).
137
138 This is not required if C<schema_class> already has connection information
139 defined inside itself (which isn't highly recommended, but can be done)
140
141 For L<DBIx::Class::Storage::DBI>, which is the only supported
142 C<storage_type> in L<DBIx::Class> at the time of this writing, the
143 parameters are your dsn, username, password, and connect options hashref.
144
145 See L<DBIx::Class::Storage::DBI/connect_info> for more details.
146
147 Examples:
148
149   connect_info => [ 'dbi:Pg:dbname=mypgdb', 'postgres', '' ],
150
151   connect_info => [
152                     'dbi:SQLite:dbname=foo.db',
153                     {
154                       on_connect_do => [
155                         'PRAGMA synchronous = OFF',
156                       ],
157                     }
158                   ],
159
160   connect_info => [
161                     'dbi:Pg:dbname=mypgdb',
162                     'postgres',
163                     '',
164                     { AutoCommit => 0 },
165                     {
166                       on_connect_do => [
167                         'some SQL statement',
168                         'another SQL statement',
169                       ],
170                     }
171                   ],
172
173 =item storage_type
174
175 Allows the use of a different C<storage_type> than what is set in your
176 C<schema_class> (which in turn defaults to C<::DBI> if not set in current
177 L<DBIx::Class>).
178
179 =back
180
181 =head1 METHODS
182
183 =over 4
184
185 =item new
186
187 Instantiates the Model based on the above-documented ->config parameters.
188 The only required parameter is C<schema_class>.  C<connect_info> is
189 required in the case that C<schema_class> does not already have connection
190 information defined for it.
191
192 =item COMPONENT
193
194 Tells the Catalyst component architecture that the encapsulated schema
195 object is to be returned for $c->model calls for this model name.
196
197 =back
198
199 =cut
200
201 sub new {
202     my $self = shift->NEXT::new(@_);
203     
204     my $class = ref($self);
205     my $model_name = $class;
206     $model_name =~ s/^[\w:]+::(?:Model|M):://;
207
208     croak "->config->{schema_class} must be defined for this model"
209         unless $self->{schema_class};
210
211     my $schema_class = $self->{schema_class};
212
213     $schema_class->require
214         or croak "Cannot load schema_class '$schema_class': $@";
215
216     my $schema_obj = $schema_class->clone;
217     $schema_obj->storage_type($self->{storage_type}) if $self->{storage_type};
218     $schema_obj->connection(@{$self->{connect_info}}) if $self->{connect_info};
219
220     if(!$schema_obj->storage) {
221         croak "Either ->config->{connect_info} must be defined for $class"
222               . " or $schema_class must have connect info defined on it. "
223               . "Here's what we got:\n"
224               . Dumper($self);
225     }
226
227     $self->{schema_obj} = $schema_obj;
228
229     no strict 'refs';
230     foreach my $moniker ($self->schema->sources) {
231         my $classname = "${class}::$moniker";
232         # XXX -- Does this need to be dynamic, or can it be done w/ COMPONENT too?
233         *{"${classname}::ACCEPT_CONTEXT"} = sub {
234             shift;
235             shift->model($model_name)->resultset($moniker);
236         }
237     }
238
239     return $self;
240 }
241
242 sub COMPONENT { shift->{schema_obj} }
243
244 =head1 SEE ALSO
245
246 General Catalyst Stuff:
247
248 L<Catalyst::Manual>, L<Catalyst::Test>, L<Catalyst::Request>,
249 L<Catalyst::Response>, L<Catalyst::Helper>, L<Catalyst>,
250
251 Stuff related to DBIC and this Model style:
252
253 L<DBIx::Class>, L<DBIx::Class::Schema>,
254 L<DBIx::Class::Schema::Loader>, L<Catalyst::Helper::Model::DBIC::Schema>
255
256 =head1 AUTHOR
257
258 Brandon L Black, C<blblack@gmail.com>
259
260 =head1 COPYRIGHT
261
262 This program is free software, you can redistribute it and/or modify it
263 under the same terms as Perl itself.
264
265 =cut
266
267 1;