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 [{...}, {...}]"); } } } } }