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