Procházet zdrojové kódy

ZF-6653
- Fixed Zend_Db_Select to accept join() & from() calls independently regardless of order they are called in

git-svn-id: http://framework.zend.com/svn/framework/standard/trunk@18475 44c647ce-9c0f-0410-b52a-842ac1e357ba

ralph před 16 roky
rodič
revize
c295c1f25f
2 změnil soubory, kde provedl 131 přidání a 8 odebrání
  1. 68 8
      library/Zend/Db/Select.php
  2. 63 0
      tests/Zend/Db/Select/TestCommon.php

+ 68 - 8
library/Zend/Db/Select.php

@@ -227,7 +227,7 @@ class Zend_Db_Select
      */
     public function from($name, $cols = '*', $schema = null)
     {
-        return $this->joinInner($name, null, $cols, $schema);
+        return $this->_join(self::FROM, $name, null, $cols, $schema);
     }
 
     /**
@@ -753,7 +753,7 @@ class Zend_Db_Select
      */
     protected function _join($type, $name, $cond, $cols, $schema = null)
     {
-        if (!in_array($type, self::$_joinTypes)) {
+        if (!in_array($type, self::$_joinTypes) && $type != self::FROM) {
             /**
              * @see Zend_Db_Select_Exception
              */
@@ -806,17 +806,41 @@ class Zend_Db_Select
                 require_once 'Zend/Db/Select/Exception.php';
                 throw new Zend_Db_Select_Exception("You cannot define a correlation name '$correlationName' more than once");
             }
-
+            
+            $lastFromCorrelationName = null;
+            if ($type == self::FROM) {
+                // append this from after the last from joinType
+                $tmpFromParts = $this->_parts[self::FROM];
+                $this->_parts[self::FROM] = array();
+                // move all the froms onto the stack
+                while ($tmpFromParts) {
+                    $currentCorrelationName = key($tmpFromParts);
+                    if ($tmpFromParts[$currentCorrelationName]['joinType'] != self::FROM) {
+                        break;
+                    }
+                    $lastFromCorrelationName = $currentCorrelationName;
+                    $this->_parts[self::FROM][$currentCorrelationName] = array_shift($tmpFromParts);
+                }
+            } else {
+                $tmpFromParts = array();
+            }
             $this->_parts[self::FROM][$correlationName] = array(
                 'joinType'      => $type,
                 'schema'        => $schema,
                 'tableName'     => $tableName,
                 'joinCondition' => $cond
-            );
+                );
+            while ($tmpFromParts) {
+                $currentCorrelationName = key($tmpFromParts);
+                $this->_parts[self::FROM][$currentCorrelationName] = array_shift($tmpFromParts);
+            }
         }
 
         // add to the columns from this joined table
-        $this->_tableCols($correlationName, $cols);
+        if ($type == self::FROM && $lastFromCorrelationName == null) {
+            $lastFromCorrelationName = true;
+        } 
+        $this->_tableCols($correlationName, $cols, $lastFromCorrelationName);
 
         return $this;
     }
@@ -890,9 +914,10 @@ class Zend_Db_Select
      * @param  string $tbl The table/join the columns come from.
      * @param  array|string $cols The list of columns; preferably as
      * an array, but possibly as a string containing one column.
+     * @param  bool|string True if it should be prepended, a correlation name if it should be inserted
      * @return void
      */
-    protected function _tableCols($correlationName, $cols)
+    protected function _tableCols($correlationName, $cols, $afterCorrelationName = null)
     {
         if (!is_array($cols)) {
             $cols = array($cols);
@@ -902,6 +927,8 @@ class Zend_Db_Select
             $correlationName = '';
         }
 
+        $columnValues = array();
+
         foreach (array_filter($cols) as $alias => $col) {
             $currentCorrelationName = $correlationName;
             if (is_string($col)) {
@@ -918,7 +945,38 @@ class Zend_Db_Select
                     $col = $m[2];
                 }
             }
-            $this->_parts[self::COLUMNS][] = array($currentCorrelationName, $col, is_string($alias) ? $alias : null);
+            $columnValues[] = array($currentCorrelationName, $col, is_string($alias) ? $alias : null);
+        }
+
+        if ($columnValues) {
+
+            // should we attempt to prepend or insert these values?
+            if ($afterCorrelationName === true || is_string($afterCorrelationName)) {
+                $tmpColumns = $this->_parts[self::COLUMNS];
+                $this->_parts[self::COLUMNS] = array();
+            } else {
+                $tmpColumns = array();
+            }
+            
+            // find the correlation name to insert after
+            if (is_string($afterCorrelationName)) {
+                while ($tmpColumns) {
+                    $this->_parts[self::COLUMNS][] = $currentColumn = array_shift($tmpColumns);
+                    if ($currentColumn[0] == $afterCorrelationName) {
+                        break;
+                    }
+                }
+            }
+
+            // apply current values to current stack
+            foreach ($columnValues as $columnValue) {
+                array_push($this->_parts[self::COLUMNS], $columnValue);
+            }
+            
+            // finish ensuring that all previous values are applied (if they exist)
+            while ($tmpColumns) {
+                array_push($this->_parts[self::COLUMNS], array_shift($tmpColumns));
+            }
         }
     }
 
@@ -1057,9 +1115,11 @@ class Zend_Db_Select
         foreach ($this->_parts[self::FROM] as $correlationName => $table) {
             $tmp = '';
 
+            $joinType = ($table['joinType'] == self::FROM) ? self::INNER_JOIN : $table['joinType'];
+            
             // Add join clause (if applicable)
             if (! empty($from)) {
-                $tmp .= ' ' . strtoupper($table['joinType']) . ' ';
+                $tmp .= ' ' . strtoupper($joinType) . ' ';
             }
 
             $tmp .= $this->_getQuotedSchema($table['schema']);

+ 63 - 0
tests/Zend/Db/Select/TestCommon.php

@@ -1627,6 +1627,69 @@ abstract class Zend_Db_Select_TestCommon extends Zend_Db_TestSetup
         $this->_db->select()->union(array(), 'foo');
     }
 
+    /**
+     * @group ZF-6653
+     */
+    public function testSelectIsTheSameWhenCallingFromAndJoinInDifferentOrders()
+    {
+        $selectFromThenJoin = $this->_db->select();
+        $selectFromThenJoin->from(array('f' => 'foo'), array('columnfoo'))
+            ->joinLeft(array('b' => 'bar'), 'f.columnfoo2 = b.barcolumn2', array('baralias' => 'barcolumn'));
+
+        $selectJoinThenFrom = $this->_db->select();
+        $selectJoinThenFrom->joinLeft(array('b' => 'bar'), 'f.columnfoo2 = b.barcolumn2', array('baralias' => 'barcolumn'))
+            ->from(array('f' => 'foo'), array('columnfoo'));
+
+        $sqlSelectFromThenJoin = $selectFromThenJoin->assemble();
+        $sqlSelectJoinThenFrom = $selectJoinThenFrom->assemble();
+        $this->assertEquals($sqlSelectFromThenJoin, $sqlSelectJoinThenFrom);
+    }
+    
+    /**
+     * @group ZF-6653
+     */
+    public function testSelectIsTheSameWhenCallingMultipleFromsAfterJoin()
+    {
+        $selectFromThenJoin = $this->_db->select();
+        $selectFromThenJoin->from(array('f' => 'foo'), array('columnfoo'))
+            ->from(array('d' => 'doo'), array('columndoo'))
+            ->joinLeft(array('b' => 'bar'), 'f.columnfoo2 = b.barcolumn2', array('baralias' => 'barcolumn'));
+
+        $selectJoinThenFrom = $this->_db->select();
+        $selectJoinThenFrom->joinLeft(array('b' => 'bar'), 'f.columnfoo2 = b.barcolumn2', array('baralias' => 'barcolumn'))
+            ->from(array('f' => 'foo'), array('columnfoo'))
+            ->from(array('d' => 'doo'), array('columndoo'));
+
+        $sqlSelectFromThenJoin = $selectFromThenJoin->assemble();
+        $sqlSelectJoinThenFrom = $selectJoinThenFrom->assemble();
+        $this->assertEquals($sqlSelectFromThenJoin, $sqlSelectJoinThenFrom);
+    }
+    
+    /**
+     * @group ZF-6653
+     */
+    public function testSelectWithMultipleFromsAfterAJoinWillProperlyOrderColumns()
+    {
+        $select = $this->_selectWithMultipleFromsAfterAJoinWillProperlyOrderColumns();
+        $quote = $this->_db->getQuoteIdentifierSymbol();
+        $target = 'SELECT `f`.`columnfoo`, `d`.`columndoo`, `b`.`barcolumn` AS `baralias` FROM `foo` AS `f`'
+            . "\n" . ' INNER JOIN `doo` AS `d`'
+            . "\n" . ' LEFT JOIN `bar` AS `b` ON f.columnfoo2 = b.barcolumn2';
+        if ($quote != '`') {
+            $target = str_replace('`', $quote, $target);
+        }
+        $this->assertEquals($target, $select->assemble());
+    }
+    
+    protected function _selectWithMultipleFromsAfterAJoinWillProperlyOrderColumns()
+    {
+        $selectJoinThenFrom = $this->_db->select();
+        $selectJoinThenFrom->joinLeft(array('b' => 'bar'), 'f.columnfoo2 = b.barcolumn2', array('baralias' => 'barcolumn'))
+            ->from(array('f' => 'foo'), array('columnfoo'))
+            ->from(array('d' => 'doo'), array('columndoo'));
+        return $selectJoinThenFrom;
+    }
+    
     public function testSerializeSelect()
     {
         /* checks if the adapter has effectively gotten serialized,