Multiple HashRefInflator improvements:
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / ResultClass / HashRefInflator.pm
CommitLineData
b0930c1e 1package DBIx::Class::ResultClass::HashRefInflator;
2
83304545 3use strict;
4use warnings;
5
2328814a 6our %inflator_cache;
7our $inflate_data;
8
137c657c 9=head1 NAME
10
11DBIx::Class::ResultClass::HashRefInflator
12
13=head1 SYNOPSIS
14
15 my $rs = $schema->resultset('CD');
16
17 $rs->result_class('DBIx::Class::ResultClass::HashRefInflator');
18
19=head1 DESCRIPTION
20
21DBIx::Class is not built for speed: it's built for convenience and
22ease of use. But sometimes you just need to get the data, and skip the
23fancy objects. That is what this class provides.
24
25There are two ways of using this class.
26
27=over
28
29=item *
30
31Specify C<< $rs->result_class >> on a specific resultset to affect only that
57dd9393 32resultset (and any chained off of it); or
137c657c 33
34=item *
35
36Specify C<< __PACKAGE__->result_class >> on your source object to force all
37uses of that result source to be inflated to hash-refs - this approach is not
57dd9393 38recommended.
137c657c 39
40=back
41
2328814a 42=head1 AUTOMATICALLY INFLATING COLUMN VALUES
43
44So you want to skip the DBIx::Class object creation part, but you still want
45all your data to be inflated according to the rules you defined in your table
46classes. Setting the global variable
47C<$DBIx::Class::ResultClass::HashRefInflator::inflate_data> to a true value
48will instruct L<mk_hash> to interrogate the processed columns and apply any
49inflation methods declared via L<DBIx::Class::InflateColumn/inflate_column>.
50
51For increased speed the inflation method lookups are cached in
52C<%DBIx::Class::ResultClass::HashRefInflator::inflator_cache>. Make sure to
53reset this hash if you modify column inflators at run time.
54
137c657c 55=head1 METHODS
56
57=head2 inflate_result
58
59Inflates the result and prefetched data into a hash-ref using L<mk_hash>.
60
61=cut
62
63sub inflate_result {
64 my ($self, $source, $me, $prefetch) = @_;
65
2328814a 66 my $hashref = mk_hash($me, $prefetch);
67 inflate_hash ($source->schema, $source->result_class, $hashref) if $inflate_data;
68 return $hashref;
137c657c 69}
70
71=head2 mk_hash
72
73This does all the work of inflating the (pre)fetched data.
74
75=cut
b0930c1e 76
2328814a 77##############
78# NOTE
79#
80# Generally people use this to gain as much speed as possible. If a new mk_hash is
81# implemented, it should be benchmarked using the maint/benchmark_hashrefinflator.pl
82# script (in addition to passing all tests of course :). Additional instructions are
83# provided in the script itself.
84#
85
86sub mk_hash {
87 if (ref $_[0] eq 'ARRAY') { # multi relationship
88 return [ map { mk_hash (@$_) || () } (@_) ];
89 }
90 else {
91 my $hash = {
92 # the main hash could be an undef if we are processing a skipped-over join
93 $_[0] ? %{$_[0]} : (),
94
95 # the second arg is a hash of arrays for each prefetched relation
96 map
97 { $_ => mk_hash( @{$_[1]->{$_}} ) }
98 ( $_[1] ? (keys %{$_[1]}) : () )
99 };
100
101 # if there is at least one defined column consider the resultset real
102 # (and not an emtpy has_many rel containing one empty hashref)
103 for (values %$hash) {
104 return $hash if defined $_;
105 }
b0930c1e 106
2328814a 107 return undef;
108 }
109}
110
111=head2 inflate_hash
112
113This walks through a hashref produced by L<mk_hash> and inflates any data
114for which there is a registered inflator in the C<column_info>
137c657c 115
2328814a 116=cut
117
118sub inflate_hash {
119 my ($schema, $rc, $data) = @_;
b0930c1e 120
2328814a 121 foreach my $column (keys %{$data}) {
b25e9fa0 122
2328814a 123 if (ref $data->{$column} eq 'HASH') {
124 inflate_hash ($schema, $schema->source ($rc)->related_class ($column), $data->{$column});
125 }
126 elsif (ref $data->{$column} eq 'ARRAY') {
127 foreach my $rel (@{$data->{$column}}) {
128 inflate_hash ($schema, $schema->source ($rc)->related_class ($column), $rel);
129 }
130 }
131 else {
132 # "null is null is null"
133 next if not defined $data->{$column};
134
135 # cache the inflator coderef
136 unless (exists $inflator_cache{$rc}{$column}) {
137 $inflator_cache{$rc}{$column} = exists $schema->source ($rc)->_relationships->{$column}
138 ? undef # currently no way to inflate a column sharing a name with a rel
139 : $rc->column_info($column)->{_inflate_info}{inflate}
140 ;
141 }
142
143 if ($inflator_cache{$rc}{$column}) {
144 $data->{$column} = $inflator_cache{$rc}{$column}->($data->{$column});
145 }
b25e9fa0 146 }
147 }
b0930c1e 148}
149
419ff184 150=head1 CAVEAT
151
152This will not work for relationships that have been prefetched. Consider the
153following:
154
155 my $artist = $artitsts_rs->search({}, {prefetch => 'cds' })->first;
156
157 my $cds = $artist->cds;
158 $cds->result_class('DBIx::Class::ResultClass::HashRefInflator');
159 my $first = $cds->first;
160
161C<$first> will B<not> be a hashref, it will be a normal CD row since
162HashRefInflator only affects resultsets at inflation time, and prefetch causes
163relations to be inflated when the master C<$artist> row is inflated.
164
165=cut
166
b0930c1e 1671;