File Coverage

blib/lib/Aniki/Plugin/SelectJoined.pm
Criterion Covered Total %
statement 66 66 100.0
branch 10 16 62.5
condition 3 5 60.0
subroutine 10 10 100.0
pod 0 2 0.0
total 89 99 89.9


line stmt bran cond sub pod time code
1             package Aniki::Plugin::SelectJoined;
2 1     1   486 use 5.014002;
  1         3  
3              
4 1     1   5 use namespace::autoclean;
  1         2  
  1         8  
5 1     1   447 use Mouse::Role;
  1         948  
  1         5  
6 1     1   304 use Aniki::QueryBuilder;
  1         2  
  1         20  
7 1     1   351 use Aniki::Result::Collection::Joined;
  1         82  
  1         36  
8 1     1   7 use Carp qw/croak/;
  1         2  
  1         577  
9              
10             requires qw/schema query_builder suppress_row_objects txn_manager execute/;
11              
12             Aniki::QueryBuilder->load_plugin('JoinSelect');
13              
14             sub select_joined {
15 3     3 0 7571 my ($self, $base_table, $join_conditions, $where, $opt) = @_;
16 3 50       13 croak '(Aniki::Plugin::SelectJoined#select_joined) `where` condition must be a reference.' unless ref $where;
17              
18 3         10 my @table_names = ($base_table);
19 3         13 for (my $i = 0; my $table = $join_conditions->[$i]; $i += 2) {
20 3         13 push @table_names => $table;
21             }
22 3         8 my @tables = map { $self->schema->get_table($_) } @table_names;
  6         23  
23              
24 3         13 my $name_sep = $self->query_builder->name_sep;
25 3         26 my @columns;
26 3         9 for my $table (@tables) {
27 6         23 my $table_name = $table->name;
28             push @columns =>
29 24         69 map { "$table_name$name_sep$_" }
30 6         24 map { $_->name } $table->get_fields();
  24         75  
31             }
32              
33 3         12 my ($sql, @bind) = $self->query_builder->join_select($base_table, $join_conditions, \@columns, $where, $opt);
34 3         3063 return $self->select_joined_by_sql($sql, \@bind, {
35             table_names => \@table_names,
36             columns => \@columns,
37             %$opt,
38             });
39             }
40              
41             sub select_joined_by_sql {
42 3     3 0 10 my ($self, $sql, $bind, $opt) = @_;
43 3   50     12 $opt //= {};
44              
45 3 50       10 my $table_names = $opt->{table_names} or croak 'table_names is required';
46 3 50       13 my $columns = $opt->{columns} or croak 'columns is required';
47 3 100       12 my $prefetch = exists $opt->{prefetch} ? $opt->{prefetch} : {};
48              
49 3   66     21 my $prefetch_enabled_fg = %$prefetch && !$self->suppress_row_objects;
50 3 100       8 if ($prefetch_enabled_fg) {
51 1 50       3 my $txn; $txn = $self->txn_scope unless $self->txn_manager->in_transaction;
  1         13  
52              
53 1         155 my $sth = $self->execute($sql, @$bind);
54 1         7 my $result = $self->_fetch_joined_by_sth($sth, $table_names, $columns);
55              
56 1         7 for my $table_name (@$table_names) {
57 2         9 my $rows = $result->rows($table_name);
58 2         5 my $prefetch = $prefetch->{$table_name};
59 2 50       8 $prefetch = [$prefetch] if ref $prefetch eq 'HASH';
60 2         11 $self->fetch_and_attach_relay_data($table_name, $prefetch, $rows);
61             }
62              
63 1 50       11 $txn->rollback if defined $txn; ## for read only
64 1         72 return $result;
65             }
66             else {
67 2         14 my $sth = $self->execute($sql, @$bind);
68 2         9 return $self->_fetch_joined_by_sth($sth, $table_names, $columns);
69             }
70             }
71              
72             sub _fetch_joined_by_sth {
73 3     3   13 my ($self, $sth, $table_names, $columns) = @_;
74 3         9 my @rows;
75              
76             my %row;
77 3         40 $sth->bind_columns(\@row{@$columns});
78 3         193 push @rows => $self->_seperate_rows(\%row) while $sth->fetch;
79 3         16 $sth->finish;
80              
81 3         72 return Aniki::Result::Collection::Joined->new(
82             table_names => $table_names,
83             handler => $self,
84             row_datas => \@rows,
85             );
86             }
87              
88             sub _seperate_rows {
89 15     15   39 my ($self, $row) = @_;
90              
91 15         48 my $name_sep = quotemeta $self->query_builder->name_sep;
92              
93 15         109 my %rows;
94 15         54 for my $full_named_column (keys %$row) {
95 120         359 my ($table_name, $column) = split /$name_sep/, $full_named_column, 2;
96 120         308 $rows{$table_name}{$column} = $row->{$full_named_column};
97             }
98              
99 15         157 return \%rows;
100             }
101              
102             1;
103             __END__
104              
105             =pod
106              
107             =encoding utf-8
108              
109             =head1 NAME
110              
111             Aniki::Plugin::SelectJoined - Support for Joined query
112              
113             =head1 SYNOPSIS
114              
115             package MyDB;
116             use Mouse v2.4.5;
117             extends qw/Aniki/;
118             with qw/Aniki::Plugin::SelectJoined/;
119              
120             package main;
121             my $db = MyDB->new(...);
122              
123             my $result = $db->select_joined(user_item => [
124             user => {'user_item.user_id' => 'user.id'},
125             item => {'user_item.item_id' => 'item.id'},
126             ], {
127             'user.id' => 2,
128             }, {
129             order_by => 'user_item.item_id',
130             });
131              
132             for my $row ($result->all) {
133             my $user_item = $row->user_item;
134             my $user = $row->user;
135             my $item = $row->item;
136              
137             ...
138             }
139              
140             =head1 SEE ALSO
141              
142             L<Teng::Plugin::SelectJoined>
143              
144             L<SQL::Maker::Plugin::JoinSelect>
145              
146             =head1 LICENSE
147              
148             Copyright (C) karupanerura.
149              
150             This library is free software; you can redistribute it and/or modify
151             it under the same terms as Perl itself.
152              
153             =head1 AUTHOR
154              
155             karupanerura E<lt>karupa@cpan.orgE<gt>
156              
157             =cut