Migracja z Sulu 2.6 do 3.0: przewodnik krok po kroku
Spis treści
Migracja z Sulu 2.6 do 3.0 to największa aktualizacja w historii tego CMS-a. Treść przechodzi z repozytorium PHPCR do zwykłych tabel Doctrine ORM, a razem z nią zmieniają się konfiguracja, szablony i część własnego kodu. Samą treść przenosi oficjalne narzędzie, które ma tryb próbny i które można uruchamiać wielokrotnie. O zakresie prac decyduje reszta: ile kodu w projekcie odwołuje się do PHPCR, w jakim stanie są pakiety i ile zbędnych danych zostało w repozytorium po latach edycji. Poniżej dwie fazy z oficjalnej dokumentacji z komendami w kolejności wykonania, lista zmian do ręcznego wprowadzenia i pięć pułapek opisanych przez zespół Sulu oraz autorów narzędzia migracyjnego.
Kluczowe wnioski. Sulu 2.6 jest wydaniem o wydłużonym wsparciu i dostaje poprawki bezpieczeństwa do czasu wydania 4.0, więc migrację można zaplanować bez presji terminu. Sulu 3.0 wymaga PHP 8.2–8.5 i Symfony 6.4–7.4, a projekt na Symfony 5.4 najpierw aktualizuje Symfony, jeszcze na 2.6. Strony, artykuły i snippety przenosi pakiet sulu/phpcr-migration-bundle: ma tryb próbny z raportem błędów w JSON i przy ponownym uruchomieniu nadpisuje dane zamiast je duplikować. Ręcznie trzeba poprawić szablony XML i Twig oraz każdy fragment kodu, który korzysta z DocumentManagera. Zespół Sulu przy migracji własnej strony uruchomił cały proces pięć razy na kopii danych produkcyjnych, zanim wdrożył wynik.
Czy musisz migrować już teraz?
Nie. Wraz z wydaniem 3.0 (26 listopada 2025) zespół Sulu ogłosił linię 2.6 wydaniem LTS (long-term support, czyli o wydłużonym wsparciu). Poprawki bezpieczeństwa i krytycznych błędów mają się ukazywać do premiery Sulu 4.0, a wsparcie skończyło się tylko dla linii 1.6. Nowe funkcje trafiają już jednak wyłącznie do 3.x.
Migrację warto zaplanować teraz, gdy:
- przed projektem jest większy etap rozwoju (nowe typy treści, przebudowa szablonów, nowy frontend) i te same pliki będą i tak zmieniane,
- utrzymujesz Jackrabbita albo Elasticsearcha wyłącznie na potrzeby Sulu i chcesz zdjąć je z infrastruktury,
- do zespołu dochodzą developerzy Symfony, którzy znają Doctrine, a PHPCR musieliby się dopiero uczyć.
Lepiej poczekać, gdy:
- projekt działa na Symfony 5.4 lub starszym (najpierw aktualizacja Symfony, osobnym wdrożeniem),
- korzystasz z pakietów firm trzecich, które nie mają wersji zgodnej z 3.0,
- duża część logiki zapisuje treść przez DocumentManagera (importy, synchronizacje z systemami zewnętrznymi) i nie ma budżetu na jej przepisanie,
- strona jest w trybie utrzymania i przez najbliższy rok nic się w niej nie zmieni.
Wymagania i zmiany w infrastrukturze
Według tabeli zgodności w dokumentacji Sulu 3.0 działa na PHP 8.2–8.5 i Symfony 6.4–7.4. Linia 2.6 obsługuje te same wersje PHP oraz Symfony od 5.4 do 7.4, więc PHP i Symfony da się podnieść jeszcze przed migracją. Dokumentacja odradza łączenie aktualizacji PHP, Symfony i Sulu w jednym kroku: przy trzech zmianach naraz trudno ustalić, która z nich coś zepsuła.
Po stronie pakietów i usług zmienia się sporo:
- Znika:
sulu/article-bundle(artykuły są częścią rdzenia),massive/search-bundlerazem zhandcraftedinthealps/zendsearchoraz pakiety Jackalope. Apache Jackrabbit w większości projektów można po migracji wyłączyć. - Dochodzi: warstwa wyszukiwania SEAL z adapterem Loupe, który trzyma indeks w SQLite i wymaga rozszerzenia
pdo_sqlite, oraz konfiguracja Flysystem 3 dla plików mediów. - Opcjonalny: Elasticsearch. Artykuły działają bez niego, a kto potrzebuje dotychczasowej integracji, instaluje osobny SuluArticleViewDocumentBundle.
Zespół Sulu podaje, że Loupe sprawnie obsługuje indeks do około 50 tysięcy dokumentów, a przejście na Meilisearch lub Elasticsearch jest zmianą w konfiguracji. Budowanie panelu administracyjnego z własnymi rozszerzeniami wymaga Node.js 20 lub nowszego.
Faza 1: porządki na Sulu 2.6
Oficjalny przewodnik aktualizacji z 2.6 do 3.0 dzieli pracę na dwie fazy. Pierwsza odbywa się w całości na starej wersji. Jej celem jest repozytorium treści bez pozostałości po dawnych szablonach, na których narzędzie migracyjne mogłoby się zatrzymać.
- Zaktualizuj
sulu/suludo najnowszego 2.6.x, a pakiety ekosystemu (formularze, headless, automatyzacje, artykuły) do ostatnich wersji zgodnych z 2.6. - Uruchom zaległe migracje PHPCR.
- Uruchom czyszczenie repozytorium. Usuwa ono właściwości, które zostały po zmianach w szablonach.
- Sprawdź klucze webspace'ów, szablonów i kontekstów nawigacji. W 3.0 mogą zawierać tylko małe litery, cyfry, podkreślnik i myślnik, najwyżej 31 znaków.
- Zrób zrzut bazy danych. To punkt powrotu, jeśli druga faza się nie powiedzie.
composer update sulu/sulu --with-dependencies
php bin/console phpcr:migrations:migrate
php bin/console sulu:document:phpcr-cleanup
Zespół Sulu po migracji własnej strony zwraca uwagę, że czyste 2.6 daje mniej szumu w wynikach migracji i łatwiej wtedy zauważyć, co naprawdę zachowuje się inaczej.
Faza 2: przejście na 3.0 i migracja treści
Drugą fazę prowadź na osobnej gałęzi, a najlepiej na sklonowanym projekcie z własną bazą. Stara instalacja zostaje nietknięta i służy za punkt odniesienia.
Najpierw kod. W composer.json podnosisz sulu/sulu i pakiety sulu/* do wersji 3.0, porównujesz projekt z szablonem sulu/skeleton między gałęziami 2.6 i 3.0 (konfiguracja, .env, lista bundli) i przechodzisz przez plik UPGRADE-3.x.md. Część wpisów wymaga ręcznego wykonania zapytań SQL. Nie pomijaj ich, bo późniejsze migracje na nich polegają. Potem uruchamiasz migracje schematu Doctrine, poprawiasz szablony XML i przebudowujesz panel administracyjny.
Dopiero wtedy przychodzi kolej na treść. Instalujesz sulu/phpcr-migration-bundle, wskazujesz mu źródło (bazę danych albo Jackrabbita) i uruchamiasz tryb próbny (dry run). Narzędzie przechodzi przez całą treść, niczego nie zapisuje, a na końcu wypisuje podsumowanie i zapisuje raport JSON z błędami. Poprawiasz szablon lub dane, uruchamiasz próbę ponownie i powtarzasz to, aż raport będzie pusty. Właściwą migrację też można uruchamiać wielokrotnie: przeniesiona wcześniej treść jest nadpisywana.
php bin/console doctrine:migrations:migrate
composer require sulu/phpcr-migration-bundle
php bin/console sulu:phpcr-migration:migrate --dry-run
php bin/console sulu:phpcr-migration:migrate
php bin/console cmsig:seal:reindex --drop
Po migracji przebuduj indeks wyszukiwania i przejrzyj w panelu strony, artykuły, snippety oraz media. Potem usuń pakiet migracyjny. Tabele phpcr_* kasuje się na samym końcu osobną migracją Doctrine wygenerowaną poleceniem doctrine:migrations:diff, po sprawdzeniu, że usuwa tylko te tabele.
Co trzeba przepisać ręcznie
Narzędzie przenosi dane. Kodu nie dotyka, więc poniższe zmiany z przewodnika aktualizacji wprowadza się ręcznie:
- Zapis treści z PHP.
DocumentManagerzostał usunięty. Strony i artykuły tworzy się i zmienia, wysyłając komunikaty (na przykładCreatePageMessagealboModifyArticleMessage) przez szynę komunikatów Symfony. Dotyczy to importów, komend konsolowych i integracji. - Szablony XML. Pole adresu staje się typem
routeo nazwieurlz tagiemsulu.rlp, a tytuł dostaje tagsulu.rlp.part. Zmienia się też klasa kontrolera wskazywana w szablonie. - Artykuły. Typy artykułów zastąpiły grupy szablonów. Każdy szablon artykułu dostaje element
<group>, a role użytkowników trzeba w panelu uprawnić do nowych grup. - Twig.
sulu_content_loadrozdzielono nasulu_page_loadisulu_article_load, a lista pobieranych pól jest teraz argumentem obowiązkowym. Funkcje nawigacji dostały prefikssulu_page_, a funkcje SEO zastąpił szablon@SuluWebsite/Extension/seo.html.twig. - Smart content. W miejsce parametrów
typesistructureTypesweszłygroups(dla artykułów) itemplateKeys(dla stron i snippetów). Stare nazwy zatrzymują budowanie kontenera. - Headless. W projektach z własnymi kontrolerami opartymi na SuluHeadlessBundle zmieniła się sygnatura metody
resolveHeadlessData().
Jeśli frontend jest osobną aplikacją (Nuxt, Next.js, Remix), zmiany w Twigu cię nie dotyczą. Odpowiedzi API porównaj jednak pole po polu, zanim przełączysz ruch.
Pięć pułapek, zanim uznasz migrację za gotową
Pierwsze dwie opisał zespół Sulu po migracji własnej strony sulu.io (wpis z 7 maja 2026). Pozostałe pochodzą z dokumentacji pakietu migracyjnego.
1. Błędy widoczne dopiero przy nowej treści
Po migracji sulu.io wszystko w panelu wyglądało poprawnie. Problem wyszedł przy pierwszym nowym wpisie na blogu: adres nie miał prefiksu /blog, bo w szablonie artykułu zabrakło parametru route_schema. Przegląd istniejących stron tego nie wykryje. Po migracji utwórz i opublikuj po jednej nowej pozycji każdego typu.
2. Wczesne wersje 3.0 i narzędzia
Pola text_area z zapisanym JSON-em były przy migracji sulu.io błędnie dekodowane, a filtrowanie smart content po tagach zwracało wszystkie artykuły zamiast wybranych. Oba błędy naprawiono w pakiecie migracyjnym i w rdzeniu. Migruj więc na bieżące wydanie 3.0.x z bieżącą wersją pakietu, a nie na 3.0.0. Od 3.0.6 wymaganą zależnością jest DoctrineMigrationsBundle.
3. Tryb próbny nie widzi wszystkiego
Według dokumentacji pakietu próba wychwytuje błędy walidacji (długość tytułu i adresu, brak webspace'u lub strony nadrzędnej) oraz kolizje unikalnych kluczy. Nie wykryje naruszeń kluczy obcych, obcięcia zbyt długich wartości ani pustych kolumn wymaganych. Na MySQL-u dochodzi różnica w porównywaniu znaków: wartości różniące się tylko akcentem, jak Café i Cafe, przejdą próbę, a właściwa migracja zakończy się na nich błędem.
4. Lawina błędów po jednej stronie
Jeśli nie uda się przenieść strony nadrzędnej, każda strona pod nią zgłosi brak rodzica. Raport z setką pozycji może więc opisywać jeden błąd. Napraw pierwszy w gałęzi i uruchom próbę ponownie, zanim zaczniesz czytać resztę.
5. Pamięć przy dużych serwisach
Tryb próbny trzyma zapisywane wiersze w pamięci do końca działania komendy, około 1,6 KB na wiersz. Przy serwisie z dużą liczbą dokumentów trzeba podnieść memory_limit.
Migracja uruchomiona pięć razy na kopii produkcji w dniu wdrożenia sprowadza się do importu bazy.
Jak zaplanować migrację: trzy zasady
Planowanie porządkują trzy zasady:
- Szacowanie po próbie. Faza pierwsza i tryb próbny na kopii produkcyjnych danych dają raport błędów. Razem z listą miejsc w kodzie, które używają DocumentManagera, mówi on o zakresie więcej niż liczba stron w serwisie.
- Dwie instalacje obok siebie. Wersja 2.6 działa do dnia przełączenia, a migrację można powtarzać na świeżym zrzucie tyle razy, ile trzeba. Zespół Sulu nazwał równoległą instalację najlepszą decyzją w całym procesie.
- Migracja razem z rozwojem. Łatwiej uzasadnić ją w budżecie, gdy towarzyszy większemu etapowi prac. Odkładana bez planu staje się pozycją w długu technicznym.
Do porównania wyników zespół Sulu użył prostego skryptu, który przechodzi po adresach z mapy strony i porównuje odpowiedzi nowej instalacji ze zrzutem produkcji.
Podsumowanie
Przejście z Sulu 2.6 na 3.0 warto prowadzić jako proces do powtarzania: porządki na 2.6, kod i konfiguracja na 3.0, próba, poprawki, właściwa migracja, porównanie stron. Przeniesienie treści jest zautomatyzowane. Ręcznej pracy wymagają szablony i własny kod oparty na PHPCR, dlatego od nich warto zacząć szacowanie. Linia 2.6 ma wsparcie do wydania 4.0, więc termin można dobrać do planu rozwoju serwisu.
Masz projekt na Sulu 2.6 i chcesz wiedzieć, ile pracy oznacza u Ciebie migracja? Napisz do nas i opisz swój projekt.
FAQ
Nie ma jednej liczby. Przeniesienie treści to kilka komend, a ręcznej pracy wymagają szablony, Twig i własny kod korzystający z PHPCR, których w każdym projekcie jest inna ilość. Wiarygodny szacunek daje tryb próbny narzędzia migracyjnego uruchomiony na kopii danych produkcyjnych oraz przegląd kodu pod kątem użycia DocumentManagera.
Nie. Sulu 2.6 jest wydaniem o wydłużonym wsparciu i otrzymuje poprawki bezpieczeństwa oraz krytycznych błędów do czasu wydania Sulu 4.0. Nowe funkcje pojawiają się już tylko w linii 3.x.
Ryzyko ogranicza sam przebieg: pracujesz na kopii projektu, instalacja 2.6 zostaje nietknięta, a przed startem robisz zrzut bazy. Pakiet migracyjny przenosi strony, artykuły i snippety z zachowaniem struktury i tłumaczeń, a ponowne uruchomienie nadpisuje dane zamiast je duplikować. Wynik trzeba mimo to sprawdzić na prawdziwej treści.
W większości projektów tak. Nowy magazyn treści nie używa Jackrabbita, a artykuły nie wymagają już Elasticsearcha. Domyślny silnik wyszukiwania Loupe działa na SQLite, a Elasticsearch pozostaje jedną z opcji obsługiwanych przez warstwę SEAL.
PHP od 8.2 do 8.5 i Symfony od 6.4 do 7.4. Domyślne wyszukiwanie wymaga rozszerzenia pdo_sqlite, a budowanie panelu administracyjnego z własnymi rozszerzeniami wymaga Node.js 20 lub nowszego.
Trzeba go przepisać. Klasy Jackalope i DocumentManager zostały usunięte, a treść zapisuje się przez komunikaty wysyłane szyną Symfony Messenger. Odczyt odbywa się przez encje Doctrine, więc zapytania można pisać w SQL lub DQL.


