Začínáme
Návod k upgradu
Verzování, dvě živé linie, co změnila 2.0 a kroky, po kterých je upgrade nuda.
Na této stránce
- Verzování
- Požadavky
- Livewire 4 (2.0)
- Markup řádku a částečné renderování (2.0)
- Řádky každé tabulky se skládají jinak
- rowPartials() — dobrovolné a ve výchozím stavu vypnuté
- Per-user úložiště preferencí se přestěhovalo do wire-core (2.0)
- Notifikace teď přežije redirect, který ji vyvolal (2.0)
- Passkeys tam, kde je Fortify routuje (2.0)
- Jednorázové kódy na cestě dovnitř (2.0)
- Karta pro nastavení dvoufázového ověření se ptá na kód v políčkách (2.0)
- Notifikace jsou nová tabulka a její id je ULID (2.0)
- TableWidget se přestěhoval do wire-table a začal kreslit (2.0)
- Widget::lazy() teď něco odkládá (2.0)
- ChartWidget::filter() se řeší na serveru a má ho každý widget (2.0)
- InvalidChartDataException::notChartItems() je teď InvalidWidgetDataException (2.0)
- Views polí: Alpine tělo se přesunulo do bundlu (2.0)
- Odstraněno: každý shim označený pro 2.0 (2.0)
- Deprecated shimy traitů končí (2.0)
- Tabulka umí číst i odjinud než z Eloquentu (2.0)
- Minimální verze závislostí (1.17)
- Kroky upgradu
- Registrace, routing a menu
- Výběr a klávesová gesta
- JavaScriptové assety
- Hledání breaking changes
- Viz také
Jak bezpečně přecházet mezi verzemi Wire a kde hledat breaking changes.
Verzování
Ekosystém Wire je jedno monorepo vydávané jako čtrnáct
balíčků, rozdělených z jednoho tagu. Jejich verze se
pohybují v zámku, takže je instalujte a omezujte jako celek — wire-table z
jednoho minoru vedle wire-core z jiného není kombinace, která se testuje.
Wire se od 1.0 drží sémantického verzování: breaking change počká na major, minor jen přidává. Živé jsou dvě linie:
| Linie | Livewire | Stav |
|---|---|---|
2.x |
4.x | aktuální — sem přistávají novinky |
1.x |
3.x | udržovaná — jen opravy |
Žádné vydání neběží na obou verzích Livewiru, a přesně proto je 2.0 major. Omezte caret na linii, na které jste:
// composer.json"require": { "nyoncode/wire-core": "^2.0", "nyoncode/wire-forms": "^2.0", "nyoncode/wire-table": "^2.0", "nyoncode/wire-panels": "^2.0"}
Nebo si vezměte celý stack jako jednu závislost, což je smyslem suite balíčku a co drží sadu v zámku bez čtyř řádků, na které je potřeba myslet:
"require": { "nyoncode/wire-suite": "^2.0"}
Požadavky
| Závislost | Podporováno |
|---|---|
| PHP | 8.2, 8.3, 8.4 |
| Laravel | 12.61+, 13.12+ |
| Livewire | 4.x |
| Tailwind CSS | 3.x nebo 4.x (témování poloměru a odsazení vyžaduje 4.x — viz Vzhled → Rozsah) |
nyoncode/laravel-package-toolkit |
^2.5 |
Před upgradem ověřte, že je vaše aplikace splňuje.
Livewire 4 (2.0)
Verze 2.0 vyžaduje Livewire 4. Linie 1.x zůstává na Livewire 3 a dál dostává opravy; žádné vydání neběží na obou. Nejdřív povyšte Livewire, ověřte, že vaše vlastní komponenty fungují, teprve pak posuňte Wire.
composer require livewire/livewire:^4.0php artisan optimize:clearcomposer update "nyoncode/wire-*"
Vlastní upgrade guide Livewiru pokrývá kód vaší aplikace. Čtyři jeho změny zasahují do toho, co Wire vykresluje za vás, a jen poslední z nich po vás něco chce.
liveOnBlur() znamená pořád totéž. Od Livewire 4 říká wire:model.blur, kdy
klient synchronizuje vlastní stav, ne kdy mluví se serverem — samotné .blur se
na server nedostane vůbec. Wire proto emituje wire:model.live.blur, takže pole
deklarované přes ->liveOnBlur() (nebo ->validateOnBlur(), které to zapíná) se
chová přesně jako dřív. Měnit není co, ledaže jste wire:model.blur napsali ručně
v přepsaném view pole — tam doplňte .live.
Vícesouborové uploady se slučují samy. Livewire 4 si nový upload přislučuje ke
stávajícím položkám vícesouborového pole sám, kdežto 3.x je nahrazoval. Wire tuhle
mezeru dřív zaceloval a už to nedělá. Pokud jste totéž sloučení napsali ve vlastním
updated() hooku, odstraňte ho — jinak se stávající položky započtou dvakrát.
Endpointy Livewiru se přesunuly. URL jsou nově /livewire-{hash}/… místo
/livewire/…, kde se hash odvozuje z vašeho APP_KEY. Pravidla firewallu, bypass
na CDN a cokoli dalšího, co ten prefix porovnává ručně, je potřeba upravit. Vlastní
asset routes Wiru to nezasahuje — pod tím prefixem nikdy nebyly.
Alpine dodává Livewire, stále. Livewire 4 dodává Alpine 3.16. Stejně jako u 3.x Alpine neinstalujte ani nestartujte samostatně.
Markup řádku a částečné renderování (2.0)
Dvě změny. Jedna je dobrovolná a můžete ji ignorovat, dokud ji nebudete chtít; ta druhá se stala každé tabulce a stojí za deset minut pozornosti, pokud stylujete, skriptujete nebo testujete proti markupu tabulky.
Řádky každé tabulky se skládají jinak
Tělo řádku se dřív rozkládalo v Blade uvnitř řádkové smyčky. Teď se skládá v PHP
z markupu, který Blade zkompiluje jednou pro tabulku (Support\RowRenderer, a
Support\CardRenderer pro stacked karty). Výsledný markup je tentýž, se dvěma
rozdíly:
- zmizely morph markery jednotlivých řádků. Livewire vkládá dvojici
<!--[if BLOCK]><![endif]-->kolem každého@ifa@foreach, který zkompiluje, a podmínky řádkové smyčky jich emitovaly 459–999 B na řádek — 848–1035 B na řádek i s whitespace mezi nimi, a 1 347 B na stacked kartu. Nic v DOM na nich nezáviselo kromě samotného morphu Livewire; - podmíněné děti řádku teď nesou
wire:key, což je to, co je při morphu páruje místo těch markerů:ctx-{key}na teleportovaném kontextovém menu,sel-{key}na výběrové buňce,exp-{key}na rozbalovači podřádků aact-{key}-{name}na každém akčním tlačítku vykresleném se záznamem. Tlačítko bez záznamu — hlavičková akce, hromadná akce, prázdný stav — se nezměnilo.
Co zkontrolovat. Cokoli, co prochází děti řádku podle pozice nebo počítá
komentářové uzly: CSS :nth-child(), které předpokládalo stabilní počet dětí,
řetězec querySelector, který markery překračoval, browser test, který na ně
asertoval. Běžné selektory — [data-row-key], [data-testid], [data-column],
tbody tr — se nezměnily a zůstávají podporovanou cestou dovnitř.
Pokud jste publikovali views tabulky, tohle je ta změna, která umí kousnout
potichu. tables/index.blade.php už tělo řádku vůbec neobsahuje: rozdělilo se do
partials/data-region.blade.php a řádek s kartou se renderují z PHP.
Publikovaná kopie z 1.x dál funguje — Laravel jí dá přednost — ale drží si starou
cenu a žádné z nového chování, a rowPartials() nepřevezme. Publikujte znovu,
nebo lépe, kopii smažte a konfigurujte:
php artisan vendor:publish --tag=wire-table::views --force
rowPartials() — dobrovolné a ve výchozím stavu vypnuté
Zápis může odpovědět oblastmi, kterými pohnul, místo překreslení celé tabulky:
$table->rowPartials()
Na stránce s 25 sloupci a 20 řádky stojí uložení buňky 49,3 ms a 556 kB při běžném renderu a 3,2 ms a 26 kB jako jeden řádek. Pro tabulku, která si o to neřekne, se nemění nic — nevykreslí se žádná kotva a neutratí se žádný bajt.
Co za to platíte je, že překreslený řádek si drží svou pozici: editace, která by záznam pod aktuálním řazením posunula, ho nechá na místě až do dalšího plného renderu. Na široké editovatelné mřížce je to ta správná výměna, a proto je to dobrovolné, ne zapnuté.
Viz Pokročilé → Řádkové partials, kde je,
čím zápis odpoví u kterého tvaru tabulky, a jak tytéž kotvy slouží poll()
a live().
Per-user úložiště preferencí se přestěhovalo do wire-core (2.0)
TablePreferenceDriver a tabulka table_preferences jsou nově
Foundation\Preferences\Contracts\PreferenceDriver a wire_preferences, ve
wire-core.
-use NyonCode\WireTable\Preferences\Contracts\TablePreferenceDriver; +use NyonCode\WireCore\Foundation\Preferences\Contracts\PreferenceDriver; -use NyonCode\WireTable\Preferences\Drivers\DatabasePreferenceDriver; +use NyonCode\WireCore\Foundation\Preferences\Drivers\DatabasePreferenceDriver;
Proč se stěhovalo. Layout dashboardu má týž tvar jako zapamatované sloupce
tabulky — malý JSON bag podle plochy a uživatele — a widgety žijí ve wire-core,
na kterém wire-table závisí, takže se z widgetu na tabulkové úložiště nedalo
dosáhnout. Napsat druhé by byla druhá implementace jedné myšlenky.
Na konfiguraci tabulky se nezměnilo nic. config('wire-table.preferences')
je pořád místo, kde se driver tabulky vybírá, se stejnými aliasy i stejným
fallbackem pro hosty. Přesunuly se jen třídy, na které aliasy míří, a dodávaná
konfigurace je upravená s nimi.
Tři věci k udělání, a jen pokud jste se jich dotkli:
- Publikujte migraci znovu. Je teď ve wire-core:
vendor:publish --tag="wire-core::migrations". Spuštění existujícítable_preferencespřejmenuje nawire_preferencesatable_keynasurface_key— nikdo nepřijde o uložený layout — a na čisté instalaci tabulku založí. - Vlastní úložiště implementuje
PreferenceDrivermístoTablePreferenceDriver. Ty čtyři metody se nezměnily; první argument je$surfaceKeymísto$tableKey, protože je to teď klíč tabulky nebo dashboardu. - Publikovaný
config/wire-table.phpmíří aliasydriversna staré namespacy. Opravte třiuseřádky.
Table::preferenceDriver(), rememberColumns() i uložené pohledy zůstávají
nedotčené a celá sada testů wire-table prochází beze změny — což je skutečný
důkaz, že přesun zachoval chování.
Jedna malá ztráta, jen u session úložiště: prefix session klíče je wire. místo
wire-table., takže layout, který měl uživatel v session neuložený, se jednou
resetuje. Řádky v databázi migrace převede.
Notifikace teď přežije redirect, který ji vyvolal (2.0)
Session driver odjakživa flashuje vedle browser události, kterou odesílá. Ten
flash ale nikdo nevykresloval, takže notifikace vyvolaná requestem, který pak
přesměroval — Action::successRedirect(), stránka se založením dopadající na
nový záznam, jakýkoliv redirect po zápisu — neohlásila vůbec nic: událost dorazila
do dokumentu, který wire:navigate o chvíli později vyměnil.
<x-wire-notifications::toast-container /> ji teď po příchodu vykreslí.
Dvakrát přijít nemůže, a je to zásluha Livewiru, ne nějakého přepínače:
SupportRedirects zapomene všechno flashnuté během updatu, který
nepřesměroval, takže uložení, které zůstalo na místě, svůj toast ukázalo jako
událost a nic po sobě nenechá. Hranici překročí jen notifikace, kterou opravdu
nešlo doručit.
Dvě věci ke kontrole, a jen pokud jste se jich dotkli:
- Vlastní obcházka. Pokud jste si po redirectu flashovali vlastní klíč a
sami ho vykreslovali, uvidíte teď obojí. Zrušte tu svoji, nebo kontejner
nasměrujte na svůj klíč:
<x-wire-notifications::toast-container session-key="…" />. - Cokoliv, co čte
session('table-notification'). Drží teď celou notifikaci —title,icon,duration,actions— ne jentypeamessage. Obojí tam pod stejnými jmény zůstává, takže čtenáře['message']se to netýká; striktní porovnání se starým dvouklíčovým polem ano.
Passkeys tam, kde je Fortify routuje (2.0)
Novinka, a potřebuje vlastní přepínač Fortify, ne náš: s Features::passkeys() v
config/fortify.php přibude na přihlašovací obrazovce tlačítko Přihlásit se
passkeyem a na profilu karta s klíči účtu. S vypnutou funkcí — což je výchozí
stav — se nemění nic.
Celou ceremonii dělá laravel/passkeys za Fortify a klient @laravel/passkeys v
prohlížeči; tohle vydání přidává ty dvě obrazovky a Alpine adaptér, který je
propojí. Tři věci, které je dobré vědět předem:
- User model potřebuje dva řádky —
PasskeyAuthenticatablea kontraktPasskeyUser— a jejich chybění je tiché: všechny routy odpovídají a zaregistrovaný klíč nepatří nikomu. Karta je jediné místo, které se na to dívá a řekne to. - Ve vývoji choďte na
localhost, nikdy na127.0.0.1. Výjimka WebAuthn pro bezpečný kontext je psaná pro jméno a Laravelův klient tu adresu odmítne hláškou, ne selháním. - Bundle se emituje po obrazovkách, ne přes
@wireStackScripts: je v něm zabalený Laravelův klient a 12 kB v hlavičce každé stránky kvůli funkci, kterou většina instalací nemá zapnutou, je přesně to, čemuBundle::serve()předchází. Aplikace, která si přihlašovací formulář nebo kartu vykresluje ve vlastním layoutu, to dostane taky — views si partial includují samy.
Jednorázové kódy na cestě dovnitř (2.0)
Novinka a dokud něco nezapnete, nemění se nic. wire-module-auth teď umí
poslat šestimístný kód tam, kde Fortify vlastní tok nemá: přihlášení bez hesla,
druhý faktor pro lidi bez autentikátoru, potvrzení adresy a e-mail pro obnovu
hesla, ve kterém je kód místo odkazu. Každý tok má vlastní klíč pod
wire-module-auth.codes a v čerstvé konfiguraci jsou všechny čtyři false —
instalace, která nic neřekne, nemá nové routy, nové e-maily ani novou tabulku.
Zapnutí kteréhokoli z nich s sebou nese tabulku, a ta se publikuje, nespouští se z balíčku:
php artisan vendor:publish --tag=wire-module-auth::migrationsphp artisan migrate
Dvě věci, které je dobré vědět předem:
- Druhý faktor e-mailem potřebuje zapnuté
Features::twoFactorAuthentication(). Pipe, který kód posílá, je ten kontrakt, který Fortify dává do přihlašovací pipeline jen se zapnutou dvoufázovou funkcí — s vypnutou se tedy žádný kód neodešle a přihlašovací obrazovka vypadá úplně normálně.php artisan abouttenhle stav pojmenuje místo toho, aby hlásil „vypnuto"; stejně tak instalátor. - Se zapnutou obnovou hesla se nahrazují dva bindingy Fortify — Laravelův
ResetPassword::toMailUsing()a FortifyhoSuccessfulPasswordResetLinkRequestResponse. Pokud si některý z nich navazuje vaše aplikace, její provider bootuje po tomhle balíčku a vyhraje; kódy pak nemají čím jet, a to je stav, který je lepší zkontrolovat než objevit.
Celý popis: Jednorázové kódy.
Karta pro nastavení dvoufázového ověření se ptá na kód v políčkách (2.0)
Profilová karta z wire-module-users kreslila na potvrzovací kód jeden <input>,
zatímco výzva na cestě dovnitř kreslila šest políček. Teď je to na obou stranách
dveří stejné pole OtpInput — políčka sama posouvají kurzor a rozeberou vložený
kód.
Na požadavku se nemění nic: hodnota pořád dorazí do $code komponenty a pořád
jde do potvrzovací akce Fortify. Změnil se markup uvnitř
[data-wire="two-factor-code"], takže CSS pravidlo nebo prohlížečový test, který
sahal po input#two-factor-code, potřebuje [data-testid="form-otp-code-0"].
Notifikace jsou nová tabulka a její id je ULID (2.0)
Není co migrovat: wire_notifications v 1.x neexistuje. Přichází s historií
notifikací ve 2.0 a jediné, co o jejím tvaru stojí za to vědět, je primární klíč.
$table->ulid('id')->primary(); // ne uuid()
Proč ne uuid(). Driver zapisuje Str::ulid() — 26 znaků, které se řadí
podle času vzniku, což je to, díky čemu „nejnovější první" drží i tehdy, když
jedna dávková úloha podá pět notifikací ve stejné vteřině. Sloupec uuid to
udrží jen tam, kde je to doopravdy string: SQLite nekontroluje nic a MySQL má za
uuid() char(36), který spolkne cokoliv. Postgres a MariaDB mají skutečný
UUID typ a ULID rovnou odmítnou — invalid input syntax for type uuid při
každém insertu, čímž se to taky našlo.
Čistá instalace dostane správný sloupec a nepotřebuje nic. Se špatným se dá skončit dvěma cestami:
-
Pustili jste ranou 2.0, ještě před touhle opravou. Publikovaná migrace existující tabulku přeskakuje, takže sloupec jednou rozšiřte ručně:
-- PostgreSQLALTER TABLE wire_notifications ALTER COLUMN id TYPE char(26) USING id::text;-- MariaDBALTER TABLE wire_notifications MODIFY id char(26) NOT NULL; -
Nasměrovali jste
wire-core.notifications.database.tablena Laravelí tabulkunotifications. To je záměrně podporované — obě strany pak vidí jednu schránku — jenže Laravelí migrace deklarujeuuid('id'), takže na Postgresu a MariaDB potřebuje totéž rozšíření, než do ní tenhle balíček smí psát.
MySQL a SQLite se to netýká ani v jednom případě.
TableWidget se přestěhoval do wire-table a začal kreslit (2.0)
WireCore\Widgets\TableWidget je nově WireTable\Widgets\TableWidget.
-use NyonCode\WireCore\Widgets\TableWidget; +use NyonCode\WireTable\Widgets\TableWidget;
Ten přesun je důvod, proč to funguje. Stará třída si callback z ->table(...)
uložila a nikdy ho nezavolala: vykreslila kartu s nadpisem a prázdným <div> a
jediný volající getTableCallback() v celém frameworku byl test ověřující
getter. Nebyla to nedbalost, ale struktura — widgety žijí ve wire-core,
tabulkový engine ve wire-table, a table na core závisí, takže se z core na
engine nedalo dosáhnout.
Teď kreslí sloupce a řádky, každou buňku přes Column::renderCell() a dotaz
plánuje TableQueryService, takže badge, formáty měn i cesty přes relace
vypadají přesně jako na plné tabulce.
Dvě věci si zkontrolujte u sebe. Callback potřebuje zdroj dat, protože se teď opravdu spouští:
TableWidget::make()+ ->limit(5) ->table(fn (Table $table) => $table+ ->model(Order::class) ->columns([TextColumn::make('reference')]))
A karta kreslí 5 řádků, pokud ->limit() neřekne jinak — stránkování, které
by zbytek zachytilo, tu není.
Co záměrně nemá: toolbar, hledání, filtry, stránkování, hromadné ani řádkové
akce. Dashboardová karta, které tohle naroste, je tabulka převlečená za widget;
postavte za ni raději stránku s WithTable.
Widget::lazy() teď něco odkládá (2.0)
Metoda přežila; změnilo se, že dělá to, co říká. Před 2.0 žádná view widgetu ten
příznak nečetla — nestálo za ním wire:init, žádná intersect direktiva ani
island — takže widget označený jako lazy se vykreslil celý jako kterýkoli jiný.
StatsOverviewWidget::make()->lazy() // kreslilo se všechno hnedStatsOverviewWidget::make()->lazy() // nakreslí placeholder a pak se načte
Není co měnit, ale zkontrolujte, co jste označili. Volání, které bylo dřív
bez účinku, je teď odklad: mřížka vykreslí skeleton kartu, wire:init zavolá na
hostiteli loadWidget() a odpověď nese markup toho widgetu jako wire:partial
oblast. Z widgetu, který ve skutečnosti pomalý není, volání smažte — pravidlo
frameworku je udělat render levným, ne ho odložit, a eager HTML nestojí žádnou
latenci při otevření.
Co to umožnilo. Odklad po jednotlivých widgetech potřebuje oblast, kterou
server umí pojmenovat, a @island jí uvnitř @foreach být nemůže: Blade vytvoří
jedno tělo islandu na jeden výskyt direktivy a to tělo proměnnou cyklu nikdy
nedostane. Partial je obyčejný atribut vybraný serverem — proto polling už uměl
odpovědět na tik jednoho widgetu jedním widgetem. Odklad je stejný mechanismus s
jiným spouštěčem.
Lazy na úrovni komponenty dál funguje a pro celou mřížku je pořád ten správný
nástroj: <livewire:my-dashboard lazy />.
Viz Widgety → Odložené vykreslení.
ChartWidget::filter() se řeší na serveru a má ho každý widget (2.0)
Rozbalovací nabídka filtru se dřív řešila v prohlížeči: <select> navázaný na Alpine
property a updateChart(), který přiřadil this.labels a this.datasets
zpátky na graf, se kterým byl sestaven. Změna výběru tedy překreslila identický
graf a closure na datasety nikdy neběžela s ničím jiným než se svou výchozí
hodnotou.
Výběr teď putuje na hostitele, ten closury znovu vyřeší a odpoví jen tím jedním widgetem.
ChartWidget::make() ->filter(['week' => 'This week', 'month' => 'This month']) ->datasets(fn (?string $filter) => $this->revenue($filter)) // teď se opravdu spustí znovu
Ve vašem kódu není co měnit. Dvě věci stojí za to znát:
- Closure teď běží při Livewire requestu, ne jednou na render stránky, takže musí být bezpečná při opakovaném volání — vždycky být měla, jen to nic nezkoušelo.
filter(),activeFilter(),getFilterOptions(),hasFilter()agetActiveFilter()se přesunuly zChartWidgetna základní třídu widgetu (HasWidgetFilter). Má je teď každý widget. Nic se nedostalo mimo dosah; graf dál odpovídá na stejná volání.
Pokud jste přepsali widgets/chart.blade.php, publikujte ho znovu. Změnily
se v něm tři věci a pohled je místo, kde se všechny tři potkávají:
<select>je sdílený partial (widgets.partials.widget-filter);- obal nese
wire:keyobsahující aktivní filtr. Ten klíč je nosný — Alpine nikdy znovu nevyhodnotíx-datana elementu, který už inicializoval, takže bez něj morph atribut jen záplatuje a Chart.js kreslí dál starou sérii; - Alpine factory
wireChartbere teď čtyři argumenty, ne šest.filterOptionsaactiveFilterz ní zmizely, protože o filtru už v prohlížeči nic nerozhoduje, aupdateChart()odešlo s nimi — byla to ta metoda, která přiřazovala tytéž dvě pole zpátky na graf.
-x-data="wireChart(@js($type), @js($labels), @js($datasets), @js($filterOptions), @js($activeFilter), @js($options))" +x-data="wireChart(@js($type), @js($labels), @js($datasets), @js($options))"
Publikovaný pohled ponechaný na starém volání předá $options tam, kde factory
teď čte $filterOptions, takže se graf postaví úplně bez nastavení — vykreslí se,
a vykreslí se špatně. Nic na to neupozorní, a právě proto to stojí za ty dvě
minuty.
InvalidChartDataException::notChartItems() je teď InvalidWidgetDataException (2.0)
items() přestalo být vlastností grafu: bar charty, progress widgety i seznamové
widgety berou sérii přes jednoho vlastníka (HasWidgetItems), a kdyby některý z
nich házel výjimku pojmenovanou po grafech, byl by to název, který lže.
-catch (InvalidChartDataException $e) +catch (InvalidWidgetDataException $e)
Obě rozšiřují InvalidArgumentException a obě implementují WireException,
takže catch na kterékoli z nich se to netýká. InvalidChartDataException dál
existuje a dál se hází pro neznámý typ grafu, neznámou variantu a procento
položky mimo rozsah.
Spolu s přesunem vlastníka získalo items() na každém widgetu, který kreslí
sérii, i tvar s closure — ->items(fn (?string $filter) => …) — a právě to dává
filtru na bar chartu smysl.
Views polí: Alpine tělo se přesunulo do bundlu (2.0)
Sedm typů polí mělo celý svůj Alpine controller inlinovaný v markupu jako x-data
objekt, takže stránka se šesti date pickery poslala tytéž stovky řádků šestkrát.
Těla jsou teď registrované Alpine.data() factory.
Nemusíte dělat nic, pokud si nepřepisujete některý z těchto views:
DateTimePicker, TimePicker, Select (searchable combobox), Tags, Rating,
RichEditor, MarkdownEditor. Pokud ano, zkopírované x-data už neexistuje —
zavolejte factory s konfiguračním objektem:
{{-- předtím --}}-<div x-data="{ open: false, value: $wire.entangle('data.at'), hasDate: true, /* …300 řádků… */ }"> {{-- potom --}}+<div x-data="wireDateTimePicker({ + state: $wire.entangle('data.at'),+ hasDate: true,+ typeable: true,+})">
Dvě věci zůstávají v markupu záměrně. state, protože $wire.entangle
a @entangle jsou Alpine magics a jsou ve scope jen uvnitř x-data výrazu —
do bundlu se přesunout nemohou. A jakýkoli řetězec ze serveru, který
controller potřebuje, například přeložený titulek pro prompt(); ten přichází
jako config.
Třetí pravidlo vás dostane, když portujete vlastní pole: Blade @if uvnitř těla
se musí stát runtime větví. Factory se kompiluje jednou a sdílí ji každá instance,
takže tvar objektu už nemůže nic měnit — jen jeho chování.
Controllery jsou v wire-forms-fields.js, searchable-select combobox
v wire-core-dropdown.js (patří core: ten partial includuje sedm povrchů napříč
forms i table). Oba jsou registrátory, takže se načítají s dokumentem, ne na
vyžádání. Každý převedený view navíc includuje
wire-forms::partials.field-assets, protože
@wireStackScripts je aditivní — aplikace,
která direktivu nikdy nepřidá, musí controller dostat stejně, jinak se x-data
vyhodnotí proti prázdnému registru a pole tiše nedělá nic.
Odstraněno: každý shim označený pro 2.0 (2.0)
Linie 1.x vezla sadu metod a tříd, kterým v docblocku stálo „Will be removed in
v2.0“. Tohle je to vydání, takže jsou pryč — volání teď vyhodí
BadMethodCallException (nebo třída nebude nalezena) místo zápisu deprecace.
Náhrady existují po celou dobu linie 1.x a každá je jen přejmenování:
| Odstraněno | Použijte místo toho |
|---|---|
Action::hiddeLabel() |
Action::hideLabel() — starý název byl překlep |
ActionHalt::modalHeading() |
ActionHalt::heading() |
ActionHalt::modalDescription() |
ActionHalt::description() |
ActionHalt::body() |
ActionHalt::description() — halt teď mluví slovníkem modalu |
ActionHalt::modalIcon() |
ActionHalt::icon() |
ActionHalt::modalSubmitLabel() |
ActionHalt::submitLabel() |
ActionHalt::modalCancelLabel() |
ActionHalt::cancelLabel() |
ActionHalt::modalWidth() |
ActionHalt::width() |
ActionHalt::formValidation() |
ActionHalt::validation() |
Table::polling() |
Table::poll() |
TableNotification |
Notification |
TableNotificationManager |
NotificationManager |
confirmTableAction(), executeConfirmedAction(), closeConfirmationModal(), confirmBulkAction(), getConfirmationModalData() |
API halt modalu — viz Životní cyklus a fronty |
WireForms\Components\Layout\{Section,Fieldset,Grid} |
WireCore\Foundation\Schema\{Section,Fieldset,Grid} |
Dalších pět bylo deprecated během 1.x, aniž by pojmenovaly vydání, a jdou stejným tahem — každé je přejmenování se stejným chováním za sebou:
| Odstraněno | Použijte místo toho |
|---|---|
Table::rowContextMenu([...]) |
Table::recordActions([Action::make('edit')->onContextMenu(), …]) |
TextInputColumn::formatForSave() |
dehydrateState() |
TextInputColumn::formatAfterLoad() |
hydrateState() |
Table::flattenSubRows() / isFlattenSubRows() |
subRowsDefaultExpanded() / isSubRowsDefaultExpanded() |
toggleFlattenMode() |
toggleAllRowExpansion() |
legacy magické properties ($this->tableSearch, $tableFilters, $flattenMode, …) |
$this->tableState->get('search') / ->set(...), nebo Table::queryString() pro stav v URL |
TableQueryingPayload::$forceSortColumn / $forceSortDirection |
klíč force_sort_column na polním hooku table.querying |
Legacy properties jsou to, co je potřeba zkontrolovat. WithTable dřív
odpovídal na $this->tableSearch a dvacet sourozenců přes __get/__set a mapoval
je na stavové cesty. Jsou pryč, takže komponenta, která některou čte, dostane od
Livewiru „property does not exist“ — včetně pole $queryString, které je jmenuje,
což je přesně to, co dokumentace pro stav v URL dřív ukazovala. Podporovaná cesta
je Table::queryString(), která si
načtené hodnoty i ověří:
-protected $queryString = ['tableSearch' => ['as' => 'q']]; +public function table(Table $table): Table +{ + return $table->queryString(); +}
Navázání kontextového menu jako record action udělá z tabulky grid. To je
smyslem té náhrady — menu se stane dosažitelným z klávesnice — ale znamená to, že
každý řádek nese roli a tabindex, které klávesová vrstva potřebuje, tedy zhruba
o 260 bajtů na řádek víc než myší ovládaný seznam. ActionGroup už se v seznamu
menu nepřijímá: naváž její akce po jedné, vykreslí se tytéž položky.
Řádky s modal*() se týkají ActionHalt a ničeho jiného. Akce má
modalHeading(), modalDescription(), modalWidth() i zbytek dál — ty jsou
kanonické a nemění se. Aliasy vlastního kratšího slovníku nesl jen halt objekt:
$action->halt()- ->modalHeading('Warnings detected') - ->modalDescription('Continue anyway?'); + ->heading('Warnings detected') + ->description('Continue anyway?');
Pět potvrzovacích metod už bylo prázdných. Zapsaly deprecaci a vrátily se;
halt modal běží přes *WithData() od chvíle, kdy přistál rámcový zásobník. Jejich
odstranění nemůže změnit chování — jen z tichého nicnedělání dělá hlasitou chybu,
což je přesně to, co si místo volání, které je pořád používá, zaslouží.
Podtřídy layoutu ve formulářích jsou jediné odstranění, které mění vykreslení —
a mění ho k lepšímu. WireForms\Components\Layout\Section existovala jen proto,
aby vyměnila form-specifickou kopii view sekce, a ta kopie zaostala za kanonickou:
žádné hlavičkové akce, žádné aside(), vlastní mapa sloupců. Sekce formuláře
postavená z Foundation\Schema\Section dostane všechno tohle a k tomu pozadí
plochy, které sekce v infolistu měla už dřív:
-use NyonCode\WireForms\Components\Layout\Section; +use NyonCode\WireCore\Foundation\Schema\Section;
Nic dalšího se nemění — stejná třída, stejné fluent API, stejné vnořování.
Deprecated shimy traitů končí (2.0)
Devět aliasů traitů pod NyonCode\WireCore\Concerns\ bylo odstraněno. Každý
byl class_alias() shim s poznámkou @deprecated … Will be removed in v2.0
a tohle je to vydání.
Všechny ukazovaly na trait stejného jména pod Actions\Concerns\, takže
migrace je řádek s importem a nic víc:
-use NyonCode\WireCore\Concerns\HasIcons; +use NyonCode\WireCore\Actions\Concerns\HasIcons;
Těch devět jmen: HasButtonStyles, HasColor, HasDynamicProperties,
HasIcons, HasKeyboardShortcut, HasLifecycle, HasLoadingState,
HasModal, HasVisibility.
Samotné traity zůstávají beze změny — stejné metody, stejné chování. Pokud jste
z WireCore\Concerns\ nikdy neimportovali, není co dělat.
Vedle nich padá NyonCode\WireTable\Concerns\TableQueryService — týž druh
aliasu, zbylý po přesunu té třídy do Services\, se stejnou poznámkou
Will be removed in v2.0:
-use NyonCode\WireTable\Concerns\TableQueryService; +use NyonCode\WireTable\Services\TableQueryService;
Dokumentace nikde nežádá, abyste ji vytvářeli sami, takže vás to nejspíš nepotká.
Jedna výjimka, která stojí za to. U barev sáhněte po
Foundation\Concerns\HasColor: ten je kanonickým vlastníkem a
Actions\Concerns\HasColor je sám jen jeho tenký alias.
Tabulka umí číst i odjinud než z Eloquentu (2.0)
Nic, co máte napsané, se nemění. ->model() i ->query() fungují přesně jako
dřív a každá closure akce si drží svůj Model $record.
Nové je, že tabulka teď čte přes DataSource, takže jí jdou předat řádky, které
nejsou v databázi:
use NyonCode\WireTable\Data\CollectionDataSource; $table->dataSource(new CollectionDataSource([ ['id' => 1, 'name' => 'Ada', 'score' => 90], ]));
Taková tabulka je omezená: zdroj deklaruje, na co umí odpovědět, a žádost
o něco, co odmítl, vyhodí UnsupportedQueryAspectException místo tichého vrácení
řádků, které ignorovaly půlku dotazu. U kolekce to znamená žádné raw SQL výrazy,
žádné cesty přes relace, žádné agregace přes subquery a žádné cursor stránkování.
Celá plocha je v Zdrojích dat. Pokud používáte jen Eloquent tabulky, není co dělat.
Minimální verze závislostí (1.17)
Laravel 10 a 11 končí. Verze 1.17 přesunula JavaScriptové bundly z package
route do reálných souborů pod public/vendor a kód, který je tam zrcadlí, žije
v nyoncode/laravel-package-toolkit — vedle deklarace hasAssets() a publish
tagu, jehož je čtecí stranou. Toolkit stojí na
illuminate/support ^12.61.1|^13.12.0 a minimum závislosti je i vaše minimum:
aplikace pod ním balíčky Wire nenainstaluje, ať v jejich vlastním
composer.json stojí ^12.0. Nejdřív povyšte Laravel, pak Wire.
Constraint toolkitu je ^2.5. Přímo si ho nevyžadujete, takže v běžném
případě ho composer update "nyoncode/wire-*" posune se vším ostatním a není co
řešit. Viditelný je jen ve dvou situacích:
- váš
composer.jsonnyoncode/laravel-package-toolkitjmenuje — protože na něm stavíte vlastní balíček, nebo ze starého pinu — a drží ho pod 2.4. Composer pak hlásí jako neinstalovatelné balíčky Wire, ne toolkit jako starý, takže ten constraint rozšiřte na^2.4jako první. - běžíte na Octane. Memo assetů, které je jinak vázané na jeden request a tady přežívá celý
worker, se na
RequestTerminatedzahazuje přesPublishedAssets::flush()z toolkitu, a 2.4 je první vydání, které ho nese. Pod ním worker, který přežije deploy, dál emituje?id=<mtime>z minulého vydání awire:navigatesi nových bundlů nikdy nevšimne.
Kroky upgradu
-
Přečtěte si changelog. Zkontrolujte
CHANGELOG.mdpro verze, které přeskakujete, zejména jakoukoli sekci Breaking Changes. -
Aktualizujte balíčky.
composer update "nyoncode/wire-*" -
Znovu zkontrolujte publikované soubory. Pokud jste publikovali konfiguraci, pohledy nebo překlady, vaše kopie se neaktualizují automaticky. Porovnejte je s novými verzemi balíčků a zapracujte relevantní změny:
config/wire-*.phpresources/views/vendor/wire-*/…lang/vendor/wire-*/…
Čím méně pohledů přepisujete, tím méně je zde ke sladění — viz Vzhled → Přepis pohledů.
-
Vyčistěte cache a přebuildujte assety.
php artisan view:clearphp artisan config:clearnpm run build -
Spusťte testovací sadu. Testovací sada je nejrychlejší způsob, jak odchytit breaking change ve vlastních formulářích a tabulkách.
Registrace, routing a menu
Jeden seam nahradil tři přímá čtení: menu, router i ⌘K paleta teď čtou
Catalog, takže jedna registrace obslouží
všechny tři. Čtyři jména se s tím přesunula a žádné si nenechalo alias — tahle
linie ještě nevyšla a compat vrstva pro přejmenování, na kterém nikdo nevisel,
je náklad bez čtenáře.
| Dřív | Teď |
|---|---|
Core\Resources\Contracts\NavigationSource |
Foundation\Registration\Contracts\RegistrySource |
NavigationSource::navigableClasses() |
RegistrySource::registeredClasses() |
WirePanels\Resources\Contracts\ProvidesResourcePages |
WireCore\Foundation\Routing\Contracts\ProvidesPages |
WirePanels\Resources\Contracts\ConfiguresResourceRoutes |
WireCore\Foundation\Routing\Contracts\ConfiguresRoutes |
WirePanels\Routing\RoutePage |
WireCore\Foundation\Routing\RoutePage |
Ta tři routovací jména se přesunula dolů, do wire-core, aby stránky mohl
deklarovat i Dashboard — dashboard teď Route::wireResources() zaroutuje stejně
jako resource. URL konvence se nepřesunula: ResourceRoutes, tvar URL i jména
wire.{key}.{page} zůstávají ve wire-panels.
Změnily se dva konstruktory a oba se resolvují z kontejneru, takže se to týká jen
kódu, který si je stavěl ručně: Workspace bere Catalog, své navigační skupiny
a ResolvesPageUrls; GlobalSearch bere Catalog místo ResourceRegistry.
Co teď můžete smazat. Položka menu nese URL stránky svého klíče a výsledek
hledání URL svého záznamu, obojí doplněné z klíče — takže ručně psaná mapa
klíč => url, kterou si držela každá aplikace, může pryč:
// dřív — mapa vedle rout, a ta kopie, co zastará-<a href="{{ $urls[$key] }}"> -url: route('orders.show', $record), // teď+<a @if($item->getUrl()) href="{{ $item->getUrl() }}" @endif> +// toGlobalSearchResult() nepředává url vůbec
Explicitní url: nebo ->url() pořád vyhraje — pro řádek, který vede někam, kam
konvence nedosáhne.
Routing je pořád opt-in a Route::wireResources() ve vašem route souboru
zůstává referenční cestou. Novinka vedle ní je
wire-panels.routes — tytéž argumenty skupiny předané
jednou — a zóny, víc mount pointů nad jedním katalogem.
Obojí je vypnuté, dokud to nezapnete.
Výběr a klávesová gesta
Z výběru v tabulce se stala plnohodnotná sada gest, ne jen sloupec zaškrtávátek (viz Výběr řádků). Při upgradu zkontrolujte čtyři věci.
1. Všechna gesta nad řádkem jsou opt-in — ->gestures(). Z výběru se stala
plnohodnotná sada gest: Shift/mod kliky pro rozsahy, tažení po sloupci se
zaškrtávátky, které nabere celý blok, a z klávesnice šipky, Space, Shift+šipky
a mod+A. Nic z toho není zapnuté, dokud si o to tabulka neřekne — každé z nich
totiž mění chování tabulky vůči návštěvníkovi, který ji ovládat nezamýšlel: řádky
jdou do pořadí tabulátoru, označuje se aktivní řádek, tažení začne vybírat
a modifikovaný klik přestane být klikem.
Tabulkám, které to chtějí, přidejte jedno volání:
->gestures()->selectable()
nebo, pokud je celý projekt back office:
// config/wire-table.php'defaults' => ['gestures' => true],
Co změna neovlivní: zaškrtávátka, oba ovladače „vybrat vše“ i bulk bar fungují beze změny a tabulka, která si o gesta neřekla, nemontuje delegovaný controller vůbec. Stejně tak kontextové menu pod pravým tlačítkem a fill handle — o oboje jste si stejně museli říct sami. Šest schopností a jak je kombinovat najdete ve Vrstvě gest.
2. ->onKey() na navigační klávese nově vyhodí výjimku. Dřív se tiše
zahodila, takže akce prostě nikdy nevystřelila. Pokud takovou vazbu máte, byla
to už dřív mrtvá větev — přemapujte ji na volnou klávesu:
Enter Space ArrowUp ArrowDown Home End PageUp PageDown ContextMenu F10 ?
Backspace zůstává k dispozici a nově funguje i jako alias klávesy Delete.
3. Rozsahová gesta už neopouštějí režim „vše odpovídající“. Když je vybráno
„vše, co odpovídá filtru“, je uložený seznam seznamem výjimek — takže rozsah
přes Shift+šipku ho nově odznačí, místo aby celý výběr zúžil na jednu
stránku. Pokud výběr čtete přímo, počítejte s tím, že getSelectedRecordKeys()
v tomto režimu záměrně vrací []; použijte selectedRecordsQuery() nebo
eachSelectedRecord().
4. Přepublikujte view tabulky, pokud jste ho přepsali. Gesta potřebují
markup, který zkompilovaný JavaScript hledá, a publikovaná kopie
resources/views/vendor/wire-table/tables/index.blade.php ho mít nebude. View
nese kontraktní značku, takže zastaralá kopie spadne hlasitě v konzoli prohlížeče
místo toho, aby tiše vybírala špatné řádky:
php artisan vendor:publish --tag=wire-table::views --force
Své úpravy pak naneste znovu na nový soubor. Pokud jste view přepsali jen kvůli vzhledu, bývá Theming menší cesta.
5. Akce nad záznamem, které byly jen chováním, se na mobilní kartě nově
vykreslí jako tlačítko. Telefon nemá dvojklik, pravý klik ani hover, kterým by
se jeden nebo druhý dal objevit — akce navázaná jen na gesto tak byla po složení
tabulky nedosažitelná. Nově se na kartě vykreslí jako obyčejné tlačítko, a jen
tam; desktopová tabulka se nemění. Nic se nezdvojí: akce už přítomná
v ->actions() i akce povýšená přes ->alsoInRowActions() dá právě jedno
tlačítko a fallbacková tlačítka se počítají do ->collapseActionsOnMobile().
Vypnout lze pro konkrétní tabulku:
->recordActionButtonsOnMobile(false)
JavaScriptové assety
Alpine controllery Wire si nově deklaruje každý balíček sám a dají se vypsat z jednoho místa ve vašem layoutu. Při upgradu udělejte dvě věci.
1. Přidejte @wireStackScripts do <head> layoutu.
<head> @vite(['resources/css/app.css', 'resources/js/app.js']) @livewireStyles @wireStackScripts </head>
Je to aditivní — každý povrch si svůj bundle stále načte sám, takže aplikace bez
direktivy funguje dál. Ale je to právě ono, co opraví komponenty umírající po
návštěvě přes wire:navigate (wireRecordSelection is not defined, mrtvé
rozbalovací nabídky, šedý scrim přes tabulku): cesta cachovaného Zpět/Vpřed v Livewire
nečeká na nově injektované <head> skripty a imunní je jen bundle, který už
v dokumentu byl. Viz
Začínáme → JavaScriptové assety.
Pokud si vaše aplikace tohle dřív obcházela @include-ováním partialů balíčků
v layoutu, tyhle includy smažte a použijte direktivu — cesty k partialům jsou
interní a direktiva se s nimi stejně deduplikuje.
2. window.Sortable už se neposkytuje. SortableJS je zkompilovaný do bundlu
wire-sortable, takže config('wire-sortable.sortablejs_cdn') je nově ve výchozím
stavu null a žádný CDN skript se nenačítá. Řazení to neovlivní — drag controller
používá zabundlovanou kopii a globál nikdy nečte.
Týká se to jen vašeho vlastního kódu, pokud na existenci toho globálu spoléhal. Buď si o skript řekněte zpět:
// config/wire-sortable.php'sortablejs_cdn' => 'https://cdn.jsdelivr.net/npm/sortablejs@1.15.6/Sortable.min.js',
nebo si SortableJS zabundlujte sami:
// resources/js/app.jsimport Sortable from 'sortablejs';window.Sortable = Sortable;
Nic dalšího se nemění: konfigurační klíč po nastavení pořád funguje a aplikací, které ho už nastavené mají, se změna nedotkne.
Hledání breaking changes
CHANGELOG.md je zdroj pravdy. Breaking changes jsou vyznačeny pod nadpisem
Breaking Changes u každého vydání, často s migrační tabulkou před/po.
Například vydání 0.1.0 přesunulo akce a notifikace z
NyonCode\WireTable\… do NyonCode\WireCore\…; changelog vypisuje každou
přesunutou třídu, takže můžete use příkazy upravit hromadným najít-a-nahradit.
Pokud třída nebo metoda zmíněná v této dokumentaci po upgradu už neexistuje,
byla pravděpodobně přesunuta nebo přejmenována — hledejte původní název
v CHANGELOG.md.
Viz také
- Začínáme — požadavky a instalace
- Konfigurace — publikovatelná konfigurace
- Vzhled — udržování přepisů pohledů na minimu
- Řešení potíží — problémy, které se objeví po aktualizaci