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