PHP Classes

File: Grid.php

Recommend this page to a friend!
  Classes of Filipe Sá   Zend Framework Data Grid   Grid.php   Download  
File: Grid.php
Role: Class source
Content type: text/plain
Description: Class source
Class: Zend Framework Data Grid
Display and edit data from a database in a grid
Author: By
Last change:
Date: 13 years ago
Size: 136,702 bytes
 

Contents

Class file image Download
<?php /** * LICENSE * * This source file is subject to the new BSD license * It is available through the world-wide-web at this URL: * http://www.petala-azul.com/bsd.txt * If you did not receive a copy of the license and are unable to * obtain it through the world-wide-web, please send an email * to geral@petala-azul.com so we can send you a copy immediately. * * @package Bvb_Grid * @author Bento Vilas Boas <geral@petala-azul.com> * @copyright 2010 ZFDatagrid * @license http://www.petala-azul.com/bsd.txt New BSD License * @version $Id$ * @link http://zfdatagrid.com */ /** * * This class will abstract results from a data source for descendants * * * @package Bvb_Grid * @author Bento Vilas Boas <geral@petala-azul.com> * @copyright 2010 ZFDatagrid * @license http://www.petala-azul.com/bsd.txt New BSD License * @version Release: @package_version@ * @category Bvb_Grid */ abstract class Bvb_Grid { /** * Current Revision * @var string */ const VERSION = '$Rev$'; /** * If we should use mod_write for URL's * * @var bool * @static */ protected static $_modRewrite = false; /** * Default Configuration to be applied to all grids * * @var array * @static */ protected static $_defaultConfig = array(); /** * Location for deploy class * * @var mixed */ protected static $_deployClassesDir = false; /** * Char encoding * * @var string */ protected $_charEncoding = 'UTF-8'; /** * Fields order * * @var array */ private $_fieldsOrder; /** * The path where we can find the library * Usually is lib or library * * @var string */ protected $_libraryDir = 'library'; /** * templates type to be used * * @var array */ protected $_templates; /** * dir and prefix list to be used when formatting fields * */ protected $_formatter; /** * Number of results per page * * @var int */ protected $_recordsPerPage = 15; /** * Number of results to per page * * @var array */ protected $_paginationInterval = array(); /** * Type of export available * * @var array */ protected $_export = array('pdf', 'word', 'wordx', 'excel', 'print', 'xml', 'csv', 'ods', 'odt', 'json'); /** * All info that is not directly related to the database */ protected $_info = array(); /** * URL to prefix in case of routes * * @var bool */ protected $_routeName = null; /** * Baseurl * * @var string */ protected $_baseUrl; /** * Array containing the query result from table(s) * * @var array */ protected $_result; /** * Total records from db query * * @var int */ protected $_totalRecords; /** * Array containing field titles * * @var array */ protected $_titles; /** * Array containing table(s) fields * * @var array */ protected $_fields = array(); /** * Filters list * * @var array */ protected $_filters = array(); /** * Filters Render * @var */ protected $_filtersRenders; /** * External fielters to be applied * * @var array */ protected $_externalFilters = array(); /** * Extra Rows * * @var array */ protected $_extraRows = array(); /** * Filters values inserted by the user * * @var array */ protected $_filtersValues; /** * All information database related * * @var array */ protected $_data = array(); /** * URL params * * @var string */ protected $_ctrlParams = array(); /** * Extra Columns array * * @var array */ protected $_extraColumns = array(); /** * Final fields list (after all procedures). * * @var array */ protected $_finalFields; /** * Use cache or not. * @var bool */ protected $_cache = false; /** * Template instance * * @var object */ protected $_temp; /** * Check if all columns have been added by ->query() * * @var bool */ private $_allFieldsAdded = false; /** * Default filters to be applied * * @var array * @return array */ protected $_defaultFilters; /** * Instead throwing an exception, * we queue the field list and call this in * getFieldsFromQuery() * * @var array */ protected $_updateColumnQueue = array(); /** * List of callback functions to apply * on grid deploy and ajax * * @var array */ protected $_configCallbacks = array(); /** * Treat hidden fields as 'remove' * * @var bool */ protected $_removeHiddenFields = false; /** * Functions to be applied on every fields before display * * @var string */ protected $_escapeFunction = 'htmlspecialchars'; /** * Grid Options. * * @var array */ protected $_options = array(); /** * Id used for multiples instances on the same page * * @var string */ protected $_gridId; /** * Colspan for table * * @var int */ protected $_colspan; /** * User defined INFO for templates * * @var array */ protected $_templateParams = array(); /** * Array of fields that should appear on detail view * * @var array */ protected $_gridColumns = null; /** * Array of columns that should appear on detail view * * @var array */ protected $_detailColumns = null; /** * If we are on detail or grid view * * @var bool */ protected $_isDetail = false; /** * @var Zend_View_Interface */ protected $_view; /** * Information from FORM * * @var object */ protected $_crud = null; /** * * @var Bvb_Grid_Source_SourceInterface */ private $_source = null; /** * Last name from deploy class (table|pdf|csv|etc...) * * @var string */ protected $_deployName = null; /** * What is being done with this request * * @var array */ protected $_willShow = array(); /** * Print class based on conditions * * @var array */ protected $_classRowCondition = array(); /** * Result to apply to every <tr> based on condition * * @var $_classRowConditionResult array */ protected $_classRowConditionResult = array(); /** * CSS classes to be used * * @var array */ protected $_cssClasses = array('odd' => 'alt', 'even' => ''); /** * Condition to apply a CSS class to a table cell <td> * * @var array */ protected $_classCellCondition = array(); /** * Order setted by adapter * * @var string */ protected $_order; /** * custom translate instance * * @var Zend_Translate */ protected $_translator; /** * If filters by user will be showed when exporting * * @var bool */ protected $_showFiltersInExport = false; /** * If whe should save filters in session * * @var bool */ protected $_paramsInSession = false; /** * Session Params Zend_Session * * @var array */ protected $_sessionParams = false; /** * Hold definitions from configurations * * @var array */ protected $_deploy = array(); /** * Contains URL's for edit and delete records * Can be called from a decorator using * {{detailUrl}} * {{deleteUrl}} * {{editUrl}} * {{addUrl}} * * @var array */ protected $_actionsUrls = array('add' => '', 'edit' => '', 'delete' => '', 'detail' => ''); /** * Permission to add records * * @var bool */ protected $_allowAdd = false; /** * Options for adition * * @var array */ protected $_allowAddButton = array(); /** * Permission to edit records * * @var bool */ protected $_allowEdit = false; /** * Permission to delete records * * @var bool */ protected $_allowDelete = false; /** * Makes shure that config callbacks will be used once * * @var boolean */ protected $_runCallbacks = true; /** * Events manager class * * @var Bvb_Grid_Event_Dispatcher */ protected $_eventDispatcher = false; /** * Request Instance * * @var Zend_Controller_Request_Abstract */ protected $_request = null; /** * Response Instance * * @var Zend_Controller_Response_Abstract */ protected $_response = null; /** * * @var Zend_Controller_Front */ protected $_controller = null; /** * Mass Actions instance holder * * @var Bvb_Grid_Mass_Actions */ protected $_massActions = null; /** * Event prefix for the current instance * * @var string/null */ protected $_eventsPrefix = null; /** * The __construct function receives the db adapter. All information related to the * URL is also processed here * * @param array $options An Array or Zend_Config object. * * @return void */ public function __construct($options) { if (!$this instanceof Bvb_Grid_Deploy_DeployInterface) { throw new Bvb_Grid_Exception(get_class($this) . ' needs to implement Bvb_Grid_Deploy_DeployInterface'); } if ($options instanceof Zend_Config) { $options = $options->toArray(); } else if (!is_array($options)) { throw new Bvb_Grid_Exception('options must be an instance from Zend_Config or an array'); } $this->_options = array_merge_recursive(self::getDefaultConfig(), $options); // get the controller params and baseurl to use with filters if (isset($this->_options['grid']['requestParams'])) { // use from configuration, remove it from _options to enforce correct usage $this->setParams($this->_options['grid']['requestParams']); unset($this->_options['grid']['requestParams']); } else { // use the request parameters $this->setParams($this->getRequest()->getParams()); } if (isset($this->_options['grid']['baseUrl'])) { // use from configuration, remove it from _options to enforce correct usage $this->_baseUrl = $this->_options['grid']['baseUrl']; unset($this->_options['grid']['baseUrl']); } else { // use controllers value $this->_baseUrl = Zend_Controller_Front::getInstance()->getBaseUrl(); } foreach (array('massActionsAll_', 'gridAction_', 'send_') as $value) { $this->clearParam($value); } foreach ($this->_ctrlParams as $key => $value) { if (is_array($value)) { $this->clearParam($key); } } /** * plugins loaders */ $this->_formatter = new Zend_Loader_PluginLoader(); //Templates loading if (is_array($this->_export)) { foreach ($this->_export as $key => $temp) { if (is_array($temp)) { $export = $key; } else { $export = $temp; } $this->_templates[$export] = new Zend_Loader_PluginLoader(array()); } } // Add the formatter fir for fields content $this->addFormatterDir('Bvb/Grid/Formatter', 'Bvb_Grid_Formatter'); $deploy = explode('_', get_class($this)); $this->_deployName = strtolower(end($deploy)); $renderDir = ucfirst($this->_deployName); $this->_filtersRenders = new Zend_Loader_PluginLoader(); $this->addFiltersRenderDir('Bvb/Grid/Filters/Render/' . $renderDir, 'Bvb_Grid_Filters_Render_' . $renderDir); if (!defined('E_USER_DEPRECATED')) { define('E_USER_DEPRECATED', E_USER_WARNING); } $this->_sessionParams = new Zend_Session_Namespace('ZFDG_FILTERS' . $this->getGridId(true)); //Set an empty event dispatcher $this->setEventDispatcher(Bvb_Grid_Event_Dispatcher::getInstance()); //set an empty mass action $this->setMassActions(new Bvb_Grid_Mass_Actions()); } /** * Defines controller * * @param Zend_Controller_Front $controller * @return Bvb_Grid */ public function setController(Zend_Controller_Front $controller) { $this->_controller = $controller; return $this; } /** * Returns current controller instance * * @return Zend_Contrller_Front */ public function getController() { if (is_null($this->_controller)) { $this->_controller = Zend_Controller_Front::getInstance(); } return $this->_controller; } /** * Backwards compatibility * * @param mixed $object A Zend_Db object * * @return Bvb_Grid * @deprecated Use setSource() */ public function query($object) { if ($object instanceof Zend_Db_Select) { $this->setSource(new Bvb_Grid_Source_Zend_Select($object)); } elseif ($object instanceof Zend_Db_Table_Abstract) { $this->setSource(new Bvb_Grid_Source_Zend_Table($object)); } else { throw new Bvb_Grid_Exception('Please use setSource() method instead'); } return $this; } /** * Sets the source to be used * * Bvb_Grid_Source_* * * @param Bvb_Grid_Source_SourceInterface $source A valid interface * * @return Bvb_Grid */ public function setSource(Bvb_Grid_Source_SourceInterface $source) { if ($this->getSource()) { throw new Bvb_Grid_Exception('You can not set source twice'); } $this->_source = $source; $this->emitEvent('grid.set_source', array('source' => $this->getSource())); $this->getSource()->setCache($this->getCache()); $tables = $this->getSource()->getMainTable(); $this->_data['table'] = $tables['table']; if (isset($tables['schema'])) $this->_data['schema'] = $tables['schema']; $this->_crudTable = $this->_data['table']; $fields = $this->getSource()->buildFields(); foreach ($fields as $key => $field) { $this->updateColumn($key, $field); } $this->_allFieldsAdded = true; $this->emitEvent('grid.all_fields_added', array('fields' => &$this->_data['fields'])); //Apply options to the fields $this->_applyOptionsToFields(); return $this; } /** * The path where we can find the library * Usually is lib or library * * @param string $dir Zend Lib location * * @return Bvb_Grid */ public function setLibraryDir($dir) { $this->_libraryDir = $dir; return $this; } /** * Returns the actual library path * * @return string */ public function getLibraryDir() { return $this->_libraryDir; } /** * Sets grid cache * * @param mixed $cache Cache arguments * * @return mixed */ public function setCache($cache) { if ($cache == false || (is_array($cache) && isset($cache['enable']) && $cache['enable'] == 0)) { $this->_cache = array('enable' => 0); return $this; } if (is_array($cache) && isset($cache['enable']) && isset($cache['instance']) && isset($cache['tag'])) { $this->_cache = $cache; if ($this->getSource() !== null) { $this->getSource()->setCache($this->getCache()); } return $this; } return false; } /** * Returns actual cache params * * @return Zend_Cache */ public function getCache() { return $this->_cache; } /** * Returns the actual source object * * @return Bvb_Grid_Source_SourceInterface */ public function getSource() { return $this->_source; } /** * Defines a custom Translator * * @param Zend_Translate $translator Translator instance to be used * * @return Bvb_Grid */ public function setTranslator(Zend_Translate $translator) { Bvb_Grid_Translator::getInstance()->setTranslator($translator); return $this; } /** * Returns current request from Zend_Controller_Front * * @return Zend_Controller_Front::getInstance()->getReguest(); */ public function getRequest() { if (!$this->_request) { $this->_request = $this->getController()->getRequest(); } return $this->_request; } /** * Defines request instance * * @param Zend_Controller_Request_Abstract $request * @return Bvb_Grid */ public function setRequest(Zend_Controller_Request_Abstract $request) { $this->_request = $request; return $this; } /** * Set view object * * @param Zend_View_Interface $view view object to use * * @return Bvb_Grid */ public function setView(Zend_View_Interface $view = null) { $this->_view = $view; return $this; } /** * Retrieve view object * * If none registered, attempts to pull from ViewRenderer. * * @return Zend_View_Interface */ public function getView() { if (null === $this->_view) { $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer'); $this->setView($viewRenderer->view); } return $this->_view; } /** * Sets the functions to be used to apply to each value * before display * * @param array $functions Default functions to escape values * * @return Bvb_Grid */ public function setDefaultEscapeFunction($functions) { $this->_escapeFunction = $functions; return $this; } /** * Returns the active escape functions * * @return string */ public function getDefaultEscapeFunction() { return $this->_escapeFunction; } /** * Character encoding * * @param string $encoding Encoding to be used * * @return Bvb_Grid */ public function setcharEncoding($encoding) { $this->_charEncoding = $encoding; return $this; } /** * Returns the actual encoding * * @return string */ public function getCharEncoding() { return $this->_charEncoding; } /** * The translator * * @param string $message Message to be translated * * @return string */ protected function __($message) { if (strlen($message) == 0) { return $message; } if ($this->getTranslator()) { return $this->getTranslator()->translate($message); } return $message; } /** * Gets the translator instance * * @return Zend_Translate */ public function getTranslator() { return Bvb_Grid_Translator::getInstance()->getTranslator(); } /** * Check if a string is available * * @param string $message Message to check if it's translated * * @return bool */ protected function isTranslated($message) { return Bvb_Grid_Translator::getInstance()->isTranslated($message); } /** * Use the overload function so we can return an object * * @param string $name Mehtod name * @param string $value Mehtod args * * @return Bvb_Grid */ public function __call($name, $value) { if (substr(strtolower($name), 0, 6) == 'source') { $meth = substr($name, 6); $meth[0] = strtolower($meth[0]); if (is_object($this->getSource()) && method_exists($this->getSource(), $meth)) { $this->getSource()->$meth(); return $this; } } $class = $this->_deployName; if ($name == 'set' . ucfirst($class) . 'GridColumns') { if (!isset($value[0])) $value[0] = array(); $this->setGridColumns($value[0]); return $this; } if ($name == 'set' . ucfirst($class) . 'DetailColumns') { if (!isset($value[0])) $value[0] = array(); $this->setDetailColumns($value[0]); return $this; } if (substr(strtolower($name), 0, strlen($class) + 3) == 'set' . $class) { $name = substr($name, strlen($class) + 3); $name[0] = strtolower($name[0]); $this->_deploy[$name] = $value[0]; return $this; } if (substr(strtolower($name), 0, 3) == 'set') { $name = substr($name, 3); if (!isset($value[0])) { $value[0] = null; } $this->__set($name, $value[0]); } else { throw new Bvb_Grid_Exception("call to unknown function $name"); } return $this; } /** * Magic function handling * * @param string $var Variable name * @param string $value Variable value * * @return Bvb_Grid */ public function __set($var, $value) { $var[0] = strtolower($var[0]); $this->_info[$var] = $value; return $this; } /** * Update data from a column * * @param string $field Field Name * @param array $options Associative array of options to be applyied to the field * * @return Bvb_Grid */ public function updateColumn($field, array $options = array()) { $this->emitEvent('grid.update_column', array('field' => $field, 'options' => $options)); if (null == $this->getSource() || ($this->_allFieldsAdded == true && !array_key_exists($field, $this->_data['fields'])) ) { /** * Add to the queue and call it from the getFieldsFromQuery() method * @var $_updateColumnQueue */ if (isset($this->_updateColumnQueue[$field])) { $this->_updateColumnQueue[$field] = array_merge($this->_updateColumnQueue[$field], $options); } else { $this->_updateColumnQueue[$field] = $options; } return $this; } if ($this->_allFieldsAdded == false) { $this->_data['fields'][$field] = $options; } elseif (array_key_exists($field, $this->_data['fields'])) { if (isset($options['hRow']) && $options['hRow'] == 1) { $options['title'] = isset($options['title']) ? $options['title'] : $this->_data['fields'][$field]['field']; $this->_info['hRow'] = array('field' => $field, 'title' => $options['title']); } $this->_data['fields'][$field] = array_merge($this->_data['fields'][$field], $options); } return $this; } /** * Set option hidden=1 on several columns * * @param array $columns Array of columns that will be hidden * * @return Bvb_Grid */ public function setColumnsHidden(array $columns) { foreach ($columns as $column) { $this->updateColumn($column, array('hidden' => 1)); } return $this; } /** * Set option hidden=0 on several columns * * @param array $columns Array of columns that will be displayed * * @return Bvb_Grid */ public function setColumnsVisible(array $columns) { $this->setColumnsHidden($this->getFields()); foreach ($columns as $column) { $this->updateColumn($column, array('hidden' => false)); } return $this; } /** * Defines columns positions. * * Hither define array('field'=>position) or array(field1, field2) * * @param array $columns * @return Bvb_Grid */ public function setColumnsPositions(array $columns) { if (isset($columns[0])) { $columns = array_flip($columns); } foreach ($columns as $field => $position) { $this->updateColumn($field, array('position' => $position)); } return $this; } /** * Add a new dir to look for when formating a field * * @param string $dir Classes Location * @param string $prefix Classes prefix * * @return Bvb_Grid */ public function addFormatterDir($dir, $prefix) { $this->_formatter->addPrefixPath(trim($prefix, '_'), trim($dir, '/') . '/'); return $this; } /** * Format a field * * @param string $value Value to be formatted * @param mixed $formatter Formatter to be used * * @return mixed */ protected function _applyFormat($value, $formatter) { if (is_array($formatter)) { $formatter = array_values($formatter); $result = reset($formatter); if (!isset($formatter[1])) { $formatter[1] = array(); } $options = (array) $formatter[1]; } else { $result = $formatter; $options = array(); } $class = $this->_formatter->load($result); $t = new $class($options); if (!$t instanceof Bvb_Grid_Formatter_FormatterInterface) { throw new Bvb_Grid_Exception("$class must implement the Bvb_Grid_Formatter_FormatterInterface"); } return $t->format($value); } /** * Number of records to show per page * * @param array $pagination key=>pair array of possible values of records per page for user to choose from * * @return Bvb_Grid */ public function setPaginationInterval(array $pagination) { $this->_paginationInterval = $pagination; return $this; } /** * Returns current pagination interval configurations * * @return array */ public function getPaginationInterval() { return $this->_paginationInterval; } /** * Number of records to show per page * * @param int $number Records to show * * @return Bvb_Grid */ public function setRecordsPerPage($number = 15) { $this->_recordsPerPage = (int) $number; return $this; } /** * Default values for filters. * This will be applied before displaying. However the user can still remove them. * * @param array $filters Associative array with fields=>Values to define default filters values * * @return Bvb_Grid */ public function setDefaultFiltersValues(array $filters) { $this->_defaultFilters = $filters; return $this; } /** * Get filters values * * @return Bvb_Grid */ protected function _buildFiltersValues() { //Build an array to know filters values $filtersValues = array(); $fields = $this->getFields(); $filters = array(); foreach ($this->_ctrlParams as $key => $value) { // only build filter if search is enabled if (isset($this->_data['fields'][$key]['search']) && $this->_data['fields'][$key]['search'] == false) continue; //This happens when we have range filters and the url look like this: // /action/field[from]/100/field[to]/500 if (stripos($key, '[')) { $name = explode('[', $key); //lets check if there ir a grid id, so we can remove it if (strlen($this->getGridId()) > 0) { $name[0] = substr($name[0], 0, - strlen($this->getGridId())); } //check if this param is in fact a field we set if (in_array($name[0], $fields)) { $filters[$name[0]][substr($name[1], 0, - 1)] = $value; } } else { //check if this param is in fact a field we set if (in_array($key, $fields)) { $filters[$key] = $value; //Can have a grid id, so we also need to check for that situation } elseif (in_array(substr($key, 0, - strlen($this->getGridId())), $fields)) { if ($this->getGridId() != '' && substr($key, - strlen($this->getGridId())) == $this->getGridId() ) { $key = substr($key, 0, - strlen($this->getGridId())); } $filters[$key] = $value; } } } if (count($filters) > 0) { //let's set the range filters as an array foreach ($filters as $key => $value) { if (is_array($value)) { $this->setParam($key, $value); } } $fieldsRaw = $this->_data['fields']; //final check for allowed fields foreach ($filters as $key => $filter) { if (!is_array($filter) && (strlen($filter) == 0 || !in_array($key, $this->_fields))) { unset($filters[$key]); } elseif (!is_array($filter)) { if (isset($fieldsRaw[$key]['searchField'])) { $key = $fieldsRaw[$key]['searchField']; } //Copy the current filter value so we can perform the vairous options and not loosing the //orignal value. This happens with transform, callbacks, etc, $oldFilter = $filter; //Check fi user has defined a transform option for the value //The transform option is used to normalise vallues, like date, currency, etc if (isset($this->_filters[$key]['transform']) && is_callable($this->_filters[$key]['transform'])) { $filter = call_user_func($this->_filters[$key]['transform'], $filter); } //A callback is set? If yes, let's call it if (isset($this->_filters[$key]['callback']) && is_array($this->_filters[$key]['callback'])) { if (!is_callable($this->_filters[$key]['callback']['function'])) { throw new Bvb_Grid_Exception($this->_filters[$key]['callback']['function'] . ' is not callable'); } if (!isset($this->_filters[$key]['callback']['params']) || !is_array($this->_filters[$key]['callback']['params']) ) { $this->_filters[$key]['callback']['params'] = array(); } $this->_filters[$key]['callback']['params'] = array_merge( $this->_filters[$key]['callback']['params'], array('field' => $key, 'value' => $filter, 'select' => $this->getSource()->getSelectObject()) ); $result = call_user_func($this->_filters[$key]['callback']['function'], $this->_filters[$key]['callback']['params'] ); } elseif (isset($this->_data['fields'][$key]['search']) && is_array($this->_data['fields'][$key]['search']) && $this->_data['fields'][$key]['search']['fulltext'] == true ) { //Fulltext search activated by user. Only possible in MySQL server $this->getSource()->addFullTextSearch($filter, $this->_data['fields'][$key]); } else { //Nothing "special" needs to be performed. So we continue with the normal procedure //Let's check if there is a special symbol in user's input //Some exaemples: =valu, >value, r:regexp $op = $this->getFilterOp($key, $filter); $this->emitEvent('source.add_condition', array('filter' => &$filter, 'op' => &$op, 'field' => &$completeField)); $this->getSource()->addCondition($op['filter'], $op['op'], $this->_data['fields'][$key]); } //We assign the filter value so it can be filled properlly. //Even if we perform any transform to the field, the original user's input must be showed $filtersValues[$key] = $oldFilter; } if (is_array($filter)) { //Load filter render $render = $this->loadFilterRender($this->_filters[$key]['render']); $render->setFieldName($key); if ($render->hasConditions()) { $cond = $render->getConditions(); $render->setSelect($this->getSource()->getSelectObject()); foreach ($filter as $nkey => $value) { if (strlen($value) > 0) { $oldValue = $value; $value = $render->normalize($value, $nkey); $this->emitEvent('source.add_condition', array('filter' => &$filter, 'op' => &$op, 'field' => &$completeField)); $this->getSource()->addCondition($value, $cond[$nkey], $this->_data['fields'][$key]); $filtersValues[$key][$nkey] = $oldValue; } } } else { $render->buildQuery($filter); } } } } $this->_filtersValues = $filtersValues; $this->_applyExternalFilters(); //If needed put current filters values in session if (count($this->_filtersValues) > 0 && $this->_paramsInSession === true) { $this->_sessionParams->filters = $this->_filtersValues; } return $this; } /** * Adds external filters to the grid * * @return void */ protected function _applyExternalFilters() { if (count($this->_externalFilters) == 0) return false; foreach ($this->_externalFilters as $id => $callback) { $val = $this->getRequestParamClean($id); if ($val) { call_user_func_array($callback, array($id, $val, $this->getSelect())); $this->_filtersValues[$id] = $this->getRequestParamClean($id); } } } /** * Returns the operand to be used in filters * This value comes from the user input * but can be override * * @param string $field Field name * @param string $filter Filter to apply to the field * * @return array */ public function getFilterOp($field, $filter) { if (!isset($this->_data['fields'][$field]['searchType'])) { $this->_data['fields'][$field]['searchType'] = 'like'; } $op = strtolower($this->_data['fields'][$field]['searchType']); if ($this->_data['fields'][$field]['searchType'] == 'sqlExp' && isset($this->_data['fields'][$field]['searchSqlExp']) ) { $op = 'sqlExp'; $sqlExp = $this->_data['fields'][$field]['searchSqlExp']; if (!isset($this->_data['fields'][$field]['searchSqlQuote'])) { $this->_data['fields'][$field]['searchSqlQuote'] = false; } $sqlQuote = (bool) $this->_data['fields'][$field]['searchSqlQuote']; if ($sqlQuote === false) { $filter = str_replace('{{value}}', $this->getSource()->quoteValue($filter), $sqlExp); } else { $filter = str_replace('{{value}}', trim(subtr($filter, 1, -1)), $sqlExp); } } elseif (substr(strtolower($filter), 0, 6) == ':empty') { $op = 'empty'; $filter = substr($filter, 2); } elseif (substr(strtolower($filter), 0, 10) == ':isnotnull') { $op = 'isnotnull'; $filter = substr($filter, 2); } elseif (substr(strtolower($filter), 0, 7) == ':isnull') { $op = 'isnull'; $filter = substr($filter, 2); } elseif (substr(strtoupper($filter), 0, 2) == 'R:') { $op = 'REGEX'; $filter = substr($filter, 2); } elseif (strpos($filter, '<>') !== false && substr($filter, 0, 2) != '<>') { $op = 'range'; } elseif (substr($filter, 0, 1) == '=') { $op = '='; $filter = substr($filter, 1); } elseif (substr($filter, 0, 2) == '>=') { $op = '>='; $filter = substr($filter, 2); } elseif ($filter[0] == '>') { $op = '>'; $filter = substr($filter, 1); } elseif (substr($filter, 0, 2) == '<=') { $op = '<='; $filter = substr($filter, 2); } elseif (substr($filter, 0, 2) == '<>' || substr($filter, 0, 2) == '!=') { $op = '<>'; $filter = substr($filter, 2); } elseif ($filter[0] == '<') { $op = '<'; $filter = substr($filter, 1); } elseif ($filter[0] == '*' and substr($filter, - 1) == '*') { $op = 'like'; $filter = substr($filter, 1, - 1); } elseif ($filter[0] == '*' and substr($filter, - 1) != '*') { $op = 'llike'; $filter = substr($filter, 1); } elseif ($filter[0] != '*' and substr($filter, - 1) == '*') { $op = 'rlike'; $filter = substr($filter, 0, - 1); } elseif (stripos($filter, ',') !== false) { $op = 'IN'; } elseif (substr($filter, 0, 2) == '||') { $op = '||'; $filter = substr($filter, 2); } if (isset($this->_data['fields'][$field]['searchTypeFixed']) && $this->_data['fields'][$field]['searchTypeFixed'] == true && $op != $this->_data['fields'][$field]['searchType'] ) { $op = $this->_data['fields'][$field]['searchType']; } return array('op' => $op, 'filter' => $filter); } /** * Build query. * * @return bool */ protected function _buildQueryOrderAndLimit() { $start = (int) $this->getParam('start'); $order = $this->getParam('order'); $order1 = explode('_', $order); $orderf = strtoupper(end($order1)); array_pop($order1); $orderField = implode('_', $order1); if (!isset($this->_fieldsOrder[$orderField])) { $this->clearParam('order'); $orderf = false; } else { $orderField = $this->_fieldsOrder[$orderField]; } if ($orderf == 'DESC' || $orderf == 'ASC' || ($this->_paramsInSession === true && is_array($this->_sessionParams->order)) ) { if ($this->_paramsInSession === true) { if ($this->getParam('noOrder')) { $this->_sessionParams->order = null; } if (is_array($this->_sessionParams->order) && !$this->getParam('order')) { $orderField = $this->_sessionParams->order['field']; $orderf = $this->_sessionParams->order['order']; $this->setParam('order' . $this->getGridId(), $orderField . '_' . $orderf); } } if (in_array($orderField, $this->_fieldsOrder)) { $this->getSource()->buildQueryOrder($orderField, $orderf, true); if ($this->_paramsInSession === true) { $this->_sessionParams->order = array('field' => $orderField, 'order' => $orderf); } } } $this->getSource()->buildQueryLimit($this->getRecordsPerPage(), $start); return true; } /** * Returns the number of records to show per page * * @return integer */ public function getRecordsPerPage() { $perPage = (int) $this->getParam('perPage', 0); if ($this->_paramsInSession === true && $this->getParam('perPage') === false) { $perPage = (int) $this->_sessionParams->perPage; $this->setParam('perPage' . $this->getGridId(), $perPage); } if ($perPage > 0 && array_key_exists($perPage, $this->_paginationInterval)) { if ($this->_paramsInSession === true) { $this->_sessionParams->perPage = $perPage; } return $perPage; } else { if ($this->_paramsInSession === true) { $this->_sessionParams->perPage = $this->_recordsPerPage; } return $this->_recordsPerPage; } } /** * Returns the url, without the param(s) specified * * @param mixed $situation Array of params to be removed * @param array $extraParams param to add to url * * @todo Use a view helper to build url * * @return string */ public function getUrl($situation = '', array $extraParams = array()) { $situation = (array) $situation; //this array the a list of params that name changes //based on grid id. The id is prepended to the name $paramsGet = array('perPage', 'order', 'start', 'filters', 'zfmassedit', 'zfmassremove', 'send_', 'postMassIds', 'gridAction_', 'massActionsAll_', 'noFilters', '_exportTo', 'add', 'edit', 'noOrder', 'comm', 'detail', 'delete'); $params = $this->getParams(); if (in_array('filters', $situation)) { $fields = array_merge($this->getFields(), array_keys($this->_externalFilters)); foreach(array_keys($this->_externalFilters) as $removeExternalFilter) { unset($params[$removeExternalFilter]); } foreach ($fields as $field) { if (isset($params[$field . $this->getGridId()])) { unset($params[$field . $this->getGridId()]); } } foreach ($params as $key => $value) { if (stripos($key, '[') !== false) { $fl = explode('[', $key); if (in_array(rtrim($fl[0], $this->getGridId()), $fields)) { unset($params[rtrim($fl[0]) . '[' . $fl[1]]); $fieldName = substr(rtrim($fl[0]), 0, strpos(rtrim($fl[0]), $this->getGridId())); if(in_array($fieldName, $this->_fields)) $this->clearParam($fieldName); } } } } foreach ($situation as $value) { if (in_array($value, $paramsGet)) { $value = $value . $this->getGridId(); } unset($params[$value]); } $paramsClean = $params; unset($paramsClean['_zfgid']); unset($paramsClean['gridmod' . $this->getGridId()]); $this->clearParam('gridmod'); if (is_array($this->_filters)) { foreach ($this->_filters as $key => $value) { if (is_array($key) && isset($key['render'])) { unset($paramsClean[$key]); } } } foreach ($extraParams as $key => $value) { if ($this->getGridId()) { $extraParams[$key . $this->getGridId()] = $value; unset($extraParams[$key]); } } $extraParams['zfghost'] = 1; $paramsClean = array_merge($paramsClean, (array) $extraParams); $ur = new Zend_View_Helper_Url(); $url = $ur->url($paramsClean, null); return str_replace("/zfghost/1", '', $url); } /** * Return variable stored in info. Return default if value is not stored. * * @param string $param Param to look for in the info var * @param mixed $default Return this value if param does not exist * * @return string */ public function getInfo($param, $default = false) { if (isset($this->_info[$param])) return $this->_info[$param]; if (strpos($param, ',')) { $params = explode(',', $param); $param = array_map('trim', $params); $final = $this->_info; foreach ($params as $check) { if (!isset($final[$check])) { return $default; } $final = $final[$check]; } return $final; } return $default; } /** * Build Filters. If defined put the values * Also check if the user wants to hide a field * * @return mixed */ protected function _buildFilters() { $return = array(); if ($this->getInfo('noFilters') == 1) { return false; } $data = $this->_fields; foreach ($this->_extraColumns as $key => $value) { if ($value['position'] == 'left') { $value['newrow'] = !isset($value['newrow']) ? false : $value['newrow']; $value['rowspan'] = !isset($value['rowspan']) ? null : $value['rowspan']; $value['colspan'] = !isset($value['colspan']) ? null : $value['colspan']; $return[$key] = array('type' => 'extraField', 'position' => 'left', 'newrow' => $value['newrow'], 'rowspan' => $value['rowspan'], 'colspan' => $value['colspan']); } } for ($i = 0; $i < count($data); $i++) { $nf = $this->_fields[$i]; if (!isset($this->_data['fields'][$nf]['search'])) { $this->_data['fields'][$nf]['search'] = true; } if ($this->_displayField($nf)) { if (!isset($this->_data['fields'][$nf]['newrow'])) { $newrow = false; } else { $newrow = $this->_data['fields'][$nf]['newrow']; } if (!isset($this->_data['fields'][$nf]['rowspan'])) { $rowspan = null; } else { $rowspan = $this->_data['fields'][$nf]['rowspan']; } if (!isset($this->_data['fields'][$nf]['colspan'])) { $colspan = null; } else { $colspan = $this->_data['fields'][$nf]['colspan']; } if (is_array($this->_filters) && array_key_exists($data[$i], $this->_filters) && $this->_data['fields'][$nf]['search'] != false ) { $filterValue = isset($this->_filtersValues[$data[$i]]) ? $this->_filtersValues[$data[$i]] : ''; $return[] = array('type' => 'field', 'value' => $filterValue, 'field' => $data[$i], 'newrow' => $newrow, 'rowspan' => $rowspan, 'colspan' => $colspan); } else { $return[] = array('type' => 'field', 'field' => $data[$i], 'newrow' => $newrow, 'rowspan' => $rowspan, 'colspan' => $colspan); } } } foreach ($this->_extraColumns as $key => $value) { if ($value['position'] == 'right') { $value['newrow'] = !isset($value['newrow']) ? false : $value['newrow']; $value['rowspan'] = !isset($value['rowspan']) ? null : $value['rowspan']; $value['colspan'] = !isset($value['colspan']) ? null : $value['colspan']; $return[$key] = array('type' => 'extraField', 'position' => 'right', 'newrow' => $value['newrow'], 'rowspan' => $value['rowspan'], 'colspan' => $value['colspan']); } } return $return; } /** * Checks if a field should be displayed or is setted as 'remove' * * @param string $field Field Name * * @return bool */ protected function _displayField($field) { if (!isset($this->_data['fields'][$field]['remove'])) { $this->_data['fields'][$field]['remove'] = false; } if (!isset($this->_data['fields'][$field]['hidden'])) { $this->_data['fields'][$field]['hidden'] = false; } if ($this->_data['fields'][$field]['remove'] == 0 && (($this->_data['fields'][$field]['hidden'] == 0) || ($this->_data['fields'][$field]['hidden'] == 1 && $this->_removeHiddenFields !== true)) ) { return true; } return false; } /** * Replaces the brackets in fields * * @param array $fields Array of fields * * @return array */ protected function _prepareReplace($fields) { // Make an array of field names in format {{$fieldname}} $map = array_map(create_function('$value', 'return "{{{$value}}}";'), $fields); if (isset($this->_options['grid']['enableUnmodifiedFieldPlaceholders']) && $this->_options['grid']['enableUnmodifiedFieldPlaceholders'] == true ) { // Enable placeholders for unmodified fields: Make an array of field names in format {{=$fieldname}} $map2 = array_map(create_function('$value', 'return "{{={$value}}}";'), $fields); $map = array_merge($map, $map2); } return $map; } /** * Build the titles with the order links (if wanted) * * @return array */ protected function _buildTitles() { static $index = 0; $return = array(); $url = $this->getUrl(array('order', 'start', 'comm', 'noOrder')); foreach ($this->getExtraColumnsForPosition('left') as $key => $value) { $index++; $value['newrow'] = !isset($value['newrow']) ? false : $value['newrow']; $value['rowspan'] = !isset($value['rowspan']) ? null : $value['rowspan']; $value['colspan'] = !isset($value['colspan']) ? null : $value['colspan']; if ($this->__(isset($value['title']))) { $fieldValue = $value['title']; } else { $fieldValue = $value['name']; } $return[$index . '-' . $key] = array('type' => 'extraField', 'value' => $fieldValue, 'position' => 'left', 'newrow' => $value['newrow'], 'rowspan' => $value['rowspan'], 'colspan' => $value['colspan']); } $titles = $this->_fields; if (!$this->getParam('noOrder')) { $selectOrder = $this->getSource()->getSelectOrder(); if (count($selectOrder) == 1) { $this->setParam('order' . $this->getGridId(), $selectOrder[0] . '_' . strtoupper($selectOrder[1])); } } for ($i = 0; $i < count($this->_fields); $i++) { if ($this->getParam('order')) { $explode = explode('_', $this->getParam('order')); $name = str_replace('_' . end($explode), '', $this->getParam('order')); $this->_order[$name] = strtoupper(end($explode)) == 'ASC' ? 'DESC' : 'ASC'; } $fieldsToOrder = array_values($this->_data['fields']); if (isset($fieldsToOrder[$i]['orderField']) && strlen($fieldsToOrder[$i]['orderField']) > 0) { $orderFinal = $fieldsToOrder[$i]['orderField']; } else { $orderFinal = $titles[$i]; } if (is_array($this->_order)) { $order = $orderFinal == key($this->_order) ? $this->_order[$orderFinal] : 'ASC'; } else { $order = 'ASC'; } if ($this->_displayField($titles[$i])) { if (!isset($this->_data['fields'][$titles[$i]]['newrow'])) { $newrow = false; } else { $newrow = $this->_data['fields'][$titles[$i]]['newrow']; } if (!isset($this->_data['fields'][$titles[$i]]['rowspan'])) { $rowspan = null; } else { $rowspan = $this->_data['fields'][$titles[$i]]['rowspan']; } if (!isset($this->_data['fields'][$titles[$i]]['colspan'])) { $colspan = null; } else { $colspan = $this->_data['fields'][$titles[$i]]['colspan']; } $noOrder = $this->getInfo('noOrder') ? $this->getInfo('noOrder') : ''; if ($this->isTranslated($titles[$i]) === true) { $fieldTitle = $this->__($titles[$i]); } else { $fieldTitle = $this->__($this->_titles[$titles[$i]]); } if ($noOrder == 1) { $return[$titles[$i]] = array('type' => 'field', 'name' => $titles[$i], 'field' => $titles[$i], 'value' => $fieldTitle, 'newrow' => $newrow, 'rowspan' => $rowspan, 'colspan' => $colspan); } else { $return[$titles[$i]] = array('type' => 'field', 'name' => $titles[$i], 'field' => $orderFinal, 'simpleUrl' => $this->getUrl(array('order', 'start', 'comm', 'noOrder')), 'url' => $this->getUrl(array('order', 'start', 'comm', 'noOrder'), array('order' => $orderFinal . "_" . $order)), 'value' => $fieldTitle, 'newrow' => $newrow, 'rowspan' => $rowspan, 'colspan' => $colspan); } } } foreach ($this->getExtraColumnsForPosition('right') as $key => $value) { $index++; $value['newrow'] = !isset($value['newrow']) ? false : $value['newrow']; $value['rowspan'] = !isset($value['rowspan']) ? null : $value['rowspan']; $value['colspan'] = !isset($value['colspan']) ? null : $value['colspan']; $return[$index . '-' . $key] = array('type' => 'extraField', 'value' => $this->__(isset($value['title']) ? $value['title'] : $value['name']), 'position' => 'right', 'newrow' => $value['newrow'], 'rowspan' => $value['rowspan'], 'colspan' => $value['colspan']); } $this->_finalFields = $return; return $return; } /** * Replaces {{field}} for the actual field value * * @param string &$item Item to apply function * @param string $key Value to search * @param string $text Value to replace * * @return void */ protected function _replaceSpecialTags(&$item, $key, $text) { $item = str_replace($text['find'], $text['replace'], $item); } /** * Applies the format option to a field * * @param string $newValue Value generated * @param string $value Field Name * @param array $search Variables to search for * @param array $replace Replace variable with these values * * @return mixed */ protected function _applyFieldFormat($newValue, $value, $search, $replace) { if (is_array($value)) { array_walk_recursive($value, array($this, '_replaceSpecialTags'), array('find' => $search, 'replace' => $replace)); } return $this->_applyFormat($newValue, $value); } /** * Applies the callback option to a field * * @param string $newValue Value generated * @param string $value Field Name * @param array $search Variables to search for * @param array $replace Replace variable with these values * @param string $field Current field * * @return mixed */ protected function _applyFieldCallback($newValue, $value, $search, $replace, $field) { if (is_string($value)) { $value = array('function' => $value); } if (!is_callable($value['function'])) { throw new Bvb_Grid_Exception($value['function'] . ' not callable'); } if (isset($value['params']) && is_array($value['params'])) { $toReplace = $value['params']; $toReplaceArray = array(); $toReplaceObj = array(); foreach ($toReplace as $key => $rep) { if (is_scalar($rep) || is_array($rep)) { $toReplaceArray[$key] = $rep; } else { $toReplaceObj[$key] = $rep; } } } else { return call_user_func($value['function'], $replace[$field]); } if (is_array($toReplace)) { array_walk_recursive( $toReplaceArray, array($this, '_replaceSpecialTags'), array('find' => $search, 'replace' => $replace) ); } for ($i = 0; $i <= count($toReplace); $i++) { if (isset($toReplaceArray[$i])) { $toReplace[$i] = $toReplaceArray[$i]; } elseif (isset($toReplaceObj[$i])) { $toReplace[$i] = $toReplaceObj[$i]; } } $toReplace = str_replace($search, $replace, $toReplace); $toReplace = $this->_checkForAllParamsInField($toReplace, $replace); return call_user_func_array($value['function'], $toReplace); } protected function _checkForAllParamsInField($params, $fields) { foreach ($params as $key => $dec) { if ($dec == '{{_ALL_}}' || $dec == '{{=_ALL_}}') { $params[$key] = $fields; } } return $params; } /** * Applies the decorator to a fields * * @param array $find Variables to search for * @param array $replace Replace variable with these values * @param string $value Field Name * * @return string */ protected function _applyFieldDecorator(array $find, array $replace, $value) { return str_replace($find, $replace, $value); } /** * Applies escape functions to a field * * @param string $value Field Value * * @return string */ protected function _applyFieldEscape($value) { if ($this->_escapeFunction === false) { return $value; } if (!is_callable($this->_escapeFunction)) { throw new Bvb_Grid_Exception($this->_escapeFunction . ' not callable'); } $value = call_user_func($this->_escapeFunction, $value); return $value; } /** * Apply escape functions to column * * @param string $field Field Name * @param string $value Field Value * * @return mixed */ private function _escapeField($field, $value) { if (!isset($this->_data['fields'][$field]['escape'])) { $this->_data['fields'][$field]['escape'] = 1; } if (($this->_data['fields'][$field]['escape'] ? 1 : 0) == 0) { return $value; } if ($this->_data['fields'][$field]['escape'] == 1) { return $this->_applyFieldEscape($value); } if (!is_callable($this->_data['fields'][$field]['escape'])) { throw new Bvb_Grid_Exception($this->_data['fields'][$field]['escape'] . ' not callable'); } return call_user_func($this->_data['fields'][$field]['escape'], $value); } /** * Applies the view helper to the field * * @param string $newValue Value generated * @param string $value Field Name * @param array $search Variables to search for * @param array $replace Replace variable with these values * * @return string */ protected function _applyFieldHelper($newValue, $value, array $search, array $replace) { if (is_array($value)) { array_walk_recursive($value, array($this, '_replaceSpecialTags'), array('find' => $search, 'replace' => $replace)); } $name = $value['name']; $t = $this->getView()->getHelper($name); $re = new ReflectionMethod($t, $name); if (isset($value['params']) && is_array($value['params'])) { $newValue = $re->invokeArgs($t, $value['params']); } else { $newValue = $re->invoke($t); } return $newValue; } /** * The loop for the results. * Check the extra-fields, * * @return array */ protected function _buildGrid($data = null) { $return = array(); $i = 0; if ($data === null) { $result = $this->_result; $fields = $this->_fields; } else { $fields = array_keys($data[0]); $result = $data; } $classConditional = array(); foreach ($result as $row) { $search = $this->_prepareReplace($this->_fields); // Create a map of field values with which to replace special field placeholders (ex. {{field_name}}) $replace = array(); foreach ($fields as $field) { $row[$field] = isset($row[$field]) ? $row[$field] : null; $replace[$field] = $row[$field]; } if (isset($this->_options['grid']['enableUnmodifiedFieldPlaceholders']) && $this->_options['grid']['enableUnmodifiedFieldPlaceholders'] == true ) { // Enable placeholders for unmodified fields: // Append a second set of fields to the replacement map, with field names prefixed by '=' // (ex. {{=field_name}}) // These will allow access to the original field values unmodified by formatters, etc. foreach ($fields as $field) { $row[$field] = isset($row[$field]) ? $row[$field] : null; $replace['=' . $field] = $row[$field]; } } $replace['editUrl'] = str_replace($search, $replace, $this->_actionsUrls['edit']); $replace['addUrl'] = str_replace($search, $replace, $this->_actionsUrls['add']); $replace['deleteUrl'] = str_replace($search, $replace, $this->_actionsUrls['delete']); $replace['detailUrl'] = str_replace($search, $replace, $this->_actionsUrls['detail']); if (!in_array('{{editUrl}}', $search)) { $search[] = '{{editUrl}}'; } if (!in_array('{{addUrl}}', $search)) { $search[] = '{{addUrl}}'; } if (!in_array('{{deleteUrl}}', $search)) { $search[] = '{{deleteUrl}}'; } if (!in_array('{{detailUrl}}', $search)) { $search[] = '{{detailUrl}}'; } $this->_classRowConditionResult[$i] = ''; if (isset($this->_classRowCondition[0]) && is_array($this->_classRowCondition[0])) { foreach ($this->_classRowCondition as $value) { $cond = str_replace($search, $replace, $value['condition']); $final = call_user_func(create_function('', "if($cond){return true;}else{return false;}")); $this->_classRowConditionResult[$i] .= $final == true ? $value['class'] . ' ' : $value['else'] . ' '; } } $this->_classRowConditionResult[$i] .= ( $i % 2) ? $this->_cssClasses['even'] : $this->_cssClasses['odd']; if (count($this->_classCellCondition) > 0) { foreach ($this->_classCellCondition as $key => $value) { $classConditional[$key] = ''; foreach ($value as $condF) { $cond = str_replace($search, $replace, $condF['condition']); $final = call_user_func(create_function('', "if($cond){return true;}else{return false;}")); $classConditional[$key] .= $final == true ? $condF['class'] . ' ' : $condF['else'] . ' '; } } } /** * Deal with extrafield from the left */ $left = $this->getExtraColumnsForPosition('left'); foreach ($left as $field) { $return[$i][] = $this->_buildExtraField($field, $search, $replace); } /** * Deal with the grid itself */ foreach ($fields as $field) { $row[$field] = isset($row[$field]) ? $row[$field] : null; $newValue = $this->_escapeField($field, $row[$field]); if (isset($this->_data['fields'][$field]['callback']['function'])) { $newValue = $this->_applyFieldCallback( $newValue, $this->_data['fields'][$field]['callback'], $search, $replace, $field ); $replace[$field] = $newValue; $search[] = '{{callback}}'; $replace[] = $newValue; } if (isset($this->_data['fields'][$field]['format'])) { $newValue = $this->_applyFieldFormat( $newValue, $this->_data['fields'][$field]['format'], $search, $replace ); $replace[$field] = $newValue; $search[] = '{{format}}'; $replace[] = $newValue; } if (isset($this->_data['fields'][$field]['helper'])) { $newValue = $this->_applyFieldHelper( $newValue, $this->_data['fields'][$field]['helper'], $search, $replace ); $replace[$field] = $newValue; $search[] = '{{helper}}'; $replace[] = $newValue; } if (isset($this->_data['fields'][$field]['decorator'])) { $newValue = $this->_applyFieldDecorator( $search, $replace, $this->_data['fields'][$field]['decorator'] ); } if ($this->_displayField($field)) { if (isset($this->_data['fields'][$field]['translate']) && $this->_data['fields'][$field]['translate'] == true ) { $newValue = $this->__($newValue); } if (!isset($this->_data['fields'][$field]['style'])) { $style = ''; } else { $style = $this->_data['fields'][$field]['style']; } if (isset($this->_data['fields'][$field]['class'])) { $fieldClass = $this->_data['fields'][$field]['class']; } else { $fieldClass = ''; } if (isset($classConditional[$field])) { $finalClassConditional = $classConditional[$field]; } else { $finalClassConditional = ''; } if (!isset($this->_data['fields'][$field]['newrow'])) { $newrow = false; } else { $newrow = $this->_data['fields'][$field]['newrow']; } if (!isset($this->_data['fields'][$field]['rowspan'])) { $rowspan = null; } else { $rowspan = $this->_data['fields'][$field]['rowspan']; } if (!isset($this->_data['fields'][$field]['colspan'])) { $colspan = null; } else { $colspan = $this->_data['fields'][$field]['colspan']; } $return[$i][] = array('class' => $fieldClass . ' ' . $finalClassConditional, 'value' => $newValue, 'field' => $field, 'type' => 'field', 'style' => $style, 'newrow' => $newrow, 'rowspan' => $rowspan, 'colspan' => $colspan); } } /** * Deal with extra fields from the right */ foreach ($this->getExtraColumnsForPosition('right') as $field) { $return[$i][] = $this->_buildExtraField($field, $search, $replace); } $i++; } return $return; } /** * Get the extra fields for a give position * * @param string $position Postion name * * @return array */ public function getExtraColumnsForPosition($position = 'left') { if (!is_array($this->_extraColumns)) { return array(); } $final = array(); foreach ($this->getExtraColumns() as $value) { if ($value['position'] == $position) { $final[] = $value; } } $newOrder = array(); foreach ($final as $key => $value) { $newOrder[$key] = (int)$value['order']; } array_multisort( $newOrder, SORT_ASC, $final); return $final; } /** * Build extra fields (apply callbacks, helpers, etc) * * @param string $field Field Name * @param array $search Variables to search for * @param array $replace Replace variable with these values * * @return array */ protected function _buildExtraField($field, $search, $replace) { $originalArray = array('class' => '', 'style' => '', 'newrow' => '', 'rowspan' => '', 'colspan' => ''); $field = array_merge($originalArray, $field); $value = ''; if (isset($field['format'])) { $value = $this->_applyFieldFormat($value, $field['format'], $search, $replace); $search[] = '{{format}}'; $replace[] = $value; } if (isset($field['callback'])) { $value = $this->_applyFieldCallback($value, $field['callback'], $search, $replace, $field); $search[] = '{{callback}}'; $replace[] = $value; } if (isset($field['helper'])) { $value = $this->_applyFieldHelper($value, $field['helper'], $search, $replace); $search[] = '{{helper}}'; $replace[] = $value; } if (isset($field['decorator'])) { $value = $this->_applyFieldDecorator($search, $replace, $field['decorator']); } return array('class' => $field['class'], 'value' => $value, 'type' => 'extraField', 'field' => $field['name'], 'style' => $field['style'], 'newrow' => $field['newrow'], 'rowspan' => $field['rowspan'], 'colspan' => $field['colspan']); } /** * Apply SQL Functions * * @param array $where Where condition to be applied * * @return array */ protected function _buildSqlExp($where = array()) { $final = $this->getInfo('sqlexp') ? $this->getInfo('sqlexp') : ''; if (!is_array($final)) { return false; } $result = array(); foreach ($final as $key => $value) { if (!array_key_exists($key, $this->_data['fields'])) continue; if (!isset($value['value'])) { $value['value'] = $key; } $value['newrow'] = !isset($value['newrow']) ? false : $value['newrow']; $value['rowspan'] = !isset($value['rowspan']) ? null : $value['rowspan']; $value['colspan'] = !isset($value['colspan']) ? null : $value['colspan']; $resultExp = $this->getSource()->getSqlExp($value, $where); if (!isset($value['format']) && isset($this->_data['fields'][$key]['format'])) { $resultExp = $this->_applyFormat($resultExp, $this->_data['fields'][$key]['format']); } elseif (isset($value['format']) && false !== $value['format']) { $resultExp = $this->_applyFormat($resultExp, $value['format']); } if (isset($value['decorator'])) { $resultExp = $this->_applyFieldDecorator(array('{{result}}'), array($resultExp), $value['decorator']); } $result[$key] = $resultExp; } if (!$result) return array(); $return = array(); foreach ($this->_finalFields as $key => $value) { $class = $this->getInfo("sqlexp,$key,class") ? ' ' . $this->getInfo("sqlexp,$key,class") : ''; $value = (array_key_exists($key, $result)) ? $result[$key] : ''; $newrow = isset($value['newrow']) ? $value['newrow'] : ''; $rowspan = isset($value['rowspan']) ? $value['rowspan'] : ''; $colspan = isset($value['colspan']) ? $value['colspan'] : ''; $return[] = array('class' => $class, 'value' => $value, 'field' => $key, 'newrow' => $newrow, 'rowspan' => $rowspan, 'colspan' => $colspan); } return $return; } /** * Make sure the fields exists on the database, if not remove them from the array * * @param array $fields Array of fields to be validated * * @return void */ protected function _validateFields(array $fields) { $hidden = array(); $show = array(); $titles = array(); foreach ($fields as $key => $value) { if (!isset($value['order']) || $value['order'] == 1) { if (isset($value['orderField'])) { $orderFields[$key] = $value['orderField']; } else { $orderFields[$key] = $key; } } if (isset($value['title'])) { $titles[$key] = $value['title']; } else { $titles[$key] = ucwords(str_replace('_', ' ', $key)); } if (isset($this->_data['fields'][$key]['hidden']) && $this->_data['fields'][$key]['hidden'] == 1) { $hidden[$key] = $key; } else { $show[$key] = $key; } } $fieldsFinal = array(); $lastIndex = 1; $norder = 0; foreach ($show as $key => $value) { $value = $this->_data['fields'][$value]; if (isset($value['position']) && (!isset($value['hidden']) || $value['hidden'] == 0)) { if ($value['position'] === 'last') { $fieldsFinal[($lastIndex + 100)] = $key; } elseif ($value['position'] === 'first') { $fieldsFinal[($lastIndex - 100)] = $key; } else { if ($value['position'] === 'next') { $norder = $lastIndex + 1; } else { $norder = (int) $value['position']; } if (array_key_exists($norder, $fieldsFinal)) { for ($i = count($fieldsFinal); $i >= $norder; $i--) { if (!isset($fieldsFinal[$i])) continue; $fieldsFinal[($i + 1)] = $fieldsFinal[$i]; } $fieldsFinal[$norder] = $key; } $fieldsFinal[$norder] = $key; } } elseif (!isset($value['hidden']) || $value['hidden'] == 0) { while (true) { if (array_key_exists($lastIndex, $fieldsFinal)) { $lastIndex++; } else { break; } } $fieldsFinal[$lastIndex] = $key; } } ksort($fieldsFinal); foreach ($fieldsFinal as $key => $value) { if (strlen($value) == 0) unset($fieldsFinal[$key]); } $fieldsFinal = array_values($fieldsFinal); //Put the hidden fields on the end of the array foreach ($hidden as $value) { $fieldsFinal[] = $value; } $this->_fields = $fieldsFinal; $this->_titles = $titles; $this->_fieldsOrder = $orderFields; } /** * Make sure the filters exists, they are the name from the table field. * If not, remove them from the array * If we get an empty array, we then create a new one with all the fields specified * in $this->_fields method * * @return array */ protected function _validateFilters() { if ($this->getInfo('noFilters') == 1) return false; if (is_array($this->_filters) && count($this->_filters) > 0) return $this->_filters; return array_combine($this->_fields, $this->_fields); } /** * Checks if there are any filters * * @return bool */ public function hasFilters() { if (count(array_intersect_key(array_combine($this->getFields(), $this->getFields()), $this->_ctrlParams)) > 0) return true; return false; } /** * Build user defined filters * * @return Bvb_Grid */ protected function _buildDefaultFiltersValues() { if ($this->_paramsInSession === true) { if ($this->getParam('noFilters')) { $this->_sessionParams->filters = null; } } if ((is_array($this->_defaultFilters) || $this->_paramsInSession === true) && !$this->hasFilters() && !$this->getParam('noFilters') ) { foreach ($this->_data['fields'] as $key => $value) { if (!$this->_displayField($key)) { continue; } if ($this->_paramsInSession === true) { if (isset($this->_sessionParams->filters[$key])) { if (is_array($this->_sessionParams->filters[$key])) { foreach ($this->_sessionParams->filters[$key] as $skey => $svalue) { if (!isset($this->_ctrlParams[$key . $this->getGridId() . '[' . $skey . ']'])) { $this->_ctrlParams[$key . $this->getGridId() . '[' . $skey . ']'] = $svalue; } } } else { if (!isset($this->_ctrlParams[$key . $this->getGridId()])) { $this->_ctrlParams[$key . $this->getGridId()] = $this->_sessionParams->filters[$key]; } } continue; } } if (is_array($this->_defaultFilters) && array_key_exists($key, $this->_defaultFilters)) { $this->_ctrlParams[$key] = $this->_defaultFilters[$key]; } } } return $this; } /** * Deploys * * @return Bvb_Grid */ public function deploy() { if ($this->getSource() === null) { throw new Bvb_Grid_Exception('Please specify your source'); } $this->emitEvent('grid.init_deploy', array()); //Disable ajax for CRUD operations if (!is_null($this->_crud)) { $this->setAjax(false); } $fields = $this->getSource()->buildFields(); $newFields = array_diff(array_keys($fields), array_keys($this->_data['fields'])); foreach ($newFields as $field) { $this->_data['fields'][$field] = $fields[$field]; } //Add columns in queue foreach ($this->_updateColumnQueue as $field => $options) { $this->updateColumn($field, $options); } // apply additional configuration $this->runConfigCallbacks(); if (($this->getParam('detail') && $this->_deployName == 'table' ) || $this->getParam('delete') ) { $this->_isDetail = true; } if ($this->_isDetail === true && is_array($this->_detailColumns)) { if (count($this->_detailColumns) > 0) { $finalColumns = array_intersect($this->_detailColumns, array_keys($this->_data['fields'])); foreach ($this->_data['fields'] as $key => $value) { if (!in_array($key, $finalColumns)) { $this->updateColumn($key, array('remove' => 1)); } } } else { foreach ($this->getHiddenFields() as $field) { $this->updateColumn($field, array('hidden' => false)); } } } if ($this->_isDetail === false && is_array($this->_gridColumns)) { $finalColumns = array_intersect($this->_gridColumns, array_keys($this->_data['fields'])); foreach ($this->_data['fields'] as $key => $value) { if (!in_array($key, $finalColumns)) { $this->updateColumn($key, array('remove' => 1)); } } foreach (array_keys($this->_extraColumns) as $value) { if ($value == 'ZFG_MASS_ACTIONS') continue; if (!in_array($value, $this->_gridColumns)) { unset($this->_extraColumns[$value]); } } } if ($this->_isDetail == true) { $result = $this->getSource()->fetchDetail($this->getIdentifierColumnsFromUrl()); if ($result == false) { $this->_gridSession->message = $this->__('Record Not Found'); $this->_gridSession->_noForm = 1; $this->_gridSession->correct = 1; $this->_redirect($this->getUrl(array('comm', 'detail', 'delete'))); } } if (count($this->getSource()->getSelectOrder()) == 1 && !$this->getParam('order')) { $norder = $this->getSource()->getSelectOrder(); if (!$norder instanceof Zend_Db_Expr) { $this->setParam('order' . $this->getGridId(), $norder[0] . '_' . strtoupper($norder[1])); } } $this->emitEvent('grid.before_filters'); $this->_buildDefaultFiltersValues(); // Validate table fields, make sure they exist... $this->_validateFields($this->_data['fields']); // Filters. Not required that every field as filter. $this->_filters = $this->_validateFilters($this->_filters); $this->_buildFiltersValues(); if ($this->_isDetail == false) { $this->_buildQueryOrderAndLimit(); } if ($this->getParam('noOrder') == 1) { $this->getSource()->resetOrder(); } if ( !$this->_deployNeedsData() ) { $result = array(); $resultCount = 0; } else { $result = $this->getSource()->execute(); $resultCount = $this->getSource()->getTotalRecords(); } $this->_totalRecords = $resultCount; $this->_result = $result; $this->_colspan(); if (count($this->getVisibleFields()) == 0) { throw new Bvb_Grid_Exception('No columns to show'); } if ($this->getParam('_option') == 'autocomplete' && $this->getParam('_gridId') == $this->getGridId(true)) { $field = $this->getParam('field'); if (!$this->getField($field)) { throw new Bvb_Grid_Exception('Field not found'); } $term = $this->getParam('term'); $specialKeys = array('sqlexp', ':empty', ':isnull', 'isnotnull', 'equal', '=', 'rege', 'rlike', '*', '>=', '>', '<>', '!=', '<=', '<', 'in', 'flag', '||', 'range', '&', 'and', 'like'); $specialKey = ''; foreach ($specialKeys as $value) { if (substr($term, 0, strlen($value)) == $value) { $specialKey = substr($term, 0, strlen($value)); $term = substr($term, strlen($value)); break; } } return $this->getSource()->getAutoCompleteForFilter($term, $field, $specialKey); } return $this; } /** * Get details about a column * * @param string $column Column name to be returned * * @return mixed */ protected function _getColumn($column) { return isset($this->_data['fields'][$column]) ? $this->_data['fields'][$column] : null; } /** * Convert Object to Array * * @param object $data Obejct to be converted to array * * @return array */ protected function _object2array($data) { if (!is_object($data) && !is_array($data)) return $data; if (is_object($data)) $data = get_object_vars($data); return array_map(array($this, '_object2array'), $data); } /** * set template locations * * @param string $dir Classes Location * @param string $prefix Classes Prefix * @param string $type Template Type * * @return Bvb_Grid */ public function addTemplateDir($dir, $prefix, $type) { if (!isset($this->_templates[$type])) { $this->_templates[$type] = new Zend_Loader_PluginLoader(); } $this->_templates[$type]->addPrefixPath(trim($prefix, '_'), trim($dir, '/') . '/', $type); return $this; } /** * Define the template to be used * * @param string $template Template Name * @param string $output Outpute type. It's the deploy class name * @param array $options Template Options * * @return void */ public function setTemplate($template, $output = 'table', $options = array()) { $tmp = $options; $options['userDefined'] = $tmp; $class = $this->_templates[$output]->load($template); if (isset($this->_options['template'][$output][$template])) { $tpOptions = array_merge($this->_options['template'][$output][$template], $options); } else { $tpOptions = $options; } $tpInfo = array('colspan' => $this->_colspan, 'charEncoding' => $this->getCharEncoding(), 'name' => $template, 'dir' => $this->_templates[$output]->getClassPath($template), 'class' => $this->_templates[$output]->getClassName($template)); $this->_temp[$output] = new $class(); $this->_temp[$output]->options = array_merge($tpInfo, $tpOptions); return $this->_temp[$output]; } /** * Add multiple columns at once * * @return Bvb_Grid */ public function updateColumns() { $fields = func_get_args(); foreach ($fields as $field) { if (!$field instanceof Bvb_Grid_Column) { throw new Bvb_Grid_Exception('Instance of Bvb_Grid_Column must be provided'); } foreach ($field->getField() as $fieldName => $options) { $this->updateColumn($fieldName, $options); } } return $this; } /** * Calculate colspan for pagination and top * * @return int */ protected function _colspan() { $row = 0; $totalFields[$row] = 0; // add the extra left fields foreach ($this->_extraColumns as $value) { if ($value['position'] != 'left') { continue; } $rows = 1; $cols = 1; if (isset($value['newrow']) && $value['newrow']) { $row++; } if (isset($value['rowspan']) && $value['rowspan'] !== null) { $rows = $value['rowspan']; } if (isset($value['colspan']) && $value['colspan'] !== null) { $cols = $value['colspan']; } if ($cols < 0) { $cols = 1; } // add the appropriate number of columns for the relevant rows for ($a = 0; $a < $rows; $a++) { $totalFields[$row + $a] += $cols; } } // loop through the fields foreach ($this->_fields as $nf) { $rows = 1; $cols = 1; if (isset($this->_data['fields'][$nf])) { // skip certain types of fields $value = $this->_data['fields'][$nf]; if (isset($value['remove']) && $value['remove'] == 1) { continue; } elseif (isset($value['hidden']) && $value['hidden'] == 1 && $this->_removeHiddenFields === true) { continue; } if (isset($value['hRow']) && $value['hRow'] == 1) { continue; } if (isset($value['newrow']) && $value['newrow']) { $row++; } if (isset($value['rowspan']) && $value['rowspan'] !== null) { $rows = $value['rowspan']; } if (isset($value['colspan']) && $value['colspan'] !== null) { $cols = $value['colspan']; } if ($cols < 0) { $cols = 1; } } // add the appropriate number of columns for the relevant rows for ($a = 0; $a < $rows; $a++) { if (!isset($totalFields[$row + $a])) { $totalFields[$row + $a] = 0; } $totalFields[$row + $a] += $cols; } } // add the extra right fields foreach ($this->_extraColumns as $value) { if ($value['position'] != 'right') { continue; } $rows = 1; $cols = 1; if (isset($value['newrow']) && $value['newrow']) { $row++; } if (isset($value['rowspan']) && $value['rowspan'] !== null) { $rows = $value['rowspan']; } if (isset($value['colspan']) && $value['colspan'] !== null) { $cols = $value['colspan']; } if ($cols < 0) { $cols = 1; } // add the appropriate number of columns for the relevant rows for ($a = 0; $a < $rows; $a++) { $totalFields[$row + $a] += $cols; } } if ($this->_allowDelete == 1) { $totalFields[$row]++; } if ($this->_allowEdit == 1) { $totalFields[$row]++; } if (is_array($this->_detailColumns) && $this->_isDetail == false) { $totalFields[$row]++; } if ($this->_massActions->hasMassActions()) { $totalFields[$row]++; } $this->_colspan = max($totalFields); return $this->_colspan; } /** * Returns a field and its options * * @param string $field Field Name * * @return mixed */ public function getField($field) { return isset($this->_data['fields'][$field]) ? $this->_data['fields'][$field] : false; } /** * Return fields list. * Optional param returns also fields options * * @param bool $returnOptions If grid should return all options or only fields names * * @return array */ public function getFields($returnOptions = false) { if (false !== $returnOptions) { return $this->_data['fields']; } return array_keys($this->_data['fields']); } /** * Returns all hidden fields * * @return array */ public function getHiddenFields() { $returnFields = array(); foreach ($this->getFields() as $value) { if (!$this->_displayField($value)) $returnFields[] = $value; } return $returnFields; } /** * Returns all visible fields * * @return array */ public function getVisibleFields() { $returnFields = array(); foreach ($this->getFields() as $value) { if ($this->_displayField($value)) $returnFields[] = $value; } return $returnFields; } /** * Add filters * * @param Bvb_Grid_Filters $filters Filters object to be added to the grid * * @return Bvb_Grid */ public function addFilters(Bvb_Grid_Filters $filters) { $filters = $filters->getFilters(); $this->emitEvent('grid.add_extra_filters', array('filters' => $filters)); foreach ($filters as $key => $value) { if (isset($filters[$key]['callback'])) { $filters[$key]['callback'] = $value['callback']; } if (isset($filters[$key]['transform'])) { $filters[$key]['transform'] = $value['transform']; } } $this->_filters = array_merge($this->_filters, $filters); foreach ($filters as $key => $filter) { if (isset($filter['searchType'])) { $this->updateColumn($key, array('searchType' => $filter['searchType'])); } if (isset($filter['searchTypeFixed'])) { $this->updateColumn($key, array('searchTypeFixed' => $filter['searchTypeFixed'])); } if (isset($filter['search'])) { $this->updateColumn($key, array('search' => $filter['search'])); } } return $this; } /** * Clears existing filters * * @return Bvb_Grid */ public function clearFilters() { $this->_filters = array(); return $this; } /** * Returns current filters * * @return array */ public function getFilters() { return $this->_filters; } /** * Returns a specific filter * * @param string $filter * @return array|false */ public function getFilter($filter) { return isset($this->_filters[$filter]) ? $this->_filters[$filter] : false; } /** * Add filters removing pre-existing ones * * @param Bvb_Grid_Filters $filters * @return Bvb_Grid */ public function setFilters($filters) { $this->_filters = array(); $this->addFilters($filters); return $this; } /** * Add extra columns * * @return Bvb_Grid */ public function addExtraColumns($columns = array()) { static $order = 10; if (is_array($columns)) { $extraColumns = $columns; } else { $extraColumns = func_get_args(); } if (count($extraColumns) == 0) { throw new Bvb_Grid_Exception('No Columns To Add'); } $this->emitEvent('grid.add_extra_columns', array('columns' => $extraColumns)); foreach ($extraColumns as $value) { if (!$value instanceof Bvb_Grid_Extra_Column) { $value = new Bvb_Grid_Extra_Column($value['name'], $value); #throw new Bvb_Grid_Exception($value . ' must be a instance of Bvb_Grid_Extra_Column'); } if (!$value->getOption('name') || !is_string($value->getOption('name'))) { throw new Bvb_Grid_Exception('You need to define the column name'); } if ($value->getOption('title') && !is_string($value->getOption('title'))) { throw new Bvb_Grid_Exception('title option must be a string'); } if (!$value->getOption('position') || !in_array($value->getOption('position'), array('left', 'right'))) { throw new Bvb_Grid_Exception('Please define column position (left|right)'); } if (!$value->getOption('order')) { $order++; $value->setOption('order', $order); } $this->_extraColumns[$value->getOption('name')] = $value->getColumn(); } return $this; } /** * Returns a extra column * * @param string $name * @return mixed */ public function getExtraColumn($name) { return isset($this->_extraColumns[$name]) ? $this->_extraColumns[$name] : null; } /** * Adds a extra colum to the grid * * @param array $column * @return Bvb_Grid */ public function addExtraColumn($column = array()) { $column = array($column); $this->addExtraColumns($column); return $this; } /** * Clears Current Extra Columns * * @return Bvb_Grid */ public function clearExtraColumns() { $this->_extraColumns = array(); return $this; } /** * Adds new extra columns * * @param mixed $columns Columns to add * @return Bvb_Grid */ public function setExtraColumns($columns = array()) { $this->_extraColumns = array(); $this->addExtraColumns($columns); return $this; } /** * Returns current extra columns * * @return array */ public function getExtraColumns() { return $this->_extraColumns; } /** * Returns the grid version * * @return string * @static */ public static function getVersion() { return self::VERSION; } /** * Return number records found * * @return integer */ public function getTotalRecords() { return (int) $this->_totalRecords; } /** * Automates export functionality * * @param string $defaultClass * @param mixed $options * @param mixed $id * @param array|array $classCallbacks key should be lowercase, functions to call once before deploy() or ajax() * @param array|boolean $requestParams request parameters will be used if FALSE * @static * * @return Bvb_Grid */ public static function factory($defaultClass = 'Table', $options = array(), $id = '', $classCallbacks = array(), $requestParams = false) { self::initDeployClass(); if (!is_string($id)) { $id = ''; } try { $defaultClass = self::loadDeployClass($defaultClass); } catch (Zend_Loader_PluginLoader_Exception $e) { // let's try if the class is not loaded already if (!class_exists($defaultClass)) { throw $e; } } if (false === $requestParams) { // use request parameters $requestParams = Zend_Controller_Front::getInstance()->getRequest()->getParams(); } if ($options instanceof Zend_Config) { $options = $options->toArray(); } // use this as request parameters if (!isset($options['grid'])) { $options['grid'] = array('requestParams' => $requestParams); } else { $options['grid']['requestParams'] = $requestParams; } // handle _exportTo parameter compatible with calling with grid id and without if (isset($requestParams['_exportTo' . $id])) { $exportTo = $requestParams['_exportTo' . $id]; } elseif (isset($requestParams['_exportTo'])) { $exportTo = $requestParams['_exportTo']; } else { $exportTo = false; } if (false === $exportTo) { // return instance of the main Bvb object, because this is not and export request $grid = new $defaultClass($options); $lClass = $defaultClass; } else { $lClass = strtolower($exportTo); // support translating of parameters specifig for the export initiator class if (isset($requestParams['_exportFrom'])) { // TODO support translating of parameters specifig for the export initiator class $requestParams = $requestParams; } // now we need to find and load the right Bvb deploy class // TODO support user defined classes $className = self::loadDeployClass($exportTo); if (Zend_Loader_Autoloader::autoload($className)) { $grid = new $className($options); } else { $grid = new $defaultClass($options); $lClass = $defaultClass; } } // add the powerfull configuration callback function if (isset($classCallbacks[$lClass])) { $grid->_configCallbacks = $classCallbacks[$lClass]; } if (is_string($id)) { $grid->setGridId($id); } return $grid; } /** * Runs configuration callbacks * * @return void */ public function runConfigCallbacks() { if (!$this->_runCallbacks) { // makes shure that config callbacks will be used once return; } $this->_runCallbacks = false; if (!is_array($this->_configCallbacks)) { call_user_func($this->_configCallbacks, $this); } elseif (count($this->_configCallbacks) == 0) { // no callback return; } elseif (count($this->_configCallbacks) > 1 && is_array($this->_configCallbacks[0])) { die("multi"); // TODO maybe fix // ordered list of callback functions defined foreach ($this->_configCallbacks as $func) { } } else { // only one callback function defined call_user_func($this->_configCallbacks, $this); } // run it only once $this->_configCallbacks = array(); } /** * Build list of exports with options * * Options: * caption - mandatory * img - (default null) * cssClass - (default ui-icon-extlink) * newWindow - (default true) * url - (default actual url) * onClick - (default null) * _class - (reserved, used internaly) * * @return array */ public function getExports() { $res = array(); foreach ($this->_export as $name => $defs) { if (!is_array($defs)) { // only export name is passed, we need to get default option $name = $defs; $className = 'Bvb_Grid_Deploy_' . ucfirst($name); // TODO support user defined classes if (Zend_Loader_Autoloader::autoload($className) && method_exists($className, 'getExportDefaults')) { // learn the defualt values $defs = call_user_func(array($className, 'getExportDefaults')); } else { // there are no defaults, we need at least some caption $defs = array('caption' => $name); } $defs['_class'] = $className; } $res[$name] = $defs; } return $res; } /** * This is useful if the deploy class has no intention of using hidden fields * * @param bool $value Field Name * * @return Bvb_Grid */ protected function _setRemoveHiddenFields($value) { $this->_removeHiddenFields = (bool) $value; return $this; } /** * Adds more options to the grid * * @param mixed $options Associative array of options to be applied * * @return Bvb_Grid */ public function updateOptions($options) { if ($options instanceof Zend_Config) { $options = $options->toArray(); } else if (!is_array($options)) { throw new Bvb_Grid_Exception('options must be an instance from Zend_Config or an array'); } $this->_options = array_merge($this->_options, $options); return $this; } /** * Return current config options * * @return array */ public function getOptions() { return $this->_options; } /** * Defines options to the grid * * @param array $options Associative array of options to be applied * * @return Bvb_Grid */ public function setOptions(array $options) { $this->_options = $options; return $this; } /** * Add options to the grid * * @param array $options Associative array of options to be applied * * @return Bvb_Grid */ public function addOptions(array $options) { $this->_options = array_merge_recursive($options, $this->_options); return $this; } /** * Apply the options to the fields * * @return void */ protected function _applyOptionsToFields() { if (isset($this->_options['fields']) && is_array($this->_options['fields'])) { foreach ($this->_options['fields'] as $field => $options) { if (isset($options['format']['function'])) { if (!isset($options['format']['params'])) { $options['format']['params'] = array(); } $options['format'] = array($options['format']['function'], $options['format']['params']); } if (isset($options['callback'])) { if (!isset($options['callback']['params'])) { $options['callback']['params'] = array(); } if (isset($options['callback']['function']) && isset($options['callback']['class'])) { $options['callback'] = array('function' => array($options['callback']['class'], $options['callback']['function']), 'params' => $options['callback']['params']); } else { $options['callback'] = array('function' => $options['callback']['function'], 'params' => $options['callback']['params']); } } $this->updateColumn($field, $options); } } if (isset($this->_options['filters']) && is_array($this->_options['filters'])) { $filters = new Bvb_Grid_Filters(); foreach ($this->_options['filters'] as $column => $filter) { if (isset($filter['values']) && is_array($filter['values'])) { $filters->addFilter($column, array('values' => $filter['values'])); } if (isset($filter['distinct']) && is_array($filter['distinct'])) { $filters->addFilter($column, array('distinct' => array('name' => $filter['distinct']['name'], 'field' => $filter['distinct']['field']))); } } $this->addFilters($filters); } $deploy = explode('_', get_class($this)); $name = strtolower(end($deploy)); if (isset($this->_options['deploy'][$name]) && is_array($this->_options['deploy'][$name])) { if (method_exists($this, '_applyConfigOptions')) { $this->_applyConfigOptions($this->_options['deploy'][$name], true); } else { $this->_deploy = $this->_options['deploy'][$name]; } } if (isset($this->_options['template'][$name]) && is_array($this->_options['template'][$name])) { $this->addTemplateParams($this->_options['template'][$name]); } if (isset($this->_options['grid']['formatter'])) { $this->_options['grid']['formatter'] = (array) $this->_options['grid']['formatter']; foreach ($this->_options['grid']['formatter'] as $formatter) { $temp = $formatter; $temp = str_replace('_', '/', $temp); $this->addFormatterDir($temp, $formatter); } } if (isset($this->_options['grid']['eventsPrefix'])) { $this->setEventsPrefix($this->_options['grid']['eventsPrefix']); } if (isset($this->_options['grid']['recordsPerPage'])) { $this->setRecordsPerPage($this->_options['grid']['recordsPerPage']); } if (isset($this->_options['grid']['modRewrite'])) { self::useModRewrite($this->_options['grid']['modRewrite']); } if (isset($this->_options['grid']['paginationInterval'])) { $this->setPaginationInterval($this->_options['grid']['paginationInterval']); } } /** * Sets the grid id, to allow multiples instances per page * * @param string $id Grid to be used in grid * * @return Bvb_Grid */ public function setGridId($id) { $this->_gridId = trim(preg_replace("/[^a-zA-Z0-9_]/", '_', $id), '_'); return $this; } /** * Returns the current id. * * ""=>emty string is a valid value * * @param bool $forceId If we should force an id to be returned in case no one is set * * @return string */ public function getGridId($forceId = false) { if ($forceId === true && strlen($this->_gridId) == 0) { return $this->getRequest()->getActionName() . '_' . $this->getRequest()->getControllerName() . '_' . $this->getRequest()->getModuleName(); } return $this->_gridId; } /** * Set user defined params for templates. * * @param array $options Associative array o options to pass to template * * @return Bvb_Grid */ public function setTemplateParams(array $options) { $this->_templateParams = $options; return $this; } /** * Set user defined params for templates. * * @param string $name Name of the variable * @param mixed $value value of the variable * * @return Bvb_Grid */ public function addTemplateParam($name, $value) { $this->_templateParams[$name] = $value; return $this; } /** * Adds user defined params for templates. * * @param array $options Options to be passed to the template * * @return Bvb_Grid */ public function addTemplateParams(array $options) { $this->_templateParams = array_merge($this->_templateParams, $options); return $this; } /** * Returns template info defined by the user * * @return array */ public function getTemplateParams() { return $this->_templateParams; } /** * Reset options for column * * @param string $column Column which should have options reseted * * @return Bvb_Grid */ public function resetColumn($column) { $support = array(); $support['title'] = isset($this->_data['fields']['title']) ? $this->_data['fields']['title'] : ''; $support['field'] = isset($this->_data['fields']['field']) ? $this->_data['fields']['field'] : ''; $this->_data['fields'][$column] = $support; return $this; } /** * Reset options for several columns * * @param array $columns Columns which should have options reseted * * @return Bvb_Grid */ public function resetColumns(array $columns) { foreach ($columns as $column) { $this->resetColumn($column); } return $this; } /** * Defines which columns will be available to user * * @param array $columns Columns to be showed * * @return Bvb_Grid */ public function setGridColumns(array $columns) { $this->_gridColumns = $columns; return $this; } /** * Adds more columns to be showed * * @param array $columns Columns to be showed * * @return Bvb_Grid */ public function addGridColumns(array $columns) { $this->_gridColumns = array_merge($this->_gridColumns, $columns); return $this; } /** * Defines which columns will be available on detail view * * @param array $columns Columns to be showed * * @return Bvb_Grid */ public function setDetailColumns(array $columns = array()) { $this->_detailColumns = $columns; return $this; } /** * Adds more columns that will be available on detail view * * @param array $columns Array of columns to be showed within detail view * * @return Bvb_Grid */ public function addDetailColumns(array $columns) { $this->_detailColumns = array_merge($this->_detailColumns, $columns); return $this; } /** * Get the list of primary keys from the URL * * @return array */ public function getIdentifierColumnsFromUrl() { $par = ''; if ($this->getParam('edit')) { $par = $this->getParam('edit'); } elseif ($this->getParam('delete')) { $par = $this->getParam('delete'); } elseif ($this->getParam('detail')) { $par = $this->getParam('detail'); } if (strlen($par) == 0) return array(); $par = explode('-', $par); $primaryKeys = $this->getSource()->getIdentifierColumns($this->_data['table']); if (count($par) != count($primaryKeys)) { return array(); } $primaryKeys = array_combine($primaryKeys, $par); return $primaryKeys; } /** * Returns request param without search for grid id * * @param string $param Param Name * @param mixed $default Default value to be returned if param does not exists * * @return mixed */ public function getRequestParamClean($param, $default=null) { $result = $this->getRequest()->getParam($param); return is_null($result) ? $default : $result; } /** * Returns request param for current grid id * * @param string $param Param Name * @param mixed $default Default value to be returned if param does not exists * * @return mixed */ public function getRequestParam($param, $default=null) { $param = $param . $this->getGridId(); $result = $this->getRequest()->getParam($param); return is_null($result) ? $default : $result; } /** * Get a param from the $this->_ctrlParams appending the grid id * * @param string $param Param Name * @param mixed $default Default value to be returned if param does not exists * * @return mixed */ public function getParam($param, $default=null) { if(array_key_exists($param, $this->_externalFilters)) { return isset($this->_ctrlParams[$param]) ? $this->_ctrlParams[$param] : $default; } $param = $param . $this->getGridId(); return isset($this->_ctrlParams[$param]) ? $this->_ctrlParams[$param] : $default; } /** * Returns all params received from Zend_Controller * * @return array */ public function getParams() { return $this->_ctrlParams; } /** * Redirects a user to a give URL and exits * * @param string $url URL to redirect * @param int $code URL Response code * * @return void */ protected function _redirect($url, $code = 302) { $response = $this->getResponse(); $response->setRedirect($url, $code); $response->sendResponse(); die(); } /** * Set a param to be used by controller. * * @param string $param Param Name * @param mixed $value Param value * * @return Bvb_Grid */ public function setParam($param, $value) { $this->_ctrlParams[$param] = $value; return $this; } /** * Remove a param * * @param string $param Param Name * * @return Bvb_Grid */ public function clearParam($param) { unset($this->_ctrlParams[$param]); return $this; } /** * Unsets all params received from controller * * @return Bvb_Grid */ public function clearParams() { $this->_ctrlParams = array(); return $this; } /** * Defines a new set of params * * @param array $params Associative array of params to use * * @return Bvb_Grid */ public function setParams(array $params) { $this->_ctrlParams = $params; return $this; } /** * Add one more para to the grid * * @param string $key Param name * @param string $value Param Value * * @return Bvb_Grid */ public function addParam($key, $value) { $this->_ctrlParams[$key] = $value; return $this; } /** * Use this method to add more params to the grid. * * @param array $params Associative array of params to be added * * @return Bvb_Grid */ public function addParams(array $params) { $this->_ctrlParams = array_merge($this->_ctrlParams, $params); return $this; } /** * Defines which export options are available * Ex: array('word','pdf'); * * @param array $export Array of key/pairs to be available when exporting * * @return Bvb_Grid */ public function setExport(array $export) { $this->_export = $export; return $this; } /** * Adds a new export option * * @param string $name Deploy classe name to be available when exporting * @param array $options Options to be applyied * * @return Bvb_Grid */ public function addExport($name, $options) { $this->_export[$name] = $options; return $this; } /** * Returns the currently setted export methods * * @return array */ public function getExport() { return $this->_export; } /** * Defines SQL expressions * * @param array $exp Array of experessions to be built * * @return Bvb_Grid */ public function setSqlExp(array $exp) { $this->_info['sqlexp'] = $exp; return $this; } /** * Defines the route name to be applied * * @param string $url route name * * @return Bvb_Grid */ public function setRoutename($name) { $this->_routeName = $name; return $this; } /** * Returns the current route name * * @return string */ public function getRouteName() { if (null === $this->_routeName) { $this->_routeName = $this->getController()->getRouter()->getCurrentRouteName(); } return $this->_routeName; } /** * Loads the filter to be rendered * * @param mixed $render Type of render to be used * * @return mixed */ public function loadFilterRender($render) { if (is_array($render)) { $toRender = key($render); } else { $toRender = $render; } $renderExists = $this->_filtersRenders->getPaths(); $renderInfo = 'Bvb_Grid_Render_' . ucfirst($this->_deployName) . '_' . ucfirst($toRender); if (!array_key_exists($renderInfo, $renderExists)) { $this->addFiltersRenderDir('Bvb/Grid/Filters/Render/Table', 'Bvb_Grid_Filters_Render_Table'); } $classname = $this->_filtersRenders->load(ucfirst($toRender)); if (is_array($render)) { $class = new $classname($render[$toRender]); } else { $class = new $classname(); } if (!$class instanceof Bvb_Grid_Filters_Render_RenderInterface) { throw new Bvb_Grid_Exception("$classname must implement Bvb_Grid_Filters_Render_RenderInterface"); } $class->setGridId($this->getGridId()); return $class; } /** * Adds a new dir to check for filters * * @param string $dir Class Name * @param string $prefix Dir where classes are located * * @return Bvb_Grid */ public function addFiltersRenderDir($dir, $prefix) { $this->_filtersRenders->addPrefixPath(trim($prefix, '_'), trim($dir, '/') . '/'); return $this; } /** * Checks if the active request is a export * * @return bool */ public function isExport() { return in_array($this->getParam('_exportTo'), $this->_export); } /** * Returns the select object from source * * @return Bvb_Grid_Source_SourceInterface */ public function getSelect() { return $this->getSource()->getSelectObject(); } /** * Adds extra rows to the grid. * * @param Bvb_Grid_Extra_Rows $rows Rowset of columns to add * * @return Bvb_Grid_Deploy_Table */ public function addExtraRows(Bvb_Grid_Extra_Rows $rows) { $this->emitEvent('grid.add_extra_rows', array('rows' => $rows)); $rows = $this->_object2array($rows); $this->_extraRows = array_merge($this->_extraRows, $rows['_rows']); return $this; } /** * Adds a new external filters * * @param string $fieldId Field id to be used * @param string $callback Callback to be called. Will receive. $id,$value,$select * * @throws Bvb_Grid_Exception * @return Bvb_Grid */ public function addExternalFilter($fieldId, $callback) { if (!is_callable($callback)) { throw new Bvb_Grid_Exception($callback . ' not callable'); } $this->_externalFilters[$fieldId] = $callback; return $this; } /** * Clears all external filters * * @return Bvb_Grid */ public function clearExternalFilters() { $this->_externalFilters = array(); return $this; } /** * Removes a specified filter * * @param string $fieldId Field to be removed * * @return Bvb_Grid */ public function removeExternalFilter($fieldId) { if (isset($this->_externalFilters[$fieldId])) { unset($this->_externalFilters[$fieldId]); } return $this; } /** * Defines if filters should be showned in export * * @param bool $show If we should show filters or not when exporting * * @return Bvb_Grid */ public function setShowFiltersInExport($show) { $this->_showFiltersInExport = $show; return $this; } /** * Whetever to save or not in session filters and order * This is based on gridId, if not provided, action_controller_module * * @param bool $status The status to be setted * * @return Bvb_Grid */ public function saveParamsInSession($status) { $this->_paramsInSession = (bool) $status; return $this; } /** * Defines options for deployment * * @param array $options Asociative array with options for deploy * * @return Bvb_Grid */ public function setDeployOptions(array $options) { foreach ($options as $option => $value) { $this->setDeployOption($option, $value); } return $this; } /** * Defines option for deployment * * @param string $option Options name * @param string $value Option value * * @return Bvb_Grid */ public function setDeployOption($option, $value) { $this->_deploy[$option] = $value; return $this; } /** * Retrieve a value and return $default if there is no element set. * * @param string $name The key name to de fetched * @param mixed $default The value to be returned if key does not exist * * @return mixed */ public function getDeployOption($name, $default = null) { return (array_key_exists($name, $this->_deploy)) ? $this->_deploy[$name] : $default; } /** * Reset Deploy Options * * @return Bvb_Grid */ public function clearDeployOptions() { $this->_deploy = array(); return $this; } /** * retrieve deploy options * * @return array */ public function getDeployOptions() { return $this->_deploy; } /** * Checks if the user has the right to export for the defined format * * @throws Bvb_Grid_Exception * * @return void */ public function checkExportRights() { if (!in_array($this->_deployName, $this->_export) && !array_key_exists($this->_deployName, $this->_export)) { throw new Bvb_Grid_Exception($this->__("You don't have permission to export the results to this format")); } } /** * Build an array based on the given key name (why this never made it to PHP core I'll never know). * * @param array $array Array to be used * @param string $key Key to be * * @see http://www.php.net/manual/en/function.array-map.php#96269 * * @return array */ public function arrayPluck($array, $key = 'value') { if (!is_array($array)) return array(); $result = array(); foreach ($array as $row) { if (array_key_exists($key, $row)) $result[] = $row[$key]; } return $result; } /** * Defines the default grid configuration * * @param Zend_Config|array $options Config Options * * @throws Bvb_Grid_Exception * @static * * @return void */ public static function setDefaultConfig($options) { if ($options instanceof Zend_Config) { $options = $options->toArray(); } elseif (!is_array($options)) { throw new Bvb_Grid_Exception('options must be an instance from Zend_Config or an array'); } self::$_defaultConfig = $options; } /** * Returns the current default configuration * * @return array */ public static function getDefaultConfig() { return self::$_defaultConfig; } /** * Reutrns field alias * * @param string $field Field alias * * @return string */ public function getFieldAlias($field) { foreach ($this->getFields(true) as $alias => $value) { if ($value['field'] == $field) return $alias; } } /** * Returns a url to be used to get a list of possible values from DB. * * @param string $field A valid field from query * * @return string */ public function getAutoCompleteUrlForFilter($field) { if (!$this->getField($field)) { throw new Bvb_Grid_Exception('Field not found'); } return $this->getUrl(array('order')) . '/_gridId/' . $this->getGridId(true) . '/field/' . $field . '/_option/autocomplete'; } /** * Sets dispatcher instance * * @param Bvb_Grid_Event_Dispatcher $dispatcher Dispatcher instance * @return Bvb_Grid */ public function setEventDispatcher(Bvb_Grid_Event_Dispatcher $dispatcher) { $this->_eventDispatcher = $dispatcher; return $this; } /** * Gets dispatcher instance * * @return Bvb_Grid_Event_Dispatcher $dispatcher Dispatcher instance */ public function getEventDispatcher() { return $this->_eventDispatcher; } public function setMassActions(Bvb_Grid_Mass_Actions $actions) { $this->emitEvent('grid.set_mass_actions', array('source' => $actions)); $this->_massActions = $actions; return $this; } /** * Returns Mass Actions instance * * @return Bvb_Grid_Mass_Actions */ public function getMassActions() { return $this->_massActions; } /** * Returns mass actions decorator so the deploy class can build * the extra column * * @return string */ protected function _getMassActionsDecorator() { if ($this->getMassActions()->getDecorator()) return $this->getMassActions()->getDecorator(); $fieldIdentifier = $this->getSource()->getIdentifierColumns($this->_data['table']); if (count($this->getMassActions()->getFields()) == 0 && count($fieldIdentifier) == 0) { throw new Bvb_Grid_Exception('No primary key defined in table. Mass actions not available'); } if (count($this->getMassActions()->getFields()) == 0) { $pk = ''; foreach ($fieldIdentifier as $value) { $aux = explode('.', $value); $pk .= end($aux) . '-'; } $pk = rtrim($pk, $this->getMassActions()->getMultipleFieldsSeparator()); $pk = explode($this->getMassActions()->getMultipleFieldsSeparator(), $pk); } $pk = "{{" . implode('}}' . $this->getMassActions()->getMultipleFieldsSeparator() . '{{', $pk) . "}}"; $this->getMassActions()->setDecorator($pk); return $this->getMassActions()->getDecorator(); } /** * Sets response object * * @param Zend_Controller_Response_Abstract $response * @return Bvb_Grid */ public function setResponse(Zend_Controller_Response_Abstract $response) { $this->_response = $response; return $this; } /** * Returns reponse instance * * @return Zend_Controller_Response_Abstract */ public function getResponse() { if (!isset($this->_response)) { $this->_response = Zend_Controller_Front::getInstance()->getResponse(); } return $this->_response; } /** * Regists a new observer * * @param string $event Event name * @param int $event Priority Number * @param calable $callback Callback to be called * * @return Bvb_Grid */ public function listenEvent($event, $callback, $priority = 10) { Bvb_Grid_Event_Dispatcher::getInstance()->connect($event, $callback, $priority); return $this; } /** * If we should use mod_write to create URL's * * @param bool $modRewrite * @return Bvb_Grid */ public static function useModRewrite($modRewrite) { self::$_modRewrite = (bool) $modRewrite; } /** * Get usage of mod_rewrite * * @return bool */ public static function getUseModRewrite() { return self::$_modRewrite; } /** * Defines the prefix to be used in all events * * @param string $prefix * @return Bvb_Grid */ public function setEventsPrefix($prefix) { $this->_eventsPrefix = (string) $prefix; return $this; } /** * Returns current event prefix. * * @return string/null */ public function getEventsPrefix() { return $this->_eventsPrefix; } /** * Proxy for emiting events * * @param string $name * @param array $params * @param object $subject */ public function emitEvent($name, $params = array(), $subject=null) { if ($subject === null) { $subject = $this; } $event = new Bvb_Grid_Event($name, $subject, $params); $this->_eventDispatcher->emit($event); if ($this->getEventsPrefix()) { $event = new Bvb_Grid_Event($this->getEventsPrefix() . $name, $subject, $params); $this->_eventDispatcher->emit($event); } } /** * Adds the default deploy classes dir location */ public static function initDeployClass() { self::$_deployClassesDir = new Zend_Loader_PluginLoader(); self::$_deployClassesDir->addPrefixPath('Bvb_Grid_Deploy', 'Bvb/Grid/Deploy/'); } /** * Adds a new deploy class dir to be loaded * * @param string $dir * @param string $prefix */ public static function addDeployPrefixPath($dir, $prefix) { self::$_deployClassesDir->addPrefixPath(trim($prefix, '_'), trim($dir, '/') . '/'); } /** * Loads a deploy class * * @param string $class * * @return Bvb_Grid */ public static function loadDeployClass($class) { return self::$_deployClassesDir->load($class); } /** * Returns current deploy paths * * @return mixed */ public static function getDeployPrefixPaths() { return self::$_deployClassesDir; } /** * In some cases we don't need to execute costly query on data source on grid deployment * * @return bool */ protected function _deployNeedsData() { return true; } }