Files
kupshop/bundles/KupShop/ContentBundle/Resources/upgrade/MenuUpgrade.php
2025-08-02 16:30:27 +02:00

529 lines
20 KiB
PHP

<?php
namespace KupShop\ContentBundle\Resources\upgrade;
use KupShop\ContentBundle\Util\MenuUtil;
use KupShop\I18nBundle\Translations\MenuLinksTranslation;
use KupShop\KupShopBundle\Config;
use KupShop\KupShopBundle\Context\LanguageContext;
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
use KupShop\KupShopBundle\Util\StringUtil;
use Query\Operator;
class MenuUpgrade extends \UpgradeNew
{
public function check_menuType()
{
return $this->checkColumnExists('menu_links', 'type');
}
/** Add menu_links.type */
public function upgrade_menuType()
{
// id_root is temporary and will be renamed to id_menu later in this migration
sqlQuery(
"ALTER TABLE menu_links
ADD COLUMN id_root INT UNSIGNED DEFAULT NULL AFTER id,
ADD COLUMN type TINYINT UNSIGNED DEFAULT 2 NOT NULL AFTER id_root,
ADD COLUMN name_short VARCHAR(50) DEFAULT '' AFTER name,
ADD COLUMN code VARCHAR(50) DEFAULT NULL UNIQUE AFTER name_short,
ADD COLUMN url VARCHAR(255) DEFAULT NULL AFTER code,
ADD COLUMN id_block INT(11) UNSIGNED NULL AFTER data,
ADD COLUMN template VARCHAR(255) DEFAULT '' NOT NULL AFTER id_block,
ADD COLUMN meta_title VARCHAR(70) NULL AFTER template,
ADD COLUMN meta_description VARCHAR(250) NULL AFTER meta_title,
ADD COLUMN meta_keywords VARCHAR(100) NULL AFTER meta_description,
ADD COLUMN old_id_page INT(11) NULL AFTER meta_keywords"
);
sqlQuery('ALTER TABLE menu_links MODIFY parent INT UNSIGNED DEFAULT NULL;');
sqlQuery('ALTER TABLE menu_links ENGINE InnoDB;');
sqlQuery('ALTER TABLE menu_links MODIFY link VARCHAR(255) DEFAULT NULL;');
sqlQuery("UPDATE menu_links SET link=NULL WHERE link='';");
$menu = Config::get()['Menu']['own'] ?? [];
$i = 0;
foreach ($menu as $menuID => $menu) {
$existing = sqlQuery('SELECT * FROM menu_links WHERE id=:menuID', ['menuID' => $menuID])->fetch();
if ($existing) {
// re-insert row with required id (so it can be used for this menu)
unset($existing['id']);
$this->insertSQL('menu_links', $existing);
$newID = sqlInsertId();
sqlQuery('UPDATE menu_links SET parent=:new_parent WHERE parent=:old_parent', [
'new_parent' => $newID,
'old_parent' => $menuID,
]);
$this->deleteSQL('menu_links', ['id' => $menuID]);
}
sqlQuery('INSERT INTO menu_links (id, id_root, parent, list_order, type, name, name_short) VALUES (:id, :id_root, NULL, :position, :type, :name, :name_short);', [
'id' => $menuID,
'id_root' => $menuID,
'position' => $i++,
'type' => MenuUtil::TYPE_GROUP,
'name' => $menu['name'],
'name_short' => $menu['title'],
]);
sqlQuery('UPDATE menu_links SET parent=:parent WHERE id_menu=:id_menu AND parent IS NULL', [
'parent' => $menuID,
'id_menu' => $menuID,
]);
sqlQuery('UPDATE menu_links SET id_root=:id_root WHERE id_menu=:id_menu', [
'id_root' => $menuID,
'id_menu' => $menuID,
]);
// move orphans to their root and set figure = N
sqlQuery('DELETE m1 FROM menu_links m1
LEFT JOIN menu_links m2 ON m2.id = m1.parent
WHERE m2.id IS NULL AND m1.parent IS NOT NULL;');
}
// edit type of menu_links
foreach (sqlQuery('SELECT * FROM menu_links WHERE type = :type', ['type' => MenuUtil::TYPE_LINK]) as $menuLink) {
if (empty($menuLink['link'])) {
if (!empty(sqlQuery('SELECT * FROM menu_links WHERE parent = :id', ['id' => $menuLink['id']])->fetch())) {
$this->updateSQL('menu_links', ['type' => MenuUtil::TYPE_GROUP], ['id' => $menuLink['id']]);
} else {
$this->updateSQL('menu_links', ['link' => '/'], ['id' => $menuLink['id']]);
}
}
}
// drop id_menu
sqlQuery('ALTER TABLE menu_links DROP COLUMN id_menu;');
sqlQuery('ALTER TABLE menu_links CHANGE id_root id_menu INT UNSIGNED DEFAULT NULL;');
sqlQuery('
ALTER TABLE menu_links
ADD CONSTRAINT menu_links_fk_parent FOREIGN KEY (parent) REFERENCES menu_links(id)
ON UPDATE CASCADE ON DELETE CASCADE,
ADD CONSTRAINT menu_links_fk_id_menu FOREIGN KEY (id_menu) REFERENCES menu_links(id)
ON UPDATE CASCADE ON DELETE CASCADE;');
sqlQuery('CREATE UNIQUE INDEX menu_links_uix_url ON menu_links(url);');
sqlQuery("
CREATE TABLE photos_menu_relation (
id_photo INT(11) UNSIGNED DEFAULT 0 NOT NULL,
id_menu INT UNSIGNED DEFAULT 0 NOT NULL,
show_in_lead ENUM('Y', 'N') DEFAULT 'N' NOT NULL,
active ENUM('Y', 'N') DEFAULT 'Y' NOT NULL,
date_added DATETIME DEFAULT '0000-00-00 00:00:00' NOT NULL,
position INT NULL,
CONSTRAINT id_photo
UNIQUE (id_photo, id_menu),
CONSTRAINT photos_menu_relation_ibfk_1
FOREIGN KEY (id_photo) REFERENCES photos(id)
ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT photos_menu_relation_ibfk_2
FOREIGN KEY (id_menu) REFERENCES menu_links(id)
ON UPDATE CASCADE ON DELETE CASCADE
);
");
$this->upgradeOK();
}
public function check_menuTranslation()
{
return findModule(\Modules::TRANSLATIONS)
&& $this->checkColumnExists('menu_links_translations', 'id_block');
}
/** Add menu_links_translations.id_block... */
public function upgrade_menuTranslation()
{
sqlQuery(
'ALTER TABLE menu_links_translations
ADD COLUMN name_short VARCHAR(50) DEFAULT NULL AFTER name,
ADD COLUMN url VARCHAR(255) DEFAULT NULL AFTER name_short,
ADD COLUMN id_block INT(11) UNSIGNED NULL,
ADD COLUMN meta_title VARCHAR(70) NULL,
ADD COLUMN meta_description VARCHAR(250) NULL,
ADD COLUMN meta_keywords VARCHAR(100) NULL;'
);
$this->upgradeOK();
}
public function check_html_pages()
{
return !$this->checkTableIsEmpty('html_pages');
}
/** Remove html_pages table */
public function upgrade_html_pages()
{
$pages = sqlQuery('SELECT * FROM html_pages');
$menuLinks = sqlQuery('SELECT * FROM menu_links')->fetchAll();
if (findModule(\Modules::TRANSLATIONS)) {
/** @var MenuLinksTranslation $translationService */
$translationService = ServiceContainer::getService(MenuLinksTranslation::class);
}
$usedLabels = [];
$unclassifiedPagesCounter = 0;
foreach ($pages as $page) {
$menuLink = null;
// homepage vytvorit v systemovych strankach
if ($page['label'] == 'leadpage') {
$this->insertSQL('pages', [
'id_block' => $page['id_block'],
'code' => 'system-home',
'type' => 'HOME',
]);
continue;
}
$label = null;
if (!empty($page['label']) && !in_array($page['label'], $usedLabels)) {
if ($seoUrl = translate_shop($page['label'], 'SEO_URL', true)) {
$pattern = '/'.$seoUrl.'/';
$label = $page['label'];
} else {
$pattern = '/_p'.$page['id'].'.html/';
}
} else {
$pattern = '/_p'.$page['id'].'.html/';
}
// zkusit najit menu_link patrici ke strance
foreach ($menuLinks as $link) {
if ($this->isExternalUrl($link['link'])) {
continue;
}
if (preg_match($pattern, $link['link'])) {
if ($label && strpos($link['link'], '.html')) {
continue;
}
$menuLink = $link;
if ($label) {
$usedLabels[] = $label;
}
break;
}
}
// pokud nebylo nic nalezeno a je nastaven label, tak zkusit hledat jeste jednou podle odkazu stranky
if (!$menuLink && !empty($page['label'])) {
$pattern = '/_p'.$page['id'].'.html/';
foreach ($menuLinks as $link) {
if ($this->isExternalUrl($link['link'])) {
continue;
}
if (preg_match($pattern, $link['link'])) {
$menuLink = $link;
// pokud ma label URL, tak ji nastavit
if ($seoUrl = translate_shop($page['label'], 'SEO_URL', true)) {
$menuLink['link'] = $seoUrl;
}
$label = $page['label'];
$usedLabels[] = $page['label'];
break;
}
}
}
$iLabel = null;
if (!empty($page['label'])) {
if (!$this->selectSQL('menu_links', ['code' => $page['label']])->fetch()) {
$iLabel = $page['label'];
}
}
$updateName = false;
if ($menuLink) { // aktualizovat menu_links podle stranky
$url = parse_url($menuLink['link']);
$url = ltrim($url['path'] ?? $menuLink['link'], '/');
if (!preg_match('/.+(.html)$/', $url)) {
$url = trim($url, '/').'/';
}
$update = [
'type' => MenuUtil::TYPE_PAGE,
'id_block' => $page['id_block'],
'template' => $page['template'],
'meta_title' => $page['meta_title'],
'meta_description' => $page['meta_description'],
'meta_keywords' => $page['meta_keywords'],
'url' => $url,
'link' => null,
'old_id_page' => $page['id'],
];
if ($iLabel) {
$update['code'] = $iLabel;
}
if ($menuLink['name'] != $page['name']) {
$update['name'] = $page['name'];
$update['name_short'] = $menuLink['name'];
$updateName = true;
}
$this->updateSQL('menu_links', $update, ['id' => $menuLink['id']]);
if (findModule(\Modules::TRANSLATIONS)) {
if (!$label) {
sqlQuery(
'UPDATE menu_links_translations SET url = link, link = null WHERE id_menu_link = :id_menu_link',
[
'id_menu_link' => $menuLink['id'],
]
);
} else {
// translate label and insert it into translations
$this->translateLabel($menuLink['id'], $label, $translationService);
}
}
$menuLinkId = $menuLink['id'];
} else { // vytvorit novy menu_link mimo strukturu se strankou
$updateName = true;
$insertLink = StringUtil::slugify($page['name']).'_p'.$page['id'].'.html';
if (!empty($page['label']) && !in_array($page['label'], $usedLabels) && $link = translate_shop($page['label'], 'SEO_URL', true)) {
$insertLink = $link.'/';
}
$this->insertSQL('menu_links', [
'type' => MenuUtil::TYPE_PAGE,
'code' => $iLabel,
'name' => $page['name'],
'list_order' => $unclassifiedPagesCounter++,
'id_block' => $page['id_block'],
'url' => $insertLink,
'template' => $page['template'],
'meta_title' => $page['meta_title'],
'meta_description' => $page['meta_description'],
'meta_keywords' => $page['meta_keywords'],
'old_id_page' => $page['id'],
]);
$menuLinkId = sqlInsertId();
}
// photos_pages_relation -> photos_menu_relation
$photos = sqlQueryBuilder()->select('*')->from('photos_pages_relation')
->where(Operator::equals(['id_page' => $page['id']]))
->orderBy('position')->execute();
foreach ($photos as $photo) {
$this->insertSQL('photos_menu_relation', [
'id_photo' => $photo['id_photo'],
'id_menu' => $menuLinkId,
'show_in_lead' => $photo['show_in_lead'],
'active' => $photo['active'],
'date_added' => $photo['date_added'],
'position' => $photo['position'],
]);
}
// pokud jsou preklady, tak premigrovat taky
if (findModule(\Modules::TRANSLATIONS)) {
$translations = sqlQuery('SELECT * FROM html_pages_translations WHERE id_html_page = :id', ['id' => $page['id']]);
foreach ($translations as $translation) {
$values = [
'meta_title' => $translation['meta_title'],
'meta_description' => $translation['meta_description'],
'meta_keywords' => $translation['meta_keywords'],
];
if ($updateName) {
$values['name'] = $translation['name'];
$menuLinkName = sqlQuery('SELECT name FROM menu_links_translations WHERE id_menu_link = :id AND id_language = :id_language', [
'id' => $menuLinkId,
'id_language' => $translation['id_language'],
])->fetchColumn();
if (!empty($menuLinkName)) {
$values['name_short'] = $menuLinkName;
}
}
$translationService->saveSingleObject($translation['id_language'], $menuLinkId, $values);
}
sqlQuery('DELETE FROM html_pages_translations');
}
}
// promazat html_pages tabulky
sqlQuery('DELETE FROM html_pages');
$this->upgradeOK();
}
/**
* @param $translationService MenuLinksTranslation
*/
private function translateLabel($menuLinkId, $label, $translationService)
{
$languageContext = ServiceContainer::getService(LanguageContext::class);
$originalLanguage = $languageContext->getActiveId();
foreach ($languageContext->getSupported() as $language) {
if ($languageContext->getDefaultId() == $language->getId()) {
continue;
}
$languageContext->activate($language->getId());
$url = translate($label, 'SEO_URL').'/';
$translationService->saveSingleObject($language->getId(), $menuLinkId, ['link' => null, 'url' => $url]);
}
$languageContext->activate($originalLanguage);
}
private function isExternalUrl($url)
{
$cfg = Config::get();
$url = parse_url($url);
if (!empty($url['host'])) {
if (strpos($url['host'], trim($cfg['Addr']['print'], '/')) !== false) {
return false;
}
return true;
}
return false;
}
public function check_OldIdPageColumn()
{
return $this->checkColumnExists('menu_links', 'old_id_page');
}
/** Add old_id_page column into menu_links table */
public function upgrade_OldIdPageColumn()
{
sqlQuery('ALTER TABLE menu_links ADD COLUMN old_id_page INT(11) NULL AFTER meta_keywords');
$this->upgradeOK();
}
public function check_CodeColumn()
{
return $this->checkColumnExists('menu_links', 'code');
}
/** Add code column into menu_links table */
public function upgrade_CodeColumn()
{
sqlQuery('ALTER TABLE menu_links ADD COLUMN code VARCHAR(50) DEFAULT NULL UNIQUE AFTER name_short');
$this->upgradeOK();
}
public function check_DeleteHtmlPagesTableTranslations()
{
return !$this->checkTableExists('html_pages_translations');
}
/** Delete html_pages_translations table */
public function upgrade_DeleteHtmlPagesTableTranslations()
{
sqlQuery('DROP TABLE html_pages_translations');
$this->upgradeOK();
}
public function check_DeleteHtmlPagesTableRelation()
{
return !$this->checkTableExists('photos_pages_relation');
}
/** Delete photos_pages_relation table */
public function upgrade_DeleteHtmlPagesTableRelation()
{
sqlQuery('DROP TABLE photos_pages_relation');
$this->upgradeOK();
}
public function check_DeleteHtmlPagesBlocksTable()
{
return !$this->checkTableExists('html_pages_blocks');
}
/** Delete html_pages and html_pages_blocks tables */
public function upgrade_DeleteHtmlPagesBlocksTable()
{
sqlQuery('DROP TABLE html_pages_blocks');
$this->upgradeOK();
}
public function check_DeleteHtmlPagesTable()
{
return !$this->checkTableExists('html_pages');
}
/** Delete html_pages and photos_pages_relation tables */
public function upgrade_DeleteHtmlPagesTable()
{
sqlQuery('DROP TABLE html_pages');
$this->upgradeOK();
}
public function check_MenuLinkSlash()
{
return $this->checkDataMigration(16);
}
/** Ensure menu_links links begin with a slash */
public function upgrade_MenuLinkSlash()
{
$this->addSlashPrefixToLink();
$this->commitDataMigration(16);
$this->upgradeOK();
}
public function check_MenuLinkSlash2()
{
return $this->checkDataMigration(17);
}
/** Ensure menu_links links begin with a slash */
public function upgrade_MenuLinkSlash2()
{
$this->addSlashPrefixToLink();
$this->commitDataMigration(17);
$this->upgradeOK();
}
public function addSlashPrefixToLink(): void
{
$qb = sqlQueryBuilder()->select('*')->from('menu_links')
->where("link != '' AND link IS NOT NULL AND link NOT LIKE '/%'
AND link NOT LIKE 'http://%' AND link NOT LIKE 'https://%'");
foreach ($qb->execute() as $menuLink) {
if (!preg_match('@^(/|[a-zA-Z]{3,6}://|mailto:|tel:)@', $menuLink['link'])) {
sqlQueryBuilder()->update('menu_links')->directValues(['link' => '/'.$menuLink['link']])
->where(Operator::equals(['id' => $menuLink['id']]))
->execute();
}
}
}
public function check_sectionsTemplateColumn()
{
return $this->checkColumnExists('sections', 'template');
}
/** Add template column to sections */
public function upgrade_sectionsTemplateColumn()
{
sqlQuery("ALTER TABLE sections ADD COLUMN template VARCHAR(255) DEFAULT '' NOT NULL AFTER data");
}
}