flesh out doc for 'from', 'select', 'as' and 'group_by'
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Schema.pm
CommitLineData
a02675cd 1package DBIx::Class::Schema;
2
3use strict;
4use warnings;
aa562407 5
6use Carp qw/croak/;
66d9ef6b 7use UNIVERSAL::require;
a02675cd 8
41a6f8c0 9use base qw/DBIx::Class/;
a02675cd 10
42a1aaa1 11__PACKAGE__->load_components(qw/Exception/);
0dc79249 12__PACKAGE__->mk_classdata('class_mappings' => {});
13__PACKAGE__->mk_classdata('source_registrations' => {});
d7156e50 14__PACKAGE__->mk_classdata('storage_type' => 'DBI');
15__PACKAGE__->mk_classdata('storage');
a02675cd 16
c2da098a 17=head1 NAME
18
19DBIx::Class::Schema - composable schemas
20
21=head1 SYNOPSIS
22
c2da098a 23 package My::Schema;
c2da098a 24 use base qw/DBIx::Class::Schema/;
a3d93194 25
26 # load My::Schema::Foo, My::Schema::Bar, My::Schema::Baz
c2da098a 27 __PACKAGE__->load_classes(qw/Foo Bar Baz/);
28
c2da098a 29 package My::Schema::Foo;
03312470 30 use base qw/DBIx::Class/;
54540863 31 __PACKAGE__->load_components(qw/PK::Auto::Pg Core/); # for example
c2da098a 32 __PACKAGE__->table('foo');
c2da098a 33
a3d93194 34 my $schema1 = My::Schema->connect(
35 $dsn,
36 $user,
37 $password,
38 $attrs
39 );
c2da098a 40
a3d93194 41 my $schema2 = My::Schema->connect( ... );
c2da098a 42
a3d93194 43 # fetch objects using My::Schema::Foo
44 my $resultset = $schema1->resultset('Foo')->search( ... );
45 my @objects = $schema2->resultset('Foo')->search( ... );
c2da098a 46
47=head1 DESCRIPTION
48
a3d93194 49Creates database classes based on a schema. This is the recommended way to
50use L<DBIx::Class> and allows you to use more than one concurrent connection
51with your classes.
429bd4f1 52
03312470 53NB: If you're used to L<Class::DBI> it's worth reading the L</SYNOPSIS>
54carefully as DBIx::Class does things a little differently. Note in
55particular which module inherits off which.
56
c2da098a 57=head1 METHODS
58
0dc79249 59=head2 register_class <moniker> <component_class>
076652e8 60
66d9ef6b 61Registers a class which isa ResultSourceInstance; equivalent to calling
62
63 $schema->register_source($moniker, $class->result_source_instance);
076652e8 64
c2da098a 65=cut
66
a02675cd 67sub register_class {
0dc79249 68 my ($self, $moniker, $to_register) = @_;
69 $self->register_source($moniker => $to_register->result_source_instance);
74b92d9a 70}
71
0dc79249 72=head2 register_source <moniker> <result source>
076652e8 73
0dc79249 74Registers the result source in the schema with the given moniker
076652e8 75
76=cut
77
0dc79249 78sub register_source {
79 my ($self, $moniker, $source) = @_;
80 my %reg = %{$self->source_registrations};
81 $reg{$moniker} = $source;
82 $self->source_registrations(\%reg);
83 $source->schema($self);
84 if ($source->result_class) {
85 my %map = %{$self->class_mappings};
86 $map{$source->result_class} = $moniker;
87 $self->class_mappings(\%map);
88 }
89}
a02675cd 90
bfb2bd4f 91=head2 class
92
93 my $class = $schema->class('Foo');
94
0dc79249 95Retrieves the result class name for a given result source
bfb2bd4f 96
97=cut
98
99sub class {
0dc79249 100 my ($self, $moniker) = @_;
101 return $self->source($moniker)->result_class;
bfb2bd4f 102}
103
ea20d0fd 104=head2 source
105
106 my $source = $schema->source('Foo');
107
108Returns the result source object for the registered name
109
110=cut
111
112sub source {
0dc79249 113 my ($self, $moniker) = @_;
114 my $sreg = $self->source_registrations;
115 return $sreg->{$moniker} if exists $sreg->{$moniker};
116
117 # if we got here, they probably passed a full class name
118 my $mapped = $self->class_mappings->{$moniker};
aa562407 119 croak "Can't find source for ${moniker}"
0dc79249 120 unless $mapped && exists $sreg->{$mapped};
121 return $sreg->{$mapped};
ea20d0fd 122}
123
0dc79249 124=head2 sources
125
126 my @source_monikers = $schema->sources;
127
128Returns the source monikers of all source registrations on this schema
129
130=cut
131
132sub sources { return keys %{shift->source_registrations}; }
133
ea20d0fd 134=head2 resultset
135
136 my $rs = $schema->resultset('Foo');
137
0dc79249 138Returns the resultset for the registered moniker
ea20d0fd 139
140=cut
141
142sub resultset {
0dc79249 143 my ($self, $moniker) = @_;
144 return $self->source($moniker)->resultset;
ea20d0fd 145}
146
130c6439 147=head2 load_classes [<classes>, (<class>, <class>), {<namespace> => [<classes>]}]
076652e8 148
429bd4f1 149Uses L<Module::Find> to find all classes under the database class' namespace,
150or uses the classes you select. Then it loads the component (using L<use>),
151and registers them (using B<register_class>);
076652e8 152
5ce32fc1 153It is possible to comment out classes with a leading '#', but note that perl
154will think it's a mistake (trying to use a comment in a qw list) so you'll
155need to add "no warnings 'qw';" before your load_classes call.
156
076652e8 157=cut
158
a02675cd 159sub load_classes {
5ce32fc1 160 my ($class, @params) = @_;
161
162 my %comps_for;
163
164 if (@params) {
165 foreach my $param (@params) {
166 if (ref $param eq 'ARRAY') {
167 # filter out commented entries
168 my @modules = grep { $_ !~ /^#/ } @$param;
169
170 push (@{$comps_for{$class}}, @modules);
171 }
172 elsif (ref $param eq 'HASH') {
173 # more than one namespace possible
174 for my $comp ( keys %$param ) {
175 # filter out commented entries
176 my @modules = grep { $_ !~ /^#/ } @{$param->{$comp}};
177
178 push (@{$comps_for{$comp}}, @modules);
179 }
180 }
181 else {
182 # filter out commented entries
183 push (@{$comps_for{$class}}, $param) if $param !~ /^#/;
184 }
185 }
186 } else {
41a6f8c0 187 eval "require Module::Find;";
188 $class->throw("No arguments to load_classes and couldn't load".
189 " Module::Find ($@)") if $@;
5ce32fc1 190 my @comp = map { substr $_, length "${class}::" } Module::Find::findallmod($class);
191 $comps_for{$class} = \@comp;
41a6f8c0 192 }
5ce32fc1 193
194 foreach my $prefix (keys %comps_for) {
195 foreach my $comp (@{$comps_for{$prefix}||[]}) {
196 my $comp_class = "${prefix}::${comp}";
5ce32fc1 197 eval "use $comp_class"; # If it fails, assume the user fixed it
bfb2bd4f 198 if ($@) {
199 die $@ unless $@ =~ /Can't locate/;
200 }
5ce32fc1 201 $class->register_class($comp => $comp_class);
202 }
a02675cd 203 }
204}
205
130c6439 206=head2 compose_connection <target> <@db_info>
429bd4f1 207
208This is the most important method in this class. it takes a target namespace,
209as well as dbh connection info, and creates a L<DBIx::Class::DB> class as
210well as subclasses for each of your database classes in this namespace, using
211this connection.
076652e8 212
54540863 213It will also setup a ->class method on the target class, which lets you
8c130052 214resolve database classes based on the schema component name, for example
215
54540863 216 MyApp::DB->class('Foo') # returns MyApp::DB::Foo,
8c130052 217 # which ISA MyApp::Schema::Foo
218
219This is the recommended API for accessing Schema generated classes, and
220using it might give you instant advantages with future versions of DBIC.
221
54540863 222WARNING: Loading components into Schema classes after compose_connection
223may not cause them to be seen by the classes in your target namespace due
224to the dispatch table approach used by Class::C3. If you do this you may find
225you need to call Class::C3->reinitialize() afterwards to get the behaviour
226you expect.
227
076652e8 228=cut
229
a02675cd 230sub compose_connection {
ea20d0fd 231 my ($self, $target, @info) = @_;
66d9ef6b 232 my $base = 'DBIx::Class::ResultSetInstance';
233 $base->require;
234 my $schema = $self->compose_namespace($target, $base);
235 $schema->connection(@info);
0dc79249 236 foreach my $moniker ($schema->sources) {
237 my $source = $schema->source($moniker);
238 my $class = $source->result_class;
239 #warn "$moniker $class $source ".$source->storage;
8c49f629 240 $class->mk_classdata(result_source_instance => $source);
ea20d0fd 241 $class->mk_classdata(resultset_instance => $source->resultset);
66d9ef6b 242 $class->mk_classdata(class_resolver => $schema);
bfb2bd4f 243 }
244 return $schema;
e678398e 245}
246
247sub compose_namespace {
66d9ef6b 248 my ($self, $target, $base) = @_;
249 my %reg = %{ $self->source_registrations };
11b78bd6 250 my %target;
251 my %map;
66d9ef6b 252 my $schema = $self->clone;
253 foreach my $moniker ($schema->sources) {
254 my $source = $schema->source($moniker);
0dc79249 255 my $target_class = "${target}::${moniker}";
66d9ef6b 256 $self->inject_base(
0dc79249 257 $target_class => $source->result_class, ($base ? $base : ())
258 );
66d9ef6b 259 $source->result_class($target_class);
b7951443 260 }
11b78bd6 261 {
262 no strict 'refs';
bfb2bd4f 263 *{"${target}::schema"} =
264 sub { $schema };
1edaf6fe 265 foreach my $meth (qw/class source resultset/) {
266 *{"${target}::${meth}"} =
267 sub { shift->schema->$meth(@_) };
268 }
11b78bd6 269 }
bfb2bd4f 270 return $schema;
b7951443 271}
272
130c6439 273=head2 setup_connection_class <$target> <@info>
076652e8 274
429bd4f1 275Sets up a database connection class to inject between the schema
276and the subclasses the schema creates.
277
076652e8 278=cut
279
b7951443 280sub setup_connection_class {
281 my ($class, $target, @info) = @_;
63e9583a 282 $class->inject_base($target => 'DBIx::Class::DB');
283 #$target->load_components('DB');
b7951443 284 $target->connection(@info);
285}
286
66d9ef6b 287=head2 connection(@args)
288
289Instantiates a new Storage object of type storage_type and passes the
290arguments to $storage->connection_info. Sets the connection in-place on
291the schema.
292
293=cut
294
295sub connection {
296 my ($self, @info) = @_;
297 my $storage_class = 'DBIx::Class::Storage::'.$self->storage_type;
298 $storage_class->require;
299 my $storage = $storage_class->new;
300 $storage->connect_info(\@info);
301 $self->storage($storage);
302 return $self;
303}
304
305=head2 connect(@info)
306
307Conveneience method, equivalent to $schema->clone->connection(@info)
308
309=cut
310
311sub connect { shift->clone->connection(@_) };
312
313=head2 clone
314
315Clones the schema and its associated result_source objects and returns the
316copy.
317
318=cut
319
320sub clone {
321 my ($self) = @_;
322 my $clone = bless({ (ref $self ? %$self : ()) }, ref $self || $self);
323 foreach my $moniker ($self->sources) {
324 my $source = $self->source($moniker);
325 my $new = $source->new($source);
326 $clone->register_source($moniker => $new);
327 }
328 return $clone;
329}
330
a02675cd 3311;
c2da098a 332
c2da098a 333=head1 AUTHORS
334
daec44b8 335Matt S. Trout <mst@shadowcatsystems.co.uk>
c2da098a 336
337=head1 LICENSE
338
339You may distribute this code under the same terms as Perl itself.
340
341=cut
342