Merge 'DBIx-Class-current' into 'load_namespaces'
[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
396 push(@to_register, [ $source_class->source_name, $source_class ]);
397 }
398 }
399
0f4ec1d2 400 foreach (sort keys %resultsets) {
401 warn "load_namespaces found ResultSet class $_ with no "
2374c5ff 402 . 'corresponding ResultSource';
403 }
404
405 Class::C3->reinitialize;
0f4ec1d2 406 $class->register_class(@$_) for (@to_register);
407
408 foreach my $source (keys %sources) {
409 my $r_class = delete $results{$source};
410 if($r_class) {
411 my $r_set = $class->source($source)->result_class;
412 if(!$r_set || $r_set eq $sources{$source}) {
413 $class->ensure_class_loaded($r_class);
414 $class->source($source)->result_class($r_class);
415 }
416 else {
417 warn "We found Result class '$r_class' for '$source', but it seems "
418 . "that you had already set '$source' to use '$r_set' instead";
419 }
420 }
421 }
2374c5ff 422
0f4ec1d2 423 foreach (sort keys %results) {
424 warn "load_namespaces found Result class $_ with no "
425 . 'corresponding ResultSource';
2374c5ff 426 }
0f4ec1d2 427
428 return;
2374c5ff 429}
430
87c4e602 431=head2 compose_connection
432
27f01d1f 433=over 4
434
ebc77b53 435=item Arguments: $target_namespace, @db_info
429bd4f1 436
d601dc88 437=item Return Value: $new_schema
27f01d1f 438
439=back
076652e8 440
2053ab2a 441Calls L<DBIx::Class::Schema/"compose_namespace"> to the target namespace,
442calls L<DBIx::Class::Schema/connection> with @db_info on the new schema,
443then injects the L<DBix::Class::ResultSetProxy> component and a
444resultset_instance classdata entry on all the new classes, in order to support
82b01c38 445$target_namespaces::$class->search(...) method calls.
446
447This is primarily useful when you have a specific need for class method access
448to a connection. In normal usage it is preferred to call
449L<DBIx::Class::Schema/connect> and use the resulting schema object to operate
450on L<DBIx::Class::ResultSet> objects with L<DBIx::Class::Schema/resultset> for
451more information.
54540863 452
076652e8 453=cut
454
a02675cd 455sub compose_connection {
ea20d0fd 456 my ($self, $target, @info) = @_;
80c90f5d 457 my $base = 'DBIx::Class::ResultSetProxy';
8ef144ff 458 eval "require ${base};";
bc0c9800 459 $self->throw_exception
460 ("No arguments to load_classes and couldn't load ${base} ($@)")
461 if $@;
be381829 462
463 if ($self eq $target) {
464 # Pathological case, largely caused by the docs on early C::M::DBIC::Plain
465 foreach my $moniker ($self->sources) {
466 my $source = $self->source($moniker);
467 my $class = $source->result_class;
468 $self->inject_base($class, $base);
469 $class->mk_classdata(resultset_instance => $source->resultset);
470 $class->mk_classdata(class_resolver => $self);
471 }
50041f3c 472 $self->connection(@info);
be381829 473 return $self;
474 }
475
66d9ef6b 476 my $schema = $self->compose_namespace($target, $base);
ecceadff 477 {
478 no strict 'refs';
479 *{"${target}::schema"} = sub { $schema };
480 }
481
66d9ef6b 482 $schema->connection(@info);
0dc79249 483 foreach my $moniker ($schema->sources) {
484 my $source = $schema->source($moniker);
485 my $class = $source->result_class;
486 #warn "$moniker $class $source ".$source->storage;
8c49f629 487 $class->mk_classdata(result_source_instance => $source);
ea20d0fd 488 $class->mk_classdata(resultset_instance => $source->resultset);
66d9ef6b 489 $class->mk_classdata(class_resolver => $schema);
bfb2bd4f 490 }
491 return $schema;
e678398e 492}
493
77254782 494=head2 compose_namespace
495
27f01d1f 496=over 4
497
498=item Arguments: $target_namespace, $additional_base_class?
82b01c38 499
d601dc88 500=item Return Value: $new_schema
27f01d1f 501
502=back
13765dad 503
82b01c38 504For each L<DBIx::Class::ResultSource> in the schema, this method creates a
505class in the target namespace (e.g. $target_namespace::CD,
506$target_namespace::Artist) that inherits from the corresponding classes
507attached to the current schema.
77254782 508
82b01c38 509It also attaches a corresponding L<DBIx::Class::ResultSource> object to the
510new $schema object. If C<$additional_base_class> is given, the new composed
511classes will inherit from first the corresponding classe from the current
512schema then the base class.
513
2053ab2a 514For example, for a schema with My::Schema::CD and My::Schema::Artist classes,
82b01c38 515
516 $schema->compose_namespace('My::DB', 'Base::Class');
517 print join (', ', @My::DB::CD::ISA) . "\n";
518 print join (', ', @My::DB::Artist::ISA) ."\n";
519
2053ab2a 520will produce the output
82b01c38 521
522 My::Schema::CD, Base::Class
523 My::Schema::Artist, Base::Class
77254782 524
525=cut
526
e678398e 527sub compose_namespace {
66d9ef6b 528 my ($self, $target, $base) = @_;
529 my %reg = %{ $self->source_registrations };
11b78bd6 530 my %target;
531 my %map;
66d9ef6b 532 my $schema = $self->clone;
e9100ff7 533 {
534 no warnings qw/redefine/;
535 local *Class::C3::reinitialize = sub { };
536 foreach my $moniker ($schema->sources) {
537 my $source = $schema->source($moniker);
538 my $target_class = "${target}::${moniker}";
539 $self->inject_base(
540 $target_class => $source->result_class, ($base ? $base : ())
541 );
542 $source->result_class($target_class);
9d3d5af3 543 $target_class->result_source_instance($source)
544 if $target_class->can('result_source_instance');
e9100ff7 545 }
b7951443 546 }
e9100ff7 547 Class::C3->reinitialize();
11b78bd6 548 {
549 no strict 'refs';
1edaf6fe 550 foreach my $meth (qw/class source resultset/) {
551 *{"${target}::${meth}"} =
552 sub { shift->schema->$meth(@_) };
553 }
11b78bd6 554 }
bfb2bd4f 555 return $schema;
b7951443 556}
557
87c4e602 558=head2 setup_connection_class
559
27f01d1f 560=over 4
561
ebc77b53 562=item Arguments: $target, @info
27f01d1f 563
564=back
076652e8 565
82b01c38 566Sets up a database connection class to inject between the schema and the
567subclasses that the schema creates.
429bd4f1 568
076652e8 569=cut
570
b7951443 571sub setup_connection_class {
572 my ($class, $target, @info) = @_;
63e9583a 573 $class->inject_base($target => 'DBIx::Class::DB');
574 #$target->load_components('DB');
b7951443 575 $target->connection(@info);
576}
577
87c4e602 578=head2 connection
579
27f01d1f 580=over 4
581
ebc77b53 582=item Arguments: @args
66d9ef6b 583
d601dc88 584=item Return Value: $new_schema
27f01d1f 585
586=back
82b01c38 587
588Instantiates a new Storage object of type
589L<DBIx::Class::Schema/"storage_type"> and passes the arguments to
590$storage->connect_info. Sets the connection in-place on the schema. See
591L<DBIx::Class::Storage::DBI/"connect_info"> for more information.
66d9ef6b 592
593=cut
594
595sub connection {
596 my ($self, @info) = @_;
e59d3e5b 597 return $self if !@info && $self->storage;
1e10a11d 598 my $storage_class = $self->storage_type;
599 $storage_class = 'DBIx::Class::Storage'.$storage_class
600 if $storage_class =~ m/^::/;
8ef144ff 601 eval "require ${storage_class};";
bc0c9800 602 $self->throw_exception(
603 "No arguments to load_classes and couldn't load ${storage_class} ($@)"
604 ) if $@;
82cc0386 605 my $storage = $storage_class->new($self);
66d9ef6b 606 $storage->connect_info(\@info);
607 $self->storage($storage);
608 return $self;
609}
610
87c4e602 611=head2 connect
612
27f01d1f 613=over 4
614
ebc77b53 615=item Arguments: @info
66d9ef6b 616
d601dc88 617=item Return Value: $new_schema
27f01d1f 618
619=back
82b01c38 620
621This is a convenience method. It is equivalent to calling
622$schema->clone->connection(@info). See L</connection> and L</clone> for more
623information.
66d9ef6b 624
625=cut
626
08b515f1 627sub connect { shift->clone->connection(@_) }
628
629=head2 txn_begin
630
82b01c38 631Begins a transaction (does nothing if AutoCommit is off). Equivalent to
632calling $schema->storage->txn_begin. See
633L<DBIx::Class::Storage::DBI/"txn_begin"> for more information.
08b515f1 634
635=cut
636
637sub txn_begin { shift->storage->txn_begin }
638
639=head2 txn_commit
640
82b01c38 641Commits the current transaction. Equivalent to calling
642$schema->storage->txn_commit. See L<DBIx::Class::Storage::DBI/"txn_commit">
643for more information.
08b515f1 644
645=cut
646
647sub txn_commit { shift->storage->txn_commit }
648
649=head2 txn_rollback
650
82b01c38 651Rolls back the current transaction. Equivalent to calling
652$schema->storage->txn_rollback. See
653L<DBIx::Class::Storage::DBI/"txn_rollback"> for more information.
08b515f1 654
655=cut
656
657sub txn_rollback { shift->storage->txn_rollback }
66d9ef6b 658
a62cf8d4 659=head2 txn_do
660
27f01d1f 661=over 4
662
ebc77b53 663=item Arguments: C<$coderef>, @coderef_args?
82b01c38 664
d601dc88 665=item Return Value: The return value of $coderef
27f01d1f 666
667=back
a62cf8d4 668
82b01c38 669Executes C<$coderef> with (optional) arguments C<@coderef_args> atomically,
670returning its result (if any). If an exception is caught, a rollback is issued
671and the exception is rethrown. If the rollback fails, (i.e. throws an
672exception) an exception is thrown that includes a "Rollback failed" message.
a62cf8d4 673
674For example,
675
24d67825 676 my $author_rs = $schema->resultset('Author')->find(1);
70634260 677 my @titles = qw/Night Day It/;
a62cf8d4 678
679 my $coderef = sub {
a62cf8d4 680 # If any one of these fails, the entire transaction fails
70634260 681 $author_rs->create_related('books', {
24d67825 682 title => $_
683 }) foreach (@titles);
a62cf8d4 684
24d67825 685 return $author->books;
a62cf8d4 686 };
687
688 my $rs;
689 eval {
70634260 690 $rs = $schema->txn_do($coderef);
a62cf8d4 691 };
692
70634260 693 if ($@) { # Transaction failed
694 die "something terrible has happened!" #
695 if ($@ =~ /Rollback failed/); # Rollback failed
696
697 deal_with_failed_transaction();
a62cf8d4 698 }
699
82b01c38 700In a nested transaction (calling txn_do() from within a txn_do() coderef) only
701the outermost transaction will issue a L<DBIx::Class::Schema/"txn_commit"> on
702the Schema's storage, and txn_do() can be called in void, scalar and list
703context and it will behave as expected.
a62cf8d4 704
705=cut
706
707sub txn_do {
708 my ($self, $coderef, @args) = @_;
709
19630353 710 $self->storage or $self->throw_exception
711 ('txn_do called on $schema without storage');
171dadd7 712 ref $coderef eq 'CODE' or $self->throw_exception
713 ('$coderef must be a CODE reference');
a62cf8d4 714
715 my (@return_values, $return_value);
716
717 $self->txn_begin; # If this throws an exception, no rollback is needed
718
e7f2b7d5 719 my $wantarray = wantarray; # Need to save this since the context
75d07914 720 # inside the eval{} block is independent
721 # of the context that called txn_do()
a62cf8d4 722 eval {
82b01c38 723
24d67825 724 # Need to differentiate between scalar/list context to allow for
725 # returning a list in scalar context to get the size of the list
a62cf8d4 726 if ($wantarray) {
eeb34228 727 # list context
a62cf8d4 728 @return_values = $coderef->(@args);
eeb34228 729 } elsif (defined $wantarray) {
730 # scalar context
a62cf8d4 731 $return_value = $coderef->(@args);
eeb34228 732 } else {
733 # void context
734 $coderef->(@args);
a62cf8d4 735 }
736 $self->txn_commit;
737 };
738
739 if ($@) {
740 my $error = $@;
741
742 eval {
743 $self->txn_rollback;
744 };
745
746 if ($@) {
747 my $rollback_error = $@;
748 my $exception_class = "DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION";
749 $self->throw_exception($error) # propagate nested rollback
75d07914 750 if $rollback_error =~ /$exception_class/;
a62cf8d4 751
bc0c9800 752 $self->throw_exception(
753 "Transaction aborted: $error. Rollback failed: ${rollback_error}"
754 );
a62cf8d4 755 } else {
756 $self->throw_exception($error); # txn failed but rollback succeeded
757 }
758 }
759
760 return $wantarray ? @return_values : $return_value;
761}
762
66d9ef6b 763=head2 clone
764
27f01d1f 765=over 4
766
d601dc88 767=item Return Value: $new_schema
27f01d1f 768
769=back
82b01c38 770
66d9ef6b 771Clones the schema and its associated result_source objects and returns the
772copy.
773
774=cut
775
776sub clone {
777 my ($self) = @_;
778 my $clone = bless({ (ref $self ? %$self : ()) }, ref $self || $self);
779 foreach my $moniker ($self->sources) {
780 my $source = $self->source($moniker);
781 my $new = $source->new($source);
782 $clone->register_source($moniker => $new);
783 }
82cc0386 784 $clone->storage->set_schema($clone) if $clone->storage;
66d9ef6b 785 return $clone;
786}
787
87c4e602 788=head2 populate
789
27f01d1f 790=over 4
791
ebc77b53 792=item Arguments: $moniker, \@data;
27f01d1f 793
794=back
a37a4697 795
796Populates the source registered with the given moniker with the supplied data.
82b01c38 797@data should be a list of listrefs -- the first containing column names, the
798second matching values.
799
800i.e.,
a37a4697 801
24d67825 802 $schema->populate('Artist', [
803 [ qw/artistid name/ ],
804 [ 1, 'Popular Band' ],
805 [ 2, 'Indie Band' ],
a62cf8d4 806 ...
807 ]);
a37a4697 808
809=cut
810
811sub populate {
812 my ($self, $name, $data) = @_;
813 my $rs = $self->resultset($name);
814 my @names = @{shift(@$data)};
84e3c114 815 my @created;
a37a4697 816 foreach my $item (@$data) {
817 my %create;
818 @create{@names} = @$item;
84e3c114 819 push(@created, $rs->create(\%create));
a37a4697 820 }
84e3c114 821 return @created;
a37a4697 822}
823
82cc0386 824=head2 exception_action
825
826=over 4
827
828=item Arguments: $code_reference
829
830=back
831
db5dc233 832If C<exception_action> is set for this class/object, L</throw_exception>
833will prefer to call this code reference with the exception as an argument,
834rather than its normal <croak> action.
835
836Your subroutine should probably just wrap the error in the exception
837object/class of your choosing and rethrow. If, against all sage advice,
838you'd like your C<exception_action> to suppress a particular exception
839completely, simply have it return true.
82cc0386 840
841Example:
842
843 package My::Schema;
844 use base qw/DBIx::Class::Schema/;
845 use My::ExceptionClass;
846 __PACKAGE__->exception_action(sub { My::ExceptionClass->throw(@_) });
847 __PACKAGE__->load_classes;
848
db5dc233 849 # or:
82cc0386 850 my $schema_obj = My::Schema->connect( .... );
851 $schema_obj->exception_action(sub { My::ExceptionClass->throw(@_) });
852
db5dc233 853 # suppress all exceptions, like a moron:
854 $schema_obj->exception_action(sub { 1 });
855
5160b401 856=head2 throw_exception
701da8c4 857
75d07914 858=over 4
82b01c38 859
ebc77b53 860=item Arguments: $message
82b01c38 861
862=back
863
864Throws an exception. Defaults to using L<Carp::Clan> to report errors from
db5dc233 865user's perspective. See L</exception_action> for details on overriding
866this method's behavior.
701da8c4 867
868=cut
869
870sub throw_exception {
82cc0386 871 my $self = shift;
db5dc233 872 croak @_ if !$self->exception_action || !$self->exception_action->(@_);
701da8c4 873}
874
ec6704d4 875=head2 deploy (EXPERIMENTAL)
1c339d71 876
82b01c38 877=over 4
878
ebc77b53 879=item Arguments: $sqlt_args
82b01c38 880
881=back
882
883Attempts to deploy the schema to the current storage using L<SQL::Translator>.
ec6704d4 884
885Note that this feature is currently EXPERIMENTAL and may not work correctly
886across all databases, or fully handle complex relationships.
1c339d71 887
51bace1c 888See L<SQL::Translator/METHODS> for a list of values for C<$sqlt_args>. The most
889common value for this would be C<< { add_drop_table => 1, } >> to have the SQL
890produced include a DROP TABLE statement for each table created.
891
1c339d71 892=cut
893
894sub deploy {
cb561d1a 895 my ($self, $sqltargs) = @_;
1c339d71 896 $self->throw_exception("Can't deploy without storage") unless $self->storage;
cb561d1a 897 $self->storage->deploy($self, undef, $sqltargs);
1c339d71 898}
899
c0f61310 900=head2 create_ddl_dir (EXPERIMENTAL)
901
902=over 4
903
904=item Arguments: \@databases, $version, $directory, $sqlt_args
905
906=back
907
908Creates an SQL file based on the Schema, for each of the specified
909database types, in the given directory.
910
911Note that this feature is currently EXPERIMENTAL and may not work correctly
912across all databases, or fully handle complex relationships.
913
914=cut
915
e673f011 916sub create_ddl_dir
917{
918 my $self = shift;
919
920 $self->throw_exception("Can't create_ddl_dir without storage") unless $self->storage;
921 $self->storage->create_ddl_dir($self, @_);
922}
923
9b83fccd 924=head2 ddl_filename (EXPERIMENTAL)
925
926 my $filename = $table->ddl_filename($type, $dir, $version)
927
928Creates a filename for a SQL file based on the table class name. Not
929intended for direct end user use.
930
931=cut
932
e673f011 933sub ddl_filename
934{
935 my ($self, $type, $dir, $version) = @_;
936
937 my $filename = ref($self);
9e7b9292 938 $filename =~ s/::/-/;
e673f011 939 $filename = "$dir$filename-$version-$type.sql";
940
941 return $filename;
942}
943
a02675cd 9441;
c2da098a 945
c2da098a 946=head1 AUTHORS
947
daec44b8 948Matt S. Trout <mst@shadowcatsystems.co.uk>
c2da098a 949
950=head1 LICENSE
951
952You may distribute this code under the same terms as Perl itself.
953
954=cut
955