File indexing completed on 2024-12-22 05:36:53
0001 <?php 0002 /** 0003 * Zend Framework 0004 * 0005 * LICENSE 0006 * 0007 * This source file is subject to the new BSD license that is bundled 0008 * with this package in the file LICENSE.txt. 0009 * It is also available through the world-wide-web at this URL: 0010 * http://framework.zend.com/license/new-bsd 0011 * If you did not receive a copy of the license and are unable to 0012 * obtain it through the world-wide-web, please send an email 0013 * to license@zend.com so we can send you a copy immediately. 0014 * 0015 * @category Zend 0016 * @package Zend_Paginator 0017 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0018 * @license http://framework.zend.com/license/new-bsd New BSD License 0019 * @version $Id$ 0020 */ 0021 0022 /** 0023 * @see Zend_Paginator_Adapter_Interface 0024 */ 0025 // require_once 'Zend/Paginator/Adapter/Interface.php'; 0026 0027 /** 0028 * @see Zend_Db 0029 */ 0030 // require_once 'Zend/Db.php'; 0031 0032 /** 0033 * @see Zend_Db_Select 0034 */ 0035 // require_once 'Zend/Db/Select.php'; 0036 0037 /** 0038 * @category Zend 0039 * @package Zend_Paginator 0040 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0041 * @license http://framework.zend.com/license/new-bsd New BSD License 0042 */ 0043 class Zend_Paginator_Adapter_DbSelect implements Zend_Paginator_Adapter_Interface 0044 { 0045 /** 0046 * Name of the row count column 0047 * 0048 * @var string 0049 */ 0050 const ROW_COUNT_COLUMN = 'zend_paginator_row_count'; 0051 0052 /** 0053 * The COUNT query 0054 * 0055 * @var Zend_Db_Select 0056 */ 0057 protected $_countSelect = null; 0058 0059 /** 0060 * Database query 0061 * 0062 * @var Zend_Db_Select 0063 */ 0064 protected $_select = null; 0065 0066 /** 0067 * Total item count 0068 * 0069 * @var integer 0070 */ 0071 protected $_rowCount = null; 0072 0073 /** 0074 * Identifies this adapter for caching purposes. This value will remain constant for 0075 * the entire life of this adapter regardless of how many different pages are queried. 0076 * 0077 * @var string 0078 */ 0079 protected $_cacheIdentifier = null; 0080 0081 /** 0082 * Constructor. 0083 * 0084 * @param Zend_Db_Select $select The select query 0085 */ 0086 public function __construct(Zend_Db_Select $select) 0087 { 0088 $this->_select = $select; 0089 $this->_cacheIdentifier = md5($select->assemble()); 0090 } 0091 0092 /** 0093 * Returns the cache identifier. 0094 * 0095 * @return string 0096 */ 0097 public function getCacheIdentifier() 0098 { 0099 return $this->_cacheIdentifier; 0100 } 0101 0102 /** 0103 * Sets the total row count, either directly or through a supplied 0104 * query. Without setting this, {@link getPages()} selects the count 0105 * as a subquery (SELECT COUNT ... FROM (SELECT ...)). While this 0106 * yields an accurate count even with queries containing clauses like 0107 * LIMIT, it can be slow in some circumstances. For example, in MySQL, 0108 * subqueries are generally slow when using the InnoDB storage engine. 0109 * Users are therefore encouraged to profile their queries to find 0110 * the solution that best meets their needs. 0111 * 0112 * @param Zend_Db_Select|integer $totalRowCount Total row count integer 0113 * or query 0114 * @return Zend_Paginator_Adapter_DbSelect $this 0115 * @throws Zend_Paginator_Exception 0116 */ 0117 public function setRowCount($rowCount) 0118 { 0119 if ($rowCount instanceof Zend_Db_Select) { 0120 $columns = $rowCount->getPart(Zend_Db_Select::COLUMNS); 0121 0122 $countColumnPart = empty($columns[0][2]) 0123 ? $columns[0][1] 0124 : $columns[0][2]; 0125 0126 if ($countColumnPart instanceof Zend_Db_Expr) { 0127 $countColumnPart = $countColumnPart->__toString(); 0128 } 0129 0130 $rowCountColumn = $this->_select->getAdapter()->foldCase(self::ROW_COUNT_COLUMN); 0131 0132 // The select query can contain only one column, which should be the row count column 0133 if (false === strpos($countColumnPart, $rowCountColumn)) { 0134 /** 0135 * @see Zend_Paginator_Exception 0136 */ 0137 // require_once 'Zend/Paginator/Exception.php'; 0138 0139 throw new Zend_Paginator_Exception('Row count column not found'); 0140 } 0141 0142 $result = $rowCount->query(Zend_Db::FETCH_ASSOC)->fetch(); 0143 0144 $this->_rowCount = count($result) > 0 ? $result[$rowCountColumn] : 0; 0145 } else if (is_integer($rowCount)) { 0146 $this->_rowCount = $rowCount; 0147 } else { 0148 /** 0149 * @see Zend_Paginator_Exception 0150 */ 0151 // require_once 'Zend/Paginator/Exception.php'; 0152 0153 throw new Zend_Paginator_Exception('Invalid row count'); 0154 } 0155 0156 return $this; 0157 } 0158 0159 /** 0160 * Returns an array of items for a page. 0161 * 0162 * @param integer $offset Page offset 0163 * @param integer $itemCountPerPage Number of items per page 0164 * @return array 0165 */ 0166 public function getItems($offset, $itemCountPerPage) 0167 { 0168 $this->_select->limit($itemCountPerPage, $offset); 0169 0170 return $this->_select->query()->fetchAll(); 0171 } 0172 0173 /** 0174 * Returns the total number of rows in the result set. 0175 * 0176 * @return integer 0177 */ 0178 public function count() 0179 { 0180 if ($this->_rowCount === null) { 0181 $this->setRowCount( 0182 $this->getCountSelect() 0183 ); 0184 } 0185 0186 return $this->_rowCount; 0187 } 0188 0189 /** 0190 * Get the COUNT select object for the provided query 0191 * 0192 * TODO: Have a look at queries that have both GROUP BY and DISTINCT specified. 0193 * In that use-case I'm expecting problems when either GROUP BY or DISTINCT 0194 * has one column. 0195 * 0196 * @return Zend_Db_Select 0197 */ 0198 public function getCountSelect() 0199 { 0200 /** 0201 * We only need to generate a COUNT query once. It will not change for 0202 * this instance. 0203 */ 0204 if ($this->_countSelect !== null) { 0205 return $this->_countSelect; 0206 } 0207 0208 $rowCount = clone $this->_select; 0209 $rowCount->__toString(); // Workaround for ZF-3719 and related 0210 0211 $db = $rowCount->getAdapter(); 0212 0213 $countColumn = $db->quoteIdentifier($db->foldCase(self::ROW_COUNT_COLUMN)); 0214 $countPart = 'COUNT(1) AS '; 0215 $groupPart = null; 0216 $unionParts = $rowCount->getPart(Zend_Db_Select::UNION); 0217 0218 /** 0219 * If we're dealing with a UNION query, execute the UNION as a subquery 0220 * to the COUNT query. 0221 */ 0222 if (!empty($unionParts)) { 0223 $expression = new Zend_Db_Expr($countPart . $countColumn); 0224 0225 $rowCount = $db 0226 ->select() 0227 ->bind($rowCount->getBind()) 0228 ->from($rowCount, $expression); 0229 } else { 0230 $columnParts = $rowCount->getPart(Zend_Db_Select::COLUMNS); 0231 $groupParts = $rowCount->getPart(Zend_Db_Select::GROUP); 0232 $havingParts = $rowCount->getPart(Zend_Db_Select::HAVING); 0233 $isDistinct = $rowCount->getPart(Zend_Db_Select::DISTINCT); 0234 0235 /** 0236 * If there is more than one column AND it's a DISTINCT query, more 0237 * than one group, or if the query has a HAVING clause, then take 0238 * the original query and use it as a subquery os the COUNT query. 0239 */ 0240 if (($isDistinct && ((count($columnParts) == 1 && $columnParts[0][1] == Zend_Db_Select::SQL_WILDCARD) 0241 || count($columnParts) > 1)) || count($groupParts) > 1 || !empty($havingParts)) { 0242 $rowCount->reset(Zend_Db_Select::ORDER); 0243 $rowCount = $db 0244 ->select() 0245 ->bind($rowCount->getBind()) 0246 ->from($rowCount); 0247 } else if ($isDistinct) { 0248 $part = $columnParts[0]; 0249 0250 if ($part[1] !== Zend_Db_Select::SQL_WILDCARD && !($part[1] instanceof Zend_Db_Expr)) { 0251 $column = $db->quoteIdentifier($part[1], true); 0252 0253 if (!empty($part[0])) { 0254 $column = $db->quoteIdentifier($part[0], true) . '.' . $column; 0255 } 0256 0257 $groupPart = $column; 0258 } 0259 } else if (!empty($groupParts)) { 0260 $groupPart = $db->quoteIdentifier($groupParts[0], true); 0261 } 0262 0263 /** 0264 * If the original query had a GROUP BY or a DISTINCT part and only 0265 * one column was specified, create a COUNT(DISTINCT ) query instead 0266 * of a regular COUNT query. 0267 */ 0268 if (!empty($groupPart)) { 0269 $countPart = 'COUNT(DISTINCT ' . $groupPart . ') AS '; 0270 } 0271 0272 /** 0273 * Create the COUNT part of the query 0274 */ 0275 $expression = new Zend_Db_Expr($countPart . $countColumn); 0276 0277 $rowCount->reset(Zend_Db_Select::COLUMNS) 0278 ->reset(Zend_Db_Select::ORDER) 0279 ->reset(Zend_Db_Select::LIMIT_OFFSET) 0280 ->reset(Zend_Db_Select::GROUP) 0281 ->reset(Zend_Db_Select::DISTINCT) 0282 ->reset(Zend_Db_Select::HAVING) 0283 ->columns($expression); 0284 } 0285 0286 $this->_countSelect = $rowCount; 0287 0288 return $rowCount; 0289 } 0290 }