Initial xentral_oss_20.3.c9ffacf

This commit is contained in:
Alex
2021-05-21 08:49:41 +02:00
parent 5406e4a551
commit 34e5ac43d9
18884 changed files with 2109867 additions and 0 deletions
@@ -0,0 +1,29 @@
<?php
namespace Xentral\Modules\Api\Resource\Feature;
use Xentral\Modules\Api\Resource\Filter\Select\SimpleSearchFilter;
trait FilterFeatureTrait
{
/**
* Festlegen welche Filter erlaubt sind
*
* @example $this->registerFilterParams([
* 'title' => 'l.bezeichnung %LIKE%',
* 'title_starts_with' => 'l.bezeichnung LIKE%',
* 'title_ends_with' => 'l.bezeichnung %LIKE',
* 'title_exact' => 'l.bezeichnung LIKE',
* 'project' => 'l.projekt =',
* 'project_not' => 'l.projekt !=',
* 'amount_min' => 'l.amount >=',
* 'amount_max' => 'l.amount <=',
* ]);
*
* @param array $params
*/
protected function registerFilterParams(array $params)
{
$this->registerSelectFilter(new SimpleSearchFilter($params));
}
}
@@ -0,0 +1,185 @@
<?php
namespace Xentral\Modules\Api\Resource\Feature;
use Xentral\Modules\Api\Exception\InvalidArgumentException;
use Xentral\Modules\Api\Exception\ResourceNotFoundException;
use Xentral\Modules\Api\Resource\AbstractResource;
trait IncludeFeatureTrait
{
/** @var array $includeSettings */
private $includeSettings;
/**
* @example in configure-Methode der Resource:
* $this->registerIncludes([
* 'projekte' => [
* 'key' => 'projekt',
* 'resource' => ProjectResource::class,
* 'columns' => [
* 'p.id',
* 'p.name',
* 'p.abkuerzung',
* 'p.beschreibung',
* 'p.farbe',
* ],
* ],
* ]);
*
* @param array $includes
*/
protected function registerIncludes($includes)
{
$this->includeSettings = $includes;
}
/**
* @param array $includes
* @param array $items
* @param bool $isCollection true=Mehrzeilig, false=Assoziatives Array
*
* @return array
*/
protected function integrateIncludes(array $includes, array &$items, $isCollection = true)
{
// Keine Includes gesetzt
if (empty($includes)) {
return $items;
}
if (empty($this->includeSettings)) {
return $items;
}
// Doppelte Includes entfernen
$includes = array_unique($includes);
// Einzelnes Item in Collection verwandeln
if (!$isCollection) {
$items = [$items];
}
foreach ($includes as $includeName) {
if (empty($includeName)) {
continue;
}
$settings = $this->getIncludeSetting($includeName);
$subKey = $settings['key'];
if (empty($subKey)) {
throw new \RuntimeException(sprintf(
'Include "%s" not posible. Key is missing.', $includeName
));
}
// Nur bestimmte Spalten inkludieren?
$columns = isset($settings['columns']) ? $settings['columns'] : [];
// 1:n Beziehung zwischen Resource und Subresource
if (isset($settings['filter'])) {
/** @var AbstractResource $subResource */
$subResource = $this->getResource($settings['resource']);
foreach ($items as &$item) {
// Filter aufbereiten
$filter = $settings['filter'];
foreach ($filter as &$filterItem) {
// Filter benötigt Wert aus Haupt-Resource
if (strpos($filterItem['value'], ':') === 0) {
$key = substr_replace($filterItem['value'], '', 0, 1);
$filterItem['value'] = $item[$key];
}
}
unset($filterItem);
$filter = ['filter' => $filter]; // In ComplexSearch-Filter wandeln
// Sortierung vorhanden?
$sort = !empty($settings['sort']) ? $settings['sort'] : [];
try {
/** @var AbstractResource $subResource */
$subResult = $subResource->getList($filter, $sort, $columns, [], 1, 1000);
$subItems = $subResult->getData();
} catch (ResourceNotFoundException $e) {
$subItems = [];
}
$item[$settings['key']] = $subItems;
}
unset($item);
// 1:1 Beziehung zwischen Resource und Subresource
} else {
// Prüfen ob Spalte zum Integrieren in Haupt-Ergebnis existiert
if (!$this->arrayColumnExists($items, $subKey)) {
throw new \RuntimeException(sprintf(
'Include "%s" not posible. Key "%s" is missing.', $includeName, $subKey
));
}
// Benötigte Subresourcen-IDs aus Haupt-Ergebnis holen
$subIds = array_unique(array_column($items, $subKey));
if (empty($subIds)) {
continue;
}
// Subresourcen anhand der IDs laden
try {
/** @var AbstractResource $subResource */
$subResource = $this->getResource($settings['resource']);
$subResult = $subResource->getIds($subIds, $columns);
} catch (ResourceNotFoundException $e) {
continue;
}
// Gefundene Subresourcen in Haupt-Ergebnis einbinden
array_walk($items, function (&$item, $id, $subItems) use ($subKey) {
$subId = (int)$item[$subKey];
if (isset($subItems[$subId])) {
$item[$subKey] = $subItems[$subId];
}
}, $subResult->getData());
}
}
if (!$isCollection) {
return $items[0];
}
return $items;
}
/**
* @param string $includeName
*
* @return array
*/
private function getIncludeSetting($includeName)
{
if (!isset($this->includeSettings[$includeName])) {
throw new InvalidArgumentException(
sprintf('Include "%s" is not registered.', $includeName)
);
}
return $this->includeSettings[$includeName];
}
/**
* @param array $items
* @param string $keyName
*
* @return bool
*/
private function arrayColumnExists(array $items, $keyName)
{
$row = current($items);
return array_key_exists($keyName, $row);
}
}
@@ -0,0 +1,24 @@
<?php
namespace Xentral\Modules\Api\Resource\Feature;
use Xentral\Modules\Api\Resource\Filter\Select\SortingFilter;
trait SortingFeatureTrait
{
/**
* Festlegen welche Sortierungen erlaubt sind
*
* @example $this->registerSortingParams([
* 'bezeichnung' => 'k.bezeichnung',
* 'projekt' => 'k.projekt',
* 'parent' => 'k.parent',
* ]);
*
* @param array $params
*/
protected function registerSortingParams(array $params)
{
$this->registerSelectFilter(new SortingFilter($params));
}
}
@@ -0,0 +1,88 @@
<?php
namespace Xentral\Modules\Api\Resource\Feature;
use Rakit\Validation\Validation;
use Xentral\Modules\Api\Exception\ValidationErrorException;
use Xentral\Modules\Api\Resource\Exception\ValidationRequiredException;
trait ValidationFeatureTrait
{
/** @var array $validationRules */
private $validationRules;
/** @var string $resourceTableName */
private $resourceTableName;
/**
* Validierungsregeln festlegen
*
* @example $this->registerValidationRules([
* 'id' => 'not_present',
* 'bezeichnung' => 'required|unique:artikelkategorien,bezeichnung',
* 'next_number' => 'numeric',
* 'projekt' => 'numeric',
* 'parent' => 'numeric',
* 'externenummer' => 'numeric',
* 'geloescht' => 'in:0,1',
* ]);
* @see https://github.com/rakit/validation#available-rules
*
* @param array $rules
*/
protected function registerValidationRules(array $rules)
{
$this->validationRules = $rules;
}
/**
* @param array $inputVars
* @param int $selfId
*/
protected function validateData($inputVars, $selfId = null)
{
if (empty($this->validationRules)) {
throw new ValidationRequiredException();
}
// Regeln aufbereiten
$rules = $this->validationRules;
if ($selfId) {
$needle = sprintf('unique:%s,', $this->resourceTableName);
foreach ($rules as $ruleKey => $ruleVal) {
if ($pos = strpos($ruleVal, $needle)) {
// Nach Anfang der nachfolgenden Regel suchen
$searchPos = $pos + strlen($needle);
$insertPos = strpos($ruleVal, '|', $searchPos);
// Keine weitere Regel gefunden; am Ende anfügen
if (!$insertPos) {
$insertPos = strlen($ruleVal);
}
// ID als dritten Parameter für UniqueRule übergeben
/** @see UniqueRule Parameter "except" */
$newRuleVal = substr_replace($ruleVal, ',' . $selfId, $insertPos, 0);
$rules[$ruleKey] = $newRuleVal;
}
}
}
/** @var Validation $validation */
$validation = $this->validator->validate($inputVars, $rules);
if ($validation->fails()) {
throw new ValidationErrorException($validation->errors()->all());
}
}
/**
* @param string $tableName
*/
protected function setTableName($tableName)
{
$this->resourceTableName = $tableName;
}
}