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; } }