diff --git a/src/FuzeWorks/Database.php b/src/FuzeWorks/Database.php
index e89bb04..1dca932 100644
--- a/src/FuzeWorks/Database.php
+++ b/src/FuzeWorks/Database.php
@@ -185,17 +185,17 @@ class Database
/**
* @param string $tableName
- * @param string $tableModelName
* @param string $connectionName
+ * @param string $engineName
* @param array $parameters
* @return iDatabaseTableModel
* @throws DatabaseException
*/
- public function getTableModel(string $tableName, string $tableModelName, string $connectionName = 'default', array $parameters = []): iDatabaseTableModel
+ public function getTableModel(string $tableName, string $connectionName = 'default', string $engineName = '', array $parameters = []): iDatabaseTableModel
{
try {
/** @var DatabaseLoadTableModelEvent $event */
- $event = Events::fireEvent('databaseLoadTableModelEvent', strtolower($tableModelName), $parameters, $connectionName, $tableName);
+ $event = Events::fireEvent('databaseLoadTableModelEvent', strtolower($engineName), $parameters, $connectionName, $tableName);
} catch (EventException $e) {
throw new DatabaseException("Could not get TableModel. databaseLoadTableModelEvent threw exception: '" . $e->getMessage() . "'");
}
@@ -208,7 +208,7 @@ class Database
if (is_object($event->tableModel) && $event->tableModel instanceof iDatabaseTableModel)
{
$tableModel = $this->tables[$event->connectionName . "|" . $event->tableName] = $event->tableModel;
- if (!$tableModel->setup())
+ if (!$tableModel->isSetup())
$tableModel->setUp($this->get($event->connectionName, $tableModel->getEngineName(), $event->parameters), $event->tableName);
}
// If the connection already exists, use that
@@ -219,9 +219,13 @@ class Database
// Otherwise use the provided configuration
else
{
- $tableModelClass = get_class($this->fetchTableModel($event->tableModelName));
+ // First the engine shall be fetched, so the name of the tableModel is known
+ $engine = $this->get($event->connectionName, $event->engineName, $event->parameters);
+ $tableModelClass = get_class($this->fetchTableModel($engine->getName()));
+
+ // Load the tableModel and add the engine
$tableModel = $this->tables[$event->connectionName . "|" . $event->tableName] = new $tableModelClass();
- $tableModel->setUp($this->get($event->connectionName, $tableModel->getEngineName(), $event->parameters), $event->tableName);
+ $tableModel->setUp($engine, $event->tableName);
}
// And return the tableModel
diff --git a/src/FuzeWorks/DatabaseEngine/DatabaseDriver.php b/src/FuzeWorks/DatabaseEngine/DatabaseDriver.php
index 689f5e5..223e468 100644
--- a/src/FuzeWorks/DatabaseEngine/DatabaseDriver.php
+++ b/src/FuzeWorks/DatabaseEngine/DatabaseDriver.php
@@ -39,7 +39,7 @@ namespace FuzeWorks\DatabaseEngine;
/**
* Class DatabaseDriver
*/
-abstract class DatabaseDriver
+abstract class DatabaseDriver implements iDatabaseEngine
{
// --- Query Logging --------------------------------------------------
@@ -82,5 +82,4 @@ abstract class DatabaseDriver
// --------------------------------------------------------------------
-
}
diff --git a/src/FuzeWorks/DatabaseEngine/MongoCommandSubscriber.php b/src/FuzeWorks/DatabaseEngine/MongoCommandSubscriber.php
index 92f578d..471210a 100644
--- a/src/FuzeWorks/DatabaseEngine/MongoCommandSubscriber.php
+++ b/src/FuzeWorks/DatabaseEngine/MongoCommandSubscriber.php
@@ -184,17 +184,12 @@ class MongoCommandSubscriber implements CommandSubscriber
$queryData = count($event->getReply()->cursor->firstBatch);
break;
+ case 'update':
+ case 'delete':
case 'insert':
$queryData = $event->getReply()->n;
break;
- case 'update':
- $queryData = $event->getReply()->n;
- break;
-
- case 'delete':
- $queryData = $event->getReply()->n;
- break;
}
// And log query
diff --git a/src/FuzeWorks/DatabaseEngine/MongoEngine.php b/src/FuzeWorks/DatabaseEngine/MongoEngine.php
index ea088e1..281d78d 100644
--- a/src/FuzeWorks/DatabaseEngine/MongoEngine.php
+++ b/src/FuzeWorks/DatabaseEngine/MongoEngine.php
@@ -66,7 +66,7 @@ use MongoDB\Model\DatabaseInfoIterator;
* @method Session startSession(array $options = [])
* @method ChangeStream watch(array $pipeline = [], array $options = [])
*/
-class MongoEngine extends DatabaseDriver implements iDatabaseEngine
+class MongoEngine extends DatabaseDriver
{
/**
diff --git a/src/FuzeWorks/DatabaseEngine/PDOEngine.php b/src/FuzeWorks/DatabaseEngine/PDOEngine.php
index c902af5..932c5d5 100644
--- a/src/FuzeWorks/DatabaseEngine/PDOEngine.php
+++ b/src/FuzeWorks/DatabaseEngine/PDOEngine.php
@@ -38,6 +38,7 @@ namespace FuzeWorks\DatabaseEngine;
use FuzeWorks\Exception\DatabaseException;
use FuzeWorks\Exception\TransactionException;
use FuzeWorks\Logger;
+use FuzeWorks\Model\iDatabaseTableModel;
use PDO;
use PDOException;
use PDOStatement;
@@ -52,7 +53,7 @@ use PDOStatement;
* @method string quote(string $string, int $parameter_type = PDO::PARAM_STR)
* @method bool setAttribute(int $attribute, mixed $value)
*/
-class PDOEngine extends DatabaseDriver implements iDatabaseEngine
+class PDOEngine extends DatabaseDriver
{
/**
@@ -164,7 +165,7 @@ class PDOEngine extends DatabaseDriver implements iDatabaseEngine
// Commit or rollback all changes to the database
$this->transactionEnd();
- //
+ // And close the connection
$this->pdoConnection = null;
return true;
}
diff --git a/src/FuzeWorks/DatabaseEngine/PDOStatementWrapper.php b/src/FuzeWorks/DatabaseEngine/PDOStatementWrapper.php
index fe1b76a..ecd05da 100644
--- a/src/FuzeWorks/DatabaseEngine/PDOStatementWrapper.php
+++ b/src/FuzeWorks/DatabaseEngine/PDOStatementWrapper.php
@@ -118,6 +118,16 @@ class PDOStatementWrapper
return $result;
}
+ /**
+ * Retrieves the statement last used
+ *
+ * @return PDOStatement
+ */
+ public function getStatement(): PDOStatement
+ {
+ return $this->statement;
+ }
+
/**
* Generates an error message for the last failure in PDO
*
@@ -146,5 +156,4 @@ class PDOStatementWrapper
{
return $this->statement->$name;
}
-
}
\ No newline at end of file
diff --git a/src/FuzeWorks/DatabaseEngine/iDatabaseEngine.php b/src/FuzeWorks/DatabaseEngine/iDatabaseEngine.php
index ffe8181..e5be1c3 100644
--- a/src/FuzeWorks/DatabaseEngine/iDatabaseEngine.php
+++ b/src/FuzeWorks/DatabaseEngine/iDatabaseEngine.php
@@ -36,7 +36,6 @@
namespace FuzeWorks\DatabaseEngine;
-
interface iDatabaseEngine
{
public function getName(): string;
diff --git a/src/FuzeWorks/Event/DatabaseLoadTableModelEvent.php b/src/FuzeWorks/Event/DatabaseLoadTableModelEvent.php
index 0f32831..4aeb595 100644
--- a/src/FuzeWorks/Event/DatabaseLoadTableModelEvent.php
+++ b/src/FuzeWorks/Event/DatabaseLoadTableModelEvent.php
@@ -56,7 +56,7 @@ class DatabaseLoadTableModelEvent extends Event
*
* @var string
*/
- public $tableModelName;
+ public $engineName;
/**
* The name of the table this model manages
@@ -80,9 +80,9 @@ class DatabaseLoadTableModelEvent extends Event
public $connectionName;
- public function init(string $tableModelName, array $parameters, string $connectionName, string $tableName)
+ public function init(string $engineName, array $parameters, string $connectionName, string $tableName)
{
- $this->tableModelName = $tableModelName;
+ $this->engineName = $engineName;
$this->parameters = $parameters;
$this->connectionName = $connectionName;
$this->tableName = $tableName;
diff --git a/src/FuzeWorks/Model/MongoTableModel.php b/src/FuzeWorks/Model/MongoTableModel.php
index c0d2a6a..10b6f62 100644
--- a/src/FuzeWorks/Model/MongoTableModel.php
+++ b/src/FuzeWorks/Model/MongoTableModel.php
@@ -122,6 +122,18 @@ class MongoTableModel implements iDatabaseTableModel
return 'mongo';
}
+ /**
+ * @return iDatabaseEngine
+ * @throws DatabaseException
+ */
+ public function getEngine(): iDatabaseEngine
+ {
+ if (!$this->setup)
+ throw new DatabaseException("Could not return Engine. Engine not setup yet.");
+
+ return $this->dbEngine;
+ }
+
/**
* @param array $data
* @param array $options
@@ -158,7 +170,7 @@ class MongoTableModel implements iDatabaseTableModel
* @return array
* @throws DatabaseException
*/
- public function read(array $filter = [], array $options = [], string $table = 'default'): array
+ public function read(array $filter = [], array $options = [], string $table = 'default'): TableModelResult
{
// Select collection
if ($table == 'default')
diff --git a/src/FuzeWorks/Model/PDOTableModel.php b/src/FuzeWorks/Model/PDOTableModel.php
index 14f60ac..bbe2f06 100644
--- a/src/FuzeWorks/Model/PDOTableModel.php
+++ b/src/FuzeWorks/Model/PDOTableModel.php
@@ -119,30 +119,37 @@ class PDOTableModel implements iDatabaseTableModel
return 'pdo';
}
+ /**
+ * @return iDatabaseEngine
+ * @throws DatabaseException
+ */
+ public function getEngine(): iDatabaseEngine
+ {
+ if (!$this->setup)
+ throw new DatabaseException("Could not return Engine. Engine not setup yet.");
+
+ return $this->dbEngine;
+ }
+
/**
* @param array $data
* @param array $options
- * @param string $table
* @return int
* @throws DatabaseException
*/
- public function create(array $data, array $options = [], string $table = 'default'): int
+ public function create(array $data, array $options = []): int
{
// If no data is provided, stop now
if (empty($data))
throw new DatabaseException("Could not create data. No data provided.");
- // Select table
- if ($table == 'default')
- $table = $this->tableName;
-
// Determine which fields will be inserted
$fieldsArr = $this->createFields($data);
$fields = $fieldsArr['fields'];
$values = $fieldsArr['values'];
// Generate the sql and create a PDOStatement
- $sql = "INSERT INTO {$table} ({$fields}) VALUES ({$values})";
+ $sql = "INSERT INTO {$this->tableName} ({$fields}) VALUES ({$values})";
/** @var PDOStatement $statement */
$this->lastStatement = $this->dbEngine->prepare($sql);
@@ -159,72 +166,38 @@ class PDOTableModel implements iDatabaseTableModel
}
/**
- * @todo: WRITE ABOUT FETCHMODE
- *
* @param array $filter
* @param array $options
- * @param string $table
- * @return array
+ * @return TableModelResult
* @throws DatabaseException
*/
- public function read(array $filter = [], array $options = [], string $table = 'default'): array
+ public function read(array $filter = [], array $options = []): TableModelResult
{
- // Select table
- if ($table == 'default')
- $table = $this->tableName;
-
// Determine which fields to select. If none provided, select all
$fields = (isset($options['fields']) && is_array($options['fields']) ? implode(',', $options['fields']) : '*');
// Apply the filter. If none provided, don't condition it
$where = $this->filter($filter);
- // If a JOIN is provided, create the statement
- if (isset($options['join']))
- {
- $joinType = (isset($options['join']['joinType']) ? strtoupper($options['join']['joinType']) : 'LEFT');
- $targetTable = (isset($options['join']['targetTable']) ? $options['join']['targetTable'] : null);
- $targetField = (isset($options['join']['targetField']) ? $options['join']['targetField'] : null);
- $sourceField = (isset($options['join']['sourceField']) ? $options['join']['sourceField'] : null);
- if (is_null($targetTable) || is_null($targetField) || is_null($sourceField))
- throw new DatabaseException("Could not read from '" . $table . "'. Missing fields in join options.");
-
- $join = "{$joinType} JOIN {$targetTable} ON {$table}.{$sourceField} = {$targetTable}.{$targetField}";
- }
- else
- $join = '';
-
// Generate the sql and create a PDOStatement
- $sql = "SELECT " . $fields . " FROM {$table} {$join} " . $where;
+ $sql = "SELECT " . $fields . " FROM {$this->tableName} " . $where;
/** @var PDOStatement $statement */
$this->lastStatement = $this->dbEngine->prepare($sql);
- // Return prepared statement, if requested to do so
- if (isset($options['returnPreparedStatement']) && $options['returnPreparedStatement'] == true)
- return [];
-
// And execute the query
$this->lastStatement->execute($filter);
- // And return the result
- $fetchMode = (isset($options['fetchMode']) ? $options['fetchMode'] : PDO::FETCH_ASSOC);
- if (is_array($fetchMode))
- return $this->lastStatement->fetchAll(...$fetchMode);
-
- return $this->lastStatement->fetchAll($fetchMode);
+ // Fetch PDO Iterable
+ return new TableModelResult($this->lastStatement->getStatement());
}
- public function update(array $data, array $filter, array $options = [], string $table = 'default'): int
+ public function update(array $data, array $filter, array $options = []): int
{
// If no data is provided, stop now
if (empty($data))
throw new DatabaseException("Could not update data. No data provided.");
- // Select table
- if ($table == 'default')
- $table = $this->tableName;
-
// Apply the filter
$where = $this->filter($filter);
@@ -235,7 +208,7 @@ class PDOTableModel implements iDatabaseTableModel
$fields = implode(', ', $fields);
// Generate the sql and create a PDOStatement
- $sql = "UPDATE {$table} SET {$fields} {$where}";
+ $sql = "UPDATE {$this->tableName} SET {$fields} {$where}";
/** @var PDOStatement $statement */
$this->lastStatement = $this->dbEngine->prepare($sql);
@@ -250,17 +223,13 @@ class PDOTableModel implements iDatabaseTableModel
return $this->lastStatement->rowCount();
}
- public function delete(array $filter, array $options = [], string $table = 'default'): int
+ public function delete(array $filter, array $options = []): int
{
- // Select table
- if ($table == 'default')
- $table = $this->tableName;
-
// Apply the filter
$where = $this->filter($filter);
// Generate the sql and create a PDOStatement
- $sql = "DELETE FROM {$table} " . $where;
+ $sql = "DELETE FROM {$this->tableName} " . $where;
/** @var PDOStatement $statement */
$this->lastStatement = $this->dbEngine->prepare($sql);
@@ -272,11 +241,6 @@ class PDOTableModel implements iDatabaseTableModel
return $this->lastStatement->rowCount();
}
- public function getLastStatement(): PDOStatementWrapper
- {
- return $this->lastStatement;
- }
-
/**
* @return bool
* @throws TransactionException
diff --git a/src/FuzeWorks/Model/TableModelResult.php b/src/FuzeWorks/Model/TableModelResult.php
new file mode 100644
index 0000000..412bd1e
--- /dev/null
+++ b/src/FuzeWorks/Model/TableModelResult.php
@@ -0,0 +1,166 @@
+raw = $results;
+ $this->traversable = $results;
+ }
+
+ /**
+ * Group the results by a certain field.
+ *
+ * @param string $field
+ * @return TableModelResult
+ */
+ public function group(string $field): self
+ {
+ // First make sure all data is fetched
+ $this->allToArray();
+
+ // Afterwards build a grouped array
+ $grouped = [];
+ foreach ($this->result->getIterator() as $key => $val)
+ {
+ // Check if this group exists within the results
+ if (isset($val[$field]))
+ {
+ // Name of the group
+ $fieldSelector = $val[$field];
+
+ // If the group has never been found before, add the array
+ if (!isset($grouped[$fieldSelector]))
+ $grouped[$fieldSelector] = [];
+
+ unset($val[$field]);
+ $grouped[$fieldSelector][] = $val;
+ }
+ }
+
+ $this->result->exchangeArray($grouped);
+ return $this;
+ }
+
+ /**
+ * Convert the result into an array
+ *
+ * @return array
+ */
+ public function toArray(): array
+ {
+ // First make sure all data is fetched
+ $this->allToArray();
+
+ // And return a copy
+ return $this->result->getArrayCopy();
+ }
+
+ /**
+ * Retrieve an external iterator
+ * @link https://php.net/manual/en/iteratoraggregate.getiterator.php
+ * @return Traversable An instance of an object implementing Iterator or
+ * Traversable
+ * @since 5.0.0
+ */
+ public function getIterator()
+ {
+ return $this->traversable;
+ }
+
+ private function allToArray(): bool
+ {
+ // If the input has already been fetched, ignore it
+ if ($this->fullyFetched)
+ return true;
+
+ $result = [];
+ foreach ($this->raw as $key => $val)
+ {
+ // Clear out all numeric keys
+ foreach ($val as $recKey => $recVal)
+ if (is_numeric($recKey))
+ unset($val[$recKey]);
+
+ $result[$key] = $val;
+ }
+
+ // Set the variable
+ $this->result = new ArrayObject($result);
+
+ // Afterwards modify the traversable
+ $this->traversable = $this->result->getIterator();
+
+ // Set fullyFetched to true so it doesn't get fetched again
+ $this->fullyFetched = true;
+
+ return true;
+ }
+
+}
\ No newline at end of file
diff --git a/src/FuzeWorks/Model/iDatabaseTableModel.php b/src/FuzeWorks/Model/iDatabaseTableModel.php
index f92df40..86939dc 100644
--- a/src/FuzeWorks/Model/iDatabaseTableModel.php
+++ b/src/FuzeWorks/Model/iDatabaseTableModel.php
@@ -35,80 +35,120 @@
*/
namespace FuzeWorks\Model;
-
-
use FuzeWorks\DatabaseEngine\iDatabaseEngine;
use FuzeWorks\Exception\DatabaseException;
interface iDatabaseTableModel
{
/**
+ * Returns the name of the TableModel.
+ *
+ * Usually 'pdo' or 'mongo'.
+ *
* @return string
*/
public function getName(): string;
/**
+ * Return the name of the engine used by this TableModel.
+ *
+ * Usually 'pdo' or 'mongo'
+ *
* @return string
*/
public function getEngineName(): string;
+ /**
+ * Return the engine used by this TableModel
+ *
+ * @return iDatabaseEngine
+ */
+ public function getEngine(): iDatabaseEngine;
+
+ /**
+ * Method invoked by FuzeWorks\Database to setup this tableModel.
+ *
+ * Provides the TableModel with the appropriate iDatabaseEngine and the name of the table.
+ *
+ * @param iDatabaseEngine $engine
+ * @param string $tableName
+ * @return mixed
+ */
public function setUp(iDatabaseEngine $engine, string $tableName);
+ /**
+ * Returns whether the TableModel has been setup yet
+ *
+ * @return bool
+ */
public function isSetup(): bool;
/**
+ * Creates data in the model.
+ *
* @param array $data
* @param array $options
- * @param string $table
* @return int
* @throws DatabaseException
*/
- public function create(array $data, array $options = [], string $table = 'default'): int;
+ public function create(array $data, array $options = []): int;
/**
+ * Returns data from the model in the form of a TableModelResult
+ *
* @param array $filter
* @param array $options
- * @param string $table
- * @return array
+ * @return TableModelResult
* @throws DatabaseException
+ * @see TableModelResult
*/
- public function read(array $filter = [], array $options = [], string $table = 'default'): array;
+ public function read(array $filter = [], array $options = []): TableModelResult;
/**
+ * Updates data in the model
+ *
* @param array $data
* @param array $filter
* @param array $options
- * @param string $table
* @return int
* @throws DatabaseException
*/
- public function update(array $data, array $filter, array $options = [], string $table = 'default'): int;
+ public function update(array $data, array $filter, array $options = []): int;
/**
+ * Deletes data from the model
+ *
* @param array $filter
* @param array $options
- * @param string $table
* @return int
* @throws DatabaseException
*/
- public function delete(array $filter, array $options = [], string $table = 'default'): int;
+ public function delete(array $filter, array $options = []): int;
/**
+ * Starts a transaction in the model when supported
+ *
* @return bool
*/
public function transactionStart(): bool;
/**
+ * Ends a transaction in the model when supported
+ *
* @return bool
*/
public function transactionEnd(): bool;
/**
+ * Commits changes in the model when supported
+ *
* @return bool
*/
public function transactionCommit(): bool;
/**
+ * Rolls back changes in the modle when supported
+ *
* @return bool
*/
public function transactionRollback(): bool;