mirror of
https://github.com/OpenXE-org/OpenXE.git
synced 2024-11-15 12:37:14 +01:00
422 lines
12 KiB
PHP
422 lines
12 KiB
PHP
|
<?php
|
||
|
|
||
|
namespace Xentral\Modules\Api\Resource;
|
||
|
|
||
|
use Exception;
|
||
|
use InvalidArgumentException;
|
||
|
use Xentral\Components\Database\Database;
|
||
|
use Xentral\Components\Database\SqlQuery\DeleteQuery;
|
||
|
use Xentral\Components\Database\SqlQuery\InsertQuery;
|
||
|
use Xentral\Components\Database\SqlQuery\SelectQuery;
|
||
|
use Xentral\Components\Database\SqlQuery\UpdateQuery;
|
||
|
use Xentral\Modules\Api\Exception\ResourceNotFoundException;
|
||
|
use Xentral\Modules\Api\Resource\Exception\EndpointNotAvailableException;
|
||
|
use Xentral\Modules\Api\Resource\Feature\FilterFeatureTrait;
|
||
|
use Xentral\Modules\Api\Resource\Feature\IncludeFeatureTrait;
|
||
|
use Xentral\Modules\Api\Resource\Feature\SortingFeatureTrait;
|
||
|
use Xentral\Modules\Api\Resource\Feature\ValidationFeatureTrait;
|
||
|
use Xentral\Modules\Api\Resource\Filter\Select\ComplexSearchFilter;
|
||
|
use Xentral\Modules\Api\Resource\Filter\Select\SelectFilterInterface;
|
||
|
use Xentral\Modules\Api\Resource\Filter\Select\SelectFilterTrait;
|
||
|
use Xentral\Modules\Api\Resource\Result\CollectionResult;
|
||
|
use Xentral\Modules\Api\Resource\Result\ItemResult;
|
||
|
use Xentral\Modules\Api\Validator\Validator;
|
||
|
|
||
|
abstract class AbstractResource
|
||
|
{
|
||
|
use SelectFilterTrait;
|
||
|
|
||
|
use FilterFeatureTrait;
|
||
|
use SortingFeatureTrait;
|
||
|
use IncludeFeatureTrait;
|
||
|
use ValidationFeatureTrait;
|
||
|
|
||
|
/** @var Database $db */
|
||
|
protected $db;
|
||
|
|
||
|
/** @var Validator $validator */
|
||
|
protected $validator;
|
||
|
|
||
|
/** @return SelectQuery|false */
|
||
|
abstract protected function selectAllQuery();
|
||
|
|
||
|
/** @return SelectQuery|false */
|
||
|
abstract protected function selectOneQuery();
|
||
|
|
||
|
/** @return SelectQuery|false */
|
||
|
abstract protected function selectIdsQuery();
|
||
|
|
||
|
/** @return InsertQuery|false */
|
||
|
abstract protected function insertQuery();
|
||
|
|
||
|
/** @return UpdateQuery|false */
|
||
|
abstract protected function updateQuery();
|
||
|
|
||
|
/** @return UpdateQuery|DeleteQuery|false */
|
||
|
abstract protected function deleteQuery();
|
||
|
|
||
|
/** @return void */
|
||
|
abstract protected function configure();
|
||
|
|
||
|
/**
|
||
|
* @param Database $database
|
||
|
* @param Validator $validator
|
||
|
*/
|
||
|
public function __construct(
|
||
|
Database $database,
|
||
|
Validator $validator
|
||
|
) {
|
||
|
$this->db = $database;
|
||
|
$this->validator = $validator;
|
||
|
|
||
|
$this->configure();
|
||
|
|
||
|
// Komplexe Suche immer aktivieren
|
||
|
$this->registerSelectFilter(new ComplexSearchFilter());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param array $filter
|
||
|
* @param array $sorting
|
||
|
* @param array $columns
|
||
|
* @param array $includes
|
||
|
* @param int $page
|
||
|
* @param int $paging
|
||
|
*
|
||
|
* @return CollectionResult
|
||
|
*/
|
||
|
public function getList(
|
||
|
array $filter = [],
|
||
|
array $sorting = [],
|
||
|
array $columns = [],
|
||
|
array $includes = [],
|
||
|
$page = 1,
|
||
|
$paging = 20
|
||
|
) {
|
||
|
/** @var SelectQuery $selectAll */
|
||
|
$selectAll = $this->selectAllQuery();
|
||
|
|
||
|
if (!$selectAll) {
|
||
|
throw new EndpointNotAvailableException();
|
||
|
}
|
||
|
if (!$selectAll instanceof SelectQuery) {
|
||
|
throw new InvalidArgumentException(sprintf(
|
||
|
'selectAllQuery() must return an instance of %s', SelectQuery::class
|
||
|
));
|
||
|
}
|
||
|
|
||
|
// Suchfilter und Sortierung hinzufügen
|
||
|
$selectAll = $this->applySelectFilter($selectAll, [
|
||
|
SelectFilterInterface::TYPE_SEARCHING => $filter,
|
||
|
SelectFilterInterface::TYPE_SORTING => $sorting,
|
||
|
]);
|
||
|
|
||
|
// Filter hinzufügen
|
||
|
//$selectAll = $this->appendFilterQuery($filter, $selectAll);
|
||
|
//$bindValues = $this->appendFilterBindings($filter, $bindValues);
|
||
|
|
||
|
// Sortierung hinzufügen
|
||
|
//$selectAll = $this->appendSorting($sorting, $selectAll);
|
||
|
|
||
|
/*echo "<pre>";
|
||
|
echo $selectAll->getStatement();
|
||
|
var_dump($selectAll->getBindValues());
|
||
|
echo "</pre>";
|
||
|
exit;*/
|
||
|
|
||
|
// Ergebnisse ermitteln
|
||
|
$selectList = clone $selectAll;
|
||
|
if (!empty($columns)) {
|
||
|
$selectList->resetCols()->cols($columns);
|
||
|
}
|
||
|
$selectList->page($page)->setPaging($paging);
|
||
|
$items = $this->db->fetchAll(
|
||
|
$selectList->getStatement(),
|
||
|
$selectList->getBindValues()
|
||
|
);
|
||
|
|
||
|
if (count($items) === 0) {
|
||
|
throw new ResourceNotFoundException();
|
||
|
}
|
||
|
|
||
|
// Gesamtanzahl der Ergebnisse ermitteln
|
||
|
$selectCount = clone $selectAll;
|
||
|
$selectCount->resetOrderBy()->resetCols()->cols(['COUNT(*)']);
|
||
|
$total = (int)$this->db->fetchValue(
|
||
|
$selectCount->getStatement(),
|
||
|
$selectCount->getBindValues()
|
||
|
);
|
||
|
$pagination = $this->getPagination($total, count($items), $paging, $page);
|
||
|
|
||
|
// Includes in Ergebnis integrieren
|
||
|
$items = $this->integrateIncludes($includes, $items);
|
||
|
|
||
|
return new CollectionResult($items, $pagination);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param array $ids
|
||
|
* @param array $columns Spalten überschreiben
|
||
|
*
|
||
|
* @return CollectionResult
|
||
|
*/
|
||
|
public function getIds(array $ids, array $columns = [])
|
||
|
{
|
||
|
/** @var SelectQuery $selectIds */
|
||
|
$selectIds = $this->selectIdsQuery();
|
||
|
if (!$selectIds) {
|
||
|
throw new EndpointNotAvailableException();
|
||
|
}
|
||
|
if (!$selectIds instanceof SelectQuery) {
|
||
|
throw new InvalidArgumentException(sprintf(
|
||
|
'selectIdsQuery() must return an instance of %s', SelectQuery::class
|
||
|
));
|
||
|
}
|
||
|
|
||
|
if (!empty($columns)) {
|
||
|
$selectIds->resetCols()->cols($columns);
|
||
|
}
|
||
|
|
||
|
$data = $this->db->fetchAssoc(
|
||
|
$selectIds->getStatement(),
|
||
|
['ids' => $ids]
|
||
|
);
|
||
|
|
||
|
if (!$data) {
|
||
|
throw new ResourceNotFoundException();
|
||
|
}
|
||
|
|
||
|
return new CollectionResult($data);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param int $id
|
||
|
* @param array $includes
|
||
|
*
|
||
|
* @return ItemResult
|
||
|
*/
|
||
|
public function getOne($id, array $includes = [])
|
||
|
{
|
||
|
/** @var SelectQuery $selectOne */
|
||
|
$selectOne = $this->selectOneQuery();
|
||
|
if (!$selectOne) {
|
||
|
throw new EndpointNotAvailableException();
|
||
|
}
|
||
|
if (!$selectOne instanceof SelectQuery) {
|
||
|
throw new InvalidArgumentException(sprintf(
|
||
|
'selectOneQuery() must return an instance of %s', SelectQuery::class
|
||
|
));
|
||
|
}
|
||
|
|
||
|
$data = $this->db->fetchRow($selectOne->getStatement(), ['id' => $id]);
|
||
|
|
||
|
if (!$data) {
|
||
|
throw new ResourceNotFoundException();
|
||
|
}
|
||
|
|
||
|
// Includes in Ergebnis integrieren
|
||
|
$data = $this->integrateIncludes($includes, $data, false);
|
||
|
|
||
|
return new ItemResult($data);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Prüfen ob übergebene ID in Datenbank vorhanden ist
|
||
|
*
|
||
|
* @param int $id
|
||
|
* @param string|null $message Fehlermeldung wenn ID nicht vorhanden ist
|
||
|
*/
|
||
|
public function checkOrFail($id, $message = null)
|
||
|
{
|
||
|
/** @var SelectQuery $selectOne */
|
||
|
$select = $this->selectOneQuery();
|
||
|
if (!$select) {
|
||
|
throw new EndpointNotAvailableException();
|
||
|
}
|
||
|
if (!$select instanceof SelectQuery) {
|
||
|
throw new InvalidArgumentException(sprintf(
|
||
|
'selectOneQuery() must return an instance of %s', SelectQuery::class
|
||
|
));
|
||
|
}
|
||
|
|
||
|
$value = $this->db->fetchValue($select->getStatement(), ['id' => $id]);
|
||
|
|
||
|
if ((int)$value !== (int)$id) {
|
||
|
throw new ResourceNotFoundException($message === null ? 'Resource not found' : $message);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param int $id
|
||
|
* @param array $inputVars
|
||
|
* @param array|null $inputMapping Assoc-Array ['Eingabefeld' => 'Datenbankfeld']
|
||
|
*
|
||
|
* @return ItemResult
|
||
|
*/
|
||
|
public function edit($id, $inputVars, $inputMapping = null)
|
||
|
{
|
||
|
$updateQuery = $this->updateQuery();
|
||
|
if (!$updateQuery) {
|
||
|
throw new EndpointNotAvailableException();
|
||
|
}
|
||
|
if (!$updateQuery instanceof UpdateQuery) {
|
||
|
throw new InvalidArgumentException(sprintf(
|
||
|
'updateQuery() must return an instance of %s', UpdateQuery::class
|
||
|
));
|
||
|
}
|
||
|
|
||
|
// Eingabe validieren
|
||
|
$this->validateData($inputVars, $id);
|
||
|
$inputVars['id'] = $id;
|
||
|
|
||
|
// Eingabe- zu Datenbankfeld mappen
|
||
|
$inputVars = $this->mapInputData($inputVars, $inputMapping);
|
||
|
|
||
|
$bindValues = [];
|
||
|
foreach ($inputVars as $inputKey => $inputVal) {
|
||
|
$updateQuery->col($inputKey);
|
||
|
$bindValues[$inputKey] = $inputVal;
|
||
|
}
|
||
|
|
||
|
$this->db->perform($updateQuery->getStatement(), $bindValues);
|
||
|
|
||
|
// Bei Erfolg die geänderte Resource zurückliefern; mit Success-Flag
|
||
|
$result = $this->getOne($id);
|
||
|
$result->setSuccess(true);
|
||
|
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param array $inputVars
|
||
|
* @param array|null $inputMapping Assoc-Array ['Eingabefeld' => 'Datenbankfeld']
|
||
|
*
|
||
|
* @return ItemResult
|
||
|
*/
|
||
|
public function insert($inputVars, $inputMapping = null)
|
||
|
{
|
||
|
$insertQuery = $this->insertQuery();
|
||
|
if (!$insertQuery) {
|
||
|
throw new EndpointNotAvailableException();
|
||
|
}
|
||
|
if (!$insertQuery instanceof InsertQuery) {
|
||
|
throw new InvalidArgumentException(sprintf(
|
||
|
'insertQuery() must return an instance of %s', InsertQuery::class
|
||
|
));
|
||
|
}
|
||
|
|
||
|
// Eingabe validieren
|
||
|
$this->validateData($inputVars);
|
||
|
|
||
|
// Eingabe- zu Datenbankfeld mappen
|
||
|
$inputVars = $this->mapInputData($inputVars, $inputMapping);
|
||
|
|
||
|
$bindValues = [];
|
||
|
foreach ($inputVars as $inputKey => $inputVal) {
|
||
|
$insertQuery->col($inputKey);
|
||
|
$bindValues[$inputKey] = $inputVal;
|
||
|
}
|
||
|
|
||
|
$this->db->perform($insertQuery->getStatement(), $bindValues);
|
||
|
$id = $this->db->lastInsertId();
|
||
|
|
||
|
// Bei Erfolg die angelegte Resource zurückliefern; mit Success-Flag
|
||
|
$result = $this->getOne($id);
|
||
|
$result->setSuccess(true);
|
||
|
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param int $id
|
||
|
*
|
||
|
* @return ItemResult
|
||
|
*/
|
||
|
public function delete($id)
|
||
|
{
|
||
|
$deleteQuery = $this->deleteQuery();
|
||
|
if (!$deleteQuery) {
|
||
|
throw new EndpointNotAvailableException();
|
||
|
}
|
||
|
if (!$deleteQuery instanceof DeleteQuery && !$deleteQuery instanceof UpdateQuery) {
|
||
|
throw new InvalidArgumentException(sprintf(
|
||
|
'deleteQuery() must return an instance of %s or %s', DeleteQuery::class, UpdateQuery::class
|
||
|
));
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
$this->db->perform($deleteQuery->getStatement(), ['id' => $id]);
|
||
|
$success = true;
|
||
|
} catch (Exception $e) {
|
||
|
$success = false;
|
||
|
}
|
||
|
|
||
|
$result = new ItemResult(['id' => $id]);
|
||
|
$result->setSuccess($success);
|
||
|
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Eingabe- zu Datenbankfeld mappen
|
||
|
*
|
||
|
* @param array $inputVars
|
||
|
* @param array|null $inputMapping Assoc-Array ['Eingabefeld' => 'Datenbankfeld']
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
protected function mapInputData($inputVars, $inputMapping = null)
|
||
|
{
|
||
|
if (empty($inputMapping)) {
|
||
|
return $inputVars;
|
||
|
}
|
||
|
|
||
|
foreach ($inputMapping as $inputKey => $dbKey) {
|
||
|
if (empty($inputKey) || empty($dbKey)) {
|
||
|
continue;
|
||
|
}
|
||
|
if ($inputKey === $dbKey) {
|
||
|
continue;
|
||
|
}
|
||
|
if (array_key_exists($inputKey, $inputVars)) {
|
||
|
$inputVars[$dbKey] = $inputVars[$inputKey];
|
||
|
unset($inputVars[$inputKey]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $inputVars;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param int $itemsTotal
|
||
|
* @param int $itemsCurrent
|
||
|
* @param int $itemsPerPage
|
||
|
* @param int $pageCurrent
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
protected function getPagination($itemsTotal, $itemsCurrent, $itemsPerPage, $pageCurrent)
|
||
|
{
|
||
|
return [
|
||
|
'items_per_page' => (int)$itemsPerPage,
|
||
|
'items_current' => (int)$itemsCurrent,
|
||
|
'items_total' => (int)$itemsTotal,
|
||
|
'page_current' => (int)$pageCurrent,
|
||
|
'page_last' => (int)ceil($itemsTotal / $itemsPerPage),
|
||
|
];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param string $resourceClass
|
||
|
*
|
||
|
* @return AbstractResource
|
||
|
*/
|
||
|
protected function getResource($resourceClass)
|
||
|
{
|
||
|
return new $resourceClass(
|
||
|
$this->db,
|
||
|
$this->validator
|
||
|
);
|
||
|
}
|
||
|
}
|