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