Files
kupshop/bundles/KupShop/ContentBundle/Util/BlocksTrait.php
2025-08-02 16:30:27 +02:00

360 lines
14 KiB
PHP

<?php
namespace KupShop\ContentBundle\Util;
use Doctrine\DBAL\Connection;
use Query\Operator;
trait BlocksTrait
{
/**
* @return array hierarchy of blocks (parental blocks has children - nested set of blocks)
*/
public function getBlocks(int $blockID, bool $forceTranslations = false)
{
$allBlocks = sqlQueryBuilder()->select(
'b.*',
"JSON_ARRAYAGG(
IF(pb.id_photo IS NULL,
NULL,
JSON_OBJECT('date_update', p.date_update, 'id_photo', pb.id_photo)
)
ORDER BY pb.position
) AS photos"
)
->from('blocks', 'b')
->leftJoin('b', 'photos_blocks_relation', 'pb', 'pb.id_block = b.id')
->where('id_root=:id_root')->setParameter('id_root', $blockID)
->leftJoin('pb', 'photos', 'p', 'pb.id_photo = p.id')
->groupBy('b.id')
->orderBy('position');
if (!isAdministration() || $forceTranslations) {
$allBlocks->andWhere(
\Query\Translation::coalesceTranslatedFields(
\KupShop\I18nBundle\Translations\BlocksTranslation::class
)
);
}
$allBlocks = $allBlocks->execute()->fetchAll();
return $this->buildBlockHierarchy($allBlocks, $blockID);
}
/**
* @param array $pool of blocks to process into hierarchy
* @param int|null $parentID to begin with - can be null
*
* @return array
*/
public function buildBlockHierarchy(array $pool, ?int $parentID = null)
{
$finalArray = [];
foreach ($pool as $index => $item) {
if ((int) $item['id_parent'] === $parentID) {
unset($pool[$index]);
$children = $this->buildBlockHierarchy($pool, $item['id']);
if (count($children) > 0) {
$item['children'] = $children;
}
$item['photos'] = array_map(function (array $photoData) {
return getImage($photoData['id_photo'], '', '', 'product_catalog', '', strtotime($photoData['date_update']));
}, array_filter(json_decode($item['photos'] ?: '[]', true)));
$finalArray[] = $item;
}
}
return $finalArray;
}
/**
* @param string $entityBlockIDFieldName = id_block
*/
public function saveBlocks(
array $data,
int $entityID,
string $entityTableName,
string $entityBlockIDFieldName = 'id_block')
{
if (!isset($_POST['relations'])) {
$this->returnError('Chyba ve formuláři');
}
// decode hierarchy relations
$relations = json_decode_strict($_POST['relations'], true);
// remove block IDs from $data and $relations when duplicating parent entity
if (getVal('Duplicate', $_REQUEST, false)) {
foreach ($data['blocks'] as $index => $block) {
if (!empty($block['id'])) {
$content = $this->selectSQL('blocks', ['id' => $block['id']], ['content', 'json_content'])->fetch();
$data['blocks'][$index] = array_merge($block, $content);
}
unset($data['blocks'][$index]['id']);
}
foreach ($relations as $index => $relation) {
$relations[$index]['parentID'] = null;
}
}
$conn = sqlGetConnection();
$conn->transactional(function (Connection $conn) use ($data, $relations, $entityID, $entityTableName, $entityBlockIDFieldName) {
$rootID = sqlQueryBuilder()->select($entityBlockIDFieldName)->from($entityTableName)
->where('id=:id')->setParameter('id', $entityID)->setMaxResults(1)->execute()->fetch();
if (isset($rootID[$entityBlockIDFieldName])) {
$rootID = $rootID[$entityBlockIDFieldName];
} elseif (count($data['blocks']) > 1) { // Create new root block only if any blocks present (0 block always present)
$this->insertSQL('blocks', []);
$rootID = sqlInsertId();
$this->updateSQL($entityTableName, [$entityBlockIDFieldName => $rootID], ['id' => $entityID]);
}
$blocksToRemove = [];
$newPosition = 1;
$counter = 0;
foreach ($data['blocks'] as $index => $block) {
$counter++;
// ignore block with index 0 (it is the default one)
if ($index == 0) {
continue;
}
// if relation is not present return error (maybe save it as a root item instead?)
if (isset($relations[$index])) {
$block['id_parent'] = $relations[$index]['parentID'];
if (is_null($block['id_parent'])) {
$block['id_parent'] = $rootID;
}
} else {
$this->returnError('Chyba ve formuláři');
}
if (!isset($block['id'])) {
// save new items
if (!isset($block['delete'])) {
$valuesToSave = [
'id_root' => $rootID,
'id_parent' => $block['id_parent'],
'position' => $newPosition,
'name' => $block['name'],
'json_content' => $block['json_content'] ?? '',
];
if (getVal('Duplicate', $_REQUEST, false)) {
$valuesToSave['content'] = $block['content'] ?? '';
}
if (isSuperuser() || getVal('Duplicate', $_REQUEST, false)) {
$valuesToSave['identifier'] = $block['identifier'];
}
$this->insertSQL('blocks', $valuesToSave);
$block['id'] = $newID = sqlInsertId();
$newPosition++;
foreach ($relations as $index2 => $arr2) {
if (!empty($arr2['parentIndex'])
&& $arr2['parentIndex'] == $index
) {
$relations[$index2]['parentID'] = (int) $newID;
}
}
}
} elseif (isset($block['delete'])) {
$blocksToRemove[] = $block['id'];
} else {
// update block
$valuesToSave = [
'id_root' => $rootID,
'id_parent' => $block['id_parent'],
'position' => $newPosition,
'name' => $block['name'],
];
if (isset($block['identifier'])) {
$valuesToSave['identifier'] = $block['identifier'];
}
$this->updateSQL('blocks', $valuesToSave, ['id' => $block['id']]);
$newPosition++;
}
// update photos - blocks relations
if (!empty($block['id'])) {
$this->deleteSQL('photos_blocks_relation', ['id_block' => $block['id']]);
foreach (getVal('photos', $block, []) as $position => $photo) {
$this->insertSQL('photos_blocks_relation', [
'id_photo' => $photo,
'id_block' => $block['id'],
'position' => $position,
]);
}
}
}
if (count($blocksToRemove) > 0) {
$conn->createQueryBuilder()->delete('blocks')->where('id IN (:ids)')
->setParameter('ids', $blocksToRemove, Connection::PARAM_INT_ARRAY)->execute();
}
});
}
/**
* @param null $relationTableName
*/
public function updateBlocksPhotosPositions(int $entityID, string $entityTableName, string $tableField, $relationTableName = null)
{
if (!$relationTableName) {
$relationTableName = 'photos_'.$entityTableName.'_relation';
}
$rootBlockId = $this->selectSQL($entityTableName, ['id' => $entityID], ['id_block'])->fetchColumn();
if ($rootBlockId) {
$blocks = $this->getBlocks((int) $rootBlockId);
$blockIds = [];
foreach ($blocks as $block) {
$blockIds[] = $block['id'];
}
if (!empty($blockIds)) {
sqlQuery(
'UPDATE photos_blocks_relation pbr
LEFT JOIN '.$relationTableName.' rt ON rt.'.$tableField.' = :id AND rt.id_photo = pbr.id_photo
SET pbr.position = rt.position WHERE pbr.id_photo = rt.id_photo AND pbr.id_block IN (:block_ids);',
['id' => $entityID, 'block_ids' => $blockIds],
['block_ids' => Connection::PARAM_INT_ARRAY]
);
}
}
}
/**
* Duplicate blocks by root ID.
*
* @return int|false
*
* @throws \Throwable
*/
public function duplicateBlock(int $rootBlockID)
{
return sqlGetConnection()->transactional(function () use ($rootBlockID) {
$mapping = [];
$blocks = sqlQueryBuilder()->select('*')
->from('blocks')
->where(Operator::equals(['id_root' => $rootBlockID]))
->execute();
if ($blocks) {
$this->insertSQL('blocks', []);
$rootID = sqlInsertId();
$mapping[$rootBlockID] = $rootID;
foreach ($blocks as $block) {
$originalBlockID = $block['id'];
unset($block['id']);
$block['id_parent'] = $mapping[$block['id_parent']] ?? null;
$block['id_root'] = $rootID;
sqlQueryBuilder()->insert('blocks')
->directValues($block)
->execute();
$blockID = sqlInsertId();
// copy photos relations
$photoRelations = sqlQueryBuilder()->select('*')
->from('photos_blocks_relation')
->where(Operator::equals(['id_block' => $originalBlockID]))
->execute();
foreach ($photoRelations as $photoRelation) {
$photoRelation['id_block'] = $blockID;
sqlQueryBuilder()->insert('photos_blocks_relation')
->directValues($photoRelation)
->execute();
}
// copy translations
if (findModule(\Modules::TRANSLATIONS)) {
sqlQuery('INSERT INTO blocks_translations (id_block, id_language, created, name, content, json_content)
SELECT
'.$blockID.' as id_block,
id_language,
NOW(),
name,
content,
json_content
FROM blocks_translations
WHERE id_block=:originalBlockId', ['originalBlockId' => $originalBlockID]);
}
$mapping[$originalBlockID] = $blockID;
}
return $rootID;
}
return false;
});
}
/**
* Remove blocks tree by root block ID.
*
* @param int|null $rootBlockID - if null, do nothing
*
* @return int number of affected rows
*/
public function removeBlocks(?int $rootBlockID = null)
{
// removing root block is enough, because fk_id_parent has ON DELETE CASCADE
if (isset($rootBlockID)) {
return $this->deleteSQL('blocks', ['id' => $rootBlockID]);
}
return 0;
}
public function renderBlock($json_content, $block = '')
{
$post_data = json_encode(['data' => $json_content]);
$curl = curl_init();
curl_setopt_array(
$curl,
[
CURLOPT_URL => isRunningOnCluster() ? 'blocek.services' : 'http://blocek.wpj.cz/',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 50,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => [
'content-type: application/json',
],
]
);
curl_setopt($curl, CURLOPT_POSTFIELDS, $post_data);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
$response = ['success' => false, 'error' => 'cURL Error: '.$err];
} elseif ($decode = json_decode($response, true)) {
$response = $decode;
} else {
$response = ['success' => false, 'response' => $response];
}
if (!($response['success'] ?? false)) {
$raven = getRaven();
$raven->captureException(new \Exception("Server-side rendering failed for {$block}."), ['extra' => $response]);
}
return $response;
}
}