use DBIx::Class::ResultSet;
use DBIx::Class::ResultSourceHandle;
+
+use DBIx::Class::Exception;
use Carp::Clan qw/^DBIx::Class/;
-use Storable;
use base qw/DBIx::Class/;
# Create a table based result source, in a result class.
package MyDB::Schema::Result::Artist;
- use base qw/DBIx::Class/;
+ use base qw/DBIx::Class::Core/;
- __PACKAGE__->load_components(qw/Core/);
__PACKAGE__->table('artist');
__PACKAGE__->add_columns(qw/ artistid name /);
__PACKAGE__->set_primary_key('artistid');
# Create a query (view) based result source, in a result class
package MyDB::Schema::Result::Year2000CDs;
+ use base qw/DBIx::Class::Core/;
- __PACKAGE__->load_components('Core');
+ __PACKAGE__->load_components('InflateColumn::DateTime');
__PACKAGE__->table_class('DBIx::Class::ResultSource::View');
__PACKAGE__->table('year2000cds');
default result source type, so one is created for you when defining a
result class as described in the synopsis above.
-More specifically, the L<DBIx::Class::Core> component pulls in the
-L<DBIx::Class::ResultSourceProxy::Table> as a base class, which
-defines the L<table|DBIx::Class::ResultSourceProxy::Table/table>
-method. When called, C<table> creates and stores an instance of
+More specifically, the L<DBIx::Class::Core> base class pulls in the
+L<DBIx::Class::ResultSourceProxy::Table> component, which defines
+the L<table|DBIx::Class::ResultSourceProxy::Table/table> method.
+When called, C<table> creates and stores an instance of
L<DBIx::Class::ResultSoure::Table>. Luckily, to use tables as result
sources, you don't need to remember any of this.
my $self = shift;
$self->throw_exception(
"columns() is a read-only accessor, did you mean add_columns()?"
- ) if (@_ > 1);
+ ) if @_;
return @{$self->{_ordered_columns}||[]};
}
# Returns the {from} structure used to express JOIN conditions
sub _resolve_join {
- my ($self, $join, $alias, $seen, $jpath, $force_left) = @_;
+ my ($self, $join, $alias, $seen, $jpath, $parent_force_left) = @_;
# we need a supplied one, because we do in-place modifications, no returns
$self->throw_exception ('You must supply a seen hashref as the 3rd argument to _resolve_join')
$self->throw_exception ('You must supply a joinpath arrayref as the 4th argument to _resolve_join')
unless ref $jpath eq 'ARRAY';
- $jpath = [@$jpath];
+ $jpath = [@$jpath]; # copy
- if (ref $join eq 'ARRAY') {
+ if (not defined $join) {
+ return ();
+ }
+ elsif (ref $join eq 'ARRAY') {
return
map {
- $self->_resolve_join($_, $alias, $seen, $jpath, $force_left);
+ $self->_resolve_join($_, $alias, $seen, $jpath, $parent_force_left);
} @$join;
- } elsif (ref $join eq 'HASH') {
- return
- map {
- my $as = ($seen->{$_} ? join ('_', $_, $seen->{$_} + 1) : $_); # the actual seen value will be incremented below
- local $force_left->{force} = $force_left->{force};
- (
- $self->_resolve_join($_, $alias, $seen, [@$jpath], $force_left),
- $self->related_source($_)->_resolve_join(
- $join->{$_}, $as, $seen, [@$jpath, $_], $force_left
- )
- );
- } keys %$join;
- } elsif (ref $join) {
- $self->throw_exception("No idea how to resolve join reftype ".ref $join);
- } else {
+ }
+ elsif (ref $join eq 'HASH') {
- return() unless defined $join;
+ my @ret;
+ for my $rel (keys %$join) {
- my $count = ++$seen->{$join};
- my $as = ($count > 1 ? "${join}_${count}" : $join);
-
- my $rel_info = $self->relationship_info($join);
- $self->throw_exception("No such relationship ${join}") unless $rel_info;
- my $type;
- if ($force_left) {
- $type = 'left';
- } else {
- $type = $rel_info->{attrs}{join_type} || '';
- $force_left = 1 if lc($type) eq 'left';
+ my $rel_info = $self->relationship_info($rel)
+ or $self->throw_exception("No such relationship ${rel}");
+
+ my $force_left = $parent_force_left;
+ $force_left ||= lc($rel_info->{attrs}{join_type}||'') eq 'left';
+
+ # the actual seen value will be incremented by the recursion
+ my $as = $self->storage->relname_to_table_alias(
+ $rel, ($seen->{$rel} && $seen->{$rel} + 1)
+ );
+
+ push @ret, (
+ $self->_resolve_join($rel, $alias, $seen, [@$jpath], $force_left),
+ $self->related_source($rel)->_resolve_join(
+ $join->{$rel}, $as, $seen, [@$jpath, { $rel => $as }], $force_left
+ )
+ );
}
+ return @ret;
+
+ }
+ elsif (ref $join) {
+ $self->throw_exception("No idea how to resolve join reftype ".ref $join);
+ }
+ else {
+ my $count = ++$seen->{$join};
+ my $as = $self->storage->relname_to_table_alias(
+ $join, ($count > 1 && $count)
+ );
+
+ my $rel_info = $self->relationship_info($join)
+ or $self->throw_exception("No such relationship ${join}");
my $rel_src = $self->related_source($join);
return [ { $as => $rel_src->from,
-source_handle => $rel_src->handle,
- -join_type => $type,
- -join_path => [@$jpath, $join],
+ -join_type => $parent_force_left
+ ? 'left'
+ : $rel_info->{attrs}{join_type}
+ ,
+ -join_path => [@$jpath, { $join => $as } ],
+ -is_single => (List::Util::first { $rel_info->{attrs}{accessor} eq $_ } (qw/single filter/) ),
-alias => $as,
-relation_chain_depth => $seen->{-relation_chain_depth} || 0,
},
# hashref of columns of the related object.
sub _pk_depends_on {
my ($self, $relname, $rel_data) = @_;
- my $cond = $self->relationship_info($relname)->{cond};
+ my $relinfo = $self->relationship_info($relname);
+
+ # don't assume things if the relationship direction is specified
+ return $relinfo->{attrs}{is_foreign_key_constraint}
+ if exists ($relinfo->{attrs}{is_foreign_key_constraint});
+
+ my $cond = $relinfo->{cond};
return 0 unless ref($cond) eq 'HASH';
# map { foreign.foo => 'self.bar' } to { bar => 'foo' }
-
my $keyhash = { map { my $x = $_; $x =~ s/.*\.//; $x; } reverse %$cond };
# assume anything that references our PK probably is dependent on us
# rather than vice versa, unless the far side is (a) defined or (b)
# auto-increment
-
my $rel_source = $self->related_source($relname);
foreach my $p ($self->primary_columns) {
#warn "$self $k $for $v";
unless ($for->has_column_loaded($v)) {
if ($for->in_storage) {
- $self->throw_exception(
- "Column ${v} not loaded or not passed to new() prior to insert()"
- ." on ${for} trying to resolve relationship (maybe you forgot "
- ."to call ->discard_changes to get defaults from the db)"
+ $self->throw_exception(sprintf
+ "Unable to resolve relationship '%s' from object %s: column '%s' not "
+ . 'loaded from storage (or not passed to new() prior to insert()). You '
+ . 'probably need to call ->discard_changes to get the server-side defaults '
+ . 'from the database.',
+ $as,
+ $for,
+ $v,
);
}
return $UNRESOLVABLE_CONDITION;
# Accepts one or more relationships for the current source and returns an
# array of column names for each of those relationships. Column names are
# prefixed relative to the current source, in accordance with where they appear
-# in the supplied relationships. Needs an alias_map generated by
-# $rs->_joinpath_aliases
+# in the supplied relationships.
sub _resolve_prefetch {
my ($self, $pre, $alias, $alias_map, $order, $collapse, $pref_path) = @_;
$pref_path ||= [];
- if( ref $pre eq 'ARRAY' ) {
+ if (not defined $pre) {
+ return ();
+ }
+ elsif( ref $pre eq 'ARRAY' ) {
return
map { $self->_resolve_prefetch( $_, $alias, $alias_map, $order, $collapse, [ @$pref_path ] ) }
@$pre;
$p = $p->{$_} for (@$pref_path, $pre);
$self->throw_exception (
- "Unable to resolve prefetch $pre - join alias map does not contain an entry for path: "
+ "Unable to resolve prefetch '$pre' - join alias map does not contain an entry for path: "
. join (' -> ', @$pref_path, $pre)
) if (ref $p->{-join_aliases} ne 'ARRAY' or not @{$p->{-join_aliases}} );
=cut
sub handle {
- return new DBIx::Class::ResultSourceHandle({
+ return DBIx::Class::ResultSourceHandle->new({
schema => $_[0]->schema,
source_moniker => $_[0]->source_name
});
sub throw_exception {
my $self = shift;
+
if (defined $self->schema) {
$self->schema->throw_exception(@_);
- } else {
- croak(@_);
+ }
+ else {
+ DBIx::Class::Exception->throw(@_);
}
}