continue datamapper bug fixes

This commit is contained in:
Dennis Eichhorn 2021-12-19 20:18:36 +01:00
parent aa004569f5
commit 1e90a5ba9d
27 changed files with 1231 additions and 412 deletions

View File

@ -32,7 +32,7 @@ abstract class GrammarAbstract
* @var string
* @since 1.0.0
*/
protected string $comment = '--';
public string $comment = '--';
/**
* String quotes style.
@ -40,7 +40,7 @@ abstract class GrammarAbstract
* @var string
* @since 1.0.0
*/
protected string $valueQuotes = '\'';
public string $valueQuotes = '\'';
/**
* System identifier.
@ -48,7 +48,7 @@ abstract class GrammarAbstract
* @var string
* @since 1.0.0
*/
protected string $systemIdentifierStart = '"';
public string $systemIdentifierStart = '"';
/**
* System identifier.
@ -56,7 +56,7 @@ abstract class GrammarAbstract
* @var string
* @since 1.0.0
*/
protected string $systemIdentifierEnd = '"';
public string $systemIdentifierEnd = '"';
/**
* And operator.
@ -64,7 +64,7 @@ abstract class GrammarAbstract
* @var string
* @since 1.0.0
*/
protected string $and = 'AND';
public string $and = 'AND';
/**
* Or operator.
@ -72,7 +72,7 @@ abstract class GrammarAbstract
* @var string
* @since 1.0.0
*/
protected string $or = 'OR';
public string $or = 'OR';
/**
* Special keywords.
@ -80,7 +80,7 @@ abstract class GrammarAbstract
* @var string[]
* @since 1.0.0
*/
protected array $specialKeywords = [
public array $specialKeywords = [
'COUNT(',
'MAX(',
'MIN(',
@ -96,7 +96,7 @@ abstract class GrammarAbstract
* @var string
* @since 1.0.0
*/
protected string $datetimeFormat = 'Y-m-d H:i:s';
public string $datetimeFormat = 'Y-m-d H:i:s';
/**
* Set the datetime format

View File

@ -28,20 +28,81 @@ use phpOMS\DataStorage\Database\Query\OrderType;
*/
abstract class DataMapperAbstract
{
/**
* Base mapper
*
* @var DataMapperFactory
* @since 1.0.0
*/
protected DataMapperFactory $mapper;
/**
* Mapper type (e.g. writer, reader, ...)
*
* @var int
* @since 1.0.0
*/
protected int $type = 0;
/**
* Mapper depths.
*
* Mappers may have relations to other models (e.g. hasMany) which can have other relations, ...
* The depths indicates how deep in the relation tree we are
*
* @var int
* @since 1.0.0
*/
protected int $depth = 1;
/**
* Relations which should be loaded
*
* @var array
* @since 1.0.0
*/
protected array $with = [];
/**
* Sort order
*
* @var array
* @since 1.0.0
*/
protected array $sort = [];
/**
* Offset
*
* @var array
* @since 1.0.0
*/
protected array $offset = [];
/**
* Limit
*
* @var array
* @since 1.0.0
*/
protected array $limit = [];
/**
* Where conditions
*
* @var array
* @since 1.0.0
*/
protected array $where = [];
/**
* Base query which is merged with the query in the mapper
*
* Sometimes you want to merge two queries together.
*
* @var null|Builder
* @since 1.0.0
*/
protected ?Builder $query = null;
/**
@ -52,12 +113,28 @@ abstract class DataMapperAbstract
*/
protected ConnectionAbstract $db;
/** Constructor.
*
* @param DataMapperFactory $mapper Base mapper
* @param ConnectionAbstract $db Database connection
*
* @since 1.0.0
*/
public function __construct(DataMapperFactory $mapper, ConnectionAbstract $db)
{
$this->mapper = $mapper;
$this->db = $db;
}
/**
* Define a query which is merged with the internal query generation.
*
* @param Builder $query Query
*
* @return static
*
* @since 1.0.0
*/
public function query(Builder $query = null) : self
{
$this->query = $query;
@ -65,7 +142,15 @@ abstract class DataMapperAbstract
return $this;
}
// Only for relations, no impact on anything else
/**
* Define model relations which should be loaded
*
* @param string $member Property name of the relation (e.g. hasMany, belongsTo, ...)
*
* @return static
*
* @since 1.0.0
*/
public function with(string $member) : self
{
$split = \explode('/', $member);
@ -78,6 +163,16 @@ abstract class DataMapperAbstract
return $this;
}
/**
* Sort order
*
* @param string $member Property name to sort by
* @param string $order Order type (DESC/ASC)
*
* @return static
*
* @since 1.0.0
*/
public function sort(string $member, string $order = OrderType::DESC) : self
{
$split = \explode('/', $member);
@ -91,6 +186,39 @@ abstract class DataMapperAbstract
return $this;
}
/**
* Define the result offset
*
* @param int $offset Offset
* @param string $member Property name to offset by ('' = base model, anything else for relations such as hasMany relations)
*
* @return static
*
* @since 1.0.0
*/
public function offset(int $offset = 0, string $member = '') : self
{
$split = \explode('/', $member);
$memberSplit = \array_shift($split);
$this->offset[$memberSplit ?? ''][] = [
'child' => \implode('/', $split),
'offset' => $offset,
];
return $this;
}
/**
* Define the result limit
*
* @param int $limit Limit
* @param string $member Property name to limit by ('' = base model, anything else for relations such as hasMany relations)
*
* @return static
*
* @since 1.0.0
*/
public function limit(int $limit = 0, string $member = '') : self
{
$split = \explode('/', $member);
@ -104,7 +232,19 @@ abstract class DataMapperAbstract
return $this;
}
public function where(string $member, mixed $value, string $logic = '=', string $comparison = 'AND') : self
/**
* Define the result filtering
*
* @param string $member Property name to filter by
* @param mixed $value Filter value
* @param string $logic Comparison logic (e.g. =, in, ...)
* @param string $connector Filter connector (e.g. AND, OR, ...)
*
* @return static
*
* @since 1.0.0
*/
public function where(string $member, mixed $value, string $logic = '=', string $connector = 'AND') : self
{
$split = \explode('/', $member);
$memberSplit = \array_shift($split);
@ -113,12 +253,22 @@ abstract class DataMapperAbstract
'child' => \implode('/', $split),
'value' => $value,
'logic' => $logic,
'comparison' => $comparison,
'comparison' => $connector,
];
return $this;
}
/**
* Populate a mapper (e.g. child mapper, relation mapper) based on the current mapper information.
*
* @param DataMapperAbstract $mapper Relation mapper to populate
* @param string $member Relation property (e.g. ownsOne, hasMany, ... property name)
*
* @return self
*
* @since 1.0.0
*/
public function createRelationMapper(self $mapper, string $member) : self
{
$relMapper = $mapper;
@ -143,6 +293,16 @@ abstract class DataMapperAbstract
}
}
if (isset($this->offset[$member])) {
foreach ($this->offset[$member] as $offset) {
if ($offset['child'] === '') {
continue;
}
$relMapper->offset($offset['offset'], $offset['child']);
}
}
if (isset($this->limit[$member])) {
foreach ($this->limit[$member] as $limit) {
if ($limit['child'] === '') {
@ -201,5 +361,14 @@ abstract class DataMapperAbstract
return $value;
}
/**
* Execute mapper
*
* @param mixed ...$options Data for the mapper
*
* @return mixed
*
* @since 1.0.0
*/
abstract public function execute(...$options) : mixed;
}

View File

@ -142,7 +142,7 @@ class DataMapperFactory
* @since 1.0.0
* @codeCoverageIgnore
*/
private function __construct()
final private function __construct()
{
}
@ -158,73 +158,199 @@ class DataMapperFactory
{
}
public static function db(ConnectionAbstract $db = null) : string
/**
* Set default database connection
*
* @param ConnectionAbstract $db Database connection
*
* @return class-string<self>
*
* @since 1.0.0
*/
public static function db(ConnectionAbstract $db) : string
{
self::$db = $db;
return static::class;
}
/**
* Create read mapper
*
* @param ConnectionAbstract $db Database connection
*
* @return ReadMapper
*
* @since 1.0.0
*/
public static function reader(ConnectionAbstract $db = null) : ReadMapper
{
return new ReadMapper(new static(), $db ?? self::$db);
}
/**
* Create read mapper
*
* @param ConnectionAbstract $db Database connection
*
* @return ReadMapper
*
* @since 1.0.0
*/
public static function get(ConnectionAbstract $db = null) : ReadMapper
{
return (new ReadMapper(new static(), $db ?? self::$db))->get();
}
/**
* Create read mapper
*
* @param ConnectionAbstract $db Database connection
*
* @return ReadMapper
*
* @since 1.0.0
*/
public static function getRaw(ConnectionAbstract $db = null) : ReadMapper
{
return (new ReadMapper(new static(), $db ?? self::$db))->getRaw();
}
/**
* Create read mapper
*
* @param ConnectionAbstract $db Database connection
*
* @return ReadMapper
*
* @since 1.0.0
*/
public static function getRandom(ConnectionAbstract $db = null) : ReadMapper
{
return (new ReadMapper(new static(), $db ?? self::$db))->getRandom();
}
/**
* Create read mapper
*
* @param ConnectionAbstract $db Database connection
*
* @return ReadMapper
*
* @since 1.0.0
*/
public static function count(ConnectionAbstract $db = null) : ReadMapper
{
return (new ReadMapper(new static(), $db ?? self::$db))->count();
}
/**
* Create read mapper
*
* @param ConnectionAbstract $db Database connection
*
* @return Builder
*
* @since 1.0.0
*/
public static function getQuery(ConnectionAbstract $db = null) : Builder
{
return (new ReadMapper(new static(), $db ?? self::$db))->getQuery();
}
/**
* Create read mapper
*
* @param ConnectionAbstract $db Database connection
*
* @return ReadMapper
*
* @since 1.0.0
*/
public static function getAll(ConnectionAbstract $db = null) : ReadMapper
{
return (new ReadMapper(new static(), $db ?? self::$db))->getAll();
}
/**
* Create write mapper
*
* @param ConnectionAbstract $db Database connection
*
* @return WriteMapper
*
* @since 1.0.0
*/
public static function writer(ConnectionAbstract $db = null) : WriteMapper
{
return new WriteMapper(new static(), $db ?? self::$db);
}
/**
* Create write mapper
*
* @param ConnectionAbstract $db Database connection
*
* @return WriteMapper
*
* @since 1.0.0
*/
public static function create(ConnectionAbstract $db = null) : WriteMapper
{
return (new WriteMapper(new static(), $db ?? self::$db))->create();
}
/**
* Create update mapper
*
* @param ConnectionAbstract $db Database connection
*
* @return UpdateMapper
*
* @since 1.0.0
*/
public static function updater(ConnectionAbstract $db = null) : UpdateMapper
{
return new UpdateMapper(new static(), $db ?? self::$db);
}
/**
* Create update mapper
*
* @param ConnectionAbstract $db Database connection
*
* @return UpdateMapper
*
* @since 1.0.0
*/
public static function update(ConnectionAbstract $db = null) : UpdateMapper
{
return (new UpdateMapper(new static(), $db ?? self::$db))->update();
}
/**
* Create delete mapper
*
* @param ConnectionAbstract $db Database connection
*
* @return DeleteMapper
*
* @since 1.0.0
*/
public static function remover(ConnectionAbstract $db = null) : DeleteMapper
{
return new DeleteMapper(new static(), $db ?? self::$db);
}
/**
* Create delete mapper
*
* @param ConnectionAbstract $db Database connection
*
* @return DeleteMapper
*
* @since 1.0.0
*/
public static function delete(ConnectionAbstract $db = null) : DeleteMapper
{
return (new DeleteMapper(new static(), $db ?? self::$db))->delete();
@ -267,11 +393,11 @@ class DataMapperFactory
/**
* Create the empty base model
*
* @return mixed
* @return object
*
* @since 1.0.0
*/
public static function createBaseModel() : mixed
public static function createBaseModel() : object
{
$class = empty(static::MODEL) ? \substr(static::class, 0, -6) : static::MODEL;
@ -283,18 +409,6 @@ class DataMapperFactory
return new $class();
}
/**
* Get model from mapper
*
* @return string
*
* @since 1.0.0
*/
public static function getModelName() : string
{
return empty(static::MODEL) ? \substr(static::class, 0, -6) : static::MODEL;
}
/**
* Get id of object
*

View File

@ -14,7 +14,6 @@ declare(strict_types=1);
namespace phpOMS\DataStorage\Database\Mapper;
use phpOMS\DataStorage\Database\Exception\InvalidMapperException;
use phpOMS\DataStorage\Database\Query\Builder;
/**
@ -27,8 +26,15 @@ use phpOMS\DataStorage\Database\Query\Builder;
* @link https://orange-management.org
* @since 1.0.0
*/
class DeleteMapper extends DataMapperAbstract
final class DeleteMapper extends DataMapperAbstract
{
/**
* Get delete mapper
*
* @return self
*
* @since 1.0.0
*/
public function delete() : self
{
$this->type = MapperType::DELETE;
@ -36,22 +42,37 @@ class DeleteMapper extends DataMapperAbstract
return $this;
}
/**
* Execute mapper
*
* @param array ...$options Options to pass to the selete mapper
*
* @return mixed
*
* @since 1.0.0
*/
public function execute(...$options) : mixed
{
switch($this->type) {
case MapperType::DELETE:
/** @var object[] ...$options */
return $this->executeDelete(...$options);
default:
return null;
}
}
public function executeDelete(mixed $obj) : mixed
/**
* Execute mapper
*
* @param object $obj Object to delete
*
* @return mixed
*
* @since 1.0.0
*/
public function executeDelete(object $obj) : mixed
{
if ($obj === null) {
$obj = $this->mapper::get()->execute(); // todo: pass where conditions to read mapper
}
$refClass = new \ReflectionClass($obj);
$objId = $this->mapper::getObjectId($obj, $refClass);
@ -67,6 +88,15 @@ class DeleteMapper extends DataMapperAbstract
return $objId;
}
/**
* Delete model
*
* @param mixed $objId Object id to delete
*
* @return void
*
* @since 1.0.0
*/
private function deleteModel(mixed $objId) : void
{
$query = new Builder($this->db);
@ -80,7 +110,18 @@ class DeleteMapper extends DataMapperAbstract
}
}
private function deleteSingleRelation(mixed $obj, \ReflectionClass $refClass, array $relation) : void
/**
* Delete ownsOne, belongsTo relations
*
* @param object $obj Object to delete
* @param \ReflectionClass $refClass Reflection of object to delete
* @param array $relation Relation data (e.g. ::BELONGS_TO, ::OWNS_ONE)
*
* @return void
*
* @since 1.0.0
*/
private function deleteSingleRelation(object $obj, \ReflectionClass $refClass, array $relation) : void
{
if (empty($relation)) {
return;
@ -95,7 +136,7 @@ class DeleteMapper extends DataMapperAbstract
$mapper = $relData['mapper'];
/** @var self $relMapper */
$relMapper = $this->createRelationMapper($mapper::delete(db: $this->db), $member);
$relMapper = $this->createRelationMapper($mapper::delete(db: $this->db), $member);
$relMapper->depth = $this->depth + 1;
$refProp = $refClass->getProperty($member);
@ -109,6 +150,17 @@ class DeleteMapper extends DataMapperAbstract
}
}
/**
* Delete hasMany
*
* @param \ReflectionClass $refClass Reflection of object to delete
* @param object $obj Object to delete
* @param mixed $objId Object id to delete
*
* @return void
*
* @since 1.0.0
*/
private function deleteHasMany(\ReflectionClass $refClass, object $obj, mixed $objId) : void
{
if (empty($this->mapper::HAS_MANY)) {
@ -121,7 +173,7 @@ class DeleteMapper extends DataMapperAbstract
continue;
}
$objIds = [];
$objIds = [];
$refProp = $refClass->getProperty($member);
if (!$refProp->isPublic()) {
$refProp->setAccessible(true);
@ -165,6 +217,17 @@ class DeleteMapper extends DataMapperAbstract
}
}
/**
* Delete has many relations if the relation is handled in a relation table
*
* @param string $member Property which contains the has many models
* @param array $objIds Objects which are related to the parent object
* @param mixed $objId Parent object id
*
* @return void
*
* @since 1.0.0
*/
public function deleteRelationTable(string $member, array $objIds = null, mixed $objId) : void
{
if ((empty($objIds) && $objIds !== null)

View File

@ -25,10 +25,25 @@ use phpOMS\Utils\ArrayUtils;
* @link https://orange-management.org
* @since 1.0.0
*/
class ReadMapper extends DataMapperAbstract
final class ReadMapper extends DataMapperAbstract
{
private $columns = [];
/**
* Columns to load
*
* @var array
* @since 1.0.0
*/
private array $columns = [];
/**
* Create get mapper
*
* This makes execute() return a single object or an array of object depending the result size
*
* @return self
*
* @since 1.0.0
*/
public function get() : self
{
$this->type = MapperType::GET;
@ -36,6 +51,13 @@ class ReadMapper extends DataMapperAbstract
return $this;
}
/**
* Get raw result set
*
* @return self
*
* @since 1.0.0
*/
public function getRaw() : self
{
$this->type = MapperType::GET_RAW;
@ -43,6 +65,15 @@ class ReadMapper extends DataMapperAbstract
return $this;
}
/**
* Create get mapper
*
* This makes execute() always return an array of objects (or an empty array)
*
* @return self
*
* @since 1.0.0
*/
public function getAll() : self
{
$this->type = MapperType::GET_ALL;
@ -50,6 +81,13 @@ class ReadMapper extends DataMapperAbstract
return $this;
}
/**
* Create count mapper
*
* @return self
*
* @since 1.0.0
*/
public function count() : self
{
$this->type = MapperType::COUNT_MODELS;
@ -57,6 +95,13 @@ class ReadMapper extends DataMapperAbstract
return $this;
}
/**
* Create random mapper
*
* @return self
*
* @since 1.0.0
*/
public function getRandom() : self
{
$this->type = MapperType::GET_RANDOM;
@ -64,6 +109,13 @@ class ReadMapper extends DataMapperAbstract
return $this;
}
/**
* Create find mapper
*
* @return self
*
* @since 1.0.0
*/
public function find() : self
{
$this->type = MapperType::FIND;
@ -71,6 +123,16 @@ class ReadMapper extends DataMapperAbstract
return $this;
}
/**
* Define the columns to load
*
* @param array $columns Columns to load
*
* @return self
*
* @since 1.0.0
* @todo: consider to accept properties instead and then check ::COLUMNS which contian the property and ADD that array into $this->columns. Maybe also consider a rename from columns() to property()
*/
public function columns(array $columns) : self
{
$this->columns = $columns;
@ -78,18 +140,30 @@ class ReadMapper extends DataMapperAbstract
return $this;
}
/**
* Execute mapper
*
* @param mixed ...$options Options to pass to read mapper
*
* @return mixed
*
* @since 1.0.0
*/
public function execute(...$options) : mixed
{
switch($this->type) {
case MapperType::GET:
/** @var null|Builder ...$options */
return $options !== null
? $this->executeGet(...$options)
: $this->executeGet();
case MapperType::GET_RAW:
/** @var null|Builder ...$options */
return $options !== null
? $this->executeGetRaw(...$options)
: $this->executeGetRaw();
case MapperType::GET_ALL:
/** @var null|Builder ...$options */
return $options !== null
? $this->executeGetAll(...$options)
: $this->executeGetAll();
@ -102,15 +176,24 @@ class ReadMapper extends DataMapperAbstract
}
}
// @todo: consider to always return an array, this way we could remove executeGetAll
/**
* Execute mapper
*
* @param null|Builder $query Query to use instead of the internally generated query (carefuly, this doesn't merge with the internal query. If you want to merge it use ->query() instead)
*
* @return object|array
*
* @todo: consider to always return an array, this way we could remove executeGetAll
* @since 1.0.0
*/
public function executeGet(Builder $query = null) : mixed
{
$primaryKeys = [];
$primaryKeys = [];
$memberOfPrimaryField = $this->mapper::COLUMNS[$this->mapper::PRIMARYFIELD]['internal'];
$emptyWhere = empty($this->where);
$emptyWhere = empty($this->where);
if (isset($this->where[$memberOfPrimaryField])) {
$keys = $this->where[$memberOfPrimaryField][0]['value'];
$keys = $this->where[$memberOfPrimaryField][0]['value'];
$primaryKeys = \array_merge(\is_array($keys) ? $keys : [$keys], $primaryKeys);
}
@ -141,6 +224,15 @@ class ReadMapper extends DataMapperAbstract
return $obj;
}
/**
* Execute mapper
*
* @param null|Builder $query Query to use instead of the internally generated query (carefuly, this doesn't merge with the internal query. If you want to merge it use ->query() instead)
*
* @return array
*
* @since 1.0.0
*/
public function executeGetRaw(Builder $query = null) : array
{
$query ??= $this->getQuery();
@ -162,6 +254,15 @@ class ReadMapper extends DataMapperAbstract
return $results === false ? [] : $results;
}
/**
* Execute mapper
*
* @param null|Builder $query Query to use instead of the internally generated query (carefuly, this doesn't merge with the internal query. If you want to merge it use ->query() instead)
*
* @return array
*
* @since 1.0.0
*/
public function executeGetAll(Builder $query = null) : array
{
$result = $this->executeGet($query);
@ -205,8 +306,8 @@ class ReadMapper extends DataMapperAbstract
/**
* Get mapper specific builder
*
* @param Builder $query Query to fill
* @param array $columns Columns to use
* @param null|Builder $query Query to fill
* @param array $columns Columns to use
*
* @return Builder
*
@ -223,7 +324,7 @@ class ReadMapper extends DataMapperAbstract
if (\is_string($values)) {
$query->selectAs($key, $values);
} else {
if (($values['writeonly'] ?? false) === false) {
if (($values['writeonly'] ?? false) === false || isset($this->with[$values['internal']])) {
$query->selectAs($this->mapper::TABLE . '_d' . $this->depth . '.' . $key, $key . '_d' . $this->depth);
}
}
@ -235,9 +336,9 @@ class ReadMapper extends DataMapperAbstract
// where
foreach ($this->where as $member => $values) {
if(($col = $this->mapper::getColumnByMember($member)) !== null) {
if (($col = $this->mapper::getColumnByMember($member)) !== null) {
/* variable in model */
foreach ($values as $index => $where) {
foreach ($values as $where) {
// @todo: the has many, etc. if checks only work if it is a relation on the first level, if we have a deeper where condition nesting this fails
if ($where['child'] !== '') {
continue;
@ -248,7 +349,8 @@ class ReadMapper extends DataMapperAbstract
}
} elseif (isset($this->mapper::HAS_MANY[$member])) {
/* variable in has many */
foreach ($values as $index => $where) {
/* @todo: maybe needed in the future, but needs adjustment, doesn't make sense at the moment
foreach ($values as $where) {
// @todo: the has many, etc. if checks only work if it is a relation on the first level, if we have a deeper where condition nesting this fails
if ($where['child'] !== '') {
continue;
@ -272,8 +374,10 @@ class ReadMapper extends DataMapperAbstract
);
}
}
*/
} elseif (isset($this->mapper::BELONGS_TO[$member])) {
/* variable in belogns to */
/* @todo: maybe needed in the future, but needs adjustment, doesn't make sense at the moment
foreach ($values as $index => $where) {
// @todo: the has many, etc. if checks only work if it is a relation on the first level, if we have a deeper where condition nesting this fails
if ($where['child'] !== '') {
@ -290,8 +394,10 @@ class ReadMapper extends DataMapperAbstract
$this->mapper::BELONGS_TO[$member]['mapper']::TABLE . '_d' . $this->depth
);
}
*/
} elseif (isset($this->mapper::OWNS_ONE[$member])) {
/* variable in owns one */
/* @todo: maybe needed in the future, but needs adjustment, doesn't make sense at the moment
foreach ($values as $index => $where) {
// @todo: the has many, etc. if checks only work if it is a relation on the first level, if we have a deeper where condition nesting this fails
if ($where['child'] !== '') {
@ -307,6 +413,7 @@ class ReadMapper extends DataMapperAbstract
$this->mapper::OWNS_ONE[$member]['mapper']::TABLE . '_d' . $this->depth
);
}
*/
}
}
@ -350,7 +457,7 @@ class ReadMapper extends DataMapperAbstract
}
/** @var self $relMapper */
$relMapper = $this->createRelationMapper($rel['mapper']::reader(db: $this->db), $member);
$relMapper = $this->createRelationMapper($rel['mapper']::reader(db: $this->db), $member);
$relMapper->depth = $this->depth + 1;
$query = $relMapper->getQuery(
@ -398,16 +505,14 @@ class ReadMapper extends DataMapperAbstract
/**
* Populate data.
*
* @param array $result Query result set
* @param mixed $obj Object to populate
* @param array $result Query result set
* @param object $obj Object to populate
*
* @return mixed
*
* @throws \UnexpectedValueException
* @return object
*
* @since 1.0.0
*/
public function populateAbstract(array $result, mixed $obj) : mixed
public function populateAbstract(array $result, object $obj) : object
{
$refClass = new \ReflectionClass($obj);
@ -627,8 +732,6 @@ class ReadMapper extends DataMapperAbstract
}
}
// @todo: MUST handle if member is in with here!!!
if (isset($this->mapper::OWNS_ONE[$member]['column'])) {
return $result[$mapper::getColumnByMember($this->mapper::OWNS_ONE[$member]['column']) . '_d' . $this->depth];
}
@ -637,8 +740,8 @@ class ReadMapper extends DataMapperAbstract
return $mapper::createNullModel();
}
/** @var class-string<DataMapperFactory> $ownsOneMapper */
$ownsOneMapper = $this->createRelationMapper($mapper::get($this->db), $member);
/** @var self $ownsOneMapper */
$ownsOneMapper = $this->createRelationMapper($mapper::get($this->db), $member);
$ownsOneMapper->depth = $this->depth + 1;
return $ownsOneMapper->populateAbstract($result, $mapper::createBaseModel());
@ -663,7 +766,7 @@ class ReadMapper extends DataMapperAbstract
/** @var class-string<DataMapperFactory> $mapper */
$mapper = $this->mapper::BELONGS_TO[$member]['mapper'];
if (!isset($this->with[$member])) {
if (!isset($this->with[$member])) {
if (\array_key_exists($this->mapper::BELONGS_TO[$member]['external'] . '_d' . ($this->depth), $result)) {
return isset($this->mapper::BELONGS_TO[$member]['column'])
? $result[$this->mapper::BELONGS_TO[$member]['external'] . '_d' . ($this->depth)]
@ -673,8 +776,6 @@ class ReadMapper extends DataMapperAbstract
}
}
// @todo: MUST handle if member is in with here!!! ???
if (isset($this->mapper::BELONGS_TO[$member]['column'])) {
return $result[$mapper::getColumnByMember($this->mapper::BELONGS_TO[$member]['column']) . '_d' . $this->depth];
}
@ -688,16 +789,16 @@ class ReadMapper extends DataMapperAbstract
// you want the profile but the account id is referenced
// in this case you can get the profile by loading the profile based on the account reference column
if (isset($this->mapper::BELONGS_TO[$member]['by'])) {
/** @var class-string<DataMapperFactory> $belongsToMapper */
$belongsToMapper = $this->createRelationMapper($mapper::get($this->db), $member);
/** @var self $belongsToMapper */
$belongsToMapper = $this->createRelationMapper($mapper::get($this->db), $member);
$belongsToMapper->depth = $this->depth + 1;
$belongsToMapper->where($this->mapper::BELONGS_TO[$member]['by'], $result[$mapper::getColumnByMember($this->mapper::BELONGS_TO[$member]['by']) . '_d' . $this->depth + 1], '=');
return $belongsToMapper->execute();
}
/** @var class-string<DataMapperFactory> $belongsToMapper */
$belongsToMapper = $this->createRelationMapper($mapper::get($this->db), $member);
/** @var self $belongsToMapper */
$belongsToMapper = $this->createRelationMapper($mapper::get($this->db), $member);
$belongsToMapper->depth = $this->depth + 1;
return $belongsToMapper->populateAbstract($result, $mapper::createBaseModel());
@ -706,13 +807,13 @@ class ReadMapper extends DataMapperAbstract
/**
* Fill object with relations
*
* @param mixed $obj Object to fill
* @param object $obj Object to fill
*
* @return void
*
* @since 1.0.0
*/
public function loadHasManyRelations(mixed $obj) : void
public function loadHasManyRelations(object $obj) : void
{
if (empty($this->with)) {
return;
@ -770,15 +871,15 @@ class ReadMapper extends DataMapperAbstract
$refProp = $refClass->getProperty($member);
if (!$refProp->isPublic()) {
$refProp->setAccessible(true);
$refProp->setValue($obj, !\is_array($objects)
$refProp->setValue($obj, !\is_array($objects) && ($many['conditional'] ?? false) === false
? [$many['mapper']::getObjectId($objects) => $objects]
: $objects
: $objects // if conditional === true the obj will be asigned (e.g. has many localizations but only one is loaded for the model)
);
$refProp->setAccessible(false);
} else {
$obj->{$member} = !\is_array($objects)
$obj->{$member} = !\is_array($objects) && ($many['conditional'] ?? false) === false
? [$many['mapper']::getObjectId($objects) => $objects]
: $objects;
: $objects; // if conditional === true the obj will be asigned (e.g. has many localizations but only one is loaded for the model)
}
continue;

View File

@ -21,16 +21,20 @@ use phpOMS\Utils\ArrayUtils;
/**
* Update mapper (CREATE).
*
* @todo: allow to define single fields which should be updated (e.g. only description)
* @todo: allow to define where clause if no object is loaded yet
*
* @package phpOMS\DataStorage\Database\Mapper
* @license OMS License 1.0
* @link https://orange-management.org
* @since 1.0.0
*/
class UpdateMapper extends DataMapperAbstract
final class UpdateMapper extends DataMapperAbstract
{
/**
* Create update mapper
*
* @return self
*
* @since 1.0.0
*/
public function update() : self
{
$this->type = MapperType::UPDATE;
@ -38,22 +42,37 @@ class UpdateMapper extends DataMapperAbstract
return $this;
}
/**
* Execute mapper
*
* @param mixed ...$options Options to pass to update mapper
*
* @return mixed
*
* @since 1.0.0
*/
public function execute(...$options) : mixed
{
switch($this->type) {
case MapperType::UPDATE:
/** @var object ...$options */
return $this->executeUpdate(...$options);
default:
return null;
}
}
public function executeUpdate(mixed $obj) : mixed
/**
* Execute mapper
*
* @param object $obj Object to update
*
* @return mixed
*
* @since 1.0.0
*/
public function executeUpdate(object $obj) : mixed
{
if (!isset($obj)) {
return null;
}
$refClass = new \ReflectionClass($obj);
$objId = $this->mapper::getObjectId($obj, $refClass);
@ -72,6 +91,17 @@ class UpdateMapper extends DataMapperAbstract
return $objId;
}
/**
* Update model
*
* @param object $obj Object to update
* @param mixed $objId Id of the object to update
* @param \ReflectionClass $refClass Reflection of the object ot update
*
* @return void
*
* @since 1.0.0
*/
private function updateModel(object $obj, mixed $objId, \ReflectionClass $refClass = null) : void
{
// Model doesn't have anything to update
@ -87,7 +117,8 @@ class UpdateMapper extends DataMapperAbstract
$propertyName = \stripos($column['internal'], '/') !== false ? \explode('/', $column['internal'])[0] : $column['internal'];
if (isset($this->mapper::HAS_MANY[$propertyName])
|| $column['internal'] === $this->mapper::PRIMARYFIELD
|| ($column['readonly'] ?? false === true)
|| (($column['readonly'] ?? false) === true && !isset($this->with[$propertyName]))
|| (($column['writeonly'] ?? false) === true && !isset($this->with[$propertyName]))
) {
continue;
}
@ -95,7 +126,7 @@ class UpdateMapper extends DataMapperAbstract
$refClass = $refClass ?? new \ReflectionClass($obj);
$property = $refClass->getProperty($propertyName);
if (!($isPublic = $property->isPublic())) {
if (!($property->isPublic())) {
$property->setAccessible(true);
$tValue = $property->getValue($obj);
$property->setAccessible(false);
@ -104,26 +135,14 @@ class UpdateMapper extends DataMapperAbstract
}
if (isset($this->mapper::OWNS_ONE[$propertyName])) {
$id = $this->updateOwnsOne($propertyName, $tValue);
$id = \is_object($tValue) ? $this->updateOwnsOne($propertyName, $tValue) : $tValue;
$value = $this->parseValue($column['type'], $id);
/**
* @todo Orange-Management/phpOMS#232
* If a model gets updated all it's relations are also updated.
* This should be prevented if the relations didn't change.
* No solution yet.
*/
$query->set([$this->mapper::TABLE . '.' . $column['name'] => $value]);
} elseif (isset($this->mapper::BELONGS_TO[$propertyName])) {
$id = $this->updateBelongsTo($propertyName, $tValue);
$id = \is_object($tValue) ? $this->updateBelongsTo($propertyName, $tValue) : $tValue;
$value = $this->parseValue($column['type'], $id);
/**
* @todo Orange-Management/phpOMS#232
* If a model gets updated all it's relations are also updated.
* This should be prevented if the relations didn't change.
* No solution yet.
*/
$query->set([$this->mapper::TABLE . '.' . $column['name'] => $value]);
} elseif ($column['name'] !== $this->mapper::PRIMARYFIELD) {
if (\stripos($column['internal'], '/') !== false) {
@ -143,46 +162,70 @@ class UpdateMapper extends DataMapperAbstract
$sth->execute();
}
} catch (\Throwable $t) {
// @codeCoverageIgnoreStart
echo $t->getMessage();
echo $query->toSql();
// @codeCoverageIgnoreEnd
}
}
private function updateBelongsTo(string $propertyName, mixed $obj) : mixed
/**
* Update belongs to
*
* @param string $propertyName Name of the property to update
* @param object $obj Object to update
*
* @return mixed
*
* @since 1.0.0
*/
private function updateBelongsTo(string $propertyName, object $obj) : mixed
{
if (!\is_object($obj)) {
return $obj;
}
/** @var class-string<DataMapperFactory> $mapper */
$mapper = $this->mapper::BELONGS_TO[$propertyName]['mapper'];
/** @var self $relMapper */
$relMapper = $this->createRelationMapper($mapper::update(db: $this->db), $propertyName);
$relMapper = $this->createRelationMapper($mapper::update(db: $this->db), $propertyName);
$relMapper->depth = $this->depth + 1;
return $relMapper->execute($obj);
}
private function updateOwnsOne(string $propertyName, mixed $obj) : mixed
/**
* Update owns one
*
* @param string $propertyName Name of the property to update
* @param object $obj Object to update
*
* @return mixed
*
* @since 1.0.0
*/
private function updateOwnsOne(string $propertyName, object $obj) : mixed
{
if (!\is_object($obj)) {
return $obj;
}
/** @var class-string<DataMapperFactory> $mapper */
$mapper = $this->mapper::OWNS_ONE[$propertyName]['mapper'];
/** @var self $relMapper */
$relMapper = $this->createRelationMapper($mapper::update(db: $this->db), $propertyName);
$relMapper = $this->createRelationMapper($mapper::update(db: $this->db), $propertyName);
$relMapper->depth = $this->depth + 1;
return $relMapper->execute($obj);
}
/**
* Update has many relations
*
* @param \ReflectionClass $refClass Reflection of the object containing the relations
* @param object $obj Object which contains the relations
* @param mixed $objId Object id which contains the relations
*
* @return void
*
* @since 1.0.0
*/
private function updateHasMany(\ReflectionClass $refClass, object $obj, mixed $objId) : void
{
// @todo: what if has_one has a has_many child (see readmapper, we already solved this here)
if (empty($this->with) || empty($this->mapper::HAS_MANY)) {
return;
}
@ -230,7 +273,7 @@ class UpdateMapper extends DataMapperAbstract
// already in db
if (!empty($primaryKey)) {
/** @var self $relMapper */
$relMapper = $this->createRelationMapper($mapper::update(db: $this->db), $propertyName);
$relMapper = $this->createRelationMapper($mapper::update(db: $this->db), $propertyName);
$relMapper->depth = $this->depth + 1;
$relMapper->execute($value);
@ -255,13 +298,23 @@ class UpdateMapper extends DataMapperAbstract
}
}
$objsIds[$propertyName][$key] = $mapper::create(db: $this->db)->execute($value); // @todo: pass where
$objsIds[$propertyName][$key] = $mapper::create(db: $this->db)->execute($value);
}
}
$this->updateRelationTable($objsIds, $objId);
}
/**
* Update has many relations if the relation is handled in a relation table
*
* @param array $objsIds Objects which should be related to the parent object
* @param mixed $objId Parent object id
*
* @return void
*
* @since 1.0.0
*/
private function updateRelationTable(array $objsIds, mixed $objId) : void
{
foreach ($this->mapper::HAS_MANY as $member => $many) {
@ -288,7 +341,7 @@ class UpdateMapper extends DataMapperAbstract
}
$sth->execute();
$result = $sth->fetchAll(\PDO::FETCH_COLUMN);
$result = $sth->fetchAll(\PDO::FETCH_COLUMN);
$removes = \array_diff($result, \array_values($objsIds[$member] ?? []));
$adds = \array_diff(\array_values($objsIds[$member] ?? []), $result);

View File

@ -27,8 +27,15 @@ use phpOMS\Utils\ArrayUtils;
* @link https://orange-management.org
* @since 1.0.0
*/
class WriteMapper extends DataMapperAbstract
final class WriteMapper extends DataMapperAbstract
{
/**
* Create create mapper
*
* @return self
*
* @since 1.0.0
*/
public function create() : self
{
$this->type = MapperType::CREATE;
@ -36,22 +43,37 @@ class WriteMapper extends DataMapperAbstract
return $this;
}
/**
* Execute mapper
*
* @param mixed ...$options Model to create
*
* @return mixed
*
* @since 1.0.0
*/
public function execute(...$options) : mixed
{
switch($this->type) {
case MapperType::CREATE:
/** @var object ...$options */
return $this->executeCreate(...$options);
default:
return null;
}
}
public function executeCreate(mixed $obj) : mixed
/**
* Create object
*
* @param object $obj Object to create
*
* @return mixed
*
* @since 1.0.0
*/
public function executeCreate(object $obj) : mixed
{
if (!isset($obj)) {
return null;
}
$refClass = new \ReflectionClass($obj);
if ($this->mapper::isNullModel($obj)) {
@ -72,6 +94,16 @@ class WriteMapper extends DataMapperAbstract
return $objId;
}
/**
* Create model
*
* @param object $obj Object to create
* @param \ReflectionClass $refClass Reflection of the object to create
*
* @return mixed
*
* @since 1.0.0
*/
private function createModel(object $obj, \ReflectionClass $refClass) : mixed
{
$query = new Builder($this->db);
@ -93,12 +125,12 @@ class WriteMapper extends DataMapperAbstract
}
if (isset($this->mapper::OWNS_ONE[$propertyName])) {
$id = $this->createOwnsOne($propertyName, $tValue);
$id = \is_object($tValue) ? $this->createOwnsOne($propertyName, $tValue) : $tValue;
$value = $this->parseValue($column['type'], $id);
$query->insert($column['name'])->value($value);
} elseif (isset($this->mapper::BELONGS_TO[$propertyName])) {
$id = $this->createBelongsTo($propertyName, $tValue);
$id = \is_object($tValue) ? $this->createBelongsTo($propertyName, $tValue) : $tValue;
$value = $this->parseValue($column['type'], $id);
$query->insert($column['name'])->value($value);
@ -131,9 +163,12 @@ class WriteMapper extends DataMapperAbstract
$sth = $this->db->con->prepare($query->toSql());
$sth->execute();
} catch (\Throwable $t) {
// @codeCoverageIgnoreStart
\var_dump($t->getMessage());
\var_dump($a = $query->toSql());
return -1;
// @codeCoverageIgnoreEND
}
$objId = empty($id = $this->mapper::getObjectId($obj, $refClass)) ? $this->db->con->lastInsertId() : $id;
@ -142,7 +177,17 @@ class WriteMapper extends DataMapperAbstract
return $objId;
}
private function createOwnsOne(string $propertyName, mixed $obj) : mixed
/**
* Create owns one model
*
* @param string $propertyName Name of the owns one property
* @param object $obj Object which contains the owns one model
*
* @return mixed
*
* @since 1.0.0
*/
private function createOwnsOne(string $propertyName, object $obj) : mixed
{
if (!\is_object($obj)) {
return $obj;
@ -159,7 +204,17 @@ class WriteMapper extends DataMapperAbstract
return $primaryKey;
}
private function createBelongsTo(string $propertyName, mixed $obj) : mixed
/**
* Create belongs to model
*
* @param string $propertyName Name of the belongs to property
* @param object $obj Object which contains the belongs to model
*
* @return mixed
*
* @since 1.0.0
*/
private function createBelongsTo(string $propertyName, object $obj) : mixed
{
if (!\is_object($obj)) {
return $obj;
@ -187,15 +242,26 @@ class WriteMapper extends DataMapperAbstract
$mapper = $this->mapper::BELONGS_TO[$propertyName]['mapper'];
$primaryKey = $mapper::getObjectId($obj);
// @todo: the $mapper::create() might cause a problem is 'by' is set. because we don't want to create this obj but the child obj.
// @todo: the $mapper::create() might cause a problem if 'by' is set. because we don't want to create this obj but the child obj.
return empty($primaryKey) ? $mapper::create(db: $this->db)->execute($obj) : $primaryKey;
}
/**
* Create has many models
*
* @param \ReflectionClass $refClass Reflection of the object to create
* @param object $obj Object to create
* @param mixed $objId Id of the parent object
*
* @return void
*
* @since 1.0.0
*/
private function createHasMany(\ReflectionClass $refClass, object $obj, mixed $objId) : void
{
foreach ($this->mapper::HAS_MANY as $propertyName => $rel) {
if (!isset($this->mapper::HAS_MANY[$propertyName]['mapper'])) {
throw new InvalidMapperException();
throw new InvalidMapperException(); // @codeCoverageIgnore
}
$property = $refClass->getProperty($propertyName);
@ -303,6 +369,17 @@ class WriteMapper extends DataMapperAbstract
}
}
/**
* Create has many relations if the relation is handled in a relation table
*
* @param string $propertyName Property which contains the has many models
* @param array $objsIds Objects which should be related to the parent object
* @param mixed $objId Parent object id
*
* @return void
*
* @since 1.0.0
*/
public function createRelationTable(string $propertyName, array $objsIds, mixed $objId) : void
{
if (empty($objsIds) || !isset($this->mapper::HAS_MANY[$propertyName]['external'])) {
@ -332,8 +409,10 @@ class WriteMapper extends DataMapperAbstract
$sth->execute();
}
} catch (\Throwable $e) {
// @codeCoverageIgnoreStart
\var_dump($e->getMessage());
\var_dump($relQuery->toSql());
// @codeCoverageIgnoreEnd
}
}
}

View File

@ -1307,10 +1307,6 @@ class Builder extends BuilderAbstract
*/
public function on(string | array $columns, string | array $operator = null, string | array $values = null, string | array $boolean = 'and', string $table = null) : self
{
if ($operator !== null && !\is_array($operator) && !\in_array(\strtolower($operator), self::OPERATORS)) {
throw new \InvalidArgumentException('Unknown operator.');
}
if (!\is_array($columns)) {
$columns = [$columns];
$operator = [$operator];
@ -1408,10 +1404,12 @@ class Builder extends BuilderAbstract
$sth->execute();
} catch (\Throwable $t) {
// @codeCoverageIgnoreStart
\var_dump($t->getMessage());
\var_dump($this->toSql());
$sth = null;
// @codeCoverageIgnoreEnd
}
return $sth;

View File

@ -32,7 +32,7 @@ class MysqlGrammar extends Grammar
* @var string
* @since 1.0.0
*/
protected string $systemIdentifierStart = '`';
public string $systemIdentifierStart = '`';
/**
* System identifier.
@ -40,7 +40,7 @@ class MysqlGrammar extends Grammar
* @var string
* @since 1.0.0
*/
protected string $systemIdentifierEnd = '`';
public string $systemIdentifierEnd = '`';
/**
* Compile random.

View File

@ -40,7 +40,7 @@ class SQLiteGrammar extends Grammar
* @var string
* @since 1.0.0
*/
protected string $systemIdentifierEnd = '`';
public string $systemIdentifierEnd = '`';
/**
* Compile random.

View File

@ -32,7 +32,7 @@ class SqlServerGrammar extends Grammar
* @var string
* @since 1.0.0
*/
protected string $systemIdentifierStart = '[';
public string $systemIdentifierStart = '[';
/**
* System identifier.
@ -40,7 +40,7 @@ class SqlServerGrammar extends Grammar
* @var string
* @since 1.0.0
*/
protected string $systemIdentifierEnd = ']';
public string $systemIdentifierEnd = ']';
/**
* Compile random.

View File

@ -33,7 +33,7 @@ class MysqlGrammar extends Grammar
* @var string
* @since 1.0.0
*/
protected string $systemIdentifierStart = '`';
public string $systemIdentifierStart = '`';
/**
* System identifier.
@ -41,7 +41,7 @@ class MysqlGrammar extends Grammar
* @var string
* @since 1.0.0
*/
protected string $systemIdentifierEnd = '`';
public string $systemIdentifierEnd = '`';
/**
* Compile remove

View File

@ -30,7 +30,7 @@ class SQLiteGrammar extends Grammar
* @var string
* @since 1.0.0
*/
protected string $systemIdentifierStart = '`';
public string $systemIdentifierStart = '`';
/**
* System identifier.
@ -38,5 +38,5 @@ class SQLiteGrammar extends Grammar
* @var string
* @since 1.0.0
*/
protected string $systemIdentifierEnd = '`';
public string $systemIdentifierEnd = '`';
}

View File

@ -30,7 +30,7 @@ class SqlServerGrammar extends Grammar
* @var string
* @since 1.0.0
*/
protected string $systemIdentifierStart = '[';
public string $systemIdentifierStart = '[';
/**
* System identifier.
@ -38,5 +38,5 @@ class SqlServerGrammar extends Grammar
* @var string
* @since 1.0.0
*/
protected string $systemIdentifierEnd = ']';
public string $systemIdentifierEnd = ']';
}

View File

@ -33,13 +33,13 @@ class CountryMapper extends DataMapperFactory
* @since 1.0.0
*/
public const COLUMNS = [
'country_id' => ['name' => 'country_id', 'type' => 'int', 'internal' => 'id'],
'country_name' => ['name' => 'country_name', 'type' => 'string', 'internal' => 'name'],
'country_code2' => ['name' => 'country_code2', 'type' => 'string', 'internal' => 'code2'],
'country_code3' => ['name' => 'country_code3', 'type' => 'string', 'internal' => 'code3'],
'country_numeric' => ['name' => 'country_numeric', 'type' => 'int', 'internal' => 'numeric'],
'country_region' => ['name' => 'country_region', 'type' => 'string', 'internal' => 'region'],
'country_developed' => ['name' => 'country_developed', 'type' => 'bool', 'internal' => 'isDeveloped'],
'country_id' => ['name' => 'country_id', 'type' => 'int', 'internal' => 'id'],
'country_name' => ['name' => 'country_name', 'type' => 'string', 'internal' => 'name'],
'country_code2' => ['name' => 'country_code2', 'type' => 'string', 'internal' => 'code2'],
'country_code3' => ['name' => 'country_code3', 'type' => 'string', 'internal' => 'code3'],
'country_numeric' => ['name' => 'country_numeric', 'type' => 'int', 'internal' => 'numeric'],
'country_region' => ['name' => 'country_region', 'type' => 'string', 'internal' => 'region'],
'country_developed' => ['name' => 'country_developed', 'type' => 'bool', 'internal' => 'isDeveloped'],
];
/**

View File

@ -24,6 +24,13 @@ namespace phpOMS\Localization;
*/
final class NullLocalization extends Localization
{
/**
* Constructor
*
* @param int $id Model id
*
* @since 1.0.0
*/
public function __construct(int $id = 0)
{
$this->id = $id;

View File

@ -639,10 +639,15 @@ final class ModuleManager
*
* @param string $module Module name
*
* @return \phpOMS\Module\ModuleAbstract
* @return object|\phpOMS\Module\ModuleAbstract
*
* @throws \Exception
*
* @todo Remove docblock type hint hack "object".
* The return type object is only used to stop the annoying warning that a method doesn't exist
* if you chain call the methods part of the returned ModuleAbstract implementation.
* Remove it once alternative inline type hinting is possible for the specific returned implementation
*
* @since 1.0.0
*/
public function get(string $module) : ModuleAbstract

View File

@ -125,28 +125,29 @@ class File extends FileAbstract implements FileInterface
{
$exists = self::exists($con, $path);
// @todo: consider to use the php://memory way, used in the seUpBeforeClass in the test
if ((ContentPutMode::hasFlag($mode, ContentPutMode::APPEND) && $exists)
|| (ContentPutMode::hasFlag($mode, ContentPutMode::PREPEND) && $exists)
|| (ContentPutMode::hasFlag($mode, ContentPutMode::REPLACE) && $exists)
|| (!$exists && ContentPutMode::hasFlag($mode, ContentPutMode::CREATE))
) {
$tmpFile = 'file' . \mt_rand();
if (ContentPutMode::hasFlag($mode, ContentPutMode::APPEND) && $exists) {
\file_put_contents($tmpFile, self::get($con, $path) . $content);
$content = self::get($con, $path) . $content;
} elseif (ContentPutMode::hasFlag($mode, ContentPutMode::PREPEND) && $exists) {
\file_put_contents($tmpFile, $content . self::get($con, $path));
$content = $content . self::get($con, $path);
} else {
if (!Directory::exists($con, \dirname($path))) {
Directory::create($con, \dirname($path), 0755, true);
}
\file_put_contents($tmpFile, $content);
}
\ftp_put($con, $path, $tmpFile, \FTP_BINARY);
$fp = \fopen('php://memory', 'r+');
\fwrite($fp, $content);
\rewind($fp);
\ftp_fput($con, $path, $fp);
\fclose($fp);
\ftp_chmod($con, 0755, $path);
\unlink($tmpFile);
return true;
}

View File

@ -39,55 +39,7 @@ final class DataMapperAbstractTest extends \PHPUnit\Framework\TestCase
*/
protected function setUp() : void
{
$this->model = new BaseModel();
$this->modelArray = [
'id' => 0,
'string' => 'Base',
'int' => 11,
'bool' => false,
'null' => null,
'float' => 1.3,
'json' => [1, 2, 3],
'jsonSerializable' => new class() implements \JsonSerializable {
public function jsonSerialize()
{
return [1, 2, 3];
}
},
'datetime' => new \DateTime('2005-10-11'),
'datetime_null' => null,
'conditional' => '',
'ownsOneSelf' => [
'id' => 0,
'string' => 'OwnsOne',
],
'belongsToOne' => [
'id' => 0,
'string' => 'BelongsTo',
],
'hasManyDirect' => [
[
'id' => 0,
'string' => 'ManyToManyDirect',
'to' => 0,
],
[
'id' => 0,
'string' => 'ManyToManyDirect',
'to' => 0,
],
],
'hasManyRelations' => [
[
'id' => 0,
'string' => 'ManyToManyRel',
],
[
'id' => 0,
'string' => 'ManyToManyRel',
],
],
];
$this->model = new BaseModel();
$GLOBALS['dbpool']->get()->con->prepare(
'CREATE TABLE `test_base` (
@ -231,10 +183,17 @@ final class DataMapperAbstractTest extends \PHPUnit\Framework\TestCase
*/
public function testRead() : void
{
$id = BaseModelMapper::create()->execute($this->model);
$id = BaseModelMapper::create()->execute($this->model);
/** @var BaseModel $modelR */
$modelR = BaseModelMapper::get()->where('id', $id)->execute();
$modelR = BaseModelMapper::get()
->with('belongsToOne')
->with('ownsOneSelf')
->with('hasManyDirect')
->with('hasMnayRelations')
->with('conditional')
->where('id', $id)
->execute();
self::assertEquals($this->model->getId(), $modelR->getId());
self::assertEquals($this->model->string, $modelR->string);
@ -292,19 +251,6 @@ final class DataMapperAbstractTest extends \PHPUnit\Framework\TestCase
self::assertEquals($model2->getId(), $by->getId());
}
public function testGetCached() : void
{
$id = BaseModelMapper::create()->execute($this->model);
/** @var BaseModel $modelR */
$modelR = BaseModelMapper::get()->where('id', $id)->execute();
/** @var BaseModel $modelR2 */
$modelR2 = BaseModelMapper::get()->where('id', $id)->execute();
self::assertEquals($modelR->getId(), $modelR2->getId());
}
public function testGetNewest() : void
{
$model1 = new BaseModel();
@ -415,7 +361,7 @@ final class DataMapperAbstractTest extends \PHPUnit\Framework\TestCase
public function testUpdate() : void
{
$id = BaseModelMapper::create()->execute($this->model);
$modelR = BaseModelMapper::get()->where('id', $id)->execute();
$modelR = BaseModelMapper::get()->with('hasManyDirect')->with('hasManyRelations')->where('id', $id)->execute();
$modelR->string = 'Update';
$modelR->int = '321';
@ -425,7 +371,7 @@ final class DataMapperAbstractTest extends \PHPUnit\Framework\TestCase
$modelR->datetime = new \DateTime('now');
$modelR->datetime_null = null;
$id2 = BaseModelMapper::update()->execute($modelR);
$id2 = BaseModelMapper::update()->with('hasManyDirect')->with('hasManyRelations')->execute($modelR);
$modelR2 = BaseModelMapper::get()->where('id', $id2)->execute();
self::assertEquals($modelR->string, $modelR2->string);
@ -435,11 +381,6 @@ final class DataMapperAbstractTest extends \PHPUnit\Framework\TestCase
self::assertEquals($modelR->null, $modelR2->null);
self::assertEquals($modelR->datetime->format('Y-m-d'), $modelR2->datetime->format('Y-m-d'));
self::assertNull($modelR2->datetime_null);
/**
* @todo Orange-Management/phpOMS#226
* Test the update of a model with relations (update relations).
*/
}
/**
@ -454,17 +395,11 @@ final class DataMapperAbstractTest extends \PHPUnit\Framework\TestCase
*/
public function testDelete() : void
{
/*
$id = BaseModelMapper::create()->execute($this->model);
BaseModelMapper::delete($this->model);
BaseModelMapper::delete()->with('belongsToOne')->with('ownsOneSelf')->with('hasManyRelations')->execute($this->model);
$modelR = BaseModelMapper::get()->where('id', $id)->execute();
self::assertInstanceOf('phpOMS\tests\DataStorage\Database\TestModel\NullBaseModel', $modelR);
*/
/**
* @todo Orange-Management/phpOMS#225
* Test the deletion of a model with relations (deleting relations).
*/
}
public function testDeleteHasManyRelation() : void
@ -482,4 +417,24 @@ final class DataMapperAbstractTest extends \PHPUnit\Framework\TestCase
self::assertCount($count1 - 1, $base->hasManyRelations);
}
public function testReader() : void
{
self::assertInstanceOf('phpOMS\DataStorage\Database\Mapper\ReadMapper', BaseModelMapper::reader());
}
public function testWriter() : void
{
self::assertInstanceOf('phpOMS\DataStorage\Database\Mapper\WriteMapper', BaseModelMapper::writer());
}
public function testUpdater() : void
{
self::assertInstanceOf('phpOMS\DataStorage\Database\Mapper\UpdateMapper', BaseModelMapper::updater());
}
public function testRemover() : void
{
self::assertInstanceOf('phpOMS\DataStorage\Database\Mapper\DeleteMapper', BaseModelMapper::remover());
}
}

View File

@ -15,6 +15,9 @@ declare(strict_types=1);
namespace phpOMS\tests\DataStorage\Database\Query;
use phpOMS\DataStorage\Database\Connection\MysqlConnection;
use phpOMS\DataStorage\Database\Connection\PostgresConnection;
use phpOMS\DataStorage\Database\Connection\SQLiteConnection;
use phpOMS\DataStorage\Database\Connection\SqlServerConnection;
use phpOMS\DataStorage\Database\Query\Builder;
use phpOMS\DataStorage\Database\Query\Parameter;
@ -25,45 +28,56 @@ use phpOMS\DataStorage\Database\Query\Parameter;
*/
final class BuilderTest extends \PHPUnit\Framework\TestCase
{
protected $con;
/**
* {@inheritdoc}
*/
protected function setUp() : void
public function dbConnectionProvider() : array
{
$this->con = new MysqlConnection($GLOBALS['CONFIG']['db']['core']['masters']['admin']);
return [
[new MysqlConnection($GLOBALS['CONFIG']['db']['core']['masters']['admin'])],
[new PostgresConnection($GLOBALS['CONFIG']['db']['core']['postgresql']['admin'])],
[new SQLiteConnection($GLOBALS['CONFIG']['db']['core']['sqlite']['admin'])],
[new SqlServerConnection($GLOBALS['CONFIG']['db']['core']['mssql']['admin'])],
];
}
/**
* @testdox Mysql selects form a valid query
* @group framework
* @dataProvider dbConnectionProvider
*/
public function testMysqlSelect() : void
public function testSelect($con) : void
{
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` WHERE `a`.`test` = 1;';
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` as t FROM `a` as b WHERE `a`.`test` = 1;';
$query = new Builder($con);
$sql = 'SELECT [a].[test] as t FROM [a] as b WHERE [a].[test] = 1;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->selectAs('a.test', 't')->fromAs('a', 'b')->where('a.test', '=', 1)->toSql());
$query = new Builder($this->con);
$sql = 'SELECT DISTINCT `a`.`test` FROM `a` WHERE `a`.`test` = 1;';
$query = new Builder($con);
$sql = 'SELECT DISTINCT [a].[test] FROM [a] WHERE [a].[test] = 1;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->distinct()->from('a')->where('a.test', '=', 1)->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test`, `b`.`test` FROM `a`, `b` WHERE `a`.`test` = \'abc\';';
$query = new Builder($con);
$sql = 'SELECT [a].[test], [b].[test] FROM [a], [b] WHERE [a].[test] = \'abc\';';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test', 'b.test')->from('a', 'b')->where('a.test', '=', 'abc')->toSql());
$query = new Builder($this->con);
$query = new Builder($con);
$datetime = new \DateTime('now');
$sql = 'SELECT `a`.`test`, `b`.`test` FROM `a`, `b` WHERE `a`.`test` = \'' . $datetime->format('Y-m-d H:i:s') . '\';';
$sql = 'SELECT [a].[test], [b].[test] FROM [a], [b] WHERE [a].[test] = \'' . $datetime->format('Y-m-d H:i:s')
. '\';';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test', 'b.test')->from('a', 'b')->where('a.test', '=', $datetime)->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test`, `b`.`test` FROM `a`, `b` WHERE `a`.`test` = \'abc\' ORDER BY `a`.`test` ASC, `b`.`test` DESC;';
$query = new Builder($con);
$sql = 'SELECT [a].[test], [b].[test] FROM [a], [b] WHERE [a].[test] = \'abc\' ORDER BY [a].[test] ASC, [b].[test] DESC;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql,
$query->select('a.test', 'b.test')
->from('a', 'b')
@ -72,8 +86,9 @@ final class BuilderTest extends \PHPUnit\Framework\TestCase
->toSql()
);
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test`, `b`.`test` FROM `a`, `b` WHERE `a`.`test` = :abcValue ORDER BY `a`.`test` ASC, `b`.`test` DESC;';
$query = new Builder($con);
$sql = 'SELECT [a].[test], [b].[test] FROM [a], [b] WHERE [a].[test] = :abcValue ORDER BY [a].[test] ASC, [b].[test] DESC;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql,
$query->select('a.test', 'b.test')
->from('a', 'b')
@ -83,429 +98,598 @@ final class BuilderTest extends \PHPUnit\Framework\TestCase
);
self::assertEquals($query->toSql(), $query->__toString());
}
$query = new Builder($this->con);
public function testRandomMysql() : void
{
$con = new MysqlConnection($GLOBALS['CONFIG']['db']['core']['masters']['admin']);
$query = new Builder($con);
$sql = 'SELECT `a`.`test` FROM `a` as b WHERE `a`.`test` = 1 ORDER BY \rand() LIMIT 1;';
self::assertEquals($sql, $query->random('a.test')->fromAs('a', 'b')->where('a.test', '=', 1)->toSql());
}
public function testRandomPostgresql() : void
{
$con = new PostgresConnection($GLOBALS['CONFIG']['db']['core']['postgresql']['admin']);
$query = new Builder($con);
$sql = 'SELECT "a"."test" FROM "a" as b ORDER BY RANDOM() LIMIT 1;';
self::assertEquals($sql, $query->random('a.test')->fromAs('a', 'b')->where('a.test', '=', 1)->toSql());
}
public function testRandomSQLite() : void
{
$con = new SQLiteConnection($GLOBALS['CONFIG']['db']['core']['sqlite']['admin']);
$query = new Builder($con);
$sql = 'SELECT `a`.`test` FROM `a` as b ORDER BY RANDOM() LIMIT 1;';
self::assertEquals($sql, $query->random('a.test')->fromAs('a', 'b')->where('a.test', '=', 1)->toSql());
}
public function testRandomSqlServer() : void
{
$con = new SqlServerConnection($GLOBALS['CONFIG']['db']['core']['mssql']['admin']);
$query = new Builder($con);
$sql = 'SELECT TOP 1 [a].[test] FROM [a] as b ORDER BY IDX FETCH FIRST 1 ROWS ONLY;';
self::assertEquals($sql, $query->random('a.test')->fromAs('a', 'b')->where('a.test', '=', 1)->toSql());
}
/**
* @testdox Mysql orders form a valid query
* @group framework
* @dataProvider dbConnectionProvider
*/
public function testMysqlOrder() : void
public function testOrder($con) : void
{
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` WHERE `a`.`test` = 1 ORDER BY `a`.`test` DESC;';
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 ORDER BY [a].[test] DESC;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->newest('a.test')->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` WHERE `a`.`test` = 1 ORDER BY `a`.`test` ASC;';
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 ORDER BY [a].[test] ASC;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->oldest('a.test')->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` WHERE `a`.`test` = 1 ORDER BY `a`.`test` DESC;';
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 ORDER BY [a].[test] DESC;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->orderBy('a.test', 'DESC')->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` WHERE `a`.`test` = 1 ORDER BY `a`.`test` ASC;';
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 ORDER BY [a].[test] ASC;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->orderBy('a.test', 'ASC')->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` WHERE `a`.`test` = 1 ORDER BY `a`.`test` DESC, `a`.`test2` DESC;';
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 ORDER BY [a].[test] DESC, [a].[test2] DESC;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->orderBy(['a.test', 'a.test2'], ['DESC', 'DESC'])->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` WHERE `a`.`test` = 1 ORDER BY `a`.`test` ASC, `a`.`test2` ASC;';
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 ORDER BY [a].[test] ASC, [a].[test2] ASC;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->orderBy(['a.test', 'a.test2'], 'ASC')->toSql());
}
/**
* @testdox Mysql offsets and limits form a valid query
* @group framework
* @dataProvider dbConnectionProvider
*/
public function testMysqlOffsetLimit() : void
public function testOffsetLimit($con) : void
{
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` WHERE `a`.`test` = 1 LIMIT 3;';
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 LIMIT 3;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->limit(3)->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` WHERE `a`.`test` = 1 OFFSET 3;';
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 OFFSET 3;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->offset(3)->toSql());
}
/**
* @testdox Mysql groupings form a valid query
* @group framework
* @dataProvider dbConnectionProvider
*/
public function testMysqlGroup() : void
public function testGroup($con) : void
{
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` WHERE `a`.`test` = 1 GROUP BY `a`;';
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 GROUP BY [a];';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->groupBy('a')->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` WHERE `a`.`test` = 1 GROUP BY `a`, `b`;';
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 GROUP BY [a], [b];';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->groupBy('a')->groupBy('b')->toSql());
$query = new Builder($this->con);
$query = new Builder($con);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->groupBy('a', 'b')->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` WHERE `a`.`test` = :test GROUP BY `a`, `b`;';
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = :test GROUP BY [a], [b];';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', new Parameter('test'))->groupBy('a', 'b')->toSql());
}
/**
* @testdox Mysql wheres form a valid query
* @group framework
* @dataProvider dbConnectionProvider
*/
public function testMysqlWheres() : void
public function testWheres($con) : void
{
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` WHERE `a`.`test` = 1;';
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` WHERE `a`.`test` = 0;';
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 0;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', false)->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` WHERE `a`.`test` = 1;';
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', true)->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` WHERE `a`.`test` = \'string\';';
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = \'string\';';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 'string')->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` WHERE `a`.`test` = 1.23;';
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1.23;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1.23)->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` WHERE `a`.`test` = 1 AND `a`.`test2` = 2;';
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 AND [a].[test2] = 2;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->where('a.test2', '=', 2, 'and')->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` WHERE `a`.`test` = 1 AND `a`.`test2` = 2;';
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 AND [a].[test2] = 2;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->andWhere('a.test2', '=', 2)->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` WHERE `a`.`test` = 1 OR `a`.`test2` = 2;';
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 OR [a].[test2] = 2;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->where('a.test2', '=', 2, 'or')->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` WHERE `a`.`test` = 1 OR `a`.`test2` = 2;';
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 OR [a].[test2] = 2;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->orWhere('a.test2', '=', 2)->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` WHERE `a`.`test` = 1 OR `a`.`test2` IS NULL;';
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 OR [a].[test2] IS NULL;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->whereNull('a.test2', 'or')->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` WHERE `a`.`test` = 1 OR `a`.`test2` IS NOT NULL;';
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 OR [a].[test2] IS NOT NULL;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->whereNotNull('a.test2', 'or')->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` WHERE `a`.`test` = 1 OR `a`.`test2` IN (1, 2, 3);';
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 OR [a].[test2] IN (1, 2, 3);';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->whereIn('a.test2', [1, 2, 3], 'or')->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` WHERE `a`.`test` = 1 OR `a`.`test2` IN (\'a\', \'b\', \'c\');';
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = 1 OR [a].[test2] IN (\'a\', \'b\', \'c\');';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', 1)->whereIn('a.test2', ['a', 'b', 'c'], 'or')->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` WHERE `a`.`test` = :testWhere OR `a`.`test2` IN (\'a\', :bValue, \'c\');';
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] WHERE [a].[test] = :testWhere OR [a].[test2] IN (\'a\', :bValue, \'c\');';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->where('a.test', '=', new Parameter('testWhere'))->whereIn('a.test2', ['a', new Parameter('bValue'), 'c'], 'or')->toSql());
}
/**
* @testdox Mysql joins form a valid query
* @group framework
* @dataProvider dbConnectionProvider
*/
public function testMysqlJoins() : void
public function testJoins($con) : void
{
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` JOIN `b` ON `a`.`id` = `b`.`id` WHERE `a`.`test` = 1;';
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = 1;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->join('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` JOIN `b` ON `a`.`id` = `b`.`id` OR `a`.`id2` = `b`.`id2` WHERE `a`.`test` = 1;';
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] JOIN [b] ON [a].[id] = [b].[id] OR [a].[id2] = [b].[id2] WHERE [a].[test] = 1;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->join('b')->on('a.id', '=', 'b.id')->orOn('a.id2', '=', 'b.id2')->where('a.test', '=', 1)->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` JOIN `b` ON `a`.`id` = `b`.`id` AND `a`.`id2` = `b`.`id2` WHERE `a`.`test` = 1;';
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] JOIN [b] ON [a].[id] = [b].[id] AND [a].[id2] = [b].[id2] WHERE [a].[test] = 1;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->join('b')->on('a.id', '=', 'b.id')->andOn('a.id2', '=', 'b.id2')->where('a.test', '=', 1)->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` LEFT JOIN `b` ON `a`.`id` = `b`.`id` WHERE `a`.`test` = 1;';
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] LEFT JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = 1;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->leftJoin('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` LEFT OUTER JOIN `b` ON `a`.`id` = `b`.`id` WHERE `a`.`test` = 1;';
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] LEFT OUTER JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = 1;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->leftOuterJoin('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` LEFT INNER JOIN `b` ON `a`.`id` = `b`.`id` WHERE `a`.`test` = 1;';
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] LEFT INNER JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = 1;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->leftInnerJoin('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` RIGHT JOIN `b` ON `a`.`id` = `b`.`id` WHERE `a`.`test` = 1;';
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] RIGHT JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = 1;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->rightJoin('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` RIGHT OUTER JOIN `b` ON `a`.`id` = `b`.`id` WHERE `a`.`test` = 1;';
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] RIGHT OUTER JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = 1;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->rightOuterJoin('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` RIGHT INNER JOIN `b` ON `a`.`id` = `b`.`id` WHERE `a`.`test` = 1;';
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] RIGHT INNER JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = 1;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->rightInnerJoin('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` OUTER JOIN `b` ON `a`.`id` = `b`.`id` WHERE `a`.`test` = 1;';
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] OUTER JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = 1;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->outerJoin('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` INNER JOIN `b` ON `a`.`id` = `b`.`id` WHERE `a`.`test` = 1;';
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] INNER JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = 1;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->innerJoin('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` CROSS JOIN `b` ON `a`.`id` = `b`.`id` WHERE `a`.`test` = 1;';
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] CROSS JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = 1;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->crossJoin('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` FULL JOIN `b` ON `a`.`id` = `b`.`id` WHERE `a`.`test` = 1;';
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] FULL JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = 1;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->fullJoin('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
$query = new Builder($this->con);
$sql = 'SELECT `a`.`test` FROM `a` FULL OUTER JOIN `b` ON `a`.`id` = `b`.`id` WHERE `a`.`test` = 1;';
$query = new Builder($con);
$sql = 'SELECT [a].[test] FROM [a] FULL OUTER JOIN [b] ON [a].[id] = [b].[id] WHERE [a].[test] = 1;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->select('a.test')->from('a')->fullOuterJoin('b')->on('a.id', '=', 'b.id')->where('a.test', '=', 1)->toSql());
}
/**
* @testdox Mysql inserts form a valid query
* @group framework
* @dataProvider dbConnectionProvider
*/
public function testMysqlInsert() : void
public function testInsert($con) : void
{
$query = new Builder($this->con);
$sql = 'INSERT INTO `a` VALUES (1, \'test\');';
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
$sql = 'INSERT INTO [a] VALUES (1, \'test\');';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->insert()->into('a')->values(1, 'test')->toSql());
$query = new Builder($this->con);
$sql = 'INSERT INTO `a` VALUES (1, \'test\');';
$query = new Builder($con);
$sql = 'INSERT INTO [a] VALUES (1, \'test\');';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->insert()->into('a')->value([1, 'test'])->toSql());
$query = new Builder($this->con);
$sql = 'INSERT INTO `a` (`test`, `test2`) VALUES (1, \'test\');';
$query = new Builder($con);
$sql = 'INSERT INTO [a] ([test], [test2]) VALUES (1, \'test\');';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->insert('test', 'test2')->into('a')->values(1, 'test')->toSql());
self::assertEquals([[1, 'test']], $query->getValues());
$query = new Builder($this->con);
$sql = 'INSERT INTO `a` (`test`, `test2`) VALUES (1, \'test\'), (2, \'test2\');';
$query = new Builder($con);
$sql = 'INSERT INTO [a] ([test], [test2]) VALUES (1, \'test\'), (2, \'test2\');';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->insert('test', 'test2')->into('a')->values(1, 'test')->values(2, 'test2')->toSql());
$query = new Builder($this->con);
$sql = 'INSERT INTO `a` (`test`, `test2`) VALUES (:test, :test2);';
$query = new Builder($con);
$sql = 'INSERT INTO [a] ([test], [test2]) VALUES (:test, :test2);';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->insert('test', 'test2')->into('a')->values(new Parameter('test'), new Parameter('test2'))->toSql());
}
/**
* @testdox Mysql deletes form a valid query
* @group framework
* @dataProvider dbConnectionProvider
*/
public function testMysqlDelete() : void
public function testDelete($con) : void
{
$query = new Builder($this->con);
$sql = 'DELETE FROM `a` WHERE `a`.`test` = 1;';
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
$sql = 'DELETE FROM [a] WHERE [a].[test] = 1;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->delete()->from('a')->where('a.test', '=', 1)->toSql());
$query = new Builder($this->con);
$sql = 'DELETE FROM `a` WHERE `a`.`test` = :testVal;';
$query = new Builder($con);
$sql = 'DELETE FROM [a] WHERE [a].[test] = :testVal;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->delete()->from('a')->where('a.test', '=', new Parameter('testVal'))->toSql());
}
/**
* @testdox Mysql updates form a valid query
* @group framework
* @dataProvider dbConnectionProvider
*/
public function testMysqlUpdate() : void
public function testUpdate($con) : void
{
$query = new Builder($this->con);
$sql = 'UPDATE `a` SET `a`.`test` = 1, `a`.`test2` = 2 WHERE `a`.`test` = 1;';
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
$sql = 'UPDATE [a] SET [a].[test] = 1, [a].[test2] = 2 WHERE [a].[test] = 1;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->update('a')->set(['a.test' => 1])->set(['a.test2' => 2])->where('a.test', '=', 1)->toSql());
$query = new Builder($this->con);
$sql = 'UPDATE `a` SET `a`.`test` = 1, `a`.`test2` = 2 WHERE `a`.`test` = 1;';
$query = new Builder($con);
$sql = 'UPDATE [a] SET [a].[test] = 1, [a].[test2] = 2 WHERE [a].[test] = 1;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->update('a')->sets('a.test', 1)->sets('a.test2', 2)->where('a.test', '=', 1)->toSql());
$query = new Builder($this->con);
$sql = 'UPDATE `a` SET `a`.`test` = 1, `a`.`test2` = :test2 WHERE `a`.`test` = :test3;';
$query = new Builder($con);
$sql = 'UPDATE [a] SET [a].[test] = 1, [a].[test2] = :test2 WHERE [a].[test] = :test3;';
$sql = \str_replace(['[', ']'], [$iS, $iE], $sql);
self::assertEquals($sql, $query->update('a')->set(['a.test' => 1])->set(['a.test2' => new Parameter('test2')])->where('a.test', '=', new Parameter('test3'))->toSql());
}
/**
* @testdox Raw queries get output as defined
* @group framework
* @dataProvider dbConnectionProvider
*/
public function testRawInputOutput() : void
public function testRawInputOutput($con) : void
{
$query = new Builder($this->con);
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con);
self::assertEquals('SELECT test.val FROM test;', $query->raw('SELECT test.val FROM test;')->toSql());
}
/**
* @testdox Read only queries allow selects
* @group framework
* @dataProvider dbConnectionProvider
*/
public function testReadOnlyRawSelect() : void
public function testReadOnlyRawSelect($con) : void
{
$query = new Builder($this->con, true);
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$query = new Builder($con, true);
self::assertInstanceOf(Builder::class, $query->raw('SELECT * from oms;'));
}
/**
* @testdox Read only queries don't allow drops
* @group framework
* @dataProvider dbConnectionProvider
*/
public function testReadOnlyRawDrop() : void
public function testReadOnlyRawDrop($con) : void
{
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$this->expectException(\Exception::class);
$query = new Builder($this->con, true);
$query = new Builder($con, true);
$query->raw('DROP DATABASE oms;');
}
/**
* @testdox Read only queries don't allow deletes
* @group framework
* @dataProvider dbConnectionProvider
*/
public function testReadOnlyRawDelete() : void
public function testReadOnlyRawDelete($con) : void
{
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$this->expectException(\Exception::class);
$query = new Builder($this->con, true);
$query = new Builder($con, true);
$query->raw('DELETE oms;');
}
/**
* @testdox Read only queries don't allow creates
* @group framework
* @dataProvider dbConnectionProvider
*/
public function testReadOnlyRawCreate() : void
public function testReadOnlyRawCreate($con) : void
{
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$this->expectException(\Exception::class);
$query = new Builder($this->con, true);
$query = new Builder($con, true);
$query->raw('CREATE oms;');
}
/**
* @testdox Read only queries don't allow modifications
* @group framework
* @dataProvider dbConnectionProvider
*/
public function testReadOnlyRawAlter() : void
public function testReadOnlyRawAlter($con) : void
{
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$this->expectException(\Exception::class);
$query = new Builder($this->con, true);
$query = new Builder($con, true);
$query->raw('ALTER oms;');
}
/**
* @testdox Read only queries don't allow inserts
* @group framework
* @dataProvider dbConnectionProvider
*/
public function testReadOnlyInsert() : void
public function testReadOnlyInsert($con) : void
{
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$this->expectException(\Exception::class);
$query = new Builder($this->con, true);
$query = new Builder($con, true);
$query->insert('test');
}
/**
* @testdox Read only queries don't allow updates
* @group framework
* @dataProvider dbConnectionProvider
*/
public function testReadOnlyUpdate() : void
public function testReadOnlyUpdate($con) : void
{
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$this->expectException(\Exception::class);
$query = new Builder($this->con, true);
$query = new Builder($con, true);
$query->update();
}
/**
* @testdox Read only queries don't allow deletes
* @group framework
* @dataProvider dbConnectionProvider
*/
public function testReadOnlyDelete() : void
public function testReadOnlyDelete($con) : void
{
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$this->expectException(\Exception::class);
$query = new Builder($this->con, true);
$query = new Builder($con, true);
$query->delete();
}
/**
* @testdox Invalid select types throw a InvalidArgumentException
* @group framework
* @dataProvider dbConnectionProvider
*/
public function testInvalidSelectParameter() : void
public function testInvalidSelectParameter($con) : void
{
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$this->expectException(\InvalidArgumentException::class);
$query = new Builder($this->con, true);
$query = new Builder($con, true);
$query->select(false);
}
/**
* @testdox Invalid from types throw a InvalidArgumentException
* @group framework
* @dataProvider dbConnectionProvider
*/
public function testInvalidFromParameter() : void
public function testInvalidFromParameter($con) : void
{
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$this->expectException(\InvalidArgumentException::class);
$query = new Builder($this->con, true);
$query = new Builder($con, true);
$query->from(false);
}
/**
* @testdox Invalid group types throw a InvalidArgumentException
* @group framework
* @dataProvider dbConnectionProvider
*/
public function testInvalidGroupByParameter() : void
public function testInvalidGroupByParameter($con) : void
{
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$this->expectException(\InvalidArgumentException::class);
$query = new Builder($this->con, true);
$query = new Builder($con, true);
$query->groupBy(false);
}
/**
* @testdox Invalid where operators throw a InvalidArgumentException
* @group framework
* @dataProvider dbConnectionProvider
*/
public function testInvalidWhereOperator() : void
public function testInvalidWhereOperator($con) : void
{
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$this->expectException(\InvalidArgumentException::class);
$query = new Builder($this->con, true);
$query = new Builder($con, true);
$query->where('a', 'invalid', 'b');
}
/**
* @testdox Invalid join operators throw a InvalidArgumentException
* @group framework
* @dataProvider dbConnectionProvider
*/
public function testInvalidJoinOperator() : void
public function testInvalidJoinOperator($con) : void
{
$iS = $con->getGrammar()->systemIdentifierStart;
$iE = $con->getGrammar()->systemIdentifierEnd;
$this->expectException(\InvalidArgumentException::class);
$query = new Builder($this->con, true);
$query = new Builder($con, true);
$query->join('b')->on('a', 'invalid', 'b');
}
}

View File

@ -47,15 +47,15 @@ class BaseModelMapper extends DataMapperFactory
*/
public const BELONGS_TO = [
'belongsToOne' => [
'mapper' => BelongsToModelMapper::class,
'external' => 'test_base_belongs_to_one',
'mapper' => BelongsToModelMapper::class,
'external' => 'test_base_belongs_to_one',
],
];
public const OWNS_ONE = [
'ownsOneSelf' => [
'mapper' => OwnsOneModelMapper::class,
'external' => 'test_base_owns_one_self',
'mapper' => OwnsOneModelMapper::class,
'external' => 'test_base_owns_one_self',
],
];
@ -67,29 +67,47 @@ class BaseModelMapper extends DataMapperFactory
*/
public const HAS_MANY = [
'hasManyDirect' => [
'mapper' => ManyToManyDirectModelMapper::class,
'table' => 'test_has_many_direct',
'self' => 'test_has_many_direct_to',
'external' => null,
'mapper' => ManyToManyDirectModelMapper::class,
'table' => 'test_has_many_direct',
'self' => 'test_has_many_direct_to',
'external' => null,
],
'hasManyRelations' => [
'mapper' => ManyToManyRelModelMapper::class,
'table' => 'test_has_many_rel_relations',
'external' => 'test_has_many_rel_relations_src',
'self' => 'test_has_many_rel_relations_dest',
'mapper' => ManyToManyRelModelMapper::class,
'table' => 'test_has_many_rel_relations',
'external' => 'test_has_many_rel_relations_src',
'self' => 'test_has_many_rel_relations_dest',
],
'conditional' => [
'mapper' => ConditionalMapper::class,
'table' => 'test_conditional',
'self' => 'test_conditional_base',
'column' => 'title',
'external' => null,
'mapper' => ConditionalMapper::class,
'table' => 'test_conditional',
'self' => 'test_conditional_base',
'column' => 'title',
'external' => null,
],
];
/**
* Primary table.
*
* @var string
* @since 1.0.0
*/
public const TABLE = 'test_base';
/**
* Created at.
*
* @var string
* @since 1.0.0
*/
public const CREATED_AT = 'test_base_datetime';
/**
* Primary field name.
*
* @var string
* @since 1.0.0
*/
public const PRIMARYFIELD ='test_base_id';
}

View File

@ -29,7 +29,19 @@ class BelongsToModelMapper extends DataMapperFactory
'test_belongs_to_one_string' => ['name' => 'test_belongs_to_one_string', 'type' => 'string', 'internal' => 'string'],
];
/**
* Primary table.
*
* @var string
* @since 1.0.0
*/
public const TABLE = 'test_belongs_to_one';
/**
* Primary field name.
*
* @var string
* @since 1.0.0
*/
public const PRIMARYFIELD ='test_belongs_to_one_id';
}

View File

@ -29,7 +29,19 @@ class ManyToManyDirectModelMapper extends DataMapperFactory
'test_has_many_direct_to' => ['name' => 'test_has_many_direct_to', 'type' => 'int', 'internal' => 'to'],
];
/**
* Primary table.
*
* @var string
* @since 1.0.0
*/
public const TABLE = 'test_has_many_direct';
/**
* Primary field name.
*
* @var string
* @since 1.0.0
*/
public const PRIMARYFIELD ='test_has_many_direct_id';
}

View File

@ -29,7 +29,19 @@ class ManyToManyRelModelMapper extends DataMapperFactory
'test_has_many_rel_string' => ['name' => 'test_has_many_rel_string', 'type' => 'string', 'internal' => 'string'],
];
/**
* Primary table.
*
* @var string
* @since 1.0.0
*/
public const TABLE = 'test_has_many_rel';
/**
* Primary field name.
*
* @var string
* @since 1.0.0
*/
public const PRIMARYFIELD ='test_has_many_rel_id';
}

View File

@ -29,7 +29,19 @@ class OwnsOneModelMapper extends DataMapperFactory
'test_owns_one_string' => ['name' => 'test_owns_one_string', 'type' => 'string', 'internal' => 'string'],
];
/**
* Primary table.
*
* @var string
* @since 1.0.0
*/
public const TABLE = 'test_owns_one';
/**
* Primary field name.
*
* @var string
* @since 1.0.0
*/
public const PRIMARYFIELD ='test_owns_one_id';
}

View File

@ -1,2 +1,14 @@
<?php declare(strict_types=1);
<?php
/**
* Orange Management
*
* PHP Version 8.0
*
* @package Template
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://orange-management.org
*/
declare(strict_types=1);
return [1, 2, 3];

View File

@ -1,2 +1,14 @@
<?php declare(strict_types=1);
<?php
/**
* Orange Management
*
* PHP Version 8.0
*
* @package Template
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://orange-management.org
*/
declare(strict_types=1);
return '<strong>Test</strong>';