move join inference cleverness into a role
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / ResultSet / Role / DQMethods.pm
1 package DBIx::Class::ResultSet::Role::DQMethods;
2
3 use Data::Query::ExprHelpers;
4 use Safe::Isa;
5 use Moo::Role;
6
7 sub _dq_converter {
8   shift->result_source->schema->storage->sql_maker->converter;
9 }
10
11 sub where {
12   my ($self, $where) = @_;
13   if ($where->$_isa('Data::Query::ExprBuilder')) {
14     return $self->_apply_dq_where($where->{expr});
15   } elsif (ref($where) eq 'HASH') {
16     return $self->_apply_dq_where(
17              $self->_dq_converter->_where_to_dq($where)
18            );
19   }
20   die "Argument to ->where must be ExprBuilder or SQL::Abstract hashref, got: "
21       .(defined($where) ? $where : 'undef');
22 }
23
24 sub _apply_dq_where {
25   my ($self, $expr) = @_;
26   my ($mapped, $need_join) = $self->_remap_identifiers($expr);
27   $self->search_rs(\$mapped, { join => $need_join });
28 }
29
30 sub _remap_identifiers {
31   my ($self, $dq) = @_;
32   my $map = {};
33   my $attrs = $self->_resolved_attrs;
34   foreach my $j ( @{$attrs->{from}}[1 .. $#{$attrs->{from}} ] ) {
35     next unless $j->[0]{-alias};
36     next unless $j->[0]{-join_path};
37     my $p = $map;
38     $p = $p->{$_} ||= {} for map { keys %$_ } @{$j->[0]{-join_path}};
39     $p->{''} = $j->[0]{-alias};
40   }
41
42   my $seen_join = { %{$attrs->{seen_join}||{}} };
43   my $storage = $self->result_source->storage;
44   my @need_join;
45   my $mapped = map_dq_tree {
46     return $_ unless is_Identifier;
47     my @el = @{$_->{elements}};
48     my $last = pop @el;
49     unless (@el) {
50       return Identifier($attrs->{alias}, $last);
51     }
52     my $p = $map;
53     $p = $p->{$_} ||= {} for @el;
54     if (my $alias = $p->{''}) {
55       return Identifier($alias, $last);
56     }
57     my $need = my $j = {};
58     $j = $j->{$_} = {} for @el;
59     push @need_join, $need;
60     my $alias = $storage->relname_to_table_alias(
61       $el[-1], ++$seen_join->{$el[-1]}
62     );
63     return Identifier($alias, $last);
64   } $dq;
65   return ($mapped, \@need_join);
66 }
67
68 1;