3 namespace Org\Mxchange\CoreFramework\Database\Frontend;
5 // Import framework stuff
6 use Org\Mxchange\CoreFramework\Bootstrap\FrameworkBootstrap;
7 use Org\Mxchange\CoreFramework\Criteria\Criteria;
8 use Org\Mxchange\CoreFramework\Criteria\Storing\StoreableCriteria;
9 use Org\Mxchange\CoreFramework\Database\Backend\BaseDatabaseBackend;
10 use Org\Mxchange\CoreFramework\Factory\Object\ObjectFactory;
11 use Org\Mxchange\CoreFramework\Object\BaseFrameworkSystem;
12 use Org\Mxchange\CoreFramework\Result\Database\BaseDatabaseResult;
13 use Org\Mxchange\CoreFramework\Traits\Cache\CacheableTrait;
16 * A generic database frontend
18 * @author Roland Haeder <webmaster@shipsimu.org>
20 * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2022 Core Developer Team
21 * @license GNU GPL 3.0 or any newer version
22 * @link http://www.shipsimu.org
24 * This program is free software: you can redistribute it and/or modify
25 * it under the terms of the GNU General Public License as published by
26 * the Free Software Foundation, either version 3 of the License, or
27 * (at your option) any later version.
29 * This program is distributed in the hope that it will be useful,
30 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32 * GNU General Public License for more details.
34 * You should have received a copy of the GNU General Public License
35 * along with this program. If not, see <http://www.gnu.org/licenses/>.
37 abstract class BaseDatabaseFrontend extends BaseFrameworkSystem {
42 * Current table name to use
44 private $tableName = 'unknown';
47 * "Cached" value 'database_cache_enabled' from configuration
49 private $databaseCacheEnabled = false;
51 * Protected constructor
53 * @param $className Name of the class
56 protected function __construct (string $className) {
57 // Call parent constructor
58 parent::__construct($className);
60 // Initialize the cache instance
61 $this->initCacheInstance();
65 * Initializes the cache instance with a new object
69 private final function initCacheInstance () {
70 // Set "cache" attributes
71 $this->databaseCacheEnabled = FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('database_cache_enabled');
73 // Is the cache enabled?
74 if ($this->databaseCacheEnabled === true) {
75 // Set the new instance
76 $this->setCacheInstance(ObjectFactory::createObjectByConfiguredName('cache_class'));
81 * Setter for table name
83 * @param $tableName Name of table name to set
86 protected final function setTableName (string $tableName) {
87 $this->tableName = $tableName;
91 * Getter for table name
93 * @return $tableName Name of table name to set
95 protected final function getTableName () {
96 return $this->tableName;
100 * Gets a cache key from Criteria instance
102 * @param $criteriaInstance An instance of a Criteria class
103 * @param $onlyKeys Only use these keys for a cache key
104 * @return $cacheKey A cache key suitable for lookup/storage purposes
106 protected function getCacheKeyByCriteria (Criteria $criteriaInstance, array $onlyKeys = []) {
108 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-FRAMEWORK-SYSTEM: criteriaInstance=' . $criteriaInstance->__toString() . ',onlyKeys()=' . count($onlyKeys) . ' - CALLED!');
109 $cacheKey = sprintf('%s@%s',
111 $criteriaInstance->getCacheKey($onlyKeys)
115 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-FRAMEWORK-SYSTEM: cacheKey=' . $cacheKey . ' - EXIT!');
120 * 'Inserts' a data set instance into a local file database folder
122 * @param $dataSetInstance A storeable data set
123 * @param $onlyKeys Only use these keys for a cache key
126 protected function queryInsertDataSet (StoreableCriteria $dataSetInstance, array $onlyKeys = []) {
127 // Default cache key is NULL
128 //* DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FRONTEND: dataSetInstance=%s,onlyKeys()=%d - CALLED!', $dataSetInstance->__toString(), count($onlyKeys)));
132 if ($this->databaseCacheEnabled === true) {
133 // First get a key suitable for our cache and extend it with this class name
134 $cacheKey = $this->getCacheKeyByCriteria($dataSetInstance, $onlyKeys);
135 //* DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-FRONTEND: Using cache key ' . $cacheKey . ' for purging ...');
138 // Does this key exists in cache?
139 //* DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FRONTEND: cacheKey[%s]=%s', gettype($cacheKey), $cacheKey));
140 if (($this->databaseCacheEnabled === true) && ($this->getCacheInstance()->offsetExists($cacheKey))) {
142 //* DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FRONTEND: Calling this->cacheInstance->purgeOffset(%s) ...', $cacheKey));
143 $this->getCacheInstance()->purgeOffset($cacheKey);
146 // Handle it over to the middleware
147 FrameworkBootstrap::getDatabaseInstance()->queryInsertDataSet($dataSetInstance);
150 //* DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-FRONTEND: EXIT!');
154 * 'Updates' a data set instance with a database layer
156 * @param $dataSetInstance A storeable data set
157 * @param $onlyKeys Only use these keys for a cache key
160 protected function queryUpdateDataSet (StoreableCriteria $dataSetInstance, array $onlyKeys = []) {
165 if ($this->databaseCacheEnabled === true) {
166 // First get a key suitable for our cache and extend it with this class name
167 $cacheKey = $this->getCacheKeyByCriteria($dataSetInstance, $onlyKeys);
168 //* DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-FRONTEND: Using cache key ' . $cacheKey . ' for purging ...');
171 // Does this key exists in cache?
172 if (($this->databaseCacheEnabled === true) && ($this->getCacheInstance()->offsetExists($cacheKey))) {
174 $this->getCacheInstance()->purgeOffset($cacheKey);
177 // Handle it over to the middleware
178 FrameworkBootstrap::getDatabaseInstance()->queryUpdateDataSet($dataSetInstance);
182 * Getter for index key
184 * @return $indexKey Index key
186 public final function getIndexKey () {
187 return FrameworkBootstrap::getDatabaseInstance()->getIndexKey();
191 * Getter for last exception
193 * @return $lastException Last exception or NULL if none occured
195 public final function getLastException () {
196 return FrameworkBootstrap::getDatabaseInstance()->getLastException();
200 * Do a "select" query on the current table with the given search criteria and
201 * store it in cache for later usage
203 * @param $criteriaInstance An instance of a Criteria class
204 * @param $onlyKeys Only use these keys for a cache key
205 * @return $resultInstance An instance of a database result class
207 public function doSelectByCriteria (Criteria $criteriaInstance, array $onlyKeys = []) {
208 // Default cache key if cache is not enabled
209 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FRONTEND: criteriaInstance=%s,onlyKeys()=%d - CALLED!', $criteriaInstance->__toString(), count($onlyKeys)));
213 // Is the cache enabled?
214 if ($this->databaseCacheEnabled === true) {
215 // First get a key suitable for our cache and extend it with this class name
216 $cacheKey = $this->getCacheKeyByCriteria($criteriaInstance, $onlyKeys);
219 // Does this key exists in cache?
220 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FRONTEND: cacheKey[%s]=%s', gettype($cacheKey), $cacheKey));
221 if (($this->databaseCacheEnabled === true) && ($this->getCacheInstance()->offsetExists($cacheKey, BaseDatabaseResult::RESULT_NAME_ROWS, 1))) {
222 // Then use this result
223 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FRONTEND: Cache used for cacheKey=%s', $cacheKey));
224 $result = $this->getCacheInstance()->offsetGet($cacheKey);
226 // Now it's time to ask the database layer for this select statement
227 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FRONTEND: Quering database, cacheKey=%s ...', $cacheKey));
228 $result = FrameworkBootstrap::getDatabaseInstance()->doSelectByTableCriteria($this->getTableName(), $criteriaInstance);
230 // Cache the result if not null
231 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FRONTEND: result[]=%s', gettype($result)));
232 if (!is_null($result)) {
234 if ($this->databaseCacheEnabled === true) {
235 // A valid result has returned from the database layer
236 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FRONTEND: Setting cacheKey=%s with result()=%d entries', $cacheKey, count($result)));
237 $this->getCacheInstance()->offsetSet($cacheKey, $result);
240 // This invalid result must be wrapped
242 BaseDatabaseResult::RESULT_NAME_STATUS => 'invalid',
243 BaseDatabaseResult::RESULT_NAME_EXCEPTION => FrameworkBootstrap::getDatabaseInstance()->getLastException()
248 // Create an instance of a CachedDatabaseResult class with the given result
249 // @TODO Minor: Update above comment to e.g. BaseDatabaseResult
250 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FRONTEND: result[%s]=%s,result[%s]?=%d,result[%s]?=%d', BaseDatabaseResult::RESULT_NAME_STATUS, $result[BaseDatabaseResult::RESULT_NAME_STATUS], BaseDatabaseResult::RESULT_NAME_ROWS, isset($result[BaseDatabaseResult::RESULT_NAME_ROWS]), BaseDatabaseResult::RESULT_NAME_EXCEPTION, isset($result[BaseDatabaseResult::RESULT_NAME_EXCEPTION])));
251 $resultInstance = ObjectFactory::createObjectByConfiguredName('database_result_class', array($result));
253 // And return the instance
254 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FRONTEND: resultInstance=%s - EXIT!', $resultInstance->__toString()));
255 return $resultInstance;
259 * Count the numbers of rows we shall receive
261 * @param $criteriaInstance An instance of a Criteria class
262 * @param $onlyKeys Only use these keys for a cache key
263 * @return $numRows Numbers of rows of database entries
265 public function doSelectCountByCriteria (Criteria $criteriaInstance, array $onlyKeys = []) {
266 // Total numbers is -1 so we can distinglish between failed and valid queries
269 // Get the result from above method
270 $resultInstance = $this->doSelectByCriteria($criteriaInstance, $onlyKeys);
272 // Was that query fine?
273 if ($resultInstance->ifStatusIsOkay()) {
274 // Then get the number of rows
275 $numRows = $resultInstance->getAffectedRows();
278 //* DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-FRONTEND: numRows=' . $numRows);
286 * Getter for primary key used in wrapped table
288 * @return $primaryKey Primary key used in wrapped table
290 public final function getPrimaryKeyValue () {
291 // Get the table name and a database instance and ask for it
292 $primaryKey = FrameworkBootstrap::getDatabaseInstance()->getPrimaryKeyOfTable($this->getTableName());
299 * Count rows of this table
301 * @return $count Count of total rows in this table
303 public final function countTotalRows () {
304 // Get the table name and a database instance and ask for it
305 $count = FrameworkBootstrap::getDatabaseInstance()->countTotalRows($this->getTableName());
312 * Removes non-public data from given array.
314 * @param $data An array with possible non-public data that needs to be removed.
315 * @return $data A cleaned up array with only public data.
317 public function removeNonPublicDataFromArray (array $data) {
318 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('WRAPPER[' . $this->__toString() . ']: Calling FrameworkBootstrap::getDatabaseInstance()->removeNonPublicDataFromArray(data) ...');
319 $data = FrameworkBootstrap::getDatabaseInstance()->removeNonPublicDataFromArray($data);
321 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('WRAPPER[' . $this->__toString() . ']: data[]=' . gettype($data));