Revert r482[45], by implementing a better version of r4760
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / ResultClass / HashRefInflator.pm
CommitLineData
b0930c1e 1package DBIx::Class::ResultClass::HashRefInflator;
2
83304545 3use strict;
4use warnings;
5
137c657c 6=head1 NAME
7
8DBIx::Class::ResultClass::HashRefInflator
9
10=head1 SYNOPSIS
11
a5b29361 12 use DBIx::Class::ResultClass::HashRefInflator;
13
137c657c 14 my $rs = $schema->resultset('CD');
15
16 $rs->result_class('DBIx::Class::ResultClass::HashRefInflator');
a5b29361 17 or
18 $rs->result_class(DBIx::Class::ResultClass::HashRefInflator->new (%args));
19
20 while (my $hashref = $rs->next) {
21 ...
22 }
137c657c 23
24=head1 DESCRIPTION
25
a5b29361 26DBIx::Class is faster than older ORMs like Class::DBI but it still isn't
27designed primarily for speed. Sometimes you need to quickly retrieve the data
28from a massive resultset, while skipping the creation of fancy row objects.
29Specifying this class as a C<result_class> for a resultset will change C<< $rs->next >>
30to return a plain data hash-ref (or a list of such hash-refs if C<< $rs->all >> is used).
137c657c 31
a5b29361 32There are two ways of using this class:
137c657c 33
34=over
35
36=item *
37
a5b29361 38Supply an instance of DBIx::Class::ResultClass::HashRefInflator to
39C<< $rs->result_class >>. See L</ARGUMENTS> for a list of valid
40arguments to new().
137c657c 41
42=item *
43
a5b29361 44Another way is to simply supply the class name as a string to
45C<< $rs->result_class >>. Equivalent to passing
46DBIx::Class::ResultClass::HashRefInflator->new().
137c657c 47
48=back
49
a5b29361 50There are two ways of applying this class to a resultset:
2328814a 51
a5b29361 52=over
137c657c 53
a5b29361 54=item *
137c657c 55
a5b29361 56Specify C<< $rs->result_class >> on a specific resultset to affect only that
57resultset (and any chained off of it); or
137c657c 58
a5b29361 59=item *
137c657c 60
a5b29361 61Specify C<< __PACKAGE__->result_class >> on your source object to force all
62uses of that result source to be inflated to hash-refs - this approach is not
63recommended.
137c657c 64
a5b29361 65=back
137c657c 66
67=cut
b0930c1e 68
2328814a 69##############
70# NOTE
71#
a5b29361 72# Generally people use this to gain as much speed as possible. If a new &mk_hash is
2328814a 73# implemented, it should be benchmarked using the maint/benchmark_hashrefinflator.pl
a5b29361 74# script (in addition to passing all tests of course :). Additional instructions are
2328814a 75# provided in the script itself.
76#
77
a5b29361 78# This coderef is a simple recursive function
79# Arguments: ($me, $prefetch) from inflate_result() below
80my $mk_hash;
81$mk_hash = sub {
2328814a 82 if (ref $_[0] eq 'ARRAY') { # multi relationship
a5b29361 83 return [ map { $mk_hash->(@$_) || () } (@_) ];
2328814a 84 }
85 else {
86 my $hash = {
87 # the main hash could be an undef if we are processing a skipped-over join
88 $_[0] ? %{$_[0]} : (),
89
90 # the second arg is a hash of arrays for each prefetched relation
91 map
a5b29361 92 { $_ => $mk_hash->( @{$_[1]->{$_}} ) }
2328814a 93 ( $_[1] ? (keys %{$_[1]}) : () )
94 };
95
96 # if there is at least one defined column consider the resultset real
97 # (and not an emtpy has_many rel containing one empty hashref)
98 for (values %$hash) {
99 return $hash if defined $_;
100 }
b0930c1e 101
2328814a 102 return undef;
103 }
a5b29361 104};
105
106# This is the inflator
107my $inflate_hash;
108$inflate_hash = sub {
109 my ($hri_instance, $schema, $rc, $data) = @_;
110
111 foreach my $column (keys %{$data}) {
112
113 if (ref $data->{$column} eq 'HASH') {
114 $inflate_hash->($hri_instance, $schema, $schema->source ($rc)->related_class ($column), $data->{$column});
115 }
116 elsif (ref $data->{$column} eq 'ARRAY') {
117 foreach my $rel (@{$data->{$column}}) {
118 $inflate_hash->($hri_instance, $schema, $schema->source ($rc)->related_class ($column), $rel);
119 }
120 }
121 else {
122 # "null is null is null"
123 next if not defined $data->{$column};
124
125 # cache the inflator coderef
126 unless (exists $hri_instance->{_inflator_cache}{$rc}{$column}) {
127 $hri_instance->{_inflator_cache}{$rc}{$column} = exists $schema->source ($rc)->_relationships->{$column}
128 ? undef # currently no way to inflate a column sharing a name with a rel
129 : $rc->column_info($column)->{_inflate_info}{inflate}
130 ;
131 }
132
133 if ($hri_instance->{_inflator_cache}{$rc}{$column}) {
134 $data->{$column} = $hri_instance->{_inflator_cache}{$rc}{$column}->($data->{$column});
135 }
136 }
137 }
138};
139
140
141=head1 METHODS
142
143=head2 new
144
145 $class->new( %args );
146 $class->new({ %args });
147
148Creates a new DBIx::Class::ResultClass::HashRefInflator object. Takes the following
149arguments:
150
151=over
152
153=item inflate_columns
154
155Sometimes you still want all your data to be inflated to the corresponding
156objects according to the rules you defined in your table classes (e.g. you
157want all dates in the resulting hash to be replaced with the equivalent
158DateTime objects). Supplying C<< inflate_columns => 1 >> to the constructor will
159interrogate the processed columns and apply any inflation methods declared
160via L<DBIx::Class::InflateColumn/inflate_column> to the contents of the
161resulting hash-ref.
162
163=back
164
165=cut
166
167sub new {
168 my $self = shift;
169 my $args = { (ref $_[0] eq 'HASH') ? %{$_[0]} : @_ };
170 return bless ($args, $self)
171}
172
173=head2 inflate_result
174
175Inflates the result and prefetched data into a hash-ref (invoked by L<DBIx::Class::ResultSet>)
176
177=cut
178
179
180sub inflate_result {
181 my ($self, $source, $me, $prefetch) = @_;
182
183 my $hashref = $mk_hash->($me, $prefetch);
184
185 # if $self is an instance and inflate_columns is set
186 if ( (ref $self) and $self->{inflate_columns} ) {
187 $inflate_hash->($self, $source->schema, $source->result_class, $hashref);
188 }
189
190 return $hashref;
2328814a 191}
192
a5b29361 193
194=head1 CAVEATS
195
196=over
197
198=item *
419ff184 199
200This will not work for relationships that have been prefetched. Consider the
201following:
202
203 my $artist = $artitsts_rs->search({}, {prefetch => 'cds' })->first;
204
205 my $cds = $artist->cds;
206 $cds->result_class('DBIx::Class::ResultClass::HashRefInflator');
207 my $first = $cds->first;
208
209C<$first> will B<not> be a hashref, it will be a normal CD row since
210HashRefInflator only affects resultsets at inflation time, and prefetch causes
211relations to be inflated when the master C<$artist> row is inflated.
212
a5b29361 213=item *
214
215When using C<inflate_columns>, the inflation method lookups are cached in the
216HashRefInflator object for additional speed. If you modify column inflators at run
217time, make sure to grab a new instance of this class to avoid cached surprises.
218
219=back
220
419ff184 221=cut
222
b0930c1e 2231;