This commit is contained in:
Dennis Eichhorn 2022-01-22 21:53:38 +01:00
parent d20da74b77
commit 169b5535d3
24 changed files with 149 additions and 100 deletions

View File

@ -192,6 +192,26 @@ class Account implements \JsonSerializable, ArrayableInterface
return $this->groups;
}
/**
* Get ids of groups
*
* @return int[]
*
* @since 1.0.0
*/
public function getGroupIds() : array
{
/*
$ids = [];
foreach ($this->groups as $group) {
$ids[] = $group->getId();
}
return $ids;
*/
return \array_keys($this->groups);
}
/**
* Add group.
*

View File

@ -123,16 +123,16 @@ abstract class GrammarAbstract
*/
public function compileQuery(BuilderAbstract $query) : string
{
return \trim(
\implode(' ',
\array_filter(
$this->compileComponents($query),
function ($value) {
return (string) $value !== '';
}
)
)
) . ';';
$components = $this->compileComponents($query);
$queryString = '';
foreach ($components as $component) {
if ($component !== '') {
$queryString .= $component . ' ';
}
}
return \substr($queryString, 0, -1) . ';';
}
/**
@ -148,12 +148,11 @@ abstract class GrammarAbstract
*/
protected function compileComponents(BuilderAbstract $query) : array
{
$sql = [];
if ($query->getType() === QueryType::RAW) {
return [$query->raw];
}
$sql = [];
$components = $this->getComponents($query->getType());
/* Loop all possible query components and if they exist compile them. */
@ -247,16 +246,24 @@ abstract class GrammarAbstract
}
}
$split = \explode('.', $system);
$fullSystem = '';
// The following code could have been handled with \explode more elegantly but \explode needs more memory and more time
// Normally this wouldn't be a problem but in this case there are so many function calls to this routine,
// that it makes sense to make this "minor" improvement.
if (($pos = \stripos($system, '.')) !== false) {
$split = [\substr($system, 0, $pos), \substr($system, $pos + 1)];
foreach ($split as $key => $system) {
$fullSystem .= '.'
. ($system !== '*' ? $identifierStart : '')
. $system
. ($system !== '*' ? $identifierEnd : '');
return ($split[0] !== '*' ? $identifierStart : '')
. $split[0]
. ($split[0] !== '*' ? $identifierEnd : '')
. '.'
. ($split[1] !== '*' ? $identifierStart : '')
. $split[1]
. ($split[1] !== '*' ? $identifierEnd : '');
}
return \ltrim($fullSystem, '.');
return ($system !== '*' ? $identifierStart : '')
. $system
. ($system !== '*' ? $identifierEnd : '');
}
}

View File

@ -350,7 +350,7 @@ abstract class DataMapperAbstract
return (bool) $value;
} elseif ($type === 'DateTime' || $type === 'DateTimeImmutable') {
return $value === null ? null : $value->format($this->mapper::$datetimeFormat);
} elseif ($type === 'Json' || $value instanceof \JsonSerializable) {
} elseif ($type === 'Json') {
return (string) \json_encode($value);
} elseif ($type === 'Serializable') {
return $value->serialize();

View File

@ -401,11 +401,6 @@ class DataMapperFactory
{
$class = empty(static::MODEL) ? \substr(static::class, 0, -6) : static::MODEL;
/**
* @todo Orange-Management/phpOMS#67
* Since some models require special initialization a model factory should be implemented.
* This could be a simple initialize() function in the mapper where the default initialize() is the current defined empty initialization in the DataMapperAbstract.
*/
return new $class();
}

View File

@ -19,8 +19,6 @@ use phpOMS\DataStorage\Database\Query\Builder;
/**
* Delete mapper (DELETE).
*
* @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

View File

@ -15,6 +15,7 @@ declare(strict_types=1);
namespace phpOMS\DataStorage\Database\Mapper;
use phpOMS\DataStorage\Database\Query\Builder;
use phpOMS\DataStorage\Database\Query\Where;
use phpOMS\Utils\ArrayUtils;
/**
@ -336,6 +337,13 @@ final class ReadMapper extends DataMapperAbstract
// where
foreach ($this->where as $member => $values) {
// handle where query
if ($member === '' && $values[0]['value'] instanceof Where) {
$query->where($values[0]['value'], boolean: $values[0]['comparison']);
continue;
}
if (($col = $this->mapper::getColumnByMember($member)) !== null) {
/* variable in model */
foreach ($values as $where) {
@ -445,7 +453,7 @@ final class ReadMapper extends DataMapperAbstract
} elseif (!isset($this->mapper::HAS_MANY[$member]['external']) && isset($this->mapper::HAS_MANY[$member]['column'])) {
// get HasManyQuery (but only for elements which have a 'column' defined)
// todo: handle self and self === null
// @todo: handle self and self === null
$query->leftJoin($rel['mapper']::TABLE, $rel['mapper']::TABLE . '_d' . ($this->depth + 1))
->on(
$this->mapper::TABLE . '_d' . $this->depth . '.' . ($rel['external'] ?? $this->mapper::PRIMARYFIELD), '=',
@ -531,7 +539,7 @@ final class ReadMapper extends DataMapperAbstract
if (\stripos($def['internal'], '/') !== false) {
$hasPath = true;
$path = \explode('/', $def['internal']);
$path = \explode('/', \ltrim($def['internal'], '/'));
$member = $path[0];
$refProp = $refClass->getProperty($path[0]);
@ -565,7 +573,7 @@ final class ReadMapper extends DataMapperAbstract
}
if (!empty($value)) {
// todo: find better solution. this was because of a bug with the sales billing list query depth = 4. The address was set (from the client, referral or creator) but then somehow there was a second address element which was all null and null cannot be asigned to a string variable (e.g. country). The problem with this solution is that if the model expects an initialization (e.g. at lest set the elements to null, '', 0 etc.) this is now not done.
// @todo: find better solution. this was because of a bug with the sales billing list query depth = 4. The address was set (from the client, referral or creator) but then somehow there was a second address element which was all null and null cannot be asigned to a string variable (e.g. country). The problem with this solution is that if the model expects an initialization (e.g. at lest set the elements to null, '', 0 etc.) this is now not done.
$refProp->setValue($obj, $value);
}
} elseif (isset($this->mapper::BELONGS_TO[$def['internal']])) {

View File

@ -343,6 +343,10 @@ final class UpdateMapper extends DataMapperAbstract
$sth->execute();
$result = $sth->fetchAll(\PDO::FETCH_COLUMN);
if ($result === false) {
return; // @codeCoverageIgnore
}
$removes = \array_diff($result, \array_values($objsIds[$member] ?? []));
$adds = \array_diff(\array_values($objsIds[$member] ?? []), $result);

View File

@ -109,19 +109,26 @@ final class WriteMapper extends DataMapperAbstract
$query = new Builder($this->db);
$query->into($this->mapper::TABLE);
$publicProperties = \get_object_vars($obj);
foreach ($this->mapper::COLUMNS as $column) {
$propertyName = \stripos($column['internal'], '/') !== false ? \explode('/', $column['internal'])[0] : $column['internal'];
if (isset($this->mapper::HAS_MANY[$propertyName])) {
$propertyName = \stripos($column['internal'], '/') !== false
? \explode('/', $column['internal'])[0]
: $column['internal'];
if (isset($this->mapper::HAS_MANY[$propertyName])
|| ($column['name'] === $this->mapper::PRIMARYFIELD && $this->mapper::AUTOINCREMENT)
) {
continue;
}
$property = $refClass->getProperty($propertyName);
if (!$property->isPublic()) {
if (!isset($publicProperties[$propertyName])) {
$property = $refClass->getProperty($propertyName);
$property->setAccessible(true);
$tValue = $property->getValue($obj);
$property->setAccessible(false);
} else {
$tValue = $obj->{$propertyName};
$tValue = $publicProperties[$propertyName];
}
if (isset($this->mapper::OWNS_ONE[$propertyName])) {
@ -134,20 +141,12 @@ final class WriteMapper extends DataMapperAbstract
$value = $this->parseValue($column['type'], $id);
$query->insert($column['name'])->value($value);
} elseif ($column['name'] !== $this->mapper::PRIMARYFIELD || !empty($tValue)) {
} else {
if (\stripos($column['internal'], '/') !== false) {
$path = \substr($column['internal'], \stripos($column['internal'], '/') + 1);
$tValue = ArrayUtils::getArray($path, $tValue, '/');
}
/*
if (($column['type'] === 'int' || $column['type'] === 'string')
&& \is_object($tValue) && \property_exists($tValue, 'id')
) {
$tValue =
}
*/
$value = $this->parseValue($column['type'], $tValue);
$query->insert($column['name'])->value($value);
@ -265,7 +264,6 @@ final class WriteMapper extends DataMapperAbstract
}
$property = $refClass->getProperty($propertyName);
if (!($isPublic = $property->isPublic())) {
$property->setAccessible(true);
$values = $property->getValue($obj);
@ -281,10 +279,12 @@ final class WriteMapper extends DataMapperAbstract
if (\is_object($values)) {
// conditionals
$relReflectionClass = new \ReflectionClass($values);
$relProperty = $relReflectionClass->getProperty($internalName);
$publicProperties = \get_object_vars($values);
if (!isset($publicProperties[$internalName])) {
$relReflectionClass = new \ReflectionClass($values);
$relProperty = $relReflectionClass->getProperty($internalName);
if (!$relProperty->isPublic()) {
$relProperty->setAccessible(true);
$relProperty->setValue($values, $objId);
$relProperty->setAccessible(false);

View File

@ -543,7 +543,7 @@ class Builder extends BuilderAbstract
*
* @since 1.0.0
*/
public function where(string | array | Where $columns, string | array $operator = null, mixed $values = null, string | array $boolean = 'and') : self
public function where(string | array | Builder $columns, string | array $operator = null, mixed $values = null, string | array $boolean = 'and') : self
{
if (!\is_array($columns)) {
$columns = [$columns];
@ -598,7 +598,7 @@ class Builder extends BuilderAbstract
*
* @since 1.0.0
*/
public function orWhere(string | array | Where $where, string | array $operator = null, mixed $values = null) : self
public function orWhere(string | array | Builder $where, string | array $operator = null, mixed $values = null) : self
{
return $this->where($where, $operator, $values, 'or');
}

View File

@ -334,13 +334,15 @@ class Grammar extends GrammarAbstract
} elseif (\is_int($value)) {
return (string) $value;
} elseif (\is_array($value)) {
$values = '';
$value = \array_values($value);
$count = \count($value) - 1;
$values = '(';
foreach ($value as $val) {
$values .= $this->compileValue($query, $val) . ', ';
for ($i = 0; $i < $count; ++$i) {
$values .= $this->compileValue($query, $value[$i]) . ', ';
}
return '(' . \rtrim($values, ', ') . ')';
return $values . $this->compileValue($query, $value[$count]) . ')';
} elseif ($value instanceof \DateTime) {
return $query->quote($value->format($this->datetimeFormat));
} elseif ($value === null) {
@ -602,17 +604,18 @@ class Grammar extends GrammarAbstract
*/
protected function compileInserts(Builder $query, array $columns) : string
{
$cols = '';
$count = \count($columns) - 1;
foreach ($columns as $column) {
$cols .= $this->compileSystem($column) . ', ';
}
if ($cols === '') {
if ($count === -1) {
return '';
}
return '(' . \rtrim($cols, ', ') . ')';
$cols = '(';
for ($i = 0; $i < $count; ++$i) {
$cols .= $this->compileSystem($columns[$i]) . ', ';
}
return $cols .= $this->compileSystem($columns[$count]) . ')';
}
/**
@ -627,17 +630,18 @@ class Grammar extends GrammarAbstract
*/
protected function compileValues(Builder $query, array $values) : string
{
$vals = '';
foreach ($values as $value) {
$vals .= $this->compileValue($query, $value) . ', ';
}
if ($vals === '') {
$values = \array_values($values);
$count = \count($values) - 1;
if ($count === -1) {
return '';
}
return 'VALUES ' . \rtrim($vals, ', ');
$vals = 'VALUES ';
for ($i = 0; $i < $count; ++$i) {
$vals .= $this->compileValue($query, $values[$i]) . ', ';
}
return $vals . $this->compileValue($query, $values[$count]);
}
/**

View File

@ -728,7 +728,7 @@ class Matrix implements \ArrayAccess, \Iterator
/**
* {@inheritdoc}
*/
public function offsetExists($offset)
public function offsetExists($offset) : bool
{
$row = (int) ($offset / $this->m);

View File

@ -14,7 +14,9 @@ declare(strict_types=1);
namespace phpOMS\Module;
use Modules\Admin\Models\PermissionAbstractMapper;
use phpOMS\Application\ApplicationAbstract;
use phpOMS\DataStorage\Database\Query\Builder;
use phpOMS\Message\RequestAbstract;
use phpOMS\Message\ResponseAbstract;
use phpOMS\System\MimeType;

View File

@ -39,7 +39,7 @@ Please note if you are only interested in using the framework without the web ap
#### End-User
After installing the requirements and configuring the webserver for the correct path navigate to https://your_url.com/Install and follow the installation process. Afterwards you will be redirected to the installed backend.
After installing the requirements and configuring the web server for the correct path navigate to https://your_url.com/Install and follow the installation process. Afterwards you will be redirected to the installed backend.
For more detailed information please checkout the [Installation Guide](https://orange-management.org/dev/guide?page=setup/installation).

View File

@ -141,6 +141,10 @@ class File extends FileAbstract implements FileInterface
}
$fp = \fopen('php://memory', 'r+');
if ($fp === false) {
return false; // @codeCoverageIgnore
}
\fwrite($fp, $content);
\rewind($fp);

View File

@ -31,6 +31,9 @@ final class KmeansTest extends \PHPUnit\Framework\TestCase
*/
public function testKmeans() : void
{
$seed = \mt_rand(\PHP_INT_MIN, \PHP_INT_MAX);
\mt_srand($seed);
$result = false;
// due to the random nature this can be false sometimes?!

View File

@ -53,6 +53,7 @@ final class DataMapperAbstractTest extends \PHPUnit\Framework\TestCase
`test_base_owns_one_self` int(11) DEFAULT NULL,
`test_base_json` varchar(254) DEFAULT NULL,
`test_base_json_serializable` varchar(254) DEFAULT NULL,
`test_base_serializable` varchar(254) DEFAULT NULL,
`test_base_datetime` datetime DEFAULT NULL,
`test_base_datetime_null` datetime DEFAULT NULL, /* There was a bug where it returned the current date because new \DateTime(null) === current date which is wrong, we want null as value! */
PRIMARY KEY (`test_base_id`)
@ -204,12 +205,9 @@ final class DataMapperAbstractTest extends \PHPUnit\Framework\TestCase
self::assertEquals($this->model->datetime->format('Y-m-d'), $modelR->datetime->format('Y-m-d'));
self::assertNull($modelR->datetime_null);
/**
* @todo Serializable and JsonSerializable data can be inserted and updated in the database but it's not possible to correctly populate a model with the data in its original format.
*/
//self::assertEquals('123', $modelR->serializable);
//self::assertEquals($this->model->json, $modelR->json);
//self::assertEquals([1, 2, 3], $modelR->jsonSerializable);
self::assertEquals($this->model->json, $modelR->json);
self::assertEquals([1, 2, 3], $modelR->jsonSerializable);
self::assertEquals('123', $modelR->serializable->value);
self::assertCount(2, $modelR->hasManyDirect);
self::assertCount(2, $modelR->hasManyRelations);
@ -364,7 +362,7 @@ final class DataMapperAbstractTest extends \PHPUnit\Framework\TestCase
$modelR = BaseModelMapper::get()->with('hasManyDirect')->with('hasManyRelations')->where('id', $id)->execute();
$modelR->string = 'Update';
$modelR->int = '321';
$modelR->int = 321;
$modelR->bool = true;
$modelR->float = 3.15;
$modelR->null = null;

View File

@ -36,6 +36,7 @@ final class SchemaMapperTest extends \PHPUnit\Framework\TestCase
`test_base_owns_one_self` int(11) DEFAULT NULL,
`test_base_json` varchar(254) DEFAULT NULL,
`test_base_json_serializable` varchar(254) DEFAULT NULL,
`test_base_serializable` varchar(254) DEFAULT NULL,
`test_base_datetime` datetime DEFAULT NULL,
`test_base_datetime_null` datetime DEFAULT NULL, /* There was a bug where it returned the current date because new \DateTime(null) === current date which is wrong, we want null as value! */
PRIMARY KEY (`test_base_id`)
@ -80,7 +81,7 @@ final class SchemaMapperTest extends \PHPUnit\Framework\TestCase
$schema = new SchemaMapper($GLOBALS['dbpool']->get());
self::assertEquals(
12,
13,
\count($schema->getFields('test_base'))
);
}

View File

@ -16,35 +16,35 @@ namespace phpOMS\tests\DataStorage\Database\TestModel;
class BaseModel
{
protected $id = 0;
protected int $id = 0;
public $string = 'Base';
public string $string = 'Base';
public $conditional = '';
public string $conditional = '';
public $int = 11;
public int $int = 11;
public $bool = false;
public bool $bool = false;
public $float = 1.3;
public float $float = 1.3;
public $null = null;
public $datetime = null;
public \DateTime $datetime;
public $datetime_null = null;
public ?\DateTime $datetime_null = null;
public $hasManyDirect = [];
public array $hasManyDirect = [];
public $hasManyRelations = [];
public array $hasManyRelations = [];
public $ownsOneSelf = 0;
public $belongsToOne = 0;
public $serializable = null;
public ?object $serializable = null;
public $json = [1, 2, 3];
public array $json = [1, 2, 3];
public $jsonSerializable = null;
@ -66,6 +66,8 @@ class BaseModel
$this->belongsToOne = new BelongsToModel();
$this->serializable = new class() implements \Serializable {
public $value = '';
public function serialize()
{
return '123';
@ -73,6 +75,7 @@ class BaseModel
public function unserialize($data) : void
{
$this->value = $data;
}
};

View File

@ -33,6 +33,7 @@ class BaseModelMapper extends DataMapperFactory
'test_base_float' => ['name' => 'test_base_float', 'type' => 'float', 'internal' => 'float'],
'test_base_json' => ['name' => 'test_base_json', 'type' => 'Json', 'internal' => 'json'],
'test_base_json_serializable' => ['name' => 'test_base_json_serializable', 'type' => 'Json', 'internal' => 'jsonSerializable'],
'test_base_serializable' => ['name' => 'test_base_serializable', 'type' => 'Serializable', 'internal' => 'serializable'],
'test_base_datetime' => ['name' => 'test_base_datetime', 'type' => 'DateTime', 'internal' => 'datetime'],
'test_base_datetime_null' => ['name' => 'test_base_datetime_null', 'type' => 'DateTime', 'internal' => 'datetime_null'],
'test_base_owns_one_self' => ['name' => 'test_base_owns_one_self', 'type' => 'int', 'internal' => 'ownsOneSelf'],

View File

@ -16,7 +16,7 @@ namespace phpOMS\tests\DataStorage\Database\TestModel;
class BelongsToModel
{
public $id = 0;
public int $id = 0;
public $string = 'BelongsTo';
public string $string = 'BelongsTo';
}

View File

@ -16,9 +16,9 @@ namespace phpOMS\tests\DataStorage\Database\TestModel;
class ManyToManyDirectModel
{
public $id = 0;
public int $id = 0;
public $string = 'ManyToManyDirect';
public string $string = 'ManyToManyDirect';
public int $to = 0;
}

View File

@ -16,7 +16,7 @@ namespace phpOMS\tests\DataStorage\Database\TestModel;
class ManyToManyRelModel
{
public $id = 0;
public int $id = 0;
public $string = 'ManyToManyRel';
public string $string = 'ManyToManyRel';
}

View File

@ -16,7 +16,7 @@ namespace phpOMS\tests\DataStorage\Database\TestModel;
class OwnsOneModel
{
public $id = 0;
public int $id = 0;
public $string = 'OwnsOne';
public string $string = 'OwnsOne';
}

View File

@ -295,6 +295,7 @@ final class ModuleAbstractTest extends \PHPUnit\Framework\TestCase
`test_base_owns_one_self` int(11) DEFAULT NULL,
`test_base_json` varchar(254) DEFAULT NULL,
`test_base_json_serializable` varchar(254) DEFAULT NULL,
`test_base_serializable` varchar(254) DEFAULT NULL,
`test_base_datetime` datetime DEFAULT NULL,
`test_base_datetime_null` datetime DEFAULT NULL, /* There was a bug where it returned the current date because new \DateTime(null) === current date which is wrong, we want null as value! */
PRIMARY KEY (`test_base_id`)