X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FResultSource.pm;h=e2e2d7491fd791237f8cfe52e286a111453ec7f4;hb=bcc5a2102b4052d6f642c870b7747af4f6f60461;hp=343c0195137b7cfd88ae9ccce8cc7fac899c0eb7;hpb=70ecd5a103ed5ab8f674df100da40ff47d4ae658;p=dbsrgits%2FDBIx-Class-Historic.git diff --git a/lib/DBIx/Class/ResultSource.pm b/lib/DBIx/Class/ResultSource.pm index 343c019..e2e2d74 100644 --- a/lib/DBIx/Class/ResultSource.pm +++ b/lib/DBIx/Class/ResultSource.pm @@ -5,9 +5,7 @@ use warnings; use DBIx::Class::ResultSet; use Carp::Clan qw/^DBIx::Class/; - use Storable; -use Scalar::Util qw/weaken/; use base qw/DBIx::Class/; __PACKAGE__->load_components(qw/AccessorGroup/); @@ -126,18 +124,15 @@ Convenience alias to add_columns sub add_columns { my ($self, @cols) = @_; - $self->_ordered_columns( \@cols ) - if !$self->_ordered_columns; + $self->_ordered_columns(\@cols) unless $self->_ordered_columns; + my @added; my $columns = $self->_columns; while (my $col = shift @cols) { - + # If next entry is { ... } use that for the column info, if not + # use an empty hashref my $column_info = ref $cols[0] ? shift(@cols) : {}; - # If next entry is { ... } use that for the column info, if not - # use an empty hashref - push(@added, $col) unless exists $columns->{$col}; - $columns->{$col} = $column_info; } push @{ $self->_ordered_columns }, @added; @@ -173,22 +168,21 @@ sub column_info { $self->throw_exception("No such column $column") unless exists $self->_columns->{$column}; #warn $self->{_columns_info_loaded}, "\n"; - if ( ! $self->_columns->{$column}->{data_type} - && ! $self->{_columns_info_loaded} - && $self->schema && $self->storage() ){ - $self->{_columns_info_loaded}++; - my $info; -############ eval for the case of storage without table - eval{ - $info = $self->storage->columns_info_for ( $self->from() ); - }; - if ( ! $@ ){ - for my $col ( keys %{$self->_columns} ){ - for my $i ( keys %{$info->{$col}} ){ - $self->_columns()->{$col}->{$i} = $info->{$col}->{$i}; - } - } + if ( ! $self->_columns->{$column}{data_type} + and ! $self->{_columns_info_loaded} + and $self->schema and $self->storage ) + { + $self->{_columns_info_loaded}++; + my $info; + # eval for the case of storage without table + eval { $info = $self->storage->columns_info_for($self->from) }; + unless ($@) { + foreach my $col ( keys %{$self->_columns} ) { + foreach my $i ( keys %{$info->{$col}} ) { + $self->_columns->{$col}{$i} = $info->{$col}{$i}; + } } + } } return $self->_columns->{$column}; } @@ -202,7 +196,7 @@ Returns all column names in the order they were declared to add_columns =cut sub columns { - my $self=shift; + my $self = shift; $self->throw_exception("columns() is a read-only accessor, did you mean add_columns()?") if (@_ > 1); return @{$self->{_ordered_columns}||[]}; } @@ -224,9 +218,9 @@ retrieve automatically created values from the database. sub set_primary_key { my ($self, @cols) = @_; # check if primary key columns are valid columns - for (@cols) { - $self->throw_exception("No such column $_ on table ".$self->name) - unless $self->has_column($_); + foreach my $col (@cols) { + $self->throw_exception("No such column $col on table " . $self->name) + unless $self->has_column($col); } $self->_primaries(\@cols); @@ -257,9 +251,9 @@ L, only columns in the constraint are searched, sub add_unique_constraint { my ($self, $name, $cols) = @_; - for (@$cols) { - $self->throw_exception("No such column $_ on table ".$self->name) - unless $self->has_column($_); + foreach my $col (@$cols) { + $self->throw_exception("No such column $col on table " . $self->name) + unless $self->has_column($col); } my %unique_constraints = $self->unique_constraints; @@ -299,13 +293,18 @@ sub storage { shift->schema->storage; } $source->add_relationship('relname', 'related_source', $cond, $attrs); -The relation name can be arbitrary, but must be unique for each relationship -attached to this result source. 'related_source' should be the name with -which the related result source was registered with the current schema -(for simple schemas this is usally either Some::Namespace::Foo or just Foo) +The relationship name can be arbitrary, but must be unique for each +relationship attached to this result source. 'related_source' should +be the name with which the related result source was registered with +the current schema. For example: + + $schema->source('Book')->add_relationship('reviews', 'Review', { + 'foreign.book_id' => 'self.id', + }); -The condition needs to be an SQL::Abstract-style representation of the join -between the tables. For example, if you're creating a rel from Author to Book, +The condition C<$cond> needs to be an SQL::Abstract-style +representation of the join between the tables. For example, if you're +creating a rel from Author to Book, { 'foreign.author_id' => 'self.id' } @@ -327,15 +326,18 @@ the SQL command immediately before C. =item proxy -An arrayref containing a list of accessors in the foreign class to -proxy in the main class. If, for example, you do the following: +An arrayref containing a list of accessors in the foreign class to proxy in +the main class. If, for example, you do the following: + + CD->might_have(liner_notes => 'LinerNotes', undef, { + proxy => [ qw/notes/ ], + }); + +Then, assuming LinerNotes has an accessor named notes, you can do: - __PACKAGE__->might_have(bar => 'Bar', undef, { proxy => [ qw/margle/] }); - -Then, assuming Bar has an accessor named margle, you can do: - - my $obj = Foo->find(1); - $obj->margle(10); # set margle; Bar object is created if it doesn't exist + my $cd = CD->find(1); + $cd->notes('Notes go here'); # set notes -- LinerNotes object is + # created if it doesn't exist =item accessor @@ -545,35 +547,56 @@ in the supplied relationships. Examples: =cut sub resolve_prefetch { - my ($self, $pre, $alias, $seen) = @_; + my ($self, $pre, $alias, $seen, $order, $collapse) = @_; $seen ||= {}; - use Data::Dumper; #$alias ||= $self->name; #warn $alias, Dumper $pre; if( ref $pre eq 'ARRAY' ) { - return map { $self->resolve_prefetch( $_, $alias, $seen ) } @$pre; + return + map { $self->resolve_prefetch( $_, $alias, $seen, $order, $collapse ) } + @$pre; } elsif( ref $pre eq 'HASH' ) { my @ret = map { - $self->resolve_prefetch($_, $alias, $seen), + $self->resolve_prefetch($_, $alias, $seen, $order, $collapse), $self->related_source($_)->resolve_prefetch( - $pre->{$_}, "${alias}.$_", $seen) - } keys %$pre; + $pre->{$_}, "${alias}.$_", $seen, $order, $collapse) + } keys %$pre; #die Dumper \@ret; return @ret; } elsif( ref $pre ) { - $self->throw_exception( "don't know how to resolve prefetch reftype " . ref $pre); + $self->throw_exception( + "don't know how to resolve prefetch reftype ".ref($pre)); } else { my $count = ++$seen->{$pre}; my $as = ($count > 1 ? "${pre}_${count}" : $pre); my $rel_info = $self->relationship_info( $pre ); - $self->throw_exception( $self->name . " has no such relationship '$pre'" ) unless $rel_info; - my $as_prefix = ($alias =~ /^.*?\.(.*)$/ ? $1.'.' : ''); + $self->throw_exception( $self->name . " has no such relationship '$pre'" ) + unless $rel_info; + my $as_prefix = ($alias =~ /^.*?\.(.+)$/ ? $1.'.' : ''); + my $rel_source = $self->related_source($pre); + + if (exists $rel_info->{attrs}{accessor} + && $rel_info->{attrs}{accessor} eq 'multi') { + $self->throw_exception( + "Can't prefetch has_many ${pre} (join cond too complex)") + unless ref($rel_info->{cond}) eq 'HASH'; + my @key = map { (/^foreign\.(.+)$/ ? ($1) : ()); } + keys %{$rel_info->{cond}}; + $collapse->{"${as_prefix}${pre}"} = \@key; + my @ord = (ref($rel_info->{attrs}{order_by}) eq 'ARRAY' + ? @{$rel_info->{attrs}{order_by}} + : (defined $rel_info->{attrs}{order_by} + ? ($rel_info->{attrs}{order_by}) + : ())); + push(@$order, map { "${as}.$_" } (@key, @ord)); + } + return map { [ "${as}.$_", "${as_prefix}${pre}.$_", ] } - $self->related_source($pre)->columns; + $rel_source->columns; #warn $alias, Dumper (\@ret); #return @ret; } @@ -595,12 +618,31 @@ sub related_source { return $self->schema->source($self->relationship_info($rel)->{source}); } +=head2 related_class + +=head3 Arguments: ($relname) + +Returns the class object for the given relationship + +=cut + +sub related_class { + my ($self, $rel) = @_; + if( !$self->has_relationship( $rel ) ) { + $self->throw_exception("No such relationship '$rel'"); + } + return $self->schema->class($self->relationship_info($rel)->{source}); +} + =head2 resultset -Returns a resultset for the given source, by calling: +Returns a resultset for the given source. This will initially be created +on demand by calling $self->resultset_class->new($self, $self->resultset_attributes) +but is cached from then on unless resultset_class changes. + =head2 resultset_class Set the class of the resultset, this is useful if you want to create your @@ -615,12 +657,9 @@ Specify here any attributes you wish to pass to your specialised resultset. sub resultset { my $self = shift; + $self->throw_exception('resultset does not take any arguments. If you want another resultset, call it on the schema instead.') if scalar @_; return $self->{_resultset} if ref $self->{_resultset} eq $self->resultset_class; - return $self->{_resultset} = do { - my $rs = $self->resultset_class->new($self, $self->{resultset_attributes}); - weaken $rs->result_source; - $rs; - }; + return $self->{_resultset} = $self->resultset_class->new($self, $self->{resultset_attributes}); } =head2 throw_exception