263 lines
9.2 KiB
PHP
263 lines
9.2 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace KupShop\GraphQLBundle\ApiAdmin\Util;
|
|
|
|
use KupShop\ContentBundle\Util\Block;
|
|
use KupShop\GraphQLBundle\ApiAdmin\Types\Content\Response\EditableContentMutateResponse;
|
|
use KupShop\GraphQLBundle\ApiShared\Types\Content\EditableContent;
|
|
use KupShop\GraphQLBundle\ApiShared\Types\Content\Input\EditableAreaInput;
|
|
use KupShop\GraphQLBundle\ApiShared\Types\Content\Input\EditableContentInput;
|
|
use KupShop\GraphQLBundle\ApiShared\Types\Content\Input\EditableContentTranslationInput;
|
|
use KupShop\GraphQLBundle\Exception\GraphQLValidationException;
|
|
use KupShop\I18nBundle\Translations\BlocksTranslation;
|
|
use KupShop\KupShopBundle\Util\ArrayUtil;
|
|
use KupShop\KupShopBundle\Util\Database\QueryHint;
|
|
use KupShop\KupShopBundle\Util\Functional\Mapping;
|
|
use KupShop\KupShopBundle\Util\ObjectUtil;
|
|
use Query\Operator;
|
|
use TheCodingMachine\GraphQLite\Exceptions\GraphQLException;
|
|
|
|
class BlockUpdater
|
|
{
|
|
public function __construct(
|
|
private Block $block,
|
|
private ?BlocksTranslation $blocksTranslation,
|
|
) {
|
|
}
|
|
|
|
public function updateEditableContent(EditableContentInput $input): EditableContentMutateResponse
|
|
{
|
|
$this->validateEditableContentUpdate($input);
|
|
|
|
$deleteAreas = false;
|
|
if (ObjectUtil::isPropertyInitialized($input, 'overwrite')) {
|
|
$deleteAreas = $input->overwrite;
|
|
}
|
|
|
|
if ($deleteAreas) {
|
|
sqlQueryBuilder()
|
|
->delete('blocks')
|
|
->where(Operator::equals(['id_root' => $input->id]))
|
|
->execute();
|
|
}
|
|
|
|
$position = 0;
|
|
if (!$input->overwrite) {
|
|
$position = sqlQueryBuilder()
|
|
->select('MAX(position)')
|
|
->from('blocks')
|
|
->where(Operator::equals(['id_root' => $input->id]))
|
|
->execute()->fetchOne() ?: 0;
|
|
}
|
|
|
|
foreach ($input->areas as $area) {
|
|
$blockId = $deleteAreas ? null : $area->id;
|
|
$update = $this->getEditableAreaUpdateData($area);
|
|
|
|
if ($blockId === null) {
|
|
// create new block
|
|
$insert = [
|
|
'id_root' => $input->id,
|
|
'id_parent' => $input->id,
|
|
'position' => ++$position,
|
|
'name' => $update['name'] ?? null,
|
|
'content' => '',
|
|
'json_content' => '[]',
|
|
];
|
|
|
|
if ($area->id) {
|
|
$insert['id'] = $area->id;
|
|
}
|
|
|
|
$blockId = sqlGetConnection()->transactional(function () use ($insert) {
|
|
sqlQueryBuilder()
|
|
->insert('blocks')
|
|
->directValues($insert)
|
|
->execute();
|
|
|
|
return (int) sqlInsertId();
|
|
});
|
|
}
|
|
|
|
if (empty($update)) {
|
|
continue;
|
|
}
|
|
|
|
if (array_key_exists('json_content', $update)) {
|
|
$update['json_content'] = json_encode($update['json_content']) ?: '[]';
|
|
$rendered = $this->block->renderBlock($update['json_content'], "Block with ID {$blockId}");
|
|
if (($rendered['success'] ?? false) !== true) {
|
|
throw new GraphQLException('Error during EditableArea save! Render of EditableArea.data failed: '.$rendered['error']);
|
|
}
|
|
|
|
$update['content'] = $rendered['html'] ?? '';
|
|
|
|
$this->block->updateBlockPhotosRelationsByData($blockId, json_decode($update['json_content'], true) ?: []);
|
|
}
|
|
|
|
sqlQueryBuilder()
|
|
->update('blocks')
|
|
->directValues($update)
|
|
->where(Operator::equals(['id' => $blockId]))
|
|
->execute();
|
|
}
|
|
|
|
$blocks = QueryHint::withRouteToMaster(fn () => $this->block->getBlocks($input->id));
|
|
|
|
return new EditableContentMutateResponse(
|
|
true,
|
|
new EditableContent(
|
|
$input->id,
|
|
$blocks
|
|
)
|
|
);
|
|
}
|
|
|
|
public function updateEditableContentTranslation(EditableContentTranslationInput $input): bool
|
|
{
|
|
$this->validateEditableContentUpdate($input);
|
|
|
|
foreach ($input->areas as $area) {
|
|
$this->translateEditableArea($input->language, $area);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public function translateBlocks(string $language, array $blocks, array $localBlocksByPositions): array
|
|
{
|
|
$blocksMap = [];
|
|
|
|
foreach ($blocks as $block) {
|
|
$blockId = $localBlocksByPositions[$block['position']] ?? null;
|
|
|
|
if (!$blockId) {
|
|
throw new GraphQLValidationException('Unable to update translation of description plus: cannot find block to translate!');
|
|
}
|
|
|
|
$this->block->translateBlock($language, (int) $blockId, $block);
|
|
|
|
// Pokud mam sub bloky, tak prelozim i ty
|
|
foreach ($block['children'] ?? [] as $childBlock) {
|
|
$this->translateBlocks($language, $childBlock, $localBlocksByPositions);
|
|
}
|
|
}
|
|
|
|
return $blocksMap;
|
|
}
|
|
|
|
public function captureErrors(callable $fn): void
|
|
{
|
|
try {
|
|
$fn();
|
|
} catch (\InvalidArgumentException $e) {
|
|
throw new GraphQLValidationException(
|
|
sprintf('Unable to update editable area: %s', $e->getMessage())
|
|
);
|
|
} catch (\RuntimeException $e) {
|
|
throw new GraphQLException(
|
|
sprintf('Unable to update editable area, try again later: %s', $e->getMessage()),
|
|
500
|
|
);
|
|
}
|
|
}
|
|
|
|
private function translateEditableArea(string $language, EditableAreaInput $area): bool
|
|
{
|
|
if (!$this->blocksTranslation) {
|
|
return false;
|
|
}
|
|
|
|
if (!ObjectUtil::isPropertyInitialized($area, 'id') || !$area->id) {
|
|
throw new GraphQLValidationException(
|
|
sprintf('EditableArea.id is mandatory when translating areas!')
|
|
);
|
|
}
|
|
|
|
$update = $this->getEditableAreaUpdateData($area);
|
|
|
|
if (empty($update)) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
return $this->blocksTranslation->saveSingleObjectForce(
|
|
$language,
|
|
$area->id,
|
|
$update,
|
|
failureAsException: true
|
|
);
|
|
} catch (\RuntimeException $e) {
|
|
throw new GraphQLException('Error during EditableArea save! Render of EditableArea.data failed: '.$e->getMessage());
|
|
}
|
|
}
|
|
|
|
private function getEditableAreaUpdateData(EditableAreaInput $area): array
|
|
{
|
|
$update = [];
|
|
|
|
if (ObjectUtil::isPropertyInitialized($area, 'name')) {
|
|
$update['name'] = $area->name;
|
|
}
|
|
|
|
if (ObjectUtil::isPropertyInitialized($area, 'dataPortable')) {
|
|
$area->data = $this->block->createBlockDataFromPortableData($area->dataPortable);
|
|
}
|
|
|
|
if (ObjectUtil::isPropertyInitialized($area, 'data')) {
|
|
$update['json_content'] = json_decode($area->data ?: '', true) ?: [];
|
|
}
|
|
|
|
return $update;
|
|
}
|
|
|
|
private function validateEditableContentUpdate(EditableContentInput $input): void
|
|
{
|
|
$blocks = Mapping::mapKeys(sqlQueryBuilder()
|
|
->select('id')
|
|
->from('blocks')
|
|
->where(Operator::equals(['id_root' => $input->id]))
|
|
->execute(), fn ($k, $v) => [$v['id'], true]);
|
|
|
|
foreach ($input->areas as $area) {
|
|
if (!ObjectUtil::isPropertyInitialized($area, 'id')) {
|
|
throw new GraphQLValidationException(
|
|
'EditableArea.id field is mandatory!'
|
|
);
|
|
}
|
|
|
|
// zvalidovat, ze bloky patri k danemu objektu. Pokud ne, tak hodit chybu.
|
|
if (ObjectUtil::isPropertyInitialized($area, 'id') && $area->id !== null) {
|
|
if (!($blocks[$area->id] ?? false)) {
|
|
throw new GraphQLValidationException(
|
|
'Unable to update EditableArea! Specified EditableArea.id does not belong to updated EditableContent!'
|
|
);
|
|
}
|
|
}
|
|
|
|
// zvalidovat, ze je v datech vyplneny validni json
|
|
if (ObjectUtil::isPropertyInitialized($area, 'data') || ObjectUtil::isPropertyInitialized($area, 'dataPortable')) {
|
|
if (ObjectUtil::isPropertyInitialized($area, 'dataPortable')) {
|
|
$plainDataField = 'dataPortable';
|
|
$plainData = $area->dataPortable;
|
|
} else {
|
|
$plainDataField = 'data';
|
|
$plainData = $area->data;
|
|
}
|
|
|
|
try {
|
|
$data = json_decode_strict($plainData, true);
|
|
} catch (\JsonException $e) {
|
|
throw new GraphQLValidationException("Failed to parse JSON in EditableArea.{$plainDataField}!");
|
|
}
|
|
|
|
if (!ArrayUtil::isVector($data)) {
|
|
throw new GraphQLValidationException("EditableArea.{$plainDataField} structure is not valid! The structure must be an array of blocks, so it must be inside the array [{...}, {...}]");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|