Commit | Line | Data |
638c1533 |
1 | package DBIx::Class::ParameterizedJoinHack; |
2 | |
3 | use strict; |
4 | use warnings; |
5 | use base qw(DBIx::Class); |
6 | |
7 | our $VERSION = '0.000001'; # 0.0.1 |
8 | $VERSION = eval $VERSION; |
9 | |
10 | our $STORE = '_parameterized_join_hack_meta_info'; |
11 | |
12 | __PACKAGE__->mk_group_accessors(inherited => $STORE); |
13 | |
14 | sub parameterized_has_many { |
15 | my ($class, $rel, $f_source, $cond, $attrs) = @_; |
16 | { |
17 | my $cond_ref = ref($cond); |
18 | die "Condition needs to be [ \\\@args, \$code ], not ${cond_ref}" |
19 | unless $cond_ref eq 'ARRAY'; |
20 | } |
21 | my ($args, $code) = @$cond; |
22 | my $store = $class->$STORE({ |
23 | %{$class->$STORE||{}}, |
24 | $rel => { params => {}, args => $args }, |
25 | }); |
26 | my $wrapped_code = sub { |
27 | my $params = $store->{$rel}{params}; |
28 | my @missing = grep !exists $params->{$_}, @$args; |
29 | die "Attempted to use parameterized rel ${rel} for ${class} without" |
30 | ." passing parameters ".join(', ', @missing) if @missing; |
31 | local *_ = $params; |
32 | &$code; |
33 | }; |
34 | $class->has_many($rel, $f_source, $wrapped_code, $attrs); |
35 | return; # no, you are not going to accidentally rely on a return value |
36 | } |
37 | |
38 | 1; |
39 | |
40 | =head1 NAME |
41 | |
42 | DBIx::Class::ParameterizedJoinHack - Parameterized Relationship Joins |
43 | |
44 | =head1 SYNOPSIS |
45 | |
46 | # |
47 | # The Result class we want to allow to join with a dynamic |
48 | # condition. |
49 | # |
50 | package MySchema::Result::Person; |
51 | use base qw(DBIx::Class::Core); |
52 | |
53 | __PACKAGE__->load_components(qw(ParameterizedJoinHack)); |
54 | __PACKAGE__->table('person'); |
55 | __PACKAGE__->add_columns( |
56 | id => { |
57 | data_type => 'integer', |
58 | is_nullable => 0, |
59 | is_auto_increment => 1, |
60 | }, |
61 | name => { |
62 | data_type => 'text', |
63 | is_nullable => 0, |
64 | } |
65 | ); |
66 | |
67 | ... |
68 | |
69 | __PACKAGE__->parameterized_has_many( |
70 | priority_tasks => 'MySchema::Result::Task', |
71 | [['min_priority'] => sub { |
72 | my $args = shift; |
73 | return +{ |
74 | "$args->{foreign_alias}.owner_id" => { |
75 | -ident => "$args->{self_alias}.id", |
76 | }, |
77 | "$args->{foreign_alias}.priority" => { |
78 | '>=' => $_{min_priority}, |
79 | }, |
80 | }; |
81 | }], |
82 | ); |
83 | |
84 | 1; |
85 | |
86 | # |
87 | # The ResultSet class belonging to your Result |
88 | # |
89 | package MySchema::ResultSet::Person; |
90 | use base qw(DBIx::Class::ResultSet); |
91 | |
92 | __PACKAGE__->load_components(qw(ResultSet::ParameterizedJoinHack)); |
93 | |
94 | 1; |
95 | |
96 | # |
97 | # A Result class to join against. |
98 | # |
99 | package MySchema::Result::Task; |
100 | use base qw(DBIx::Class::Core); |
101 | |
102 | __PACKAGE__->table('task'); |
103 | __PACKAGE__->add_columns( |
104 | id => { |
105 | data_type => 'integer', |
106 | is_nullable => 0, |
107 | is_auto_increment => 1, |
108 | }, |
109 | owner_id => { |
110 | data_type => 'integer', |
111 | is_nullable => 0, |
112 | }, |
113 | priority => { |
114 | data_type => 'integer', |
115 | is_nullable => 0, |
116 | }, |
117 | ); |
118 | |
119 | ... |
120 | |
121 | 1; |
122 | |
123 | # |
124 | # Using the parameterized join. |
125 | # |
126 | my @urgent = MySchema |
127 | ->connect(...) |
128 | ->resultset('Person') |
129 | ->with_parameterized_join( |
130 | priority_tasks => { |
131 | min_priority => 300, |
132 | }, |
133 | ) |
134 | ->all; |
135 | |
136 | =head1 DESCRIPTION |
137 | |
138 | This L<DBIx::Class> component allows to declare dynamically parameterized |
139 | has-many relationships. |
140 | |
141 | Add the component to your Result class as usual: |
142 | |
143 | __PACKAGE__->load_components(qw( ParameterizedJoinHack )); |
144 | |
145 | See L<parameterized_has_many> for details on declaring relations. |
146 | |
147 | See L<DBIx::Class::ResultSet::ParameterizedJoinHack> for ResultSet usage. |
148 | |
149 | =head1 METHODS |
150 | |
151 | =head2 parameterized_has_many |
152 | |
153 | __PACKAGE__->parameterized_has_many( |
154 | $relation_name, |
155 | $foreign_source, |
156 | [\@join_arg_names, \&join_builder], |
157 | $attrs, |
158 | ); |
159 | |
160 | The C<$relation_name>, C<$foreign_source>, and C<$attrs> are passed |
161 | through to C<has_many> as usual. The third argument is an array reference |
162 | containing an (array reference) list of argument names and a code |
163 | reference used to build the join conditions. |
164 | |
165 | The code reference will be called with the same arguments as if it had |
166 | been passed to C<has_many> directly, but the global C<%_> hash will |
167 | contain the named arguments for the join. |
168 | |
169 | See the L</SYNOPSIS> for an example of a definition. |
170 | |
171 | =head1 SPONSORS |
172 | |
173 | Development of this module was sponsored by |
174 | |
175 | =over |
176 | |
177 | =item * Ctrl O L<http://ctrlo.com> |
178 | |
179 | =back |
180 | |
181 | =head1 AUTHOR |
182 | |
183 | Matt S. Trout <mst@shadowcat.co.uk> |
184 | |
185 | =head1 CONTRIBUTORS |
186 | |
187 | None yet. |
188 | |
189 | =head1 COPYRIGHT |
190 | |
191 | Copyright (c) 2015 the DBIx::Class::ParameterizedJoinHack L</AUTHOR> and L</CONTRIBUTORS> |
192 | as listed above. |
193 | |
194 | =head1 LICENSE |
195 | |
196 | This library is free software and may be distributed under the same terms |
197 | as perl itself. |