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