draft table search/filter impl.

This commit is contained in:
Dennis Eichhorn 2022-08-01 20:51:10 +02:00
parent d09b3db948
commit 87d07b3574
32 changed files with 423 additions and 21 deletions

View File

@ -402,6 +402,8 @@ abstract class DataMapperAbstract
return $value === null ? null : $value->format($this->mapper::$datetimeFormat);
} elseif ($type === 'Json') {
return (string) \json_encode($value);
} elseif ($type === 'compress') {
return (string) \gzdeflate($value);
} elseif ($type === 'Serializable') {
return $value->serialize();
} elseif (\is_object($value) && \method_exists($value, 'getId')) {

View File

@ -16,6 +16,8 @@ namespace phpOMS\DataStorage\Database\Mapper;
use phpOMS\DataStorage\Database\Connection\ConnectionAbstract;
use phpOMS\DataStorage\Database\Query\Builder;
use phpOMS\DataStorage\Database\Query\OrderType;
use phpOMS\DataStorage\Database\Query\Where;
/**
* Mapper factory.
@ -477,4 +479,223 @@ class DataMapperFactory
return null;
}
/**
* Find data.
*
* @param string $search Search string
* @param DataMapperAbstract $mapper Mapper to populate
* @param int $id Pivot element id
* @param string $secondaryId Secondary id which becomes necessary for sorted results.
* @param string $type Page type (p = get previous elements, n = get next elements)
* @param int $pageLimit Limit result set
* @param string $sortBy Model member name to sort by
* @param string $sortOrder Sort order
* @param array $searchFields Fields to search in. ([] = all) @todo: maybe change to all which have autocomplete = true defined?
* @param array $filters Additional search filters applied ['type', 'value1', 'logic1', 'value2', 'logic2']
*
* @return array{hasPrevious:bool, hasNext:bool, data:object[]}
*
* @since 1.0.0
*/
public static function find(
string $search = null,
DataMapperAbstract $mapper = null,
int $id = 0,
string $secondaryId = '',
string $type = null,
int $pageLimit = 25,
string $sortBy = null,
string $sortOrder = OrderType::DESC,
array $searchFields = [],
array $filters = []
) : array {
$mapper ??= static::getAll();
$sortOrder = \strtoupper($sortOrder);
$data = [];
$type = $id === 0 ? null : $type;
$hasPrevious = false;
$hasNext = false;
$primarySortField = static::COLUMNS[static::PRIMARYFIELD]['internal'];
$sortBy = empty($sortBy) || static::getColumnByMember($sortBy) === null ? $primarySortField : $sortBy;
$sortById = $sortBy === $primarySortField;
$secondaryId = $sortById ? $id : $secondaryId;
foreach ($filters as $key => $filter) {
$mapper->where($key, '%' . $filter['value1'] . '%', $filter['logic1'] ?? 'like');
if (!empty($filter['value2'])) {
$mapper->where($key, '%' . $filter['value2'] . '%', $filter['logic2'] ?? 'like');
}
}
if (!empty($search)) {
$where = new Where(static::$db);
$counter = 0;
if (empty($searchFields)) {
foreach (static::COLUMNS as $column) {
$searchFields[] = $column['internal'];
}
}
foreach ($searchFields as $searchField) {
if (($column = static::getColumnByMember($searchField)) === null) {
continue;
}
$where->where($column, 'like', '%' . $search . '%', 'OR');
++$counter;
}
if ($counter > 0) {
$mapper->where('', $where);
}
}
// @todo: how to handle columns which are NOT members (columns which are manipulated)
// Maybe pass callback array which can handle these cases?
if ($type === 'p') {
$cloned = clone $mapper;
$mapper->sort(
$sortBy,
$sortOrder === OrderType::DESC ? OrderType::ASC : OrderType::DESC
)
->where($sortBy, $secondaryId, $sortOrder === OrderType::DESC ? '>=' : '<=')
->limit($pageLimit + 2);
if (!$sortById) {
$where = new Where(static::$db);
$where->where(static::PRIMARYFIELD, '>=', $id)
->orWhere(
static::getColumnByMember($sortBy),
$sortOrder === OrderType::DESC ? '>' : '<',
$secondaryId
);
$mapper->where('', $where)
->sort($primarySortField, OrderType::ASC);
}
$data = $mapper->execute();
if (($count = \count($data)) < 2) {
$cloned->sort($sortBy, $sortOrder)
->limit($pageLimit + 1);
if (!$sortById) {
$where = new Where(static::$db);
$where->where(static::PRIMARYFIELD, '<=', $id)
->orWhere(
static::getColumnByMember($sortBy),
$sortOrder === OrderType::DESC ? '<' : '>',
$secondaryId
);
$cloned->where('', $where)
->sort($primarySortField, OrderType::DESC);
}
$data = $mapper->execute();
$hasNext = $count > $pageLimit;
if ($hasNext) {
\array_pop($data);
--$count;
}
} else {
if (\reset($data)->getId() === $id) {
\array_shift($data);
$hasNext = true;
--$count;
}
if ($count > $pageLimit) {
if (!$hasNext) { // @todo: can be maybe removed?
\array_pop($data);
$hasNext = true;
--$count;
}
if ($count > $pageLimit) {
$hasPrevious = true;
\array_pop($data);
}
}
$data = \array_reverse($data);
}
} elseif ($type === 'n') {
$mapper->sort($sortBy, $sortOrder)
->where($sortBy, $secondaryId, $sortOrder === OrderType::DESC ? '<=' : '>=')
->limit($pageLimit + 2);
if (!$sortById) {
$where = new Where(static::$db);
$where->where(static::PRIMARYFIELD, '<=', $id)
->orWhere(
static::getColumnByMember($sortBy),
$sortOrder === OrderType::DESC ? '<' : '>',
$secondaryId
);
$mapper->where('', $where)
->sort($primarySortField, OrderType::DESC);
}
$data = $mapper->execute();
$count = \count($data);
if ($count < 1) {
return [
'hasPrevious' => false,
'hasNext' => false,
'data' => [],
];
}
if (\reset($data)->getId() === $id) {
\array_shift($data);
$hasPrevious = true;
--$count;
}
if ($count > $pageLimit) {
\array_pop($data);
$hasNext = true;
--$count;
}
if ($count > $pageLimit) {
\array_pop($data);
--$count;
}
} else {
$mapper = $mapper->sort($sortBy, $sortOrder)
->limit($pageLimit + 1);
if (!$sortById) {
$mapper = $mapper->sort($primarySortField, OrderType::DESC);
}
$data = $mapper->execute();
$hasNext = ($count = \count($data)) > $pageLimit;
if ($hasNext) {
\array_pop($data);
}
}
return [
'hasPrevious' => $hasPrevious,
'hasNext' => $hasNext,
'data' => $data,
];
}
}

View File

@ -110,20 +110,6 @@ final class ReadMapper extends DataMapperAbstract
return $this;
}
/**
* Create find mapper
*
* @return self
*
* @since 1.0.0
*/
public function find() : self
{
$this->type = MapperType::FIND;
return $this;
}
/**
* Define the columns to load
*
@ -589,7 +575,13 @@ final class ReadMapper extends DataMapperAbstract
}
$refProp->setValue($obj, $value);
} elseif (\in_array($def['type'], ['string', 'int', 'float', 'bool'])) {
} elseif (\in_array($def['type'], ['string', 'compress', 'int', 'float', 'bool'])) {
if ($value !== null && $def['type'] === 'compress') {
$def['type'] = 'string';
$value = \gzinflate($value);
}
if ($value !== null || $refProp->getValue($obj) !== null) {
\settype($value, $def['type']);
}

View File

@ -11,6 +11,7 @@
* @link https://karaka.app
*/
declare(strict_types=1);
namespace phpOMS\Math\Geometry\Shape\D2;
/**

View File

@ -11,6 +11,7 @@
* @link https://karaka.app
*/
declare(strict_types=1);
namespace phpOMS\Math\Geometry\Shape\D3;
use phpOMS\Math\Geometry\Shape\D2\Polygon;

View File

@ -11,6 +11,7 @@
* @link https://karaka.app
*/
declare(strict_types=1);
namespace phpOMS\Math\Statistic\Forecast\Regression;
use phpOMS\Math\Matrix\Matrix;

View File

@ -11,6 +11,7 @@
* @link https://karaka.app
*/
declare(strict_types=1);
namespace phpOMS\Math\Stochastic\Distribution;
use phpOMS\Math\Functions\Beta;

View File

@ -11,6 +11,7 @@
* @link https://karaka.app
*/
declare(strict_types=1);
namespace phpOMS\Math\Stochastic\Distribution;
use phpOMS\Math\Functions\Beta;

View File

@ -11,6 +11,7 @@
* @link https://karaka.app
*/
declare(strict_types=1);
namespace phpOMS\Math\Stochastic\Distribution;
use phpOMS\Math\Functions\Gamma;

View File

@ -11,6 +11,7 @@
* @link https://karaka.app
*/
declare(strict_types=1);
namespace phpOMS\Math\Stochastic\Distribution;
use phpOMS\Math\Functions\Functions;

View File

@ -11,6 +11,7 @@
* @link https://karaka.app
*/
declare(strict_types=1);
namespace phpOMS\Math\Stochastic\Distribution;
use phpOMS\Math\Functions\Beta;

View File

@ -11,6 +11,7 @@
* @link https://karaka.app
*/
declare(strict_types=1);
namespace phpOMS\Math\Stochastic\Distribution;
use phpOMS\Math\Functions\Functions;

View File

@ -11,6 +11,7 @@
* @link https://karaka.app
*/
declare(strict_types=1);
namespace phpOMS\Math\Stochastic\Distribution;
/**

View File

@ -11,6 +11,7 @@
* @link https://karaka.app
*/
declare(strict_types=1);
namespace phpOMS\Math\Stochastic\Distribution;
/**

View File

@ -11,6 +11,7 @@
* @link https://karaka.app
*/
declare(strict_types=1);
namespace phpOMS\Math\Stochastic\Distribution;
use phpOMS\Math\Functions\Gamma;

View File

@ -11,6 +11,7 @@
* @link https://karaka.app
*/
declare(strict_types=1);
namespace phpOMS\Math\Stochastic;
use phpOMS\Math\Statistic\Average;

View File

@ -132,9 +132,29 @@ final class HttpRequest extends RequestAbstract
if (\stripos($_SERVER['CONTENT_TYPE'], 'application/json') !== false) {
// @codeCoverageIgnoreStart
// Tested but coverage doesn't show up
$input = \file_get_contents('php://input');
$stream = \fopen('php://input', 'r');
if ($stream === false) {
return;
}
if ($input === false || empty($input) || $input === 'null') {
$input = '';
$size = 0;
while (($lineRaw = \fgets($stream, 1024)) !== false) {
// Limit json data to 1MB
if ($size > 1000000) {
\fclose($stream);
return;
}
$input += $lineRaw;
$size += \strlen($lineRaw);
}
\fclose($stream);
if (empty($input)) {
return;
}
@ -148,9 +168,29 @@ final class HttpRequest extends RequestAbstract
} elseif (\stripos($_SERVER['CONTENT_TYPE'], 'application/x-www-form-urlencoded') !== false) {
// @codeCoverageIgnoreStart
// Tested but coverage doesn't show up
$content = \file_get_contents('php://input');
$stream = \fopen('php://input', 'r');
if ($stream === false) {
return;
}
if ($content === false || empty($content)) {
$content = '';
$size = 0;
while (($lineRaw = \fgets($stream, 1024)) !== false) {
// Limit json data to 1MB
if ($size > 1000000) {
\fclose($stream);
return;
}
$content += $lineRaw;
$size += \strlen($lineRaw);
}
\fclose($stream);
if (empty($content)) {
return;
}

View File

@ -11,6 +11,7 @@
* @link https://karaka.app
*/
declare(strict_types=1);
namespace phpOMS\Message\Http;
use phpOMS\Stdlib\Base\Enum;

View File

@ -226,6 +226,45 @@ class Imap implements MailBoxInterface
return \imap_num_recent($this->mailbox);
}
/**
* {@inheritdoc}
*/
public function countUnseen(string $box) : int
{
if ($this->box !== $box) {
\imap_reopen($this->mailbox, '{' . $this->host . ':' . $this->port . $this->flags . '}' . $box);
$this->box = $box;
}
return \count(\imap_search($this->mailbox, 'UNSEEN'));
}
/**
* {@inheritdoc}
*/
public function search(
string $box,
string $subject = '',
string $body = '',
string $to = '',
string $cc = '',
string $from = '',
string $bcc = '',
\DateTime $before = null,
\DateTime $since = null,
\DateTime $on = null,
bool $deleted = false,
bool $flagged = false
) : array
{
if ($this->box !== $box) {
\imap_reopen($this->mailbox, '{' . $this->host . ':' . $this->port . $this->flags . '}' . $box);
$this->box = $box;
}
return [];
}
/**
* Copy message to another mailbox
*

View File

@ -64,6 +64,52 @@ interface MailBoxInterface
*/
public function countRecent(string $box) : int;
/**
* Count unseen mail in mailbox
*
* @param string $box Box to count the mail in
*
* @return int
*
* @since 1.0.0
*/
public function countUnseen(string $box) : int;
/**
* Get messages by search criterium
*
* @param string $box Box to count the mail in
* @param string $subject Subject
* @param string $body Body
* @param string $to To
* @param string $cc CC
* @param string $from From
* @param string $bcc BCC
* @param \DateTime $before Message before
* @param \DateTime $sicne Message since
* @param \DateTime $on Message on date
* @param bool $deleted Message is deleted
* @param bool $flagged Message is flagged (false = any message)
*
* @return array
*
* @since 1.0.0
*/
public function search(
string $box,
string $subject = '',
string $body = '',
string $to = '',
string $cc = '',
string $from = '',
string $bcc = '',
\DateTime $before = null,
\DateTime $since = null,
\DateTime $on = null,
bool $deleted = false,
bool $flagged = false
) : array;
/**
* Get all message headers from a mailbox
*

View File

@ -226,6 +226,45 @@ class Pop3 implements MailBoxInterface
return \imap_num_recent($this->mailbox);
}
/**
* {@inheritdoc}
*/
public function countUnseen(string $box) : int
{
if ($this->box !== $box) {
\imap_reopen($this->mailbox, '{' . $this->host . ':' . $this->port . $this->flags . '}' . $box);
$this->box = $box;
}
return \count(\imap_search($this->mailbox, 'UNSEEN'));
}
/**
* {@inheritdoc}
*/
public function search(
string $box,
string $subject = '',
string $body = '',
string $to = '',
string $cc = '',
string $from = '',
string $bcc = '',
\DateTime $before = null,
\DateTime $since = null,
\DateTime $on = null,
bool $deleted = false,
bool $flagged = false
) : array
{
if ($this->box !== $box) {
\imap_reopen($this->mailbox, '{' . $this->host . ':' . $this->port . $this->flags . '}' . $box);
$this->box = $box;
}
return [];
}
/**
* Copy message to another mailbox
*

View File

@ -11,6 +11,7 @@
* @link https://karaka.app
*/
declare(strict_types=1);
namespace phpOMS\Module;
use phpOMS\Stdlib\Base\Enum;

View File

@ -56,7 +56,7 @@ General updates can be found in our info section at https://karaka.app/info and
## Tech stack
* Language: php, js, c++, html, css, markdown, shell script
* Database: Maria/MySQL, PostgreSQL, MSSQL, SQLite
* Database: Maria/MySQL, PostgreSQL, MSSQL/SQLSrv, SQLite
* Webserver: apache2, nginx
* Cache: Redis, Memcached

View File

@ -286,7 +286,7 @@ final class UriFactory
}
$parsed = \preg_replace_callback(
'(\{[\/#\?%@\.\$][a-zA-Z0-9\-]*\})',
'(\{[\/#\?%@\.\$][a-zA-Z0-9_\-]*\})',
function ($match) use ($toMatch) : string {
$match = \substr($match[0], 1, \strlen($match[0]) - 2);

View File

@ -11,6 +11,7 @@
* @link https://karaka.app
*/
declare(strict_types=1);
namespace phpOMS\tests\Business\Sales;
use phpOMS\Business\Sales\MarketShareEstimation;

View File

@ -11,6 +11,7 @@
* @link https://karaka.app
*/
declare(strict_types=1);
namespace phpOMS\tests\DataStorage\Database\Connection;
use phpOMS\DataStorage\Database\Connection\PostgresConnection;

View File

@ -11,6 +11,7 @@
* @link https://karaka.app
*/
declare(strict_types=1);
namespace phpOMS\tests\DataStorage\Database\Connection;
use phpOMS\DataStorage\Database\Connection\SQLiteConnection;

View File

@ -11,6 +11,7 @@
* @link https://karaka.app
*/
declare(strict_types=1);
namespace phpOMS\tests\DataStorage\Database;
use phpOMS\DataStorage\Database\Query\OrderType;

View File

@ -11,6 +11,7 @@
* @link https://karaka.app
*/
declare(strict_types=1);
namespace phpOMS\tests\DataStorage\Database\Schema\Grammar;
use phpOMS\DataStorage\Database\Schema\Grammar\SQLiteGrammar;

View File

@ -11,6 +11,7 @@
* @link https://karaka.app
*/
declare(strict_types=1);
namespace phpOMS\tests\DataStorage\Database;
use phpOMS\DataStorage\Database\SchemaMapper;

View File

@ -11,6 +11,7 @@
* @link https://karaka.app
*/
declare(strict_types=1);
namespace phpOMS\tests\DataStorage\Database\TestModel;
use phpOMS\DataStorage\Database\Mapper\DataMapperFactory;

View File

@ -11,6 +11,7 @@
* @link https://karaka.app
*/
declare(strict_types=1);
namespace phpOMS\tests\Dispatcher;
class TestController