File Coverage

blib/lib/DBIx/Class/ResultSet/Void.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package DBIx::Class::ResultSet::Void;
2             $DBIx::Class::ResultSet::Void::VERSION = '0.07';
3             # ABSTRACT: improve DBIx::Class::ResultSet with void context
4              
5 1     1   42508 use strict;
  1         3  
  1         22  
6 1     1   4 use warnings;
  1         2  
  1         23  
7 1     1   85 use Carp::Clan qw/^DBIx::Class/;
  0            
  0            
8             use Try::Tiny;
9              
10             use base qw(DBIx::Class::ResultSet);
11              
12             sub exists {
13             my ($self, $query) = @_;
14              
15             return $self->search(
16             $query,
17             {
18             rows => 1,
19             select => [\'1']})->single;
20             }
21              
22             sub find_or_create {
23             my $self = shift;
24              
25             return $self->next::method(@_) if (defined wantarray);
26              
27             my $attrs = (@_ > 1 && ref $_[$#_] eq 'HASH' ? pop(@_) : {});
28             my $hash = ref $_[0] eq 'HASH' ? shift : {@_};
29              
30             my $query = $self->___get_primary_or_unique_key($hash, $attrs);
31             my $exists = $self->exists($query);
32             $self->create($hash) unless $exists;
33             }
34              
35             sub update_or_create {
36             my $self = shift;
37              
38             return $self->next::method(@_) if (defined wantarray);
39              
40             my $attrs = (@_ > 1 && ref $_[$#_] eq 'HASH' ? pop(@_) : {});
41             my $cond = ref $_[0] eq 'HASH' ? shift : {@_};
42              
43             my $query = $self->___get_primary_or_unique_key($cond, $attrs);
44             my $exists = $self->exists($query);
45              
46             if ($exists) {
47             # dirty hack, to remove WHERE cols from SET
48             my $query_array = ref $query eq 'ARRAY' ? $query : [$query];
49             foreach my $_query (@$query_array) {
50             foreach my $_key (keys %$_query) {
51             delete $cond->{$_key};
52             delete $cond->{$1} if $_key =~ /\w+\.(\w+)/; # $alias.$col
53             }
54             }
55             $self->search($query)->update($cond) if keys %$cond;
56             } else {
57             $self->create($cond);
58             }
59             }
60              
61             # mostly copied from sub find
62             sub ___get_primary_or_unique_key {
63             my $self = shift;
64             my $attrs = (@_ > 1 && ref $_[$#_] eq 'HASH' ? pop(@_) : {});
65              
66             my $rsrc = $self->result_source;
67              
68             my $constraint_name;
69             if (exists $attrs->{key}) {
70             $constraint_name =
71             defined $attrs->{key}
72             ? $attrs->{key}
73             : $self->throw_exception("An undefined 'key' resultset attribute makes no sense");
74             }
75              
76             # Parse out the condition from input
77             my $call_cond;
78              
79             if (ref $_[0] eq 'HASH') {
80             $call_cond = {%{$_[0]}};
81             } else {
82             # if only values are supplied we need to default to 'primary'
83             $constraint_name = 'primary' unless defined $constraint_name;
84              
85             my @c_cols = $rsrc->unique_constraint_columns($constraint_name);
86              
87             $self->throw_exception("No constraint columns, maybe a malformed '$constraint_name' constraint?") unless @c_cols;
88              
89             $self->throw_exception('find() expects either a column/value hashref, or a list of values '
90             . "corresponding to the columns of the specified unique constraint '$constraint_name'")
91             unless @c_cols == @_;
92              
93             @{$call_cond}{@c_cols} = @_;
94             }
95              
96             # process relationship data if any
97             for my $key (keys %$call_cond) {
98             if (
99             length ref($call_cond->{$key})
100             and my
101             $relinfo = $rsrc->relationship_info($key)
102             and
103             # implicitly skip has_many's (likely MC)
104             (ref(my $val = delete $call_cond->{$key}) ne 'ARRAY'))
105             {
106             my ($rel_cond, $crosstable) = $rsrc->_resolve_condition($relinfo->{cond}, $val, $key, $key);
107              
108             $self->throw_exception("Complex condition via relationship '$key' is unsupported in find()")
109             if $crosstable
110             or ref($rel_cond) ne 'HASH';
111              
112             # supplement condition
113             # relationship conditions take precedence (?)
114             @{$call_cond}{keys %$rel_cond} = values %$rel_cond;
115             }
116             }
117              
118             my $alias = exists $attrs->{alias} ? $attrs->{alias} : $self->{attrs}{alias};
119             my $final_cond;
120             if (defined $constraint_name) {
121             $final_cond = $self->_qualify_cond_columns(
122              
123             $self->result_source->_minimal_valueset_satisfying_constraint(
124             constraint_name => $constraint_name,
125             values => ($self->_merge_with_rscond($call_cond))[0],
126             carp_on_nulls => 1,
127             ),
128              
129             $alias,
130             );
131             } elsif ($self->{attrs}{accessor} and $self->{attrs}{accessor} eq 'single') {
132             # This means that we got here after a merger of relationship conditions
133             # in ::Relationship::Base::search_related (the row method), and furthermore
134             # the relationship is of the 'single' type. This means that the condition
135             # provided by the relationship (already attached to $self) is sufficient,
136             # as there can be only one row in the database that would satisfy the
137             # relationship
138             } else {
139             my (@unique_queries, %seen_column_combinations, $ci, @fc_exceptions);
140              
141             # no key was specified - fall down to heuristics mode:
142             # run through all unique queries registered on the resultset, and
143             # 'OR' all qualifying queries together
144             #
145             # always start from 'primary' if it exists at all
146             for my $c_name (sort { $a eq 'primary' ? -1 : $b eq 'primary' ? 1 : $a cmp $b } $rsrc->unique_constraint_names) {
147              
148             next if $seen_column_combinations{join "\x00", sort $rsrc->unique_constraint_columns($c_name)}++;
149              
150             try {
151             push @unique_queries,
152             $self->_qualify_cond_columns(
153             $self->result_source->_minimal_valueset_satisfying_constraint(
154             constraint_name => $c_name,
155             values => ($self->_merge_with_rscond($call_cond))[0],
156             columns_info => ($ci ||= $self->result_source->columns_info),
157             ),
158             $alias
159             );
160             }
161             catch {
162             push @fc_exceptions, $_ if $_ =~ /\bFilterColumn\b/;
163             };
164             }
165              
166             $final_cond =
167             @unique_queries ? \@unique_queries
168             : @fc_exceptions ? $self->throw_exception(join "; ", map { $_ =~ /(.*) at .+ line \d+$/s } @fc_exceptions)
169             : $self->_non_unique_find_fallback($call_cond, $attrs);
170             }
171              
172             return $final_cond;
173             }
174              
175             1;
176              
177             __END__