File Coverage

lib/DBIx/EAV/ResultSet.pm
Criterion Covered Total %
statement 21 134 15.6
branch 0 50 0.0
condition 0 14 0.0
subroutine 7 31 22.5
pod 7 19 36.8
total 35 248 14.1


line stmt bran cond sub pod time code
1             package DBIx::EAV::ResultSet;
2              
3 10     10   32 use Moo;
  10         9  
  10         41  
4 10     10   1829 use DBIx::EAV::Entity;
  10         11  
  10         161  
5 10     10   2739 use DBIx::EAV::Cursor;
  10         19  
  10         275  
6 10     10   48 use Data::Dumper;
  10         8  
  10         403  
7 10     10   34 use Carp qw/ croak confess /;
  10         12  
  10         435  
8             use overload
9 10         54 '0+' => "_to_num",
10             'bool' => "_to_bool",
11 10     10   51 fallback => 1;
  10         12  
12              
13             my $sql = SQL::Abstract->new;
14              
15             has 'eav', is => 'ro', required => 1;
16             has 'type', is => 'ro', required => 1;
17             has '_query', is => 'rw', default => sub { [] }, init_arg => 'query';
18             has '_options', is => 'rw', default => sub { {} }, init_arg => 'options';
19             has 'cursor', is => 'rw',
20             lazy => 1,
21             init_arg => undef,
22             predicate => '_has_cursor',
23             clearer => '_clear_cursor',
24             builder => '_build_cursor';
25              
26             has 'entity_class', is => 'ro', init_arg => undef, lazy => 1, default => sub {
27             my $self = shift;
28             $self->eav->_resolve_entity_class($self->type->name) || 'DBIx::EAV::Entity';
29             };
30              
31 0     0     sub _to_num { $_[0]->count }
32              
33 0     0     sub _to_bool { 1 }
34              
35              
36             sub _build_cursor {
37 0     0     my $self = shift;
38              
39 0           DBIx::EAV::Cursor->new(
40             eav => $self->eav,
41             type => $self->type,
42             query => $self->_query,
43             options => $self->_options,
44             );
45             }
46              
47              
48             sub new_entity {
49 0     0 1   my ($self, $data) = @_;
50 0           my $entity = $self->entity_class->new( eav => $self->eav, type => $self->type );
51 0 0         $entity->set($data) if ref $data eq 'HASH';
52 0           $entity;
53             }
54              
55              
56             sub inflate_entity {
57 0     0 0   my ($self, $data) = @_;
58 0           my $type = $self->type;
59             $type = $self->eav->type_by_id($data->{entity_type_id})
60 0 0 0       if $data->{entity_type_id} && $data->{entity_type_id} != $type->id;
61              
62 0           my $entity = $self->entity_class->new( eav => $self->eav, type => $type, raw => $data );
63 0           $entity->load_attributes;
64 0           $entity;
65             }
66              
67              
68             {
69 10     10   3503 no warnings;
  10         10  
  10         10002  
70             *create = \&insert;
71             }
72              
73             sub insert {
74 0     0 1   my ($self, $data) = @_;
75 0           $self->new_entity($data)->save;
76             }
77              
78              
79             sub populate {
80 0     0 1   my ($self, $data) = @_;
81 0 0         die 'Call populate(\@items)' unless ref $data eq 'ARRAY';
82              
83 0           my @result;
84 0           foreach my $item (@$data) {
85 0           push @result, $self->insert($item);
86             }
87              
88 0 0         return wantarray ? @result : \@result;
89             }
90              
91              
92             sub update {
93 0     0 0   my ($self, $data, $where) = @_;
94              
95 0   0       $where //= {};
96 0           $where->{entity_type_id} = $self->type->id;
97              
98             # do a direct update for static attributes
99              
100             }
101              
102              
103             sub delete {
104 0     0 1   my $self = shift;
105 0           my $eav = $self->eav;
106 0           my $type = $self->type;
107 0           my $entities_table = $eav->table('entities');
108              
109             # Call delete_all for SQLite since it doesn't
110             # support delete with joins.
111             # Better solution welcome.
112 0 0         return $self->delete_all if
113             $self->eav->schema->db_driver_name eq 'SQLite';
114              
115 0 0         unless ($eav->schema->database_cascade_delete) {
116              
117             # delete links by relationship id
118 0           my @ids = map { $_->{id} } $type->relationships;
  0            
119              
120 0           $eav->table('entity_relationships')->delete(
121             {
122             relationship_id => \@ids,
123             $entities_table->name.'.entity_type_id' => $type->id
124             },
125             { join => { $entities_table->name => [{ 'me.left_entity_id' => 'their.id' }, { 'me.right_entity_id' => 'their.id' }] } }
126             );
127              
128             # delete attributes:
129             # - group attrs by data type so only one DELETE command is sent per data type
130             # - restrict by entity_type_id so we dont delete parent/sibiling/child data
131 0           my %types;
132 0           push @{ $types{$_->{data_type}} }, $_->{id}
133 0           for $type->attributes(no_static => 1);
134              
135 0           while (my ($data_type, $ids) = each %types) {
136              
137 0           my $value_table = $eav->table('value_'.$data_type);
138 0           $value_table->delete(
139             {
140             attribute_id => $ids,
141             $entities_table->name.'.entity_type_id' => $type->id
142             },
143             { join => { $entities_table->name => { 'me.entity_id' => 'their.id' } } }
144             );
145             }
146             }
147              
148 0           $entities_table->delete({ entity_type_id => $type->id });
149             }
150              
151              
152             sub delete_all {
153 0     0 1   my $self = shift;
154              
155 0 0         my $rs = scalar @_ > 0 ? $self->search_rs(@_) : $self;
156 0           my $i = 0;
157              
158 0           while (my $entity = $rs->next) {
159 0           $entity->delete;
160 0           $i++;
161             }
162              
163 0           $i;
164             }
165              
166              
167             sub find {
168 0     0 0   my ($self, $criteria, $options) = @_;
169              
170 0 0         croak "Missing find() criteria."
171             unless defined $criteria;
172              
173             # simple id search
174 0 0         return $self->search_rs({ id => $criteria }, $options)->next
175             unless ref $criteria;
176              
177 0           my $rs = $self->search_rs($criteria, $options);
178 0           my $result = $rs->next;
179              
180             # criteria is a search query, die if this query returns multiple items
181 0 0 0       croak "find() returned more than one entity. If this is what you want, use search or search_rs."
182             if defined $result && defined $rs->cursor->next;
183              
184 0           $result;
185             }
186              
187              
188             sub search {
189 0     0 0   my ($self, $query, $options) = @_;
190              
191 0           my $rs = $self->search_rs($query, $options);
192              
193 0 0         return wantarray ? $rs->all : $rs;
194             }
195              
196             sub search_rs {
197 0     0 0   my ($self, $query, $options) = @_;
198              
199             # simple combine queries using AND
200 0           my @new_query = @{ $self->_query };
  0            
201 0 0         push @new_query, $query if $query;
202              
203             # merge options
204 0           my $merged_options = $self->_merge_options($options);
205              
206 0           (ref $self)->new(
207             eav => $self->eav,
208             type => $self->type,
209             query => \@new_query,
210             options => $merged_options
211             );
212             }
213              
214              
215             sub _merge_options {
216 0     0     my ($self, $options) = @_;
217              
218 0           my %merged = %{ $self->_options };
  0            
219              
220 0 0         return \%merged
221             unless defined $options;
222              
223 0 0         confess "WTF" if $options eq '';
224              
225 0           foreach my $opt (keys %$options) {
226              
227             # doesnt even exist, just copy
228 0 0         if (not exists $merged{$opt}) {
    0          
    0          
229 0           $merged{$opt} = $options->{$opt};
230             }
231              
232             # having: combine queries using AND
233             elsif ($opt eq 'having') {
234              
235 0           $merged{$opt} = [$merged{$opt}, $options->{$opt}];
236             }
237              
238             # merge array
239             elsif (ref $merged{$opt} eq 'ARRAY') {
240              
241             $merged{$opt} = [
242 0           @{$merged{$opt}},
243 0 0         ref $options->{$opt} eq 'ARRAY' ? @{$options->{$opt}} : $options->{$opt}
  0            
244             ];
245             }
246              
247             else {
248 0           $merged{$opt} = $options->{$opt};
249             }
250             }
251              
252 0           \%merged;
253             }
254              
255              
256             sub count {
257 0     0 1   my $self = shift;
258 0 0         return $self->search(@_)->count if @_;
259              
260             # from DBIx::Class::ResultSet::count()
261             # this is a little optimization - it is faster to do the limit
262             # adjustments in software, instead of a subquery
263 0           my $options = $self->_options;
264 0           my ($limit, $offset) = @$options{qw/ limit offset /};
265              
266 0           my $count = $self->_count_rs($options)->cursor->next->{count};
267              
268 0 0         $count -= $offset if $offset;
269 0 0         $count = 0 if $count < 0;
270 0 0 0       $count = $limit if $limit && $count > $limit;
271              
272 0           $count;
273             }
274              
275              
276             sub _count_rs {
277 0     0     my ($self, $options) = @_;
278 0           my %tmp_options = ( %$options, select => [\'COUNT(*) AS count'] );
279              
280             # count using subselect if needed
281             $tmp_options{from} = $self->as_query
282 0 0 0       if $options->{group_by} || $options->{distinct};
283              
284 0           delete @tmp_options{qw/ limit offset order_by group_by distinct /};
285              
286             (ref $self)->new(
287             eav => $self->eav,
288             type => $self->type,
289 0           query => [@{ $self->_query }],
  0            
290             options => \%tmp_options
291             );
292             }
293              
294              
295             sub as_query {
296 0     0 0   my $self = shift;
297 0           $self->cursor->as_query;
298             }
299              
300              
301             sub reset {
302 0     0 0   my $self = shift;
303 0           $self->_clear_cursor;
304 0           $self;
305             }
306              
307             sub first {
308 0     0 0   $_[0]->reset->next;
309             }
310              
311             sub next {
312 0     0 0   my $self = shift;
313              
314             # fetch next
315 0           my $entity_row = $self->cursor->next;
316 0 0         return unless defined $entity_row;
317              
318             # instantiate entity
319 0           $self->inflate_entity($entity_row);
320             }
321              
322             sub all {
323 0     0 0   my $self = shift;
324 0           my @entities;
325              
326 0           $self->reset;
327              
328 0           while (my $entity = $self->next) {
329 0           push @entities, $entity;
330             }
331              
332 0           $self->reset;
333              
334 0 0         return wantarray ? @entities : \@entities;
335             }
336              
337             sub pager {
338 0     0 0   die "pager() not implemented";
339             }
340              
341             sub distinct {
342 0     0 1   die "distinct() not implemented";
343             }
344              
345             sub storage_size {
346 0     0 0   die "storage_size() not implemented";
347             }
348              
349              
350             1;
351              
352             __END__