rework the local-izing code to navigate the maze of references correctly
[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
7our $VERSION = '0.000001'; # 0.0.1
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) = @_;
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 },
ddc15dd9 25 })->{$rel};
638c1533 26 my $wrapped_code = sub {
ddc15dd9 27 my $params = $store->{params};
638c1533 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
381;
39
40=head1 NAME
41
42DBIx::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
138This L<DBIx::Class> component allows to declare dynamically parameterized
139has-many relationships.
140
141Add the component to your Result class as usual:
142
143 __PACKAGE__->load_components(qw( ParameterizedJoinHack ));
144
145See L<parameterized_has_many> for details on declaring relations.
146
147See 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
160The C<$relation_name>, C<$foreign_source>, and C<$attrs> are passed
161through to C<has_many> as usual. The third argument is an array reference
162containing an (array reference) list of argument names and a code
163reference used to build the join conditions.
164
165The code reference will be called with the same arguments as if it had
166been passed to C<has_many> directly, but the global C<%_> hash will
167contain the named arguments for the join.
168
169See the L</SYNOPSIS> for an example of a definition.
170
171=head1 SPONSORS
172
173Development 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
187None yet.
188
189=head1 COPYRIGHT
190
191Copyright (c) 2015 the DBIx::Class::ParameterizedJoinHack L</AUTHOR> and L</CONTRIBUTORS>
192as listed above.
193
194=head1 LICENSE
195
196This library is free software and may be distributed under the same terms
197as perl itself.