6097215022481aa832aa6bed89fd05b15ea78992
[dbsrgits/DBIx-Class-ParameterizedJoinHack.git] / lib / DBIx / Class / ResultSet / ParameterizedJoinHack.pm
1 package DBIx::Class::ResultSet::ParameterizedJoinHack;
2
3 use strict;
4 use warnings;
5 use DBIx::Class::ParameterizedJoinHack;
6 use base qw(DBIx::Class::ResultSet);
7
8 sub _parameterized_join_store {
9   $_[0]->result_source->result_class
10        ->$DBIx::Class::ParameterizedJoinHack::STORE
11 }
12
13 sub with_parameterized_join {
14   my ($self, $rel, $params) = @_;
15   die "Missing relation name in with_parameterized_join"
16     unless defined $rel;
17
18   {
19     my $params_ref = ref($params);
20     $params_ref = 'non-reference-value'
21       unless $params_ref;
22     die "Parameters value must be a hash ref, not ${params_ref}"
23       unless $params_ref eq 'HASH';
24   }
25
26   die "Parameterized join can only be used once per relation"
27     if exists(($self->{attrs}{join_parameters} || {})->{$rel});
28
29   $self->search_rs(
30     {},
31     { join => $rel,
32       join_parameters => {
33         %{$self->{attrs}{join_parameters}||{}},
34         $rel => $params
35       }
36     },
37   );
38 }
39
40 sub call_with_parameters {
41   my ($self, $method, @args) = @_;
42   my $params = $self->{attrs}{join_parameters}||{};
43   my @param_names = keys %$params;
44   my $store = $self->_parameterized_join_store;
45   # This loop is implemented using "goto"; this means the "local" doesn't have
46   # to be in a separate scope, which would defeat the point. Nor does "local
47   # ... foreach ..." work; the localization doesn't survive the loop. The
48   # alternative would be to use recursive subroutine calls, but that would
49   # require not only the N save-stack entries for the localized parameters, but
50   # N call frames too, so this approach saves both time and memory. It also
51   # avoids any risk of a "deep recursion" warning if you have a lot of
52   # parameters; and it avoids cluttering stack traces if the called method
53   # throws an exception. The downside is obviously that it uses "goto" for an
54   # otherwise-simple loop, but at least it's confined to this method. Further,
55   # the result is more than twice as fast as the recursive approach even when
56   # there are no parameters, and the "goto" version wins even more with larger
57   # parameter lists.
58   Loop:
59     return $self->$method(@args)
60       unless @param_names;
61     my $key = shift @param_names;
62     local $store->{$key}{params} = $params->{$key};
63     goto Loop; # Watch Dijkstra roll in his grave
64 }
65
66 sub _resolved_attrs { my $self = shift; $self->call_with_parameters($self->next::can, @_) }
67 sub related_resultset { my $self = shift; $self->call_with_parameters($self->next::can, @_) }
68
69 1;
70
71 =head1 NAME
72
73 DBIx::Class::ResultSet::ParameterizedJoinHack
74
75 =head1 SYNOPSIS
76
77     package MySchema::ResultSet::Person;
78     use base qw(DBIx::Class::ResultSet);
79
80     __PACKAGE__->load_components(qw(ResultSet::ParameterizedJoinHack));
81
82     1;
83
84 =head1 DESCRIPTION
85
86 This is a ResultSet component allowing you to access the dynamically
87 parameterized relations declared with
88 L<DBIx::Class::ParameterizedJoinHack>.
89
90 Enable the component as usual with:
91     
92     __PACKAGE__->load_components(qw( ResultSet::ParameterizedJoinHack ));
93
94 in your ResultSet class.
95
96 See L<DBIx::Class::ParameterizedJoinHack> for declaration documentation,
97 a general overview, and examples.
98
99 =head1 METHODS
100
101 =head2 with_parameterized_join
102
103     my $joined_rs = $resultset->with_parameterized_join(
104         $relation_name,
105         $parameters,
106     );
107
108 This method constructs a ResultSet joined with the given C<$relation_name>
109 by the passed C<$parameters>. The C<$relation_name> is the name as
110 declared on the Result, C<$parameters> is a hash reference with the keys
111 being the parameter names, and the values being the arguments to the join
112 builder.
113
114 =head1 SPONSORS
115
116 Development of this module was sponsored by
117
118 =over
119
120 =item * Ctrl O L<http://ctrlo.com>
121
122 =back
123
124 =head1 AUTHOR
125
126  Matt S. Trout <mst@shadowcat.co.uk>
127
128 =head1 CONTRIBUTORS
129
130 None yet.
131
132 =head1 COPYRIGHT
133
134 Copyright (c) 2015 the DBIx::Class::ParameterizedJoinHack L</AUTHOR> and L</CONTRIBUTORS>
135 as listed above.
136
137 =head1 LICENSE
138
139 This library is free software and may be distributed under the same terms
140 as perl itself.
141
142 =cut