proxy result_class, and fix load_namespace to use it correctly
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Schema.pm
CommitLineData
a02675cd 1package DBIx::Class::Schema;
2
3use strict;
4use warnings;
aa562407 5
701da8c4 6use Carp::Clan qw/^DBIx::Class/;
a917fb06 7use Scalar::Util qw/weaken/;
a02675cd 8
41a6f8c0 9use base qw/DBIx::Class/;
a02675cd 10
0dc79249 11__PACKAGE__->mk_classdata('class_mappings' => {});
12__PACKAGE__->mk_classdata('source_registrations' => {});
1e10a11d 13__PACKAGE__->mk_classdata('storage_type' => '::DBI');
d7156e50 14__PACKAGE__->mk_classdata('storage');
82cc0386 15__PACKAGE__->mk_classdata('exception_action');
a02675cd 16
c2da098a 17=head1 NAME
18
19DBIx::Class::Schema - composable schemas
20
21=head1 SYNOPSIS
22
24d67825 23 package Library::Schema;
c2da098a 24 use base qw/DBIx::Class::Schema/;
bab77431 25
24d67825 26 # load Library::Schema::CD, Library::Schema::Book, Library::Schema::DVD
27 __PACKAGE__->load_classes(qw/CD Book DVD/);
c2da098a 28
24d67825 29 package Library::Schema::CD;
03312470 30 use base qw/DBIx::Class/;
77254782 31 __PACKAGE__->load_components(qw/PK::Auto Core/); # for example
24d67825 32 __PACKAGE__->table('cd');
c2da098a 33
5d9076f2 34 # Elsewhere in your code:
24d67825 35 my $schema1 = Library::Schema->connect(
a3d93194 36 $dsn,
37 $user,
38 $password,
24d67825 39 { AutoCommit => 0 },
a3d93194 40 );
bab77431 41
24d67825 42 my $schema2 = Library::Schema->connect($coderef_returning_dbh);
c2da098a 43
24d67825 44 # fetch objects using Library::Schema::DVD
45 my $resultset = $schema1->resultset('DVD')->search( ... );
46 my @dvd_objects = $schema2->resultset('DVD')->search( ... );
c2da098a 47
48=head1 DESCRIPTION
49
a3d93194 50Creates database classes based on a schema. This is the recommended way to
51use L<DBIx::Class> and allows you to use more than one concurrent connection
52with your classes.
429bd4f1 53
03312470 54NB: If you're used to L<Class::DBI> it's worth reading the L</SYNOPSIS>
2053ab2a 55carefully, as DBIx::Class does things a little differently. Note in
03312470 56particular which module inherits off which.
57
c2da098a 58=head1 METHODS
59
87c4e602 60=head2 register_class
61
27f01d1f 62=over 4
63
ebc77b53 64=item Arguments: $moniker, $component_class
27f01d1f 65
66=back
076652e8 67
71f9df37 68Registers a class which isa DBIx::Class::ResultSourceProxy. Equivalent to
2053ab2a 69calling:
66d9ef6b 70
181a28f4 71 $schema->register_source($moniker, $component_class->result_source_instance);
076652e8 72
c2da098a 73=cut
74
a02675cd 75sub register_class {
0dc79249 76 my ($self, $moniker, $to_register) = @_;
77 $self->register_source($moniker => $to_register->result_source_instance);
74b92d9a 78}
79
87c4e602 80=head2 register_source
81
27f01d1f 82=over 4
83
ebc77b53 84=item Arguments: $moniker, $result_source
27f01d1f 85
86=back
076652e8 87
82b01c38 88Registers the L<DBIx::Class::ResultSource> in the schema with the given
89moniker.
076652e8 90
91=cut
92
0dc79249 93sub register_source {
94 my ($self, $moniker, $source) = @_;
95 my %reg = %{$self->source_registrations};
96 $reg{$moniker} = $source;
97 $self->source_registrations(\%reg);
98 $source->schema($self);
a917fb06 99 weaken($source->{schema}) if ref($self);
0dc79249 100 if ($source->result_class) {
101 my %map = %{$self->class_mappings};
102 $map{$source->result_class} = $moniker;
103 $self->class_mappings(\%map);
104 }
75d07914 105}
a02675cd 106
bfb2bd4f 107=head2 class
108
27f01d1f 109=over 4
82b01c38 110
ebc77b53 111=item Arguments: $moniker
27f01d1f 112
d601dc88 113=item Return Value: $classname
27f01d1f 114
115=back
82b01c38 116
2053ab2a 117Retrieves the result class name for the given moniker. For example:
82b01c38 118
119 my $class = $schema->class('CD');
bfb2bd4f 120
121=cut
122
123sub class {
0dc79249 124 my ($self, $moniker) = @_;
125 return $self->source($moniker)->result_class;
bfb2bd4f 126}
127
ea20d0fd 128=head2 source
129
27f01d1f 130=over 4
131
ebc77b53 132=item Arguments: $moniker
27f01d1f 133
d601dc88 134=item Return Value: $result_source
82b01c38 135
27f01d1f 136=back
82b01c38 137
24d67825 138 my $source = $schema->source('Book');
ea20d0fd 139
82b01c38 140Returns the L<DBIx::Class::ResultSource> object for the registered moniker.
ea20d0fd 141
142=cut
143
144sub source {
0dc79249 145 my ($self, $moniker) = @_;
146 my $sreg = $self->source_registrations;
147 return $sreg->{$moniker} if exists $sreg->{$moniker};
148
149 # if we got here, they probably passed a full class name
150 my $mapped = $self->class_mappings->{$moniker};
701da8c4 151 $self->throw_exception("Can't find source for ${moniker}")
0dc79249 152 unless $mapped && exists $sreg->{$mapped};
153 return $sreg->{$mapped};
ea20d0fd 154}
155
0dc79249 156=head2 sources
157
27f01d1f 158=over 4
159
d601dc88 160=item Return Value: @source_monikers
27f01d1f 161
162=back
82b01c38 163
164Returns the source monikers of all source registrations on this schema.
2053ab2a 165For example:
82b01c38 166
167 my @source_monikers = $schema->sources;
0dc79249 168
169=cut
170
171sub sources { return keys %{shift->source_registrations}; }
172
ea20d0fd 173=head2 resultset
174
27f01d1f 175=over 4
176
ebc77b53 177=item Arguments: $moniker
27f01d1f 178
d601dc88 179=item Return Value: $result_set
82b01c38 180
27f01d1f 181=back
82b01c38 182
24d67825 183 my $rs = $schema->resultset('DVD');
ea20d0fd 184
82b01c38 185Returns the L<DBIx::Class::ResultSet> object for the registered moniker.
ea20d0fd 186
187=cut
188
189sub resultset {
0dc79249 190 my ($self, $moniker) = @_;
191 return $self->source($moniker)->resultset;
ea20d0fd 192}
193
87c4e602 194=head2 load_classes
195
27f01d1f 196=over 4
197
198=item Arguments: @classes?, { $namespace => [ @classes ] }+
199
200=back
076652e8 201
82b01c38 202With no arguments, this method uses L<Module::Find> to find all classes under
203the schema's namespace. Otherwise, this method loads the classes you specify
204(using L<use>), and registers them (using L</"register_class">).
076652e8 205
2053ab2a 206It is possible to comment out classes with a leading C<#>, but note that perl
207will think it's a mistake (trying to use a comment in a qw list), so you'll
208need to add C<no warnings 'qw';> before your load_classes call.
5ce32fc1 209
2053ab2a 210Example:
82b01c38 211
212 My::Schema->load_classes(); # loads My::Schema::CD, My::Schema::Artist,
75d07914 213 # etc. (anything under the My::Schema namespace)
82b01c38 214
215 # loads My::Schema::CD, My::Schema::Artist, Other::Namespace::Producer but
216 # not Other::Namespace::LinerNotes nor My::Schema::Track
217 My::Schema->load_classes(qw/ CD Artist #Track /, {
218 Other::Namespace => [qw/ Producer #LinerNotes /],
219 });
220
076652e8 221=cut
222
a02675cd 223sub load_classes {
5ce32fc1 224 my ($class, @params) = @_;
bab77431 225
5ce32fc1 226 my %comps_for;
bab77431 227
5ce32fc1 228 if (@params) {
229 foreach my $param (@params) {
230 if (ref $param eq 'ARRAY') {
231 # filter out commented entries
232 my @modules = grep { $_ !~ /^#/ } @$param;
bab77431 233
5ce32fc1 234 push (@{$comps_for{$class}}, @modules);
235 }
236 elsif (ref $param eq 'HASH') {
237 # more than one namespace possible
238 for my $comp ( keys %$param ) {
239 # filter out commented entries
240 my @modules = grep { $_ !~ /^#/ } @{$param->{$comp}};
241
242 push (@{$comps_for{$comp}}, @modules);
243 }
244 }
245 else {
246 # filter out commented entries
247 push (@{$comps_for{$class}}, $param) if $param !~ /^#/;
248 }
249 }
250 } else {
41a6f8c0 251 eval "require Module::Find;";
bc0c9800 252 $class->throw_exception(
253 "No arguments to load_classes and couldn't load Module::Find ($@)"
254 ) if $@;
255 my @comp = map { substr $_, length "${class}::" }
256 Module::Find::findallmod($class);
5ce32fc1 257 $comps_for{$class} = \@comp;
41a6f8c0 258 }
5ce32fc1 259
e6efde04 260 my @to_register;
261 {
262 no warnings qw/redefine/;
263 local *Class::C3::reinitialize = sub { };
264 foreach my $prefix (keys %comps_for) {
265 foreach my $comp (@{$comps_for{$prefix}||[]}) {
266 my $comp_class = "${prefix}::${comp}";
c037c03a 267 $class->ensure_class_loaded($comp_class);
bab77431 268 $comp_class->source_name($comp) unless $comp_class->source_name;
269
270 push(@to_register, [ $comp_class->source_name, $comp_class ]);
bfb2bd4f 271 }
5ce32fc1 272 }
a02675cd 273 }
e6efde04 274 Class::C3->reinitialize;
275
276 foreach my $to (@to_register) {
277 $class->register_class(@$to);
278 # if $class->can('result_source_instance');
279 }
a02675cd 280}
281
2374c5ff 282=head2 load_namespaces
283
284=over 4
285
85bd0538 286=item Arguments: %options?
2374c5ff 287
288=back
289
290This is an alternative to L</load_classes> above which assumes an alternative
85bd0538 291layout for automatic class loading. It assumes that all ResultSource classes
0f4ec1d2 292to be loaded are underneath a sub-namespace of the schema called
293"ResultSource", any corresponding ResultSet classes to be underneath a
294sub-namespace of the schema called "ResultSet", and any corresponing
295Result classes to be underneath a sub-namespace of the schema called "Result".
2374c5ff 296
0f4ec1d2 297All of those sub-namespaces are configurable if you don't like the defaults,
298via the options C<resultsource_namespace>, C<resultset_namespace>, and
299C<result_namespace>, respectively.
85bd0538 300
0f4ec1d2 301If (and only if) you specify the option C<default_resultset_base>, any found
302ResultSource classes that have no manually-created corresponding ResultSet
303class will have one created for them in memory in the C<resultset_namespace>,
304based on C<default_resultset_base>.
305
306All of the namespace and classname options to this method are relative to
307the schema classname by default. To specify a fully-qualified name, prefix
308it with a literal C<+>.
2374c5ff 309
310This method requires L<Module::Find> to be installed on the system.
311
312Example:
313
0f4ec1d2 314 # load My::Schema::ResultSource::CD, My::Schema::ResultSource::Artist,
2374c5ff 315 # My::Schema::ResultSet::CD, etc...
0f4ec1d2 316 My::Schema->load_namespaces;
317
318 # Override everything...
319 My::Schema->load_namespaces(
320 resultsource_namespace => 'RSources',
321 resultset_namespace => 'RSets',
322 result_namespace => 'Results',
323 default_resultset_base => 'RSetBase',
324 );
325 # ... and if there is a My::Schema::RSources::Foo, but no matching
326 # My::Schema::RSets::Foo, then My::Schema::RSets::Foo will be created
327 # for you in memory, based on My::Schema::RSetBase
2374c5ff 328
0f4ec1d2 329 # Put things in other namespaces
85bd0538 330 My::Schema->load_namespaces(
0f4ec1d2 331 resultsource_namespace => '+Some::Place::RSources',
332 resultset_namespace => '+Another::Place::RSets',
333 result_namespace => '+Crazy::Stuff::Results',
334 default_resultset_base => '+You::Never::Know::RSetBase',
85bd0538 335 );
0f4ec1d2 336
85bd0538 337
2374c5ff 338=cut
339
340sub load_namespaces {
85bd0538 341 my ($class, %args) = @_;
2374c5ff 342
0f4ec1d2 343 my $resultsource_namespace = $args{resultsource_namespace} || 'ResultSource';
344 my $resultset_namespace = $args{resultset_namespace} || 'ResultSet';
345 my $result_namespace = $args{result_namespace} || 'Result';
346 my $default_resultset_base = $args{default_resultset_base};
347
348 foreach ($resultsource_namespace, $resultset_namespace,
349 $result_namespace, $default_resultset_base) {
350 next if !$_;
351 $_ = $class . '::' . $_ if !s/^\+//;
352 }
2374c5ff 353
354 eval "require Module::Find";
355 $class->throw_exception("Couldn't load Module::Find ($@)") if $@;
356
85bd0538 357 my %sources = map { (substr($_, length "${resultsource_namespace}::"), $_) }
358 Module::Find::findallmod($resultsource_namespace);
2374c5ff 359
360 my %resultsets = map { (substr($_, length "${resultset_namespace}::"), $_) }
361 Module::Find::findallmod($resultset_namespace);
362
0f4ec1d2 363 my %results = map { (substr($_, length "${result_namespace}::"), $_) }
364 Module::Find::findallmod($result_namespace);
365
2374c5ff 366 my @to_register;
367 {
368 no warnings qw/redefine/;
369 local *Class::C3::reinitialize = sub { };
0f4ec1d2 370 use warnings qw/redefine/;
371
2374c5ff 372 foreach my $source (keys %sources) {
373 my $source_class = $sources{$source};
374 $class->ensure_class_loaded($source_class);
375 $source_class->source_name($source) unless $source_class->source_name;
0f4ec1d2 376
377 my $rs_class = delete $resultsets{$source};
378 my $rs_set = $source_class->resultset_class;
379 if(!$rs_set || $rs_set eq 'DBIx::Class::ResultSet') {
380 if($rs_class) {
2374c5ff 381 $class->ensure_class_loaded($rs_class);
382 $source_class->resultset_class($rs_class);
383 }
0f4ec1d2 384 elsif($default_resultset_base) {
385 $class->ensure_class_loaded($default_resultset_base);
386 $rs_class = "$resultset_namespace\::$source";
387 { no strict qw/refs/; @{"$rs_class\::ISA"} = ($default_resultset_base); }
2374c5ff 388 $source_class->resultset_class($rs_class);
389 }
390 }
0f4ec1d2 391 elsif($rs_set && $rs_class) {
392 warn "We found ResultSet class '$rs_class' for '$source', but it seems "
393 . "that you had already set '$source' to use '$rs_set' instead";
394 }
2374c5ff 395
fdcd8145 396 my $r_class = delete $results{$source};
397 if($r_class) {
398 my $r_set = $source_class->result_class;
399 if(!$r_set || $r_set eq $sources{$source}) {
400 $class->ensure_class_loaded($r_class);
401 $source_class->result_class($r_class);
402 }
403 else {
404 warn "We found Result class '$r_class' for '$source', but it seems "
405 . "that you had already set '$source' to use '$r_set' instead";
406 }
407 }
408
2374c5ff 409 push(@to_register, [ $source_class->source_name, $source_class ]);
410 }
411 }
412
0f4ec1d2 413 foreach (sort keys %resultsets) {
414 warn "load_namespaces found ResultSet class $_ with no "
2374c5ff 415 . 'corresponding ResultSource';
416 }
417
0f4ec1d2 418 foreach (sort keys %results) {
419 warn "load_namespaces found Result class $_ with no "
420 . 'corresponding ResultSource';
2374c5ff 421 }
0f4ec1d2 422
fdcd8145 423 Class::C3->reinitialize;
424 $class->register_class(@$_) for (@to_register);
425
0f4ec1d2 426 return;
2374c5ff 427}
428
87c4e602 429=head2 compose_connection
430
27f01d1f 431=over 4
432
ebc77b53 433=item Arguments: $target_namespace, @db_info
429bd4f1 434
d601dc88 435=item Return Value: $new_schema
27f01d1f 436
437=back
076652e8 438
2053ab2a 439Calls L<DBIx::Class::Schema/"compose_namespace"> to the target namespace,
440calls L<DBIx::Class::Schema/connection> with @db_info on the new schema,
441then injects the L<DBix::Class::ResultSetProxy> component and a
442resultset_instance classdata entry on all the new classes, in order to support
82b01c38 443$target_namespaces::$class->search(...) method calls.
444
445This is primarily useful when you have a specific need for class method access
446to a connection. In normal usage it is preferred to call
447L<DBIx::Class::Schema/connect> and use the resulting schema object to operate
448on L<DBIx::Class::ResultSet> objects with L<DBIx::Class::Schema/resultset> for
449more information.
54540863 450
076652e8 451=cut
452
a02675cd 453sub compose_connection {
ea20d0fd 454 my ($self, $target, @info) = @_;
80c90f5d 455 my $base = 'DBIx::Class::ResultSetProxy';
8ef144ff 456 eval "require ${base};";
bc0c9800 457 $self->throw_exception
458 ("No arguments to load_classes and couldn't load ${base} ($@)")
459 if $@;
be381829 460
461 if ($self eq $target) {
462 # Pathological case, largely caused by the docs on early C::M::DBIC::Plain
463 foreach my $moniker ($self->sources) {
464 my $source = $self->source($moniker);
465 my $class = $source->result_class;
466 $self->inject_base($class, $base);
467 $class->mk_classdata(resultset_instance => $source->resultset);
468 $class->mk_classdata(class_resolver => $self);
469 }
50041f3c 470 $self->connection(@info);
be381829 471 return $self;
472 }
473
66d9ef6b 474 my $schema = $self->compose_namespace($target, $base);
ecceadff 475 {
476 no strict 'refs';
477 *{"${target}::schema"} = sub { $schema };
478 }
479
66d9ef6b 480 $schema->connection(@info);
0dc79249 481 foreach my $moniker ($schema->sources) {
482 my $source = $schema->source($moniker);
483 my $class = $source->result_class;
484 #warn "$moniker $class $source ".$source->storage;
8c49f629 485 $class->mk_classdata(result_source_instance => $source);
ea20d0fd 486 $class->mk_classdata(resultset_instance => $source->resultset);
66d9ef6b 487 $class->mk_classdata(class_resolver => $schema);
bfb2bd4f 488 }
489 return $schema;
e678398e 490}
491
77254782 492=head2 compose_namespace
493
27f01d1f 494=over 4
495
496=item Arguments: $target_namespace, $additional_base_class?
82b01c38 497
d601dc88 498=item Return Value: $new_schema
27f01d1f 499
500=back
13765dad 501
82b01c38 502For each L<DBIx::Class::ResultSource> in the schema, this method creates a
503class in the target namespace (e.g. $target_namespace::CD,
504$target_namespace::Artist) that inherits from the corresponding classes
505attached to the current schema.
77254782 506
82b01c38 507It also attaches a corresponding L<DBIx::Class::ResultSource> object to the
508new $schema object. If C<$additional_base_class> is given, the new composed
509classes will inherit from first the corresponding classe from the current
510schema then the base class.
511
2053ab2a 512For example, for a schema with My::Schema::CD and My::Schema::Artist classes,
82b01c38 513
514 $schema->compose_namespace('My::DB', 'Base::Class');
515 print join (', ', @My::DB::CD::ISA) . "\n";
516 print join (', ', @My::DB::Artist::ISA) ."\n";
517
2053ab2a 518will produce the output
82b01c38 519
520 My::Schema::CD, Base::Class
521 My::Schema::Artist, Base::Class
77254782 522
523=cut
524
e678398e 525sub compose_namespace {
66d9ef6b 526 my ($self, $target, $base) = @_;
527 my %reg = %{ $self->source_registrations };
11b78bd6 528 my %target;
529 my %map;
66d9ef6b 530 my $schema = $self->clone;
e9100ff7 531 {
532 no warnings qw/redefine/;
533 local *Class::C3::reinitialize = sub { };
534 foreach my $moniker ($schema->sources) {
535 my $source = $schema->source($moniker);
536 my $target_class = "${target}::${moniker}";
537 $self->inject_base(
538 $target_class => $source->result_class, ($base ? $base : ())
539 );
540 $source->result_class($target_class);
9d3d5af3 541 $target_class->result_source_instance($source)
542 if $target_class->can('result_source_instance');
e9100ff7 543 }
b7951443 544 }
e9100ff7 545 Class::C3->reinitialize();
11b78bd6 546 {
547 no strict 'refs';
1edaf6fe 548 foreach my $meth (qw/class source resultset/) {
549 *{"${target}::${meth}"} =
550 sub { shift->schema->$meth(@_) };
551 }
11b78bd6 552 }
bfb2bd4f 553 return $schema;
b7951443 554}
555
87c4e602 556=head2 setup_connection_class
557
27f01d1f 558=over 4
559
ebc77b53 560=item Arguments: $target, @info
27f01d1f 561
562=back
076652e8 563
82b01c38 564Sets up a database connection class to inject between the schema and the
565subclasses that the schema creates.
429bd4f1 566
076652e8 567=cut
568
b7951443 569sub setup_connection_class {
570 my ($class, $target, @info) = @_;
63e9583a 571 $class->inject_base($target => 'DBIx::Class::DB');
572 #$target->load_components('DB');
b7951443 573 $target->connection(@info);
574}
575
87c4e602 576=head2 connection
577
27f01d1f 578=over 4
579
ebc77b53 580=item Arguments: @args
66d9ef6b 581
d601dc88 582=item Return Value: $new_schema
27f01d1f 583
584=back
82b01c38 585
586Instantiates a new Storage object of type
587L<DBIx::Class::Schema/"storage_type"> and passes the arguments to
588$storage->connect_info. Sets the connection in-place on the schema. See
589L<DBIx::Class::Storage::DBI/"connect_info"> for more information.
66d9ef6b 590
591=cut
592
593sub connection {
594 my ($self, @info) = @_;
e59d3e5b 595 return $self if !@info && $self->storage;
1e10a11d 596 my $storage_class = $self->storage_type;
597 $storage_class = 'DBIx::Class::Storage'.$storage_class
598 if $storage_class =~ m/^::/;
8ef144ff 599 eval "require ${storage_class};";
bc0c9800 600 $self->throw_exception(
601 "No arguments to load_classes and couldn't load ${storage_class} ($@)"
602 ) if $@;
82cc0386 603 my $storage = $storage_class->new($self);
66d9ef6b 604 $storage->connect_info(\@info);
605 $self->storage($storage);
606 return $self;
607}
608
87c4e602 609=head2 connect
610
27f01d1f 611=over 4
612
ebc77b53 613=item Arguments: @info
66d9ef6b 614
d601dc88 615=item Return Value: $new_schema
27f01d1f 616
617=back
82b01c38 618
619This is a convenience method. It is equivalent to calling
620$schema->clone->connection(@info). See L</connection> and L</clone> for more
621information.
66d9ef6b 622
623=cut
624
08b515f1 625sub connect { shift->clone->connection(@_) }
626
627=head2 txn_begin
628
82b01c38 629Begins a transaction (does nothing if AutoCommit is off). Equivalent to
630calling $schema->storage->txn_begin. See
631L<DBIx::Class::Storage::DBI/"txn_begin"> for more information.
08b515f1 632
633=cut
634
635sub txn_begin { shift->storage->txn_begin }
636
637=head2 txn_commit
638
82b01c38 639Commits the current transaction. Equivalent to calling
640$schema->storage->txn_commit. See L<DBIx::Class::Storage::DBI/"txn_commit">
641for more information.
08b515f1 642
643=cut
644
645sub txn_commit { shift->storage->txn_commit }
646
647=head2 txn_rollback
648
82b01c38 649Rolls back the current transaction. Equivalent to calling
650$schema->storage->txn_rollback. See
651L<DBIx::Class::Storage::DBI/"txn_rollback"> for more information.
08b515f1 652
653=cut
654
655sub txn_rollback { shift->storage->txn_rollback }
66d9ef6b 656
a62cf8d4 657=head2 txn_do
658
27f01d1f 659=over 4
660
ebc77b53 661=item Arguments: C<$coderef>, @coderef_args?
82b01c38 662
d601dc88 663=item Return Value: The return value of $coderef
27f01d1f 664
665=back
a62cf8d4 666
82b01c38 667Executes C<$coderef> with (optional) arguments C<@coderef_args> atomically,
668returning its result (if any). If an exception is caught, a rollback is issued
669and the exception is rethrown. If the rollback fails, (i.e. throws an
670exception) an exception is thrown that includes a "Rollback failed" message.
a62cf8d4 671
672For example,
673
24d67825 674 my $author_rs = $schema->resultset('Author')->find(1);
70634260 675 my @titles = qw/Night Day It/;
a62cf8d4 676
677 my $coderef = sub {
a62cf8d4 678 # If any one of these fails, the entire transaction fails
70634260 679 $author_rs->create_related('books', {
24d67825 680 title => $_
681 }) foreach (@titles);
a62cf8d4 682
24d67825 683 return $author->books;
a62cf8d4 684 };
685
686 my $rs;
687 eval {
70634260 688 $rs = $schema->txn_do($coderef);
a62cf8d4 689 };
690
70634260 691 if ($@) { # Transaction failed
692 die "something terrible has happened!" #
693 if ($@ =~ /Rollback failed/); # Rollback failed
694
695 deal_with_failed_transaction();
a62cf8d4 696 }
697
82b01c38 698In a nested transaction (calling txn_do() from within a txn_do() coderef) only
699the outermost transaction will issue a L<DBIx::Class::Schema/"txn_commit"> on
700the Schema's storage, and txn_do() can be called in void, scalar and list
701context and it will behave as expected.
a62cf8d4 702
703=cut
704
705sub txn_do {
706 my ($self, $coderef, @args) = @_;
707
19630353 708 $self->storage or $self->throw_exception
709 ('txn_do called on $schema without storage');
171dadd7 710 ref $coderef eq 'CODE' or $self->throw_exception
711 ('$coderef must be a CODE reference');
a62cf8d4 712
713 my (@return_values, $return_value);
714
715 $self->txn_begin; # If this throws an exception, no rollback is needed
716
e7f2b7d5 717 my $wantarray = wantarray; # Need to save this since the context
75d07914 718 # inside the eval{} block is independent
719 # of the context that called txn_do()
a62cf8d4 720 eval {
82b01c38 721
24d67825 722 # Need to differentiate between scalar/list context to allow for
723 # returning a list in scalar context to get the size of the list
a62cf8d4 724 if ($wantarray) {
eeb34228 725 # list context
a62cf8d4 726 @return_values = $coderef->(@args);
eeb34228 727 } elsif (defined $wantarray) {
728 # scalar context
a62cf8d4 729 $return_value = $coderef->(@args);
eeb34228 730 } else {
731 # void context
732 $coderef->(@args);
a62cf8d4 733 }
734 $self->txn_commit;
735 };
736
737 if ($@) {
738 my $error = $@;
739
740 eval {
741 $self->txn_rollback;
742 };
743
744 if ($@) {
745 my $rollback_error = $@;
746 my $exception_class = "DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION";
747 $self->throw_exception($error) # propagate nested rollback
75d07914 748 if $rollback_error =~ /$exception_class/;
a62cf8d4 749
bc0c9800 750 $self->throw_exception(
751 "Transaction aborted: $error. Rollback failed: ${rollback_error}"
752 );
a62cf8d4 753 } else {
754 $self->throw_exception($error); # txn failed but rollback succeeded
755 }
756 }
757
758 return $wantarray ? @return_values : $return_value;
759}
760
66d9ef6b 761=head2 clone
762
27f01d1f 763=over 4
764
d601dc88 765=item Return Value: $new_schema
27f01d1f 766
767=back
82b01c38 768
66d9ef6b 769Clones the schema and its associated result_source objects and returns the
770copy.
771
772=cut
773
774sub clone {
775 my ($self) = @_;
776 my $clone = bless({ (ref $self ? %$self : ()) }, ref $self || $self);
777 foreach my $moniker ($self->sources) {
778 my $source = $self->source($moniker);
779 my $new = $source->new($source);
780 $clone->register_source($moniker => $new);
781 }
82cc0386 782 $clone->storage->set_schema($clone) if $clone->storage;
66d9ef6b 783 return $clone;
784}
785
87c4e602 786=head2 populate
787
27f01d1f 788=over 4
789
ebc77b53 790=item Arguments: $moniker, \@data;
27f01d1f 791
792=back
a37a4697 793
794Populates the source registered with the given moniker with the supplied data.
82b01c38 795@data should be a list of listrefs -- the first containing column names, the
796second matching values.
797
798i.e.,
a37a4697 799
24d67825 800 $schema->populate('Artist', [
801 [ qw/artistid name/ ],
802 [ 1, 'Popular Band' ],
803 [ 2, 'Indie Band' ],
a62cf8d4 804 ...
805 ]);
a37a4697 806
807=cut
808
809sub populate {
810 my ($self, $name, $data) = @_;
811 my $rs = $self->resultset($name);
812 my @names = @{shift(@$data)};
84e3c114 813 my @created;
a37a4697 814 foreach my $item (@$data) {
815 my %create;
816 @create{@names} = @$item;
84e3c114 817 push(@created, $rs->create(\%create));
a37a4697 818 }
84e3c114 819 return @created;
a37a4697 820}
821
82cc0386 822=head2 exception_action
823
824=over 4
825
826=item Arguments: $code_reference
827
828=back
829
db5dc233 830If C<exception_action> is set for this class/object, L</throw_exception>
831will prefer to call this code reference with the exception as an argument,
832rather than its normal <croak> action.
833
834Your subroutine should probably just wrap the error in the exception
835object/class of your choosing and rethrow. If, against all sage advice,
836you'd like your C<exception_action> to suppress a particular exception
837completely, simply have it return true.
82cc0386 838
839Example:
840
841 package My::Schema;
842 use base qw/DBIx::Class::Schema/;
843 use My::ExceptionClass;
844 __PACKAGE__->exception_action(sub { My::ExceptionClass->throw(@_) });
845 __PACKAGE__->load_classes;
846
db5dc233 847 # or:
82cc0386 848 my $schema_obj = My::Schema->connect( .... );
849 $schema_obj->exception_action(sub { My::ExceptionClass->throw(@_) });
850
db5dc233 851 # suppress all exceptions, like a moron:
852 $schema_obj->exception_action(sub { 1 });
853
5160b401 854=head2 throw_exception
701da8c4 855
75d07914 856=over 4
82b01c38 857
ebc77b53 858=item Arguments: $message
82b01c38 859
860=back
861
862Throws an exception. Defaults to using L<Carp::Clan> to report errors from
db5dc233 863user's perspective. See L</exception_action> for details on overriding
864this method's behavior.
701da8c4 865
866=cut
867
868sub throw_exception {
82cc0386 869 my $self = shift;
db5dc233 870 croak @_ if !$self->exception_action || !$self->exception_action->(@_);
701da8c4 871}
872
ec6704d4 873=head2 deploy (EXPERIMENTAL)
1c339d71 874
82b01c38 875=over 4
876
ebc77b53 877=item Arguments: $sqlt_args
82b01c38 878
879=back
880
881Attempts to deploy the schema to the current storage using L<SQL::Translator>.
ec6704d4 882
883Note that this feature is currently EXPERIMENTAL and may not work correctly
884across all databases, or fully handle complex relationships.
1c339d71 885
51bace1c 886See L<SQL::Translator/METHODS> for a list of values for C<$sqlt_args>. The most
887common value for this would be C<< { add_drop_table => 1, } >> to have the SQL
888produced include a DROP TABLE statement for each table created.
889
1c339d71 890=cut
891
892sub deploy {
cb561d1a 893 my ($self, $sqltargs) = @_;
1c339d71 894 $self->throw_exception("Can't deploy without storage") unless $self->storage;
cb561d1a 895 $self->storage->deploy($self, undef, $sqltargs);
1c339d71 896}
897
c0f61310 898=head2 create_ddl_dir (EXPERIMENTAL)
899
900=over 4
901
902=item Arguments: \@databases, $version, $directory, $sqlt_args
903
904=back
905
906Creates an SQL file based on the Schema, for each of the specified
907database types, in the given directory.
908
909Note that this feature is currently EXPERIMENTAL and may not work correctly
910across all databases, or fully handle complex relationships.
911
912=cut
913
e673f011 914sub create_ddl_dir
915{
916 my $self = shift;
917
918 $self->throw_exception("Can't create_ddl_dir without storage") unless $self->storage;
919 $self->storage->create_ddl_dir($self, @_);
920}
921
9b83fccd 922=head2 ddl_filename (EXPERIMENTAL)
923
924 my $filename = $table->ddl_filename($type, $dir, $version)
925
926Creates a filename for a SQL file based on the table class name. Not
927intended for direct end user use.
928
929=cut
930
e673f011 931sub ddl_filename
932{
933 my ($self, $type, $dir, $version) = @_;
934
935 my $filename = ref($self);
9e7b9292 936 $filename =~ s/::/-/;
e673f011 937 $filename = "$dir$filename-$version-$type.sql";
938
939 return $filename;
940}
941
a02675cd 9421;
c2da098a 943
c2da098a 944=head1 AUTHORS
945
daec44b8 946Matt S. Trout <mst@shadowcatsystems.co.uk>
c2da098a 947
948=head1 LICENSE
949
950You may distribute this code under the same terms as Perl itself.
951
952=cut
953