Showing 5 changed files with 105 additions and 31 deletions
+3
Changes
... ...
@@ -1,4 +1,7 @@
1 1
 0.1655
2
+    - added experimental DBIx::Custom::Model join attribute
3
+    - added experimental select() join option
4
+    - deprecated select() relation option
2 5
     - added experimental update_param and insert_param
3 6
     - remove experimental DBIx::Custom::Model relation
4 7
 0.1654
+46 -22
lib/DBIx/Custom.pm
... ...
@@ -613,7 +613,19 @@ sub register_filter {
613 613
 sub register_tag { shift->query_builder->register_tag(@_) }
614 614
 
615 615
 our %VALID_SELECT_ARGS
616
-  = map { $_ => 1 } qw/table column where append relation filter query selection left_join/;
616
+  = map { $_ => 1 } qw/table column where append relation filter query selection join/;
617
+
618
+sub _need_tables {
619
+    my ($self, $tree, $need_tables, $tables) = @_;
620
+    
621
+    foreach my $table (@$tables) {
622
+        
623
+        if ($tree->{$table}) {
624
+            $need_tables->{$table} = 1;
625
+            $self->_need_tables($tree, $need_tables, [$tree->{$table}{parent}])
626
+        }
627
+    }
628
+}
617 629
 
618 630
 sub select {
619 631
     my ($self, %args) = @_;
... ...
@@ -635,9 +647,9 @@ sub select {
635 647
     my $where     = $args{where} || {};
636 648
     my $append    = $args{append};
637 649
     my $filter    = $args{filter};
638
-    my $left_join = $args{left_join} || [];
639
-    croak qq{"left_join" must be array reference}
640
-      unless ref $left_join eq 'ARRAY';
650
+    my $join =     $args{join} || [];
651
+    croak qq{"join" must be array reference}
652
+      unless ref $join eq 'ARRAY';
641 653
     
642 654
     my @join_tables;
643 655
     unshift @join_tables, $tables->[-1];
... ...
@@ -697,27 +709,39 @@ sub select {
697 709
     # Table name in Where
698 710
     unshift @join_tables, @{$self->_tables($swhere)};
699 711
     
700
-    # Left join
701
-    if (@$left_join) {
702
-        for (my $i = 0; $i < @$left_join; $i += 2) {
703
-            my $column1 = $left_join->[$i];
704
-            my $column2 = $left_join->[$i + 1];
705
-            
706
-            my $table1 = (split (/\./, $column1))[0];
707
-            my $table2 = (split (/\./, $column2))[0];
712
+    # Join
713
+    if (@$join) {
714
+        my $tree = {};
715
+        
716
+        for (my $i = 0; $i < @$join; $i++) {
708 717
             
709
-            my $table1_exists;
710
-            my $table2_exists;
718
+            my $join_clause = $join->[$i];
711 719
             
712
-            foreach my $table (@join_tables) {
713
-                $table1_exists = 1 if $table eq $table1;
714
-                $table2_exists = 1 if $table eq $table2;
720
+            if ($join_clause =~ /\s([^\.\s]+?)\..+\s([^\.\s]+?)\./) {
721
+                
722
+                my $table1 = $1;
723
+                my $table2 = $2;
724
+                
725
+                croak qq{right side table of "$join_clause" must be uniq}
726
+                  if exists $tree->{$table2};
727
+                
728
+                $tree->{$table2}
729
+                  = {position => $i, parent => $table1, join => $join_clause};
715 730
             }
716
-            
717
-            if ($table1_exists && $table2_exists) {
718
-                push @sql, "left outer join $table2 on $column1 = $column2";
731
+            else {
732
+                croak qq{join "$join_clause" must be two table name};
719 733
             }
720 734
         }
735
+        
736
+        my $need_tables = {};
737
+        $self->_need_tables($tree, $need_tables, \@join_tables);
738
+        
739
+        
740
+        my @need_tables = sort { $tree->{$a}{position} <=> $tree->{$b}{position} } keys %$need_tables;
741
+
742
+        foreach my $need_table (@need_tables) {
743
+            push @sql, $tree->{$need_table}{join};
744
+        }
721 745
     }
722 746
     
723 747
     # Add where
... ...
@@ -1748,7 +1772,7 @@ This is same as L<DBI>'s C<rollback>.
1748 1772
         where     => \%where,
1749 1773
         append    => $append,
1750 1774
         relation  => \%relation,
1751
-        left_join => ['book.company_id' => 'company.id']
1775
+        join => ['left outer join company on book.company_id = company.id']
1752 1776
         filter    => \%filter,
1753 1777
         query     => 1,
1754 1778
         selection => $selection
... ...
@@ -1773,7 +1797,7 @@ First element is a string. it contains tags,
1773 1797
 such as "{= title} or {like author}".
1774 1798
 Second element is paramters.
1775 1799
 
1776
-C<left_join> is add left outer join clause after from clause.
1800
+C<join> is join clause after from clause.
1777 1801
 This is experimental.
1778 1802
 
1779 1803
 =head3 C<(experimental) select_at()>
+11
lib/DBIx/Custom/Model.pm
... ...
@@ -15,6 +15,7 @@ __PACKAGE__->attr(
15 15
     columns => sub { [] },
16 16
     filter => sub { [] },
17 17
     primary_key => sub { [] },
18
+    join => sub { [] }
18 19
 );
19 20
 
20 21
 our $AUTOLOAD;
... ...
@@ -116,6 +117,7 @@ sub select {
116 117
     my $self = shift;
117 118
     $self->dbi->select(
118 119
         table => $self->table,
120
+        join => $self->join,
119 121
         @_
120 122
     );
121 123
 }
... ...
@@ -191,6 +193,15 @@ This filter is applied when L<DBIx::Custom> C<include_model()> is called.
191 193
 
192 194
 Model name.
193 195
 
196
+=head2 C<(experimental) join>
197
+
198
+    my $join = $model->join;
199
+    $model   = $model->join(
200
+        ['left outer join company on book.company_id = company.id']
201
+    );
202
+    
203
+Default join clause. This is used by C<select()>.
204
+
194 205
 =head2 C<table>
195 206
 
196 207
     my $table = $model->table;
+41 -5
t/dbix-custom-core-sqlite.t
... ...
@@ -27,7 +27,8 @@ my $CREATE_TABLE = {
27 27
     0 => 'create table table1 (key1 char(255), key2 char(255));',
28 28
     1 => 'create table table1 (key1 char(255), key2 char(255), key3 char(255), key4 char(255), key5 char(255));',
29 29
     2 => 'create table table2 (key1 char(255), key3 char(255));',
30
-    3 => 'create table table1 (key1 Date, key2 datetime);'
30
+    3 => 'create table table1 (key1 Date, key2 datetime);',
31
+    4 => 'create table table3 (key3 int, key4 int);'
31 32
 };
32 33
 
33 34
 my $SELECT_SOURCES = {
... ...
@@ -1681,7 +1682,7 @@ $result = $model->select(column => $model->column_clause, where => {'table1.key1
1681 1682
 is_deeply($result->fetch_hash_first, {key1 => 1, key2 => 2});
1682 1683
 $result = $model->select(column => $model->column_clause(remove => ['key1']), where => {'table1.key1' => 1});
1683 1684
 is_deeply($result->fetch_hash_first, {key2 => 2});
1684
-$result = $model->select(relation => {'table1.key1' => 'table2.key1'}, column => $model->column_clause(add => ['key3']), where => {'table1.key1' => 1});
1685
+$result = $model->select(column => $model->column_clause(add => ['table2.key3']), where => {'table1.key1' => 1});
1685 1686
 is_deeply($result->fetch_hash_first, {key1 => 1, key2 => 2, key3 => 3});
1686 1687
 
1687 1688
 test 'update_param';
... ...
@@ -1724,27 +1725,62 @@ eval { $dbi->insert_param({";" => 1}) };
1724 1725
 like($@, qr/not safety/);
1725 1726
 
1726 1727
 
1727
-test 'left_join';
1728
+test 'join';
1728 1729
 $dbi = DBIx::Custom->connect($NEW_ARGS->{0});
1729 1730
 $dbi->execute($CREATE_TABLE->{0});
1730 1731
 $dbi->insert(table => 'table1', param => {key1 => 1, key2 => 2});
1731 1732
 $dbi->insert(table => 'table1', param => {key1 => 3, key2 => 4});
1732 1733
 $dbi->execute($CREATE_TABLE->{2});
1733 1734
 $dbi->insert(table => 'table2', param => {key1 => 1, key3 => 5});
1735
+$dbi->execute($CREATE_TABLE->{4});
1736
+$dbi->insert(table => 'table3', param => {key3 => 5, key4 => 4});
1734 1737
 $rows = $dbi->select(
1735 1738
     table => 'table1',
1736 1739
     column => 'table1.key1 as table1_key1, table2.key1 as table2_key1, key2, key3',
1737 1740
     where   => {'table1.key2' => 2},
1738
-    left_join  => ['table1.key1' => 'table2.key1']
1741
+    join  => ['left outer join table2 on table1.key1 = table2.key1']
1739 1742
 )->fetch_hash_all;
1740 1743
 is_deeply($rows, [{table1_key1 => 1, table2_key1 => 1, key2 => 2, key3 => 5}]);
1741 1744
 
1745
+$rows = $dbi->select(
1746
+    table => 'table1',
1747
+    where   => {'key1' => 1},
1748
+    join  => ['left outer join table2 on table1.key1 = table2.key1']
1749
+)->fetch_hash_all;
1750
+is_deeply($rows, [{key1 => 1, key2 => 2}]);
1751
+
1742 1752
 eval {
1743 1753
     $rows = $dbi->select(
1744 1754
         table => 'table1',
1745 1755
         column => 'table1.key1 as table1_key1, table2.key1 as table2_key1, key2, key3',
1746 1756
         where   => {'table1.key2' => 2},
1747
-        left_join  => {'table1.key1' => 'table2.key1'}
1757
+        join  => {'table1.key1' => 'table2.key1'}
1748 1758
     );
1749 1759
 };
1750 1760
 like ($@, qr/array/);
1761
+
1762
+$rows = $dbi->select(
1763
+    table => 'table1',
1764
+    where   => {'key1' => 1},
1765
+    join  => ['left outer join table2 on table1.key1 = table2.key1',
1766
+              'left outer join table3 on table2.key3 = table3.key3']
1767
+)->fetch_hash_all;
1768
+is_deeply($rows, [{key1 => 1, key2 => 2}]);
1769
+
1770
+$rows = $dbi->select(
1771
+    column => 'table3.key4 as table3__key4',
1772
+    table => 'table1',
1773
+    where   => {'table1.key1' => 1},
1774
+    join  => ['left outer join table2 on table1.key1 = table2.key1',
1775
+              'left outer join table3 on table2.key3 = table3.key3']
1776
+)->fetch_hash_all;
1777
+is_deeply($rows, [{table3__key4 => 4}]);
1778
+
1779
+$rows = $dbi->select(
1780
+    column => 'table1.key1 as table1__key1',
1781
+    table => 'table1',
1782
+    where   => {'table3.key4' => 4},
1783
+    join  => ['left outer join table2 on table1.key1 = table2.key1',
1784
+              'left outer join table3 on table2.key3 = table3.key3']
1785
+)->fetch_hash_all;
1786
+is_deeply($rows, [{table1__key1 => 1}]);
+4 -4
t/dbix-custom-core-sqlite/MyModel6/table1.pm
... ...
@@ -3,10 +3,10 @@ package MyModel6::table1;
3 3
 use base 'MyModel6';
4 4
 
5 5
 __PACKAGE__->attr(
6
-    relation => sub {
7
-        {
8
-            'table1.key1' => 'table2.key1'
9
-        }
6
+    join => sub {
7
+        [
8
+            'left outer join table2 on table1.key1 = table2.key1'
9
+        ]
10 10
     },
11 11
     primary_key => sub { ['key1'] }
12 12
 );