* @license PHP License * @package WB * @subpackage db */ WBClass::load('WBDatasource_SQLCommon' , 'WBDatasource_TableMicroCache' ); /** * Datasource views * * @version 0.4.0 * @package WB * @subpackage db */ class WBDatasource_View extends WBDatasource_SQLCommon { /** * database connection * @var WBDatasource_SQL */ protected $db; /** * microcache * @var WBDatasource_TableMicroCache */ private $mc; /** * datasource item renderer * @var WBDatasource_Renderer */ private $renderer; /** * datasource decorators * @var array WBDatasource_Decorator */ private $decorators = array(); /** * datasource filler * @var array WBDatasource_Filler */ private $filler = array(); /** * configuration loader * @var WBConfig */ protected $config; /** * Fill placeholders with vars * @var array */ protected $vars = array(); /** * List of translatable columns * @var array */ private $translatable = array(); /** * List of foreign tables * @var array */ private $foreign = array(); /** * constructor * * The parameter will be loaded from config.xml, section "db", you may * overwrite the list of tables. This will cause to load the table list * from a different file. * * Parameter: * - "tablelist": config file which contains the list of known tables. * * This class utilizes @link WBDatasource_SQL this implies that you may * use all constructor parameter of this class, as well. * * @param array $parameter */ public function __construct($parameter = array()) { parent::__construct($parameter); $this->db = WBClass::create('WBDatasource_SQL', $parameter); $this->loadTables($parameter); $this->config = WBClass::create('WBConfig'); $this->mc = WBClass::create('WBDatasource_TableMicroCache', array('name' => $this->db->getId())); } /** * Set variables to use in datasource * * @param array $vars */ public function addVars($vars) { $this->vars = array_merge($this->vars, $vars); } /** * set renderer object * * @param $renderer */ public function setRenderer(WBDatasource_Renderer $renderer) { $this->renderer = $renderer; } /** * render datasource * * @param string $ds datasource name */ public function render($datasource) { if (!$this->renderer) { WBClass::load('WBException_Call'); throw new WBException_Call('Call setRenderer() first', 1, __CLASS__); } $this->config->load('datasource/' . $datasource); $this->renderer->start($datasource); // get foreign keys to fill preload $this->foreign = array(); $tables = $this->config->get('view/foreign', array()); foreach ($tables as $t) { $this->foreign[$t] = $this->getIdentifier($t); } $query = $this->config->get('view/select'); if (!is_array($query)) { $query = array($query); } $this->translatable = $this->config->get('view/translatable', array()); $this->startFiller(); $this->startDecorators(); // prefill placeholders $ph = $this->config->get('view/placeholder', array()); if (!is_array($ph)) { $ph = array(); } $this->vars = array_merge($ph, $this->vars); $this->query($query, $this->config->get('view/selectmode', 'last')); $this->flushDecorators(); $this->flushFillers(); $this->renderer->end(); } private function query($query, $mode = 'last') { switch ($mode) { case 'collect': $this->selectCollect($query); break; case 'last': default: $this->selectLast($query); break; } } private function selectCollect($query) { $count = 0; foreach ($query as $i => $q) { $q = $this->fillPlaceholders($q); $res = $this->db->query($q); $item = $this->fetch($res); if (0 == $i) { $this->itemOnStart($item); } while ($item) { ++$count; $this->itemOnEach($item); $item = $this->fetch($res); } } $this->addEmptyItems($count); $this->itemOnEnd($item); } private function selectLast($query) { foreach ($query as $q) { $q = $this->fillPlaceholders($q); $res = $this->db->query($q); } $item = $this->fetch($res); $this->itemOnStart($item); $count = 0; while ($item) { ++$count; $this->itemOnEach($item); $item = $this->fetch($res); } $this->addEmptyItems($count); $this->itemOnEnd($item); } private function addEmptyItems($count) { if (0 <= $count) { return; } $empty = $this->config->get('view/empty', array()); if (empty($empty)) { return; } foreach($empty as $item) { $this->itemOnEach($item); } } private function itemOnStart($item) { foreach ($this->filler as $filler) { $filler->onStart($item); } foreach ($this->decorators as $dec) { $dec->onStart($item); } } private function itemOnEach($item) { // remember id for preloading foreach ($this->foreign as $table => $key) { $this->mc->setTable($table)->addPreload($item[$key]); } $list = array(); if (empty($this->filler)) { $list = array($item); } else { foreach ($this->filler as $filler) { if (!$filler->mergeItem2List($item, $list)) { break; } } } foreach ($list as $l) { foreach ($this->decorators as $dec) { $dec->decorate($l); } $this->renderer->renderItem($l); } } private function itemOnEnd($item) { foreach ($this->filler as $filler) { $filler->onEnd($item); } foreach ($this->decorators as $dec) { $dec->onEnd($item); } } /** * Fetch data from result set * * @param resource * @return array */ private function fetch($res) { $row = $res->fetch_assoc(); if (empty($row)) { return $row; } if (empty($this->translatable)) { return $row; } foreach ($this->translatable as $t) { $row[$t] = patI18n::gettext($row[$t]); } return $row; } /** * start all configured fillers * * Load fillers and config from datasource config. Instantiate all fillers * configure them and keep them in list * * @see $filler */ private function startFiller() { $config = $this->config->get('view/filler', array()); if (empty($config)) { return; } foreach ($config as $c) { $filler = WBClass::create('WBDatasource_Filler_' . $c['filler']); if (!isset($c['params'])) { $c['params'] = array(); } $filler->configure($c['params']); $this->filler[] = $filler; } } /** * Remove All Fillser * * Free all fillers. This allows to restart for next datasource */ private function flushFillers() { $this->filler = array(); } /** * Start All Configured Decorator * * Load decorators and config from datasource config. Instantiate all decorators, * configure them and keep them in list * * @see $decorators */ private function startDecorators() { $config = $this->config->get('view/decorator', array()); if (empty($config)) { return; } foreach ($config as $c) { $dec = WBClass::create('WBDatasource_Decorator_' . $c['decorator']); if (!isset($c['params'])) { $c['params'] = array(); } $dec->configure($c['params']); $this->decorators[] = $dec; } } /** * Remove All Decorators * * Free all decorators. This allows to restart for next datasource */ private function flushDecorators() { $this->decorators = array(); } /** * Fill placeholders in query * * Use $vars to replace placeholders * * @param string $query * @return string */ private function fillPlaceholders($query) { if (empty($this->vars)) { return $query; } $search = array(); $replace = array(); foreach ($this->vars as $k => $v) { if (is_array($v)) { $v = array_map(array($this->db, 'quote'), $v); $v = implode(', ', $v); } else { $v = $this->db->quote($v); } $search[] = '{' . strtoupper($k) . '}'; $replace[] = $v; } return str_replace($search, $replace, $query); } }