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