123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752 |
- <?php
- /**
- *
- * Cube Framework $Id$ u7R4F2jbFhaZUUCQKCw5dP2tKElL+a2HCx31dKAxQV4=
- *
- * @link http://codecu.be/framework
- * @copyright Copyright (c) 2017 CodeCube SRL
- * @license http://codecu.be/framework/license Commercial License
- *
- * @version 1.10 [rev.1.10.01]
- */
- namespace Cube\Db\Table;
- use Cube\Db,
- Cube\Db\Select,
- Cube\Db\Adapter\AbstractAdapter,
- Cube\Controller\Front,
- Cube\Cache\Adapter\AbstractAdapter as CacheAdapter;
- abstract class AbstractTable
- {
- /**
- * class constants
- */
- const NAME = 'name';
- const COLS = 'cols';
- const PRIMARY = 'primary';
- const METADATA = 'metadata';
- const REFERENCE_MAP = 'referenceMap';
- const DEPENDENT_TABLES = 'dependentTables';
- const COLUMNS = 'columns';
- const REF_TABLE_CLASS = 'refTableClass';
- const REF_COLUMNS = 'refColumns';
- const QUERIES_CACHE_EXPIRES = 300; // 5 minutes
- /**
- *
- * table name
- *
- * @var string
- */
- protected $_name;
- /**
- *
- * table prefix
- * (set in configuration)
- *
- * @var string
- */
- protected $_prefix;
- /**
- *
- * database adapter
- *
- * @var \Cube\Db\Adapter\AbstractAdapter
- */
- protected $_adapter;
- /**
- * table column names derived from
- * \Cube\Db\Adapter\AbstractAdapter::describeTable()
- *
- * @var array
- */
- protected $_cols = null;
- /**
- *
- * primary key column(s)
- * A compound key should be declared as an array
- *
- * @var string|array
- */
- protected $_primary = null;
- /**
- *
- * information provided by the adapter's describeTable() method
- *
- * @var array
- */
- protected $_metadata = array();
- /**
- *
- * class name for row
- *
- * @var string
- */
- protected $_rowClass = '\Cube\Db\Table\Row';
- /**
- *
- * class name for rowset
- *
- * @var string
- */
- protected $_rowsetClass = '\Cube\Db\Table\Rowset';
- /**
- * Associative array map of declarative referential integrity rules.
- * This array has one entry per foreign key in the current table.
- * Each key is a mnemonic name for one reference rule.
- *
- * Each value is also an associative array, with the following keys:
- * - columns = array of names of column(s) in the child table.
- * - refTableClass = class name of the parent table.
- * - refColumns = array of names of column(s) in the parent table,
- * in the same order as those in the 'columns' entry.
- * - onDelete = "cascade" means that a delete in the parent table also
- * causes a delete of referencing rows in the child table.
- * - onUpdate = "cascade" means that an update of primary key values in
- * the parent table also causes an update of referencing
- * rows in the child table.
- *
- * @var array
- */
- protected $_referenceMap = array();
- /**
- * Simple array of class names of tables that are "children" of the current
- * table, in other words tables that contain a foreign key to this one.
- * Array elements are not table names; they are class names of classes that
- * extend Zend_Db_Table_Abstract.
- *
- * @var array
- */
- protected $_dependentTables = array();
- /**
- *
- * flag that sets if table queries are cacheable
- * used to determine if queries cache is to be purged on a create/update/delete operation
- *
- * @var bool
- */
- protected $_cacheableQueries = false;
- /**
- *
- * cache object
- *
- * @var \Cube\Cache|false false if caching is disabled
- */
- protected $_cache = false;
- /**
- *
- * class constructor
- *
- * @param \Cube\Db\Adapter\AbstractAdapter $adapter
- *
- * @throws \RuntimeException
- */
- public function __construct(AbstractAdapter $adapter = null)
- {
- $bootstrap = Front::getInstance()->getBootstrap();
- $this->_cache = $bootstrap->getResource('cache');
- if ($adapter === null) {
- $adapter = $bootstrap->getResource('db');
- }
- if (!$adapter instanceof AbstractAdapter) {
- throw new \RuntimeException("Could not create table.
- The database adapter must be an instance of \Cube\Db\Adapter\AbstractAdapter");
- }
- if (empty($this->_name)) {
- $this->_name = strtolower(get_class());
- }
- $adapterConfig = $adapter->getConfig();
- if (isset($adapterConfig['prefix'])) {
- $this->_prefix = $adapterConfig['prefix'];
- }
- $this->setAdapter($adapter);
- }
- /**
- *
- * get table name
- *
- * @return string
- */
- public function getName()
- {
- return $this->_name;
- }
- /**
- *
- * get table prefix
- *
- * @return string
- */
- public function getPrefix()
- {
- return $this->_prefix;
- }
- /**
- *
- * set table prefix
- *
- * @param string $prefix
- *
- * @return $this
- */
- public function setPrefix($prefix = null)
- {
- $this->_prefix = $prefix;
- return $this;
- }
- /**
- *
- * get database adapter
- *
- * @return \Cube\Db\Adapter\AbstractAdapter
- */
- public function getAdapter()
- {
- return $this->_adapter;
- }
- /**
- *
- * set database adapter
- *
- * @param \Cube\Db\Adapter\AbstractAdapter $adapter
- *
- * @return $this
- * @throws \RuntimeException
- */
- public function setAdapter($adapter)
- {
- if (!$adapter instanceof AbstractAdapter) {
- throw new \RuntimeException("Could not create table.
- The database adapter must be an instance of \Cube\Db\Adapter\AbstractAdapter");
- }
- $this->_adapter = $adapter;
- return $this;
- }
- /**
- *
- * get row object class
- *
- * @return string
- */
- public function getRowClass()
- {
- return $this->_rowClass;
- }
- /**
- *
- * set row object class
- *
- * @param string $rowClass
- *
- * @return $this
- */
- public function setRowClass($rowClass)
- {
- $this->_rowClass = (string)$rowClass;
- return $this;
- }
- /**
- *
- * get rowset object class
- *
- * @return string
- */
- public function getRowsetClass()
- {
- return $this->_rowsetClass;
- }
- /**
- *
- * set rowset object class
- *
- * @param string $rowsetClass
- *
- * @return $this
- */
- public function setRowsetClass($rowsetClass)
- {
- $this->_rowsetClass = (string)$rowsetClass;
- return $this;
- }
- /**
- *
- * get the reference between the table and a requested table
- *
- * @param string $refTableClass
- * @param string $ruleKey
- *
- * @return array
- * @throws \RuntimeException
- */
- public function getReference($refTableClass, $ruleKey = null)
- {
- if ($ruleKey !== null) {
- if (!isset($this->_referenceMap[$ruleKey])) {
- throw new \RuntimeException(
- sprintf("A reference rule with the name '%s' does not exist in the definition of '%s'.", $ruleKey,
- get_class($this)));
- }
- if ($this->_referenceMap[$ruleKey][self::REF_TABLE_CLASS] != $refTableClass) {
- throw new \RuntimeException(
- sprintf("The reference rule '%s' does not reference the table '%s'.", $ruleKey, $refTableClass));
- }
- return $this->_referenceMap[$ruleKey];
- }
- foreach ($this->_referenceMap as $reference) {
- if ($reference[self::REF_TABLE_CLASS] == $refTableClass) {
- return $reference;
- }
- }
- throw new \RuntimeException(
- sprintf("There is no reference from table '%s' to table '%s'.", get_class($this), $refTableClass));
- }
- /**
- *
- * add a reference to the reference map of the table
- *
- * @param string $ruleKey
- * @param mixed $columns
- * @param string $refTableClass
- * @param mixed $refColumns
- *
- * @return $this
- */
- public function setReference($ruleKey, $columns, $refTableClass, $refColumns)
- {
- $reference = array(self::COLUMNS => (array)$columns,
- self::REF_TABLE_CLASS => $refTableClass,
- self::REF_COLUMNS => (array)$refColumns);
- $this->_referenceMap[$ruleKey] = $reference;
- return $this;
- }
- /**
- *
- * set the reference map of the table
- *
- * @param array $referenceMap
- *
- * @return $this
- */
- public function setReferenceMap(array $referenceMap)
- {
- $this->_referenceMap = $referenceMap;
- return $this;
- }
- /**
- *
- * get dependent tables
- *
- * @return array
- */
- public function getDependentTables()
- {
- return $this->_dependentTables;
- }
- /**
- *
- * set dependent tables
- *
- * @param array $dependentTables
- *
- * @return $this
- */
- public function setDependentTables(array $dependentTables)
- {
- $this->_dependentTables = $dependentTables;
- return $this;
- }
- /**
- *
- * create an instance of the select object
- *
- * @param array|string|\Cube\Db\Expr $cols The columns to select from this table.
- *
- * @return \Cube\Db\Select
- */
- public function select($cols = '*')
- {
- $select = new Select($this->_adapter);
- $select->setPrefix($this->getPrefix())
- ->from($this->_name, $cols);
- return $select;
- }
- /**
- *
- * Inserts a table row with specified data.
- *
- * @param array $data Column-value pairs.
- *
- * @return int the id of the inserted column.
- */
- public function insert(array $data)
- {
- $this->_purgeQueriesCache();
- $this->_adapter->insert($this->_prefix . $this->_name, $data);
- return $this->lastInsertId();
- }
- /**
- *
- * Updates table rows with specified data based on a WHERE clause.
- *
- * @param array $data Column-value pairs.
- * @param mixed $where UPDATE WHERE clause(s).
- *
- * @return int The number of affected rows.
- */
- public function update(array $data, $where)
- {
- $result = $this->_adapter->update($this->_prefix . $this->_name, $data, $where);
- if ($result > 0) {
- $this->_purgeQueriesCache();
- }
- return $result;
- }
- /**
- *
- * delete table rows based on a WHERE clause.
- *
- * @param mixed $where DELETE WHERE clause(s).
- *
- * @return int The number of affected rows.
- */
- public function delete($where)
- {
- $result = $this->_adapter->delete($this->_prefix . $this->_name, $where);
- if ($result > 0) {
- $this->_purgeQueriesCache();
- }
- return $result;
- }
- /**
- *
- * fetches all matched rows
- *
- * @param string|\Cube\Db\Select $where SQL where clause, or a select object
- * @param string|array $order
- * @param int $count
- * @param int $offset
- * @param string|array $cache
- *
- * @return \Cube\Db\Table\Rowset\AbstractRowset
- */
- public function fetchAll($where = null, $order = null, $count = null, $offset = null, $cache = null)
- {
- if (!$where instanceof Select) {
- $select = $this->select()
- ->where($where)
- ->order($order)
- ->limit($count, $offset);
- }
- else {
- $select = $where;
- }
- $cachedData = false;
- $cacheFile = null;
- $rows = null;
- // cacheQueries: if requested in method, if enabled for the table and if enabled globally
- $cacheQueries = ($cache !== null) ? $this->_getCache('cacheQueries') : false;
- if ($cacheQueries !== false) {
- $cacheFile = md5($select->assemble());
- if (($data = $this->_cache->read($cacheFile, CacheAdapter::QUERIES)) !== false) {
- $cacheCol = (is_array($cache)) ? $cache[CacheAdapter::CACHE_COL] : $cache;
- $data = empty($data) ? array(0) : $data;
- $select->reset(Select::WHERE)
- ->where("{$cacheCol} IN (?)", $data);
- $cachedData = true;
- }
- }
- $stmt = $this->_adapter->query($select);
- $rows = $stmt->fetchAll(Db::FETCH_ASSOC);
- if ($cachedData === false) {
- if ($cacheQueries !== false) {
- $data = array();
- $cacheWhere = (is_array($cache)) ? $cache[CacheAdapter::CACHE_WHERE] : $cache;
- foreach ($rows as $row) {
- $data[] = $row[$cacheWhere];
- }
- $this->_cache->write($cacheFile, CacheAdapter::QUERIES, $data, self::QUERIES_CACHE_EXPIRES);
- }
- }
- $data = array(
- 'table' => $this,
- 'data' => $rows,
- );
- return new $this->_rowsetClass($data);
- }
- /**
- *
- * fetch a single matched row from a result set
- *
- * @param string|\Cube\Db\Select $where SQL where clause, or a select object
- * @param string|array $order
- * @param int $offset
- * @param string $cacheId
- *
- * @return \Cube\Db\Table\Row\AbstractRow|null
- */
- public function fetchRow($where = null, $order = null, $offset = null, $cacheId = null)
- {
- return $this->fetchAll($where, $order, 1, $offset, $cacheId)->getRow(0);
- }
- /**
- *
- * get the id resulted from an insert operation
- *
- * @return int
- */
- public function lastInsertId()
- {
- return $this->_adapter->lastInsertId();
- }
- /**
- *
- * returns table information
- *
- * @param string $key specific info part to return
- *
- * @throws \InvalidArgumentException
- * @return mixed
- */
- public function info($key = null)
- {
- $this->_getPrimary();
- $info = array(
- self::NAME => $this->_name,
- self::COLS => $this->_getCols(),
- self::PRIMARY => (array)$this->_primary,
- self::METADATA => $this->_metadata,
- self::REFERENCE_MAP => $this->_referenceMap,
- self::DEPENDENT_TABLES => $this->_dependentTables,
- );
- if ($key === null) {
- return $info;
- }
- if (!array_key_exists($key, $info)) {
- throw new \InvalidArgumentException(
- sprintf("There is no table information for the key '%s'.", $key));
- }
- return $info[$key];
- }
- /**
- *
- * get table metadata
- * use cache if caching is enabled
- *
- * @return array
- */
- protected function _getMetadata()
- {
- if (!count($this->_metadata)) {
- $cachedData = false;
- $cacheMetadata = $this->_getCache('cacheMetadata');
- $cacheFile = null;
- if ($cacheMetadata !== false) {
- $cacheFile = md5("DESCRIBE " . $this->_prefix . $this->_name);
- }
- if ($cacheMetadata !== false) {
- if (($data = $this->_cache->read($cacheFile, CacheAdapter::METADATA)) !== false) {
- $this->_metadata = $data;
- $cachedData = true;
- }
- }
- if ($cachedData === false) {
- $this->_metadata = $this->_adapter->describeTable($this->_prefix . $this->_name);
- if ($cacheMetadata !== false) {
- $this->_cache->write($cacheFile, CacheAdapter::METADATA, $this->_metadata);
- }
- }
- }
- return $this->_metadata;
- }
- /**
- *
- * get cache
- *
- * @param string $key
- *
- * @return mixed
- */
- protected function _getCache($key = null)
- {
- if ($this->_cache !== false) {
- if ($key === null) {
- return $this->_cache;
- }
- else {
- $methodName = 'get' . ucfirst($key);
- if (method_exists($this->_cache, $methodName)) {
- if ($key != 'cacheQueries' || $this->_cacheableQueries) {
- return $this->_cache->$methodName();
- }
- }
- }
- }
- return false;
- }
- /**
- *
- * purge queries cache
- *
- * @return $this
- */
- protected function _purgeQueriesCache()
- {
- $cacheQueries = $this->_getCache('cacheQueries');
- if ($cacheQueries !== false) {
- $this->_cache->getAdapter()->purge(CacheAdapter::QUERIES, true);
- }
- return $this;
- }
- /**
- *
- * get table columns
- *
- * @return array
- */
- protected function _getCols()
- {
- if ($this->_cols === null) {
- $this->_cols = array_keys(
- $this->_getMetadata());
- }
- return $this->_cols;
- }
- /**
- *
- * get primary key(s)
- *
- * @throws \RuntimeException
- * @return array
- */
- protected function _getPrimary()
- {
- if (!$this->_primary) {
- $this->_getMetadata();
- $this->_primary = array();
- foreach ($this->_metadata as $col) {
- if ($col['PRIMARY']) {
- $this->_primary[$col['PRIMARY_POSITION']] = $col['COLUMN_NAME'];
- }
- }
- }
- else if (!is_array($this->_primary)) {
- $this->_primary = array(1 => $this->_primary);
- }
- else if (isset($this->_primary[0])) {
- array_unshift($this->_primary, null);
- unset($this->_primary[0]);
- }
- $cols = $this->_getCols();
- if (!array_intersect((array)$this->_primary, $cols) == (array)$this->_primary) {
- throw new \RuntimeException(
- sprintf("Invalid primary key column(s): %s.", implode(',', (array)$this->_primary)));
- }
- return $this->_primary;
- }
- }
|