add some docs for the role
[dbsrgits/SQL-Abstract.git] / lib / DBIx / Class / SQLMaker / Role / SQLA2Passthrough.pm
1 package DBIx::Class::SQLMaker::Role::SQLA2Passthrough;
2
3 use strict;
4 use warnings;
5 use Exporter 'import';
6
7 our @EXPORT = qw(on);
8
9 sub on (&) {
10   my ($on) = @_;
11   sub {
12     my ($args) = @_;
13     $args->{self_resultsource}
14          ->schema->storage->sql_maker
15          ->expand_join_condition(
16              $on->($args),
17              $args
18            );
19   }
20 }
21
22 use Role::Tiny;
23
24 around select => sub {
25   my ($orig, $self, $table, $fields, $where, $rs_attrs, $limit, $offset) = @_;
26
27   $fields = \[ $self->render_expr({ -list => [
28     grep defined,
29     map +(ref($_) eq 'HASH'
30           ? do {
31               my %f = %$_;
32               my $as = delete $f{-as};
33               my ($f, $rhs) = %f;
34               my $func = +{ ($f =~ /^-/ ? $f : "-${f}") => $rhs };
35               ($as
36                 ? +{ -op => [ 'as', $func, { -ident => [ $as ] } ] }
37                 : $func)
38             }
39           : $_), ref($fields) eq 'ARRAY' ? @$fields : $fields
40   ] }, -ident) ];
41
42   if (my $gb = $rs_attrs->{group_by}) {
43     $rs_attrs = {
44       %$rs_attrs,
45       group_by => \[ $self->render_expr({ -list => $gb }, -ident) ]
46     };
47   }
48   $self->$orig($table, $fields, $where, $rs_attrs, $limit, $offset);
49 };
50
51 sub expand_join_condition {
52   my ($self, $cond, $args) = @_;
53   my $wrap = sub {
54     my ($orig) = @_;
55     sub {
56       my $res = $orig->(@_);
57       my ($name, @rest) = @{$res->{-ident}};
58       if ($name eq 'self' or $name eq 'foreign') {
59         $res->{-ident} = [ $args->{"${name}_alias"}, @rest ];
60       }
61       return $res;
62     };
63   };
64   my $sqla = $self->clone->wrap_op_expander(ident => $wrap);
65   $sqla->expand_expr($cond, -ident);
66 }
67
68 1;
69
70 __END__
71
72 =head1 NAME
73
74 DBIx::Class::SQLMaker::Role::SQLA2Passthrough - A test of future possibilities
75
76 =head1 SYNOPSIS
77
78 =over 4
79
80 =item * select and group_by options are processed using the richer SQLA2 code
81
82 =item * expand_join_condition is provided to more easily express rich joins
83
84 =back
85
86 See C<examples/sqla2passthrough.pl> for a small amount of running code.
87
88 =head1 SETUP
89
90   (on_connect_call => sub {
91      my ($storage) = @_;
92      $storage->sql_maker
93              ->with::roles('DBIx::Class::SQLMaker::Role::SQLA2Passthrough');
94   })
95
96 =head2 expand_join_condition
97
98   __PACKAGE__->has_many(minions => 'Blah::Person' => sub {
99     my ($args) = @_;
100     $args->{self_resultsource}
101          ->schema->storage->sql_maker
102          ->expand_join_condition(
103              $args
104            );
105   });
106
107 =head2 on
108
109   __PACKAGE__->has_many(minions => 'Blah::Person' => on {
110     { 'self.group_id' => 'foreign.group_id',
111       'self.rank' => { '>', 'foreign.rank' } }
112   });
113
114 Or with ParameterizedJoinHack,
115
116   __PACKAGE__->parameterized_has_many(
117       priority_tasks => 'MySchema::Result::Task',
118       [['min_priority'] => sub {
119           my $args = shift;
120           return +{
121               "$args->{foreign_alias}.owner_id" => {
122                   -ident => "$args->{self_alias}.id",
123               },
124               "$args->{foreign_alias}.priority" => {
125                   '>=' => $_{min_priority},
126               },
127           };
128       }],
129   );
130
131 becomes
132
133   __PACKAGE__->parameterized_has_many(
134       priority_tasks => 'MySchema::Result::Task',
135       [['min_priority'] => on {
136         { 'foreign.owner_id' => 'self.id',
137           'foreign.priority' => { '>=', { -value => $_{min_priority} } } }
138       }]
139   );
140
141 =cut