{"id":20868,"date":"2026-10-21T20:26:00","date_gmt":"2026-10-21T18:26:00","guid":{"rendered":"https:\/\/www.lukaswojcik.com\/blog\/?p=20868"},"modified":"2026-10-09T22:33:54","modified_gmt":"2026-10-09T20:33:54","slug":"dynamiczny-blok-block-json","status":"publish","type":"post","link":"https:\/\/www.lukaswojcik.com\/blog\/pl\/wordpress-plugins-tricks-pl\/tutorials-pl-wordpress-plugins-tricks-pl\/dynamiczny-blok-block-json\/","title":{"rendered":"Dynamiczny blok Gutenberga z block.json i render.php"},"content":{"rendered":"<p>Blok, kt\u00f3rego wynik zale\u017cy od danych zmieniaj\u0105cych si\u0119 ju\u017c po zapisaniu wpisu (najnowsze wpisy, cena, lista wydarze\u0144), nie mo\u017ce przechowywa\u0107 gotowego HTML w <code>post_content<\/code>. Ka\u017cdy nowy wpis w kategorii sprawia\u0142by, \u017ce zapisany kod si\u0119 zdezaktualizuje. Bloki statyczne maj\u0105 te\u017c drug\u0105 s\u0142abo\u015b\u0107: gdy zmienia si\u0119 kod zwracany przez funkcj\u0119 <code>save<\/code>, starsze wpisy przestaj\u0105 do niego pasowa\u0107, edytor zg\u0142asza nieprawid\u0142ow\u0105 tre\u015b\u0107, a blok potrzebuje wpisu w tablicy deprecations. Blok dynamiczny omija oba problemy. Edytor zapisuje wy\u0142\u0105cznie komentarz bloku z atrybutami, a kod HTML generuje PHP przy ka\u017cdym \u017c\u0105daniu.<\/p>\n<p>Artyku\u0142 pokazuje, jak od podstaw zbudowa\u0107 list\u0119 najnowszych wpis\u00f3w z jednej kategorii z atrybutami <code>categoryId<\/code> i <code>count<\/code>. Wyja\u015bnia, co generuje <code>@wordpress\/create-block<\/code> wed\u0142ug stanu na 29.09.2026, jak <code>block.json<\/code> \u0142\u0105czy edytor z serwerem, dlaczego szkielet rejestruje dzi\u015b bloki przez wygenerowany manifest i jakie zmienne otrzymuje <code>render.php<\/code>. Kod jest kompletny; wtyczka dzia\u0142a od WordPressa 6.9, a w chwili pisania aktualnym wydaniem jest 7.1.2.<\/p>\n<h2>Co dzi\u015b generuje szkielet create-block<\/h2>\n<p>Oficjalnym narz\u0119dziem jest pakiet npm <code>@wordpress\/create-block<\/code>; w dniu pobrania najnowsz\u0105 opublikowan\u0105 wersj\u0105 jest 4.99.0 z 23.09.2026. Wymagany jest Node.js w wersji 20.10.0 lub nowszej. Standardowy szablon zna dwa warianty, <code>static<\/code> i <code>dynamic<\/code>. Wariant dynamiczny r\u00f3\u017cni si\u0119 dok\u0142adnie w trzech miejscach: <code>block.json<\/code> dostaje pole <code>render<\/code> wskazuj\u0105ce na <code>render.php<\/code>, <code>index.js<\/code> rejestruje blok bez funkcji <code>save<\/code>, a skrypty npm kopiuj\u0105 pliki PHP do katalogu build. Dla przyk\u0142adu polecenia wygl\u0105daj\u0105 nast\u0119puj\u0105co:<\/p>\n\n\n<figure style=\"--cbb-digits:1\" class=\"cbb cbb--wrap cbb--numbers wp-block-code-beautifier-block-code\" data-lang=\"bash\"><figcaption class=\"cbb-head\"><span class=\"cbb-lang\">Shell<\/span><span class=\"cbb-actions\"><button type=\"button\" class=\"cbb-button cbb-button--wrap\" data-cbb-action=\"wrap\" aria-pressed=\"true\" title=\"Wrap long lines\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M2 4h12M2 8h9.5a2.5 2.5 0 0 1 0 5H8m0 0 1.5-1.5M8 13l1.5 1.5M2 12h3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><span class=\"cbb-sr\">Wrap long lines<\/span><\/button><button type=\"button\" class=\"cbb-button cbb-button--copy\" data-cbb-action=\"copy\" data-cbb-done=\"Copied\" data-cbb-failed=\"Press Ctrl+C to copy\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><rect x=\"5.5\" y=\"5.5\" width=\"8\" height=\"8.5\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><path d=\"M10.5 3.5V3A1.5 1.5 0 0 0 9 1.5H4A1.5 1.5 0 0 0 2.5 3v6.5A1.5 1.5 0 0 0 4 11h.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><\/svg><span class=\"cbb-button-label\" aria-live=\"polite\">Copy<\/span><\/button><\/span><\/figcaption><pre class=\"cbb-pre\" tabindex=\"0\"><code class=\"cbb-code language-bash\" style=\"counter-reset:cbb-line 0;\"><span class=\"cbb-line\"><span class=\"cbb-function\">npx<\/span> @wordpress\/create-block@latest category-posts <span class=\"cbb-attr\">--variant<\/span> dynamic <span class=\"cbb-attr\">--namespace<\/span> lw <span class=\"cbb-attr\">--title<\/span> <span class=\"cbb-string\">\"Category Posts\"<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-function\">cd<\/span> category-posts\n<\/span><span class=\"cbb-line\"><span class=\"cbb-function\">npm<\/span> install @wordpress\/components @wordpress\/core-data @wordpress\/data @wordpress\/html-entities @wordpress\/server-side-render <span class=\"cbb-attr\">--save<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-function\">npm<\/span> start<\/span><\/code><\/pre><\/figure>\n\n\n<p>Dodatkowa linia z <code>npm install<\/code> nie jest konieczna do samego budowania, poniewa\u017c <code>@wordpress\/scripts<\/code> traktuje importy z <code>@wordpress\/*<\/code> jako zale\u017cno\u015bci zewn\u0119trzne i mapuje je na uchwyty skrypt\u00f3w, kt\u00f3re WordPress i tak dostarcza. Zainstalowane pakiety pozwalaj\u0105 jednak narz\u0119dziom edytora i linterowi rozpozna\u0107 importy. <code>npm start<\/code> obserwuje <code>src\/<\/code> i zapisuje wynik w <code>build\/<\/code>, a <code>npm run build<\/code> tworzy zminimalizowan\u0105 wersj\u0119 do publikacji. Katalog wtyczki wygl\u0105da potem mniej wi\u0119cej tak:<\/p>\n\n\n<figure style=\"--cbb-digits:2\" class=\"cbb cbb--wrap cbb--numbers wp-block-code-beautifier-block-code\" data-lang=\"plain\"><figcaption class=\"cbb-head\"><span class=\"cbb-lang\">Text<\/span><span class=\"cbb-actions\"><button type=\"button\" class=\"cbb-button cbb-button--wrap\" data-cbb-action=\"wrap\" aria-pressed=\"true\" title=\"Wrap long lines\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M2 4h12M2 8h9.5a2.5 2.5 0 0 1 0 5H8m0 0 1.5-1.5M8 13l1.5 1.5M2 12h3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><span class=\"cbb-sr\">Wrap long lines<\/span><\/button><button type=\"button\" class=\"cbb-button cbb-button--copy\" data-cbb-action=\"copy\" data-cbb-done=\"Copied\" data-cbb-failed=\"Press Ctrl+C to copy\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><rect x=\"5.5\" y=\"5.5\" width=\"8\" height=\"8.5\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><path d=\"M10.5 3.5V3A1.5 1.5 0 0 0 9 1.5H4A1.5 1.5 0 0 0 2.5 3v6.5A1.5 1.5 0 0 0 4 11h.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><\/svg><span class=\"cbb-button-label\" aria-live=\"polite\">Copy<\/span><\/button><\/span><\/figcaption><pre class=\"cbb-pre\" tabindex=\"0\"><code class=\"cbb-code language-plain\" style=\"counter-reset:cbb-line 0;\"><span class=\"cbb-line\">category-posts\/                 (abridged)\n<\/span><span class=\"cbb-line\">\u251c\u2500\u2500 category-posts.php          plugin header and block registration\n<\/span><span class=\"cbb-line\">\u251c\u2500\u2500 package.json                build\/start scripts with --blocks-manifest\n<\/span><span class=\"cbb-line\">\u251c\u2500\u2500 readme.txt\n<\/span><span class=\"cbb-line\">\u251c\u2500\u2500 src\/\n<\/span><span class=\"cbb-line\">\u2502   \u2514\u2500\u2500 category-posts\/\n<\/span><span class=\"cbb-line\">\u2502       \u251c\u2500\u2500 block.json\n<\/span><span class=\"cbb-line\">\u2502       \u251c\u2500\u2500 index.js\n<\/span><span class=\"cbb-line\">\u2502       \u251c\u2500\u2500 edit.js\n<\/span><span class=\"cbb-line\">\u2502       \u251c\u2500\u2500 render.php\n<\/span><span class=\"cbb-line\">\u2502       \u251c\u2500\u2500 view.js             not needed here, deleted\n<\/span><span class=\"cbb-line\">\u2502       \u251c\u2500\u2500 style.scss\n<\/span><span class=\"cbb-line\">\u2502       \u2514\u2500\u2500 editor.scss\n<\/span><span class=\"cbb-line\">\u2514\u2500\u2500 build\/                      written by npm start \/ npm run build\n<\/span><span class=\"cbb-line\">    \u251c\u2500\u2500 blocks-manifest.php\n<\/span><span class=\"cbb-line\">    \u2514\u2500\u2500 category-posts\/\n<\/span><span class=\"cbb-line\">        \u251c\u2500\u2500 block.json\n<\/span><span class=\"cbb-line\">        \u251c\u2500\u2500 index.js\n<\/span><span class=\"cbb-line\">        \u251c\u2500\u2500 index.asset.php\n<\/span><span class=\"cbb-line\">        \u251c\u2500\u2500 index.css\n<\/span><span class=\"cbb-line\">        \u251c\u2500\u2500 style-index.css\n<\/span><span class=\"cbb-line\">        \u2514\u2500\u2500 render.php<\/span><\/code><\/pre><\/figure>\n\n\n<p>W standardowym szablonie blok znajduje si\u0119 w <code>src\/&lt;slug&gt;\/<\/code>, a nie bezpo\u015brednio w <code>src\/<\/code>. Text domain domy\u015blnie odpowiada slugowi, tutaj <code>category-posts<\/code>. Szkielet tworzy te\u017c plik <code>view.js<\/code> wraz z wpisem <code>viewScript<\/code> dla JavaScriptu na froncie. Lista renderowana po stronie serwera, bez interakcji, tego nie potrzebuje, dlatego przyk\u0142ad usuwa oba elementy. Dwa skrypty npm istotne dla bloku dynamicznego szkielet zapisuje tak:<\/p>\n\n\n<figure style=\"--cbb-digits:1\" class=\"cbb cbb--wrap cbb--numbers wp-block-code-beautifier-block-code\" data-lang=\"json\"><figcaption class=\"cbb-head\"><span class=\"cbb-lang\">JSON<\/span><span class=\"cbb-actions\"><button type=\"button\" class=\"cbb-button cbb-button--wrap\" data-cbb-action=\"wrap\" aria-pressed=\"true\" title=\"Wrap long lines\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M2 4h12M2 8h9.5a2.5 2.5 0 0 1 0 5H8m0 0 1.5-1.5M8 13l1.5 1.5M2 12h3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><span class=\"cbb-sr\">Wrap long lines<\/span><\/button><button type=\"button\" class=\"cbb-button cbb-button--copy\" data-cbb-action=\"copy\" data-cbb-done=\"Copied\" data-cbb-failed=\"Press Ctrl+C to copy\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><rect x=\"5.5\" y=\"5.5\" width=\"8\" height=\"8.5\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><path d=\"M10.5 3.5V3A1.5 1.5 0 0 0 9 1.5H4A1.5 1.5 0 0 0 2.5 3v6.5A1.5 1.5 0 0 0 4 11h.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><\/svg><span class=\"cbb-button-label\" aria-live=\"polite\">Copy<\/span><\/button><\/span><\/figcaption><pre class=\"cbb-pre\" tabindex=\"0\"><code class=\"cbb-code language-json\" style=\"counter-reset:cbb-line 0;\"><span class=\"cbb-line\">{\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-attr\">\"scripts\"<\/span>: {\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-attr\">\"build\"<\/span>: <span class=\"cbb-string\">\"wp-scripts build --webpack-copy-php --blocks-manifest\"<\/span>,\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-attr\">\"start\"<\/span>: <span class=\"cbb-string\">\"wp-scripts start --webpack-copy-php --blocks-manifest\"<\/span>\n<\/span><span class=\"cbb-line\">\t}\n<\/span><span class=\"cbb-line\">}<\/span><\/code><\/pre><\/figure>\n\n\n<p><code>--webpack-copy-php<\/code> kopiuje wszystkie pliki PHP z <code>src<\/code> do <code>build<\/code>; bez tej flagi <code>@wordpress\/scripts<\/code> przenosi tylko pliki PHP wskazane w <code>block.json<\/code> w polach <code>render<\/code> lub <code>variations<\/code>. <code>--blocks-manifest<\/code> zapisuje plik <code>build\/blocks-manifest.php<\/code>, o kt\u00f3rym mowa w kolejnych sekcjach. Wygenerowany nag\u0142\u00f3wek wtyczki podaje \u201eRequires at least: 6.8\u201d, a <code>readme.txt<\/code> \u201eTested up to: 6.8\u201d. To warto\u015bci domy\u015blne szablonu, a nie informacja o bie\u017c\u0105cym wydaniu, i przed publikacj\u0105 nale\u017cy je dostosowa\u0107. Pola nag\u0142\u00f3wka omawia <a href=\"https:\/\/www.lukaswojcik.com\/blog\/pl\/wordpress-plugins-tricks-pl\/readme-naglowek-wtyczki-svn\/\">osobny artyku\u0142 z tej serii, po\u015bwi\u0119cony <code>readme.txt<\/code>, nag\u0142\u00f3wkowi wtyczki i SVN<\/a>.<\/p>\n<h2>block.json jako umowa mi\u0119dzy edytorem a serwerem<\/h2>\n<p><code>block.json<\/code> jest czytany po obu stronach. PHP rejestruje na jego podstawie typ bloku, atrybuty i zasoby; pakiet edytora importuje ten sam plik, \u017ceby JavaScript zna\u0142 nazw\u0119 i schemat atrybut\u00f3w. W przyk\u0142adzie plik w <code>src\/category-posts\/<\/code> ma nast\u0119puj\u0105c\u0105 tre\u015b\u0107:<\/p>\n\n\n<figure style=\"--cbb-digits:2\" class=\"cbb cbb--wrap cbb--numbers wp-block-code-beautifier-block-code\" data-lang=\"json\"><figcaption class=\"cbb-head\"><span class=\"cbb-lang\">JSON<\/span><span class=\"cbb-actions\"><button type=\"button\" class=\"cbb-button cbb-button--wrap\" data-cbb-action=\"wrap\" aria-pressed=\"true\" title=\"Wrap long lines\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M2 4h12M2 8h9.5a2.5 2.5 0 0 1 0 5H8m0 0 1.5-1.5M8 13l1.5 1.5M2 12h3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><span class=\"cbb-sr\">Wrap long lines<\/span><\/button><button type=\"button\" class=\"cbb-button cbb-button--copy\" data-cbb-action=\"copy\" data-cbb-done=\"Copied\" data-cbb-failed=\"Press Ctrl+C to copy\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><rect x=\"5.5\" y=\"5.5\" width=\"8\" height=\"8.5\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><path d=\"M10.5 3.5V3A1.5 1.5 0 0 0 9 1.5H4A1.5 1.5 0 0 0 2.5 3v6.5A1.5 1.5 0 0 0 4 11h.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><\/svg><span class=\"cbb-button-label\" aria-live=\"polite\">Copy<\/span><\/button><\/span><\/figcaption><pre class=\"cbb-pre\" tabindex=\"0\"><code class=\"cbb-code language-json\" style=\"counter-reset:cbb-line 0;\"><span class=\"cbb-line\">{\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-attr\">\"$schema\"<\/span>: <span class=\"cbb-string\">\"https:\/\/schemas.wp.org\/trunk\/block.json\"<\/span>,\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-attr\">\"apiVersion\"<\/span>: <span class=\"cbb-number\">3<\/span>,\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-attr\">\"name\"<\/span>: <span class=\"cbb-string\">\"lw\/category-posts\"<\/span>,\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-attr\">\"version\"<\/span>: <span class=\"cbb-string\">\"0.1.0\"<\/span>,\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-attr\">\"title\"<\/span>: <span class=\"cbb-string\">\"Category Posts\"<\/span>,\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-attr\">\"category\"<\/span>: <span class=\"cbb-string\">\"widgets\"<\/span>,\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-attr\">\"icon\"<\/span>: <span class=\"cbb-string\">\"list-view\"<\/span>,\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-attr\">\"description\"<\/span>: <span class=\"cbb-string\">\"Lists the latest posts of one category, rendered on the server.\"<\/span>,\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-attr\">\"attributes\"<\/span>: {\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-attr\">\"categoryId\"<\/span>: {\n<\/span><span class=\"cbb-line\">\t\t\t<span class=\"cbb-attr\">\"type\"<\/span>: <span class=\"cbb-string\">\"integer\"<\/span>,\n<\/span><span class=\"cbb-line\">\t\t\t<span class=\"cbb-attr\">\"default\"<\/span>: <span class=\"cbb-number\">0<\/span>\n<\/span><span class=\"cbb-line\">\t\t},\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-attr\">\"count\"<\/span>: {\n<\/span><span class=\"cbb-line\">\t\t\t<span class=\"cbb-attr\">\"type\"<\/span>: <span class=\"cbb-string\">\"integer\"<\/span>,\n<\/span><span class=\"cbb-line\">\t\t\t<span class=\"cbb-attr\">\"default\"<\/span>: <span class=\"cbb-number\">5<\/span>\n<\/span><span class=\"cbb-line\">\t\t}\n<\/span><span class=\"cbb-line\">\t},\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-attr\">\"usesContext\"<\/span>: &#91; <span class=\"cbb-string\">\"postId\"<\/span> &#93;,\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-attr\">\"supports\"<\/span>: {\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-attr\">\"html\"<\/span>: <span class=\"cbb-constant\">false<\/span>,\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-attr\">\"align\"<\/span>: &#91; <span class=\"cbb-string\">\"wide\"<\/span>, <span class=\"cbb-string\">\"full\"<\/span> &#93;,\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-attr\">\"color\"<\/span>: {\n<\/span><span class=\"cbb-line\">\t\t\t<span class=\"cbb-attr\">\"background\"<\/span>: <span class=\"cbb-constant\">true<\/span>,\n<\/span><span class=\"cbb-line\">\t\t\t<span class=\"cbb-attr\">\"text\"<\/span>: <span class=\"cbb-constant\">true<\/span>,\n<\/span><span class=\"cbb-line\">\t\t\t<span class=\"cbb-attr\">\"link\"<\/span>: <span class=\"cbb-constant\">true<\/span>\n<\/span><span class=\"cbb-line\">\t\t},\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-attr\">\"spacing\"<\/span>: {\n<\/span><span class=\"cbb-line\">\t\t\t<span class=\"cbb-attr\">\"margin\"<\/span>: <span class=\"cbb-constant\">true<\/span>,\n<\/span><span class=\"cbb-line\">\t\t\t<span class=\"cbb-attr\">\"padding\"<\/span>: <span class=\"cbb-constant\">true<\/span>\n<\/span><span class=\"cbb-line\">\t\t},\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-attr\">\"typography\"<\/span>: {\n<\/span><span class=\"cbb-line\">\t\t\t<span class=\"cbb-attr\">\"fontSize\"<\/span>: <span class=\"cbb-constant\">true<\/span>\n<\/span><span class=\"cbb-line\">\t\t}\n<\/span><span class=\"cbb-line\">\t},\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-attr\">\"textdomain\"<\/span>: <span class=\"cbb-string\">\"category-posts\"<\/span>,\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-attr\">\"editorScript\"<\/span>: <span class=\"cbb-string\">\"file:.\/index.js\"<\/span>,\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-attr\">\"editorStyle\"<\/span>: <span class=\"cbb-string\">\"file:.\/index.css\"<\/span>,\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-attr\">\"style\"<\/span>: <span class=\"cbb-string\">\"file:.\/style-index.css\"<\/span>,\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-attr\">\"render\"<\/span>: <span class=\"cbb-string\">\"file:.\/render.php\"<\/span>\n<\/span><span class=\"cbb-line\">}<\/span><\/code><\/pre><\/figure>\n\n\n<p><code>apiVersion<\/code> 3 to aktualna wersja Block API, wprowadzona w WordPressie 6.3. Gdy pola brakuje, WordPress przyjmuje wersj\u0119 1. Wersja 3 wi\u0105\u017ce si\u0119 z edytorem w iframe: od 6.3 edytor wpis\u00f3w u\u017cywa\u0142 iframe tylko wtedy, gdy wszystkie zarejestrowane bloki deklarowa\u0142y wersj\u0119 3 lub wy\u017csz\u0105. WordPress 7.0 zmieni\u0142 kryterium na bloki faktycznie obecne w tre\u015bci wpisu, a wed\u0142ug dokumentacji Block API WordPress 7.1 zawsze u\u017cywa iframe dla edytora wpis\u00f3w, niezale\u017cnie od <code>apiVersion<\/code>. Dla nowego bloku wynika z tego jedna konsekwencja: style edytora musz\u0105 dzia\u0142a\u0107 wewn\u0105trz iframe, co jest spe\u0142nione, gdy \u0142aduj\u0105 si\u0119 przez <code>editorStyle<\/code> i <code>style<\/code> w <code>block.json<\/code>.<\/p>\n<p>Oba atrybuty maj\u0105 typ <code>integer<\/code> i warto\u015b\u0107 domy\u015bln\u0105. Przed renderowaniem WordPress sprawdza ka\u017cdy atrybut wzgl\u0119dem schematu, odrzuca warto\u015bci niezgodne ze schematem i uzupe\u0142nia brakuj\u0105ce warto\u015bciami domy\u015blnymi. <code>usesContext<\/code> \u017c\u0105da kontekstu <code>postId<\/code>, kt\u00f3ry <code>render_block()<\/code> wype\u0142nia na podstawie globalnego wpisu. Dzi\u0119ki temu blok mo\u017ce pomin\u0105\u0107 wpis, w kt\u00f3rym sam si\u0119 znajduje.<\/p>\n<p>Sekcja <code>supports<\/code> w\u0142\u0105cza wyr\u00f3wnanie, kolory, odst\u0119py i rozmiar czcionki. HTML dla tych ustawie\u0144 generuj\u0105 p\u00f3\u017aniej <code>get_block_wrapper_attributes()<\/code> i <code>useBlockProps()<\/code>, wi\u0119c nie potrzeba w\u0142asnego kodu. <code>editorScript<\/code> i <code>editorStyle<\/code> s\u0105 \u0142adowane tylko w edytorze, a <code>style<\/code> w edytorze i na stronie. <code>render<\/code> jest polem wy\u0142\u0105cznie serwerowym.<\/p>\n<h2>Rejestracja przez manifest zamiast pojedynczych plik\u00f3w block.json<\/h2>\n<p>Przez d\u0142ugi czas typow\u0105 rejestracj\u0105 by\u0142o <code>register_block_type( __DIR__ . '\/build' )<\/code> albo \u015bcie\u017cka do katalogu bloku. To nadal dzia\u0142a i sprawdza si\u0119 przy pojedynczym bloku bez etapu budowania. Przy ka\u017cdym \u017c\u0105daniu wymaga jednak odczytu pliku JSON z dysku i jego dekodowania. WordPress 6.7 doda\u0142 <code>wp_register_block_metadata_collection()<\/code>, kt\u00f3ra rejestruje manifest PHP z metadanymi wielu blok\u00f3w; notatka deweloperska Felixa Arntza jako pow\u00f3d podaje unikni\u0119cie operacji na systemie plik\u00f3w i dekodowania JSON. W 6.7 ka\u017cdy blok nadal potrzebowa\u0142 osobnego wywo\u0142ania <code>register_block_type()<\/code>. WordPress 6.8 zamkn\u0105\u0142 t\u0119 luk\u0119 funkcj\u0105 <code>wp_register_block_types_from_metadata_collection()<\/code>: jedno wywo\u0142anie rejestruje kolekcj\u0119 i wszystkie zawarte w niej typy blok\u00f3w.<\/p>\n<p>Obecny szkielet u\u017cywa w\u0142a\u015bnie tej funkcji, gdy w\u0142\u0105czony jest <code>@wordpress\/scripts<\/code>, co jest ustawieniem domy\u015blnym. G\u0142\u00f3wny plik wtyczki w przyk\u0142adzie wygl\u0105da wi\u0119c tak:<\/p>\n\n\n<figure style=\"--cbb-digits:2\" class=\"cbb cbb--wrap cbb--numbers wp-block-code-beautifier-block-code\" data-lang=\"php\"><figcaption class=\"cbb-head\"><span class=\"cbb-lang\">PHP<\/span><span class=\"cbb-actions\"><button type=\"button\" class=\"cbb-button cbb-button--wrap\" data-cbb-action=\"wrap\" aria-pressed=\"true\" title=\"Wrap long lines\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M2 4h12M2 8h9.5a2.5 2.5 0 0 1 0 5H8m0 0 1.5-1.5M8 13l1.5 1.5M2 12h3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><span class=\"cbb-sr\">Wrap long lines<\/span><\/button><button type=\"button\" class=\"cbb-button cbb-button--copy\" data-cbb-action=\"copy\" data-cbb-done=\"Copied\" data-cbb-failed=\"Press Ctrl+C to copy\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><rect x=\"5.5\" y=\"5.5\" width=\"8\" height=\"8.5\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><path d=\"M10.5 3.5V3A1.5 1.5 0 0 0 9 1.5H4A1.5 1.5 0 0 0 2.5 3v6.5A1.5 1.5 0 0 0 4 11h.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><\/svg><span class=\"cbb-button-label\" aria-live=\"polite\">Copy<\/span><\/button><\/span><\/figcaption><pre class=\"cbb-pre\" tabindex=\"0\"><code class=\"cbb-code language-php\" style=\"counter-reset:cbb-line 0;\"><span class=\"cbb-line\"><span class=\"cbb-meta\">&lt;?php<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\">\/**<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * Plugin Name:       Category Posts<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * Description:       Server-rendered block that lists the latest posts of one category.<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * Version:           0.1.0<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * Requires at least: 6.9<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * Requires PHP:      7.4<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * Author:            Lukas Wojcik<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * License:           GPL-2.0-or-later<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * License URI:       https:\/\/www.gnu.org\/licenses\/gpl-2.0.html<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * Text Domain:       category-posts<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> *<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * @package Lw<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> *\/<\/span>\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">if<\/span> ( ! <span class=\"cbb-function\">defined<\/span>( <span class=\"cbb-string\">'ABSPATH'<\/span> ) ) {\n<\/span><span class=\"cbb-line\">\texit; <span class=\"cbb-comment\">\/\/ Exit if accessed directly.<\/span>\n<\/span><span class=\"cbb-line\">}\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\">\/**<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * Registers every block listed in build\/blocks-manifest.php.<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> *<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * The manifest is generated by wp-scripts (--blocks-manifest), so WordPress<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * does not have to read and decode each block.json on every request.<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> *\/<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">function<\/span> <span class=\"cbb-function\">lw_category_posts_block_init<\/span>() {\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-function\">wp_register_block_types_from_metadata_collection<\/span>(\n<\/span><span class=\"cbb-line\">\t\t__DIR__ . <span class=\"cbb-string\">'\/build'<\/span>,\n<\/span><span class=\"cbb-line\">\t\t__DIR__ . <span class=\"cbb-string\">'\/build\/blocks-manifest.php'<\/span>\n<\/span><span class=\"cbb-line\">\t);\n<\/span><span class=\"cbb-line\">}\n<\/span><span class=\"cbb-line\"><span class=\"cbb-function\">add_action<\/span>( <span class=\"cbb-string\">'init'<\/span>, <span class=\"cbb-string\">'lw_category_posts_block_init'<\/span> );<\/span><\/code><\/pre><\/figure>\n\n\n<p>Pierwszy parametr to katalog bazowy kolekcji, drugi to manifest. Je\u015bli drugi zostanie pusty, katalog bazowy musi wskazywa\u0107 kolekcj\u0119 zarejestrowan\u0105 wcze\u015bniej. Wtyczki obs\u0142uguj\u0105ce jeszcze WordPressa 6.7 lub starszego potrzebuj\u0105 sprawdzenia <code>function_exists()<\/code> i \u015bcie\u017cki zapasowej, opisanej w notatce do 6.8. Przyk\u0142ad unika tego dzi\u0119ki \u201eRequires at least: 6.9\u201d, czego i tak wymaga pokazany ni\u017cej import w edytorze. Bez etapu budowania wystarcza pojedyncze wywo\u0142anie:<\/p>\n\n\n<figure style=\"--cbb-digits:1\" class=\"cbb cbb--wrap wp-block-code-beautifier-block-code\" data-lang=\"plain\"><figcaption class=\"cbb-head\"><span class=\"cbb-lang\">Text<\/span><span class=\"cbb-actions\"><button type=\"button\" class=\"cbb-button cbb-button--wrap\" data-cbb-action=\"wrap\" aria-pressed=\"true\" title=\"Wrap long lines\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M2 4h12M2 8h9.5a2.5 2.5 0 0 1 0 5H8m0 0 1.5-1.5M8 13l1.5 1.5M2 12h3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><span class=\"cbb-sr\">Wrap long lines<\/span><\/button><button type=\"button\" class=\"cbb-button cbb-button--copy\" data-cbb-action=\"copy\" data-cbb-done=\"Copied\" data-cbb-failed=\"Press Ctrl+C to copy\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><rect x=\"5.5\" y=\"5.5\" width=\"8\" height=\"8.5\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><path d=\"M10.5 3.5V3A1.5 1.5 0 0 0 9 1.5H4A1.5 1.5 0 0 0 2.5 3v6.5A1.5 1.5 0 0 0 4 11h.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><\/svg><span class=\"cbb-button-label\" aria-live=\"polite\">Copy<\/span><\/button><\/span><\/figcaption><pre class=\"cbb-pre\" tabindex=\"0\"><code class=\"cbb-code language-plain\" style=\"counter-reset:cbb-line 0;\"><span class=\"cbb-line\">\/\/ Single block, without a manifest: reads build\/category-posts\/block.json.\n<\/span><span class=\"cbb-line\">register_block_type( __DIR__ . '\/build\/category-posts' );<\/span><\/code><\/pre><\/figure>\n\n\n<p>\u0141atwo przeoczy\u0107 jeden szczeg\u00f3\u0142: manifest jest produktem budowania. Zmieniony w <code>src\/<\/code> plik <code>block.json<\/code> zaczyna obowi\u0105zywa\u0107 dopiero po kolejnym buildzie, tak\u017ce po stronie serwera.<\/p>\n<h2>render.php i jego trzy zmienne<\/h2>\n<p>Gdy <code>block.json<\/code> zawiera pole <code>render<\/code>, WordPress przy rejestracji tworzy <code>render_callback<\/code>. W kodzie \u017ar\u00f3d\u0142owym rdzenia jest to statyczna closure, kt\u00f3ra uruchamia buforowanie wyj\u015bcia, wczytuje plik przez <code>require<\/code> i zwraca zawarto\u015b\u0107 bufora. W pliku dost\u0119pne s\u0105 trzy zmienne: <code>$attributes<\/code> (tablica atrybut\u00f3w bloku z uzupe\u0142nionymi warto\u015bciami domy\u015blnymi), <code>$content<\/code> (zapisana tre\u015b\u0107 wewn\u0119trzna, istotna dla blok\u00f3w z blokami zagnie\u017cd\u017conymi, tutaj zawsze pusta) oraz <code>$block<\/code> (instancja <code>WP_Block<\/code> z nazw\u0105, kontekstem i sparsowanym blokiem). Plik renderuj\u0105cy w przyk\u0142adzie:<\/p>\n\n\n<figure style=\"--cbb-digits:2\" class=\"cbb cbb--wrap cbb--numbers wp-block-code-beautifier-block-code\" data-lang=\"php\"><figcaption class=\"cbb-head\"><span class=\"cbb-lang\">PHP<\/span><span class=\"cbb-actions\"><button type=\"button\" class=\"cbb-button cbb-button--wrap\" data-cbb-action=\"wrap\" aria-pressed=\"true\" title=\"Wrap long lines\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M2 4h12M2 8h9.5a2.5 2.5 0 0 1 0 5H8m0 0 1.5-1.5M8 13l1.5 1.5M2 12h3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><span class=\"cbb-sr\">Wrap long lines<\/span><\/button><button type=\"button\" class=\"cbb-button cbb-button--copy\" data-cbb-action=\"copy\" data-cbb-done=\"Copied\" data-cbb-failed=\"Press Ctrl+C to copy\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><rect x=\"5.5\" y=\"5.5\" width=\"8\" height=\"8.5\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><path d=\"M10.5 3.5V3A1.5 1.5 0 0 0 9 1.5H4A1.5 1.5 0 0 0 2.5 3v6.5A1.5 1.5 0 0 0 4 11h.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><\/svg><span class=\"cbb-button-label\" aria-live=\"polite\">Copy<\/span><\/button><\/span><\/figcaption><pre class=\"cbb-pre\" tabindex=\"0\"><code class=\"cbb-code language-php\" style=\"counter-reset:cbb-line 0;\"><span class=\"cbb-line\"><span class=\"cbb-meta\">&lt;?php<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\">\/**<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * Server-side rendering of the lw\/category-posts block.<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> *<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * Variables provided by WordPress:<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> *<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * @var array    $attributes Block attributes, defaults from block.json applied.<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * @var string   $content    Inner content (always empty for this block).<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * @var WP_Block $block      Block instance: name, context, parsed block.<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> *<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * @package Lw<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> *\/<\/span>\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">if<\/span> ( ! <span class=\"cbb-function\">defined<\/span>( <span class=\"cbb-string\">'ABSPATH'<\/span> ) ) {\n<\/span><span class=\"cbb-line\">\texit; <span class=\"cbb-comment\">\/\/ Exit if accessed directly.<\/span>\n<\/span><span class=\"cbb-line\">}\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\"><span class=\"cbb-variable\">$lw_category_id<\/span> = <span class=\"cbb-keyword\">isset<\/span>( <span class=\"cbb-variable\">$attributes<\/span>&#91;<span class=\"cbb-string\">'categoryId'<\/span>&#93; ) ? <span class=\"cbb-function\">absint<\/span>( <span class=\"cbb-variable\">$attributes<\/span>&#91;<span class=\"cbb-string\">'categoryId'<\/span>&#93; ) : <span class=\"cbb-number\">0<\/span>;\n<\/span><span class=\"cbb-line\"><span class=\"cbb-variable\">$lw_count<\/span>       = <span class=\"cbb-keyword\">isset<\/span>( <span class=\"cbb-variable\">$attributes<\/span>&#91;<span class=\"cbb-string\">'count'<\/span>&#93; ) ? <span class=\"cbb-function\">absint<\/span>( <span class=\"cbb-variable\">$attributes<\/span>&#91;<span class=\"cbb-string\">'count'<\/span>&#93; ) : <span class=\"cbb-number\">5<\/span>;\n<\/span><span class=\"cbb-line\"><span class=\"cbb-variable\">$lw_count<\/span>       = <span class=\"cbb-function\">min<\/span>( <span class=\"cbb-function\">max<\/span>( <span class=\"cbb-variable\">$lw_count<\/span>, <span class=\"cbb-number\">1<\/span> ), <span class=\"cbb-number\">10<\/span> );\n<\/span><span class=\"cbb-line\"><span class=\"cbb-variable\">$lw_term<\/span>        = <span class=\"cbb-variable\">$lw_category_id<\/span> ? <span class=\"cbb-function\">get_term<\/span>( <span class=\"cbb-variable\">$lw_category_id<\/span>, <span class=\"cbb-string\">'category'<\/span> ) : <span class=\"cbb-constant\">null<\/span>;\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\">\/\/ Render nothing until a valid category has been chosen.<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">if<\/span> ( ! <span class=\"cbb-variable\">$lw_term<\/span> <span class=\"cbb-keyword\">instanceof<\/span> <span class=\"cbb-type\">WP_Term<\/span> ) {\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-keyword\">return<\/span>;\n<\/span><span class=\"cbb-line\">}\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\">\/\/ Do not list the post that contains the block (context from \"usesContext\").<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-variable\">$lw_current_id<\/span> = <span class=\"cbb-keyword\">isset<\/span>( <span class=\"cbb-variable\">$block<\/span><span class=\"cbb-operator\">-&gt;<\/span>context&#91;<span class=\"cbb-string\">'postId'<\/span>&#93; ) ? <span class=\"cbb-function\">absint<\/span>( <span class=\"cbb-variable\">$block<\/span><span class=\"cbb-operator\">-&gt;<\/span>context&#91;<span class=\"cbb-string\">'postId'<\/span>&#93; ) : <span class=\"cbb-number\">0<\/span>;\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\"><span class=\"cbb-variable\">$lw_posts<\/span> = <span class=\"cbb-function\">get_posts<\/span>(\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-keyword\">array<\/span>(\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-string\">'category'<\/span>     <span class=\"cbb-operator\">=&gt;<\/span> <span class=\"cbb-variable\">$lw_term<\/span><span class=\"cbb-operator\">-&gt;<\/span>term_id,\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-string\">'numberposts'<\/span>  <span class=\"cbb-operator\">=&gt;<\/span> <span class=\"cbb-variable\">$lw_count<\/span>,\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-string\">'post_status'<\/span>  <span class=\"cbb-operator\">=&gt;<\/span> <span class=\"cbb-string\">'publish'<\/span>,\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-string\">'has_password'<\/span> <span class=\"cbb-operator\">=&gt;<\/span> <span class=\"cbb-constant\">false<\/span>,\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-string\">'exclude'<\/span>      <span class=\"cbb-operator\">=&gt;<\/span> <span class=\"cbb-variable\">$lw_current_id<\/span> ? <span class=\"cbb-keyword\">array<\/span>( <span class=\"cbb-variable\">$lw_current_id<\/span> ) : <span class=\"cbb-keyword\">array<\/span>(),\n<\/span><span class=\"cbb-line\">\t)\n<\/span><span class=\"cbb-line\">);\n<\/span><span class=\"cbb-line\"><span class=\"cbb-meta\">?&gt;<\/span>\n<\/span><span class=\"cbb-line\">&lt;div <span class=\"cbb-meta\">&lt;?php<\/span> <span class=\"cbb-keyword\">echo<\/span> <span class=\"cbb-function\">wp_kses_data<\/span>( <span class=\"cbb-function\">get_block_wrapper_attributes<\/span>() ); <span class=\"cbb-meta\">?&gt;<\/span>&gt;\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-meta\">&lt;?php<\/span> <span class=\"cbb-keyword\">if<\/span> ( <span class=\"cbb-keyword\">empty<\/span>( <span class=\"cbb-variable\">$lw_posts<\/span> ) ) : <span class=\"cbb-meta\">?&gt;<\/span>\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-punct\">&lt;<\/span><span class=\"cbb-tag\">p<\/span><span class=\"cbb-punct\">&gt;<\/span><span class=\"cbb-meta\">&lt;?php<\/span> <span class=\"cbb-function\">esc_html_e<\/span>( <span class=\"cbb-string\">'No posts in this category yet.'<\/span>, <span class=\"cbb-string\">'category-posts'<\/span> ); <span class=\"cbb-meta\">?&gt;<\/span><span class=\"cbb-punct\">&lt;\/<\/span><span class=\"cbb-tag\">p<\/span><span class=\"cbb-punct\">&gt;<\/span>\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-meta\">&lt;?php<\/span> <span class=\"cbb-keyword\">else<\/span> : <span class=\"cbb-meta\">?&gt;<\/span>\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-punct\">&lt;<\/span><span class=\"cbb-tag\">ul<\/span> <span class=\"cbb-attr\">class<\/span><span class=\"cbb-punct\">=<\/span><span class=\"cbb-string\">\"wp-block-lw-category-posts__list\"<\/span><span class=\"cbb-punct\">&gt;<\/span>\n<\/span><span class=\"cbb-line\">\t\t\t<span class=\"cbb-meta\">&lt;?php<\/span> <span class=\"cbb-keyword\">foreach<\/span> ( <span class=\"cbb-variable\">$lw_posts<\/span> <span class=\"cbb-keyword\">as<\/span> <span class=\"cbb-variable\">$lw_post<\/span> ) : <span class=\"cbb-meta\">?&gt;<\/span>\n<\/span><span class=\"cbb-line\">\t\t\t\t<span class=\"cbb-punct\">&lt;<\/span><span class=\"cbb-tag\">li<\/span><span class=\"cbb-punct\">&gt;<\/span>\n<\/span><span class=\"cbb-line\">\t\t\t\t\t<span class=\"cbb-punct\">&lt;<\/span><span class=\"cbb-tag\">a<\/span> <span class=\"cbb-attr\">href<\/span><span class=\"cbb-punct\">=<\/span><span class=\"cbb-string\">\"&lt;?php echo esc_url( get_permalink( $lw_post ) ); ?&gt;\"<\/span><span class=\"cbb-punct\">&gt;<\/span><span class=\"cbb-meta\">&lt;?php<\/span> <span class=\"cbb-keyword\">echo<\/span> <span class=\"cbb-function\">esc_html<\/span>( <span class=\"cbb-function\">get_the_title<\/span>( <span class=\"cbb-variable\">$lw_post<\/span> ) ); <span class=\"cbb-meta\">?&gt;<\/span><span class=\"cbb-punct\">&lt;\/<\/span><span class=\"cbb-tag\">a<\/span><span class=\"cbb-punct\">&gt;<\/span>\n<\/span><span class=\"cbb-line\">\t\t\t\t\t<span class=\"cbb-punct\">&lt;<\/span><span class=\"cbb-tag\">time<\/span> <span class=\"cbb-attr\">datetime<\/span><span class=\"cbb-punct\">=<\/span><span class=\"cbb-string\">\"&lt;?php echo esc_attr( get_the_date( 'c', $lw_post ) ); ?&gt;\"<\/span><span class=\"cbb-punct\">&gt;<\/span><span class=\"cbb-meta\">&lt;?php<\/span> <span class=\"cbb-keyword\">echo<\/span> <span class=\"cbb-function\">esc_html<\/span>( <span class=\"cbb-function\">get_the_date<\/span>( <span class=\"cbb-string\">''<\/span>, <span class=\"cbb-variable\">$lw_post<\/span> ) ); <span class=\"cbb-meta\">?&gt;<\/span><span class=\"cbb-punct\">&lt;\/<\/span><span class=\"cbb-tag\">time<\/span><span class=\"cbb-punct\">&gt;<\/span>\n<\/span><span class=\"cbb-line\">\t\t\t\t<span class=\"cbb-punct\">&lt;\/<\/span><span class=\"cbb-tag\">li<\/span><span class=\"cbb-punct\">&gt;<\/span>\n<\/span><span class=\"cbb-line\">\t\t\t<span class=\"cbb-meta\">&lt;?php<\/span> <span class=\"cbb-keyword\">endforeach<\/span>; <span class=\"cbb-meta\">?&gt;<\/span>\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-punct\">&lt;\/<\/span><span class=\"cbb-tag\">ul<\/span><span class=\"cbb-punct\">&gt;<\/span>\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-meta\">&lt;?php<\/span> <span class=\"cbb-keyword\">endif<\/span>; <span class=\"cbb-meta\">?&gt;<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-punct\">&lt;\/<\/span><span class=\"cbb-tag\">div<\/span><span class=\"cbb-punct\">&gt;<\/span><\/span><\/code><\/pre><\/figure>\n\n\n<p>Kilka decyzji w tym pliku wynika ze sposobu jego wczytywania. Poniewa\u017c <code>require<\/code> wykonuje si\u0119 dla ka\u017cdej instancji bloku na stronie, plik nie mo\u017ce deklarowa\u0107 funkcji ani klas; druga instancja bloku zako\u0144czy\u0142aby si\u0119 b\u0142\u0119dem krytycznym. Funkcje pomocnicze nale\u017cy umieszcza\u0107 w g\u0142\u00f3wnym pliku wtyczki lub w osobnym pliku do\u0142\u0105czanym. Dla PHP_CodeSniffera zmienne wygl\u0105daj\u0105 na globalne, dlatego maj\u0105 prefiks <code>lw_<\/code>. Kontroli to jednak nie zadowala: WPCS odrzuca prefiksy kr\u00f3tsze ni\u017c cztery znaki, a Plugin Check 2.1.0, kt\u00f3ry wyprowadza oczekiwany prefiks z kodu wtyczki (tutaj z nazwy funkcji <code>lw_category_posts_block_init<\/code>), zg\u0142asza ka\u017cd\u0105 z tych zmiennych jako ostrze\u017cenie. Na dzia\u0142anie bloku ostrze\u017cenia nie maj\u0105 wp\u0142ywu. <code>return<\/code> ko\u0144czy plik wcze\u015bniej i zostawia pusty bufor, i tak w\u0142a\u015bnie blok nie renderuje niczego.<\/p>\n<p>Atrybuty pochodz\u0105 z <code>post_content<\/code>, wi\u0119c s\u0105 danymi wej\u015bciowymi. Walidacja schematu gwarantuje liczby ca\u0142kowite, ale nie rozs\u0105dny zakres, dlatego <code>absint()<\/code> razem z <code>min()<\/code> i <code>max()<\/code> ograniczaj\u0105 liczb\u0119 wpis\u00f3w do przedzia\u0142u od 1 do 10. <code>get_term()<\/code> potwierdza, \u017ce kategoria istnieje. <code>get_posts()<\/code> to cienka nak\u0142adka na <code>WP_Query<\/code>, kt\u00f3ra sama ustawia <code>ignore_sticky_posts<\/code>, <code>no_found_rows<\/code> i <code>suppress_filters<\/code>; to pasuje do kr\u00f3tkiej listy bez paginacji. Jak kolejne argumenty zapytania, na przyk\u0142ad <code>meta_query<\/code>, zamieniaj\u0105 si\u0119 w SQL, pokazuje <a href=\"https:\/\/www.lukaswojcik.com\/blog\/pl\/wordpress-plugins-tricks-pl\/meta_query-w-wp_query-jak-kazda-klauzula-staje-sie-zlaczeniem-i-ile\/\">artyku\u0142 o <code>meta_query<\/code> w <code>WP_Query<\/code><\/a>. Ka\u017cde wyj\u015bcie przechodzi przez odpowiedni\u0105 funkcj\u0119 escapuj\u0105c\u0105: <code>esc_url()<\/code> dla odno\u015bnika, <code>esc_html()<\/code> dla tytu\u0142u i daty, <code>esc_attr()<\/code> dla atrybutu <code>datetime<\/code>. Og\u00f3lne zasady opisuje <a href=\"https:\/\/www.lukaswojcik.com\/blog\/pl\/wordpress-plugins-tricks-pl\/sanityzacja-escaping-nonce-capability-wtyczki\/\">osobny artyku\u0142 z tej serii o sanityzacji, escapowaniu, Nonce i Capabilities<\/a>.<\/p>\n<p><code>get_block_wrapper_attributes()<\/code> zwraca ci\u0105g atrybut\u00f3w dla elementu opakowuj\u0105cego: klas\u0119 <code>wp-block-lw-category-posts<\/code> oraz klasy i style inline wynikaj\u0105ce z ustawie\u0144 <code>supports<\/code>. Funkcja sama escapuje ka\u017cd\u0105 warto\u015b\u0107 przez <code>esc_attr()<\/code>, co wida\u0107 w jej kodzie \u017ar\u00f3d\u0142owym. Nie ma jej jednak na li\u015bcie funkcji automatycznie escapuj\u0105cych w WordPress Coding Standards, wi\u0119c go\u0142e <code>echo<\/code> uruchamia sniff dotycz\u0105cy escapowania; Plugin Check 2.1.0 zg\u0142asza je jako b\u0142\u0105d (<code>WordPress.Security.EscapeOutput.OutputNotEscaped<\/code>). <code>wp_kses_data()<\/code> pozostawia ci\u0105g atrybut\u00f3w bez tag\u00f3w w niezmienionej postaci i zadowala t\u0119 kontrol\u0119; alternatyw\u0105 jest uzasadniony komentarz <code>phpcs:ignore<\/code>.<\/p>\n<h2>Strona edytora: edit.js z ServerSideRender<\/h2>\n<p>W JavaScripcie <code>index.js<\/code> rejestruje blok wy\u0142\u0105cznie z <code>edit<\/code>. Gdy <code>save<\/code> nie zostanie podane, Block API u\u017cywa domy\u015blnej funkcji <code>save<\/code>, zwracaj\u0105cej <code>null<\/code>:<\/p>\n\n\n<figure style=\"--cbb-digits:2\" class=\"cbb cbb--wrap cbb--numbers wp-block-code-beautifier-block-code\" data-lang=\"javascript\"><figcaption class=\"cbb-head\"><span class=\"cbb-lang\">JavaScript<\/span><span class=\"cbb-actions\"><button type=\"button\" class=\"cbb-button cbb-button--wrap\" data-cbb-action=\"wrap\" aria-pressed=\"true\" title=\"Wrap long lines\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M2 4h12M2 8h9.5a2.5 2.5 0 0 1 0 5H8m0 0 1.5-1.5M8 13l1.5 1.5M2 12h3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><span class=\"cbb-sr\">Wrap long lines<\/span><\/button><button type=\"button\" class=\"cbb-button cbb-button--copy\" data-cbb-action=\"copy\" data-cbb-done=\"Copied\" data-cbb-failed=\"Press Ctrl+C to copy\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><rect x=\"5.5\" y=\"5.5\" width=\"8\" height=\"8.5\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><path d=\"M10.5 3.5V3A1.5 1.5 0 0 0 9 1.5H4A1.5 1.5 0 0 0 2.5 3v6.5A1.5 1.5 0 0 0 4 11h.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><\/svg><span class=\"cbb-button-label\" aria-live=\"polite\">Copy<\/span><\/button><\/span><\/figcaption><pre class=\"cbb-pre\" tabindex=\"0\"><code class=\"cbb-code language-javascript\" style=\"counter-reset:cbb-line 0;\"><span class=\"cbb-line\"><span class=\"cbb-keyword\">import<\/span> { registerBlockType } <span class=\"cbb-keyword\">from<\/span> <span class=\"cbb-string\">'@wordpress\/blocks'<\/span>;\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">import<\/span> <span class=\"cbb-string\">'.\/style.scss'<\/span>;\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">import<\/span> <span class=\"cbb-type\">Edit<\/span> <span class=\"cbb-keyword\">from<\/span> <span class=\"cbb-string\">'.\/edit'<\/span>;\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">import<\/span> metadata <span class=\"cbb-keyword\">from<\/span> <span class=\"cbb-string\">'.\/block.json'<\/span>;\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\">\/\/ No save(): the default save returns null, so only the block comment<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\">\/\/ with its attributes is stored in post_content.<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-function\">registerBlockType<\/span>( metadata.name, {\n<\/span><span class=\"cbb-line\">\tedit: <span class=\"cbb-type\">Edit<\/span>,\n<\/span><span class=\"cbb-line\">} );<\/span><\/code><\/pre><\/figure>\n\n\n<p><code>edit.js<\/code> dostarcza ustawienia w panelu bocznym oraz podgl\u0105d. Kategorie pochodz\u0105 z magazynu danych rdzenia, z tym samym zapytaniem, kt\u00f3rego u\u017cywa blok rdzenia \u201eNajnowsze wpisy\u201d. Podgl\u0105d tworzy <code>ServerSideRender<\/code>: komponent wysy\u0142a atrybuty do trasy REST <code>\/wp\/v2\/block-renderer\/lw\/category-posts<\/code> i wstawia HTML zwr\u00f3cony przez <code>render.php<\/code>:<\/p>\n\n\n<figure style=\"--cbb-digits:2\" class=\"cbb cbb--wrap cbb--numbers wp-block-code-beautifier-block-code\" data-lang=\"javascript\"><figcaption class=\"cbb-head\"><span class=\"cbb-lang\">JavaScript<\/span><span class=\"cbb-actions\"><button type=\"button\" class=\"cbb-button cbb-button--wrap\" data-cbb-action=\"wrap\" aria-pressed=\"true\" title=\"Wrap long lines\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M2 4h12M2 8h9.5a2.5 2.5 0 0 1 0 5H8m0 0 1.5-1.5M8 13l1.5 1.5M2 12h3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><span class=\"cbb-sr\">Wrap long lines<\/span><\/button><button type=\"button\" class=\"cbb-button cbb-button--copy\" data-cbb-action=\"copy\" data-cbb-done=\"Copied\" data-cbb-failed=\"Press Ctrl+C to copy\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><rect x=\"5.5\" y=\"5.5\" width=\"8\" height=\"8.5\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><path d=\"M10.5 3.5V3A1.5 1.5 0 0 0 9 1.5H4A1.5 1.5 0 0 0 2.5 3v6.5A1.5 1.5 0 0 0 4 11h.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><\/svg><span class=\"cbb-button-label\" aria-live=\"polite\">Copy<\/span><\/button><\/span><\/figcaption><pre class=\"cbb-pre\" tabindex=\"0\"><code class=\"cbb-code language-javascript\" style=\"counter-reset:cbb-line 0;\"><span class=\"cbb-line\"><span class=\"cbb-keyword\">import<\/span> { __ } <span class=\"cbb-keyword\">from<\/span> <span class=\"cbb-string\">'@wordpress\/i18n'<\/span>;\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">import<\/span> { <span class=\"cbb-type\">InspectorControls<\/span>, useBlockProps } <span class=\"cbb-keyword\">from<\/span> <span class=\"cbb-string\">'@wordpress\/block-editor'<\/span>;\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">import<\/span> {\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-type\">PanelBody<\/span>,\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-type\">Placeholder<\/span>,\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-type\">RangeControl<\/span>,\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-type\">SelectControl<\/span>,\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-type\">Spinner<\/span>,\n<\/span><span class=\"cbb-line\">} <span class=\"cbb-keyword\">from<\/span> <span class=\"cbb-string\">'@wordpress\/components'<\/span>;\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">import<\/span> { useSelect } <span class=\"cbb-keyword\">from<\/span> <span class=\"cbb-string\">'@wordpress\/data'<\/span>;\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">import<\/span> { store as coreStore } <span class=\"cbb-keyword\">from<\/span> <span class=\"cbb-string\">'@wordpress\/core-data'<\/span>;\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">import<\/span> { decodeEntities } <span class=\"cbb-keyword\">from<\/span> <span class=\"cbb-string\">'@wordpress\/html-entities'<\/span>;\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">import<\/span> { <span class=\"cbb-type\">ServerSideRender<\/span> } <span class=\"cbb-keyword\">from<\/span> <span class=\"cbb-string\">'@wordpress\/server-side-render'<\/span>;\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">import<\/span> metadata <span class=\"cbb-keyword\">from<\/span> <span class=\"cbb-string\">'.\/block.json'<\/span>;\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">import<\/span> <span class=\"cbb-string\">'.\/editor.scss'<\/span>;\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\">\/\/ Same query shape as the core Latest Posts block uses for its category list.<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">const<\/span> CATEGORIES_QUERY = { per_page: <span class=\"cbb-number\">-1<\/span>, _fields: <span class=\"cbb-string\">'id,name'<\/span>, context: <span class=\"cbb-string\">'view'<\/span> };\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">export<\/span> <span class=\"cbb-keyword\">default<\/span> <span class=\"cbb-keyword\">function<\/span> <span class=\"cbb-function\">Edit<\/span>( { attributes, setAttributes } ) {\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-keyword\">const<\/span> { categoryId, count } = attributes;\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-keyword\">const<\/span> blockProps = <span class=\"cbb-function\">useBlockProps<\/span>();\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-keyword\">const<\/span> categories = <span class=\"cbb-function\">useSelect<\/span>(\n<\/span><span class=\"cbb-line\">\t\t( select ) <span class=\"cbb-operator\">=&gt;<\/span>\n<\/span><span class=\"cbb-line\">\t\t\t<span class=\"cbb-function\">select<\/span>( coreStore ).<span class=\"cbb-function\">getEntityRecords<\/span>(\n<\/span><span class=\"cbb-line\">\t\t\t\t<span class=\"cbb-string\">'taxonomy'<\/span>,\n<\/span><span class=\"cbb-line\">\t\t\t\t<span class=\"cbb-string\">'category'<\/span>,\n<\/span><span class=\"cbb-line\">\t\t\t\tCATEGORIES_QUERY\n<\/span><span class=\"cbb-line\">\t\t\t),\n<\/span><span class=\"cbb-line\">\t\t&#91;&#93;\n<\/span><span class=\"cbb-line\">\t);\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-keyword\">const<\/span> options = &#91;\n<\/span><span class=\"cbb-line\">\t\t{ value: <span class=\"cbb-string\">'0'<\/span>, label: <span class=\"cbb-function\">__<\/span>( <span class=\"cbb-string\">'\u2014 Select a category \u2014'<\/span>, <span class=\"cbb-string\">'category-posts'<\/span> ) },\n<\/span><span class=\"cbb-line\">\t\t...( categories ?? &#91;&#93; ).<span class=\"cbb-function\">map<\/span>( ( term ) <span class=\"cbb-operator\">=&gt;<\/span> ( {\n<\/span><span class=\"cbb-line\">\t\t\tvalue: <span class=\"cbb-function\">String<\/span>( term.id ),\n<\/span><span class=\"cbb-line\">\t\t\tlabel: <span class=\"cbb-function\">decodeEntities<\/span>( term.name ),\n<\/span><span class=\"cbb-line\">\t\t} ) ),\n<\/span><span class=\"cbb-line\">\t&#93;;\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-keyword\">return<\/span> (\n<\/span><span class=\"cbb-line\">\t\t&lt;&gt;\n<\/span><span class=\"cbb-line\">\t\t\t&lt;<span class=\"cbb-type\">InspectorControls<\/span>&gt;\n<\/span><span class=\"cbb-line\">\t\t\t\t&lt;<span class=\"cbb-type\">PanelBody<\/span> title={ <span class=\"cbb-function\">__<\/span>( <span class=\"cbb-string\">'Settings'<\/span>, <span class=\"cbb-string\">'category-posts'<\/span> ) }&gt;\n<\/span><span class=\"cbb-line\">\t\t\t\t\t{ categories ? (\n<\/span><span class=\"cbb-line\">\t\t\t\t\t\t&lt;<span class=\"cbb-type\">SelectControl<\/span>\n<\/span><span class=\"cbb-line\">\t\t\t\t\t\t\t__next40pxDefaultSize\n<\/span><span class=\"cbb-line\">\t\t\t\t\t\t\t__nextHasNoMarginBottom\n<\/span><span class=\"cbb-line\">\t\t\t\t\t\t\tlabel={ <span class=\"cbb-function\">__<\/span>( <span class=\"cbb-string\">'Category'<\/span>, <span class=\"cbb-string\">'category-posts'<\/span> ) }\n<\/span><span class=\"cbb-line\">\t\t\t\t\t\t\tvalue={ <span class=\"cbb-function\">String<\/span>( categoryId ) }\n<\/span><span class=\"cbb-line\">\t\t\t\t\t\t\toptions={ options }\n<\/span><span class=\"cbb-line\">\t\t\t\t\t\t\tonChange={ ( value ) <span class=\"cbb-operator\">=&gt;<\/span>\n<\/span><span class=\"cbb-line\">\t\t\t\t\t\t\t\t<span class=\"cbb-function\">setAttributes<\/span>( { categoryId: <span class=\"cbb-function\">Number<\/span>( value ) } )\n<\/span><span class=\"cbb-line\">\t\t\t\t\t\t\t}\n<\/span><span class=\"cbb-line\">\t\t\t\t\t\t\/&gt;\n<\/span><span class=\"cbb-line\">\t\t\t\t\t) : (\n<\/span><span class=\"cbb-line\">\t\t\t\t\t\t&lt;<span class=\"cbb-type\">Spinner<\/span> \/&gt;\n<\/span><span class=\"cbb-line\">\t\t\t\t\t) }\n<\/span><span class=\"cbb-line\">\t\t\t\t\t&lt;<span class=\"cbb-type\">RangeControl<\/span>\n<\/span><span class=\"cbb-line\">\t\t\t\t\t\t__next40pxDefaultSize\n<\/span><span class=\"cbb-line\">\t\t\t\t\t\t__nextHasNoMarginBottom\n<\/span><span class=\"cbb-line\">\t\t\t\t\t\tlabel={ <span class=\"cbb-function\">__<\/span>( <span class=\"cbb-string\">'Number of posts'<\/span>, <span class=\"cbb-string\">'category-posts'<\/span> ) }\n<\/span><span class=\"cbb-line\">\t\t\t\t\t\tvalue={ count }\n<\/span><span class=\"cbb-line\">\t\t\t\t\t\tmin={ <span class=\"cbb-number\">1<\/span> }\n<\/span><span class=\"cbb-line\">\t\t\t\t\t\tmax={ <span class=\"cbb-number\">10<\/span> }\n<\/span><span class=\"cbb-line\">\t\t\t\t\t\tonChange={ ( value ) <span class=\"cbb-operator\">=&gt;<\/span>\n<\/span><span class=\"cbb-line\">\t\t\t\t\t\t\t<span class=\"cbb-function\">setAttributes<\/span>( { count: value ?? <span class=\"cbb-number\">5<\/span> } )\n<\/span><span class=\"cbb-line\">\t\t\t\t\t\t}\n<\/span><span class=\"cbb-line\">\t\t\t\t\t\/&gt;\n<\/span><span class=\"cbb-line\">\t\t\t\t&lt;\/<span class=\"cbb-type\">PanelBody<\/span>&gt;\n<\/span><span class=\"cbb-line\">\t\t\t&lt;\/<span class=\"cbb-type\">InspectorControls<\/span>&gt;\n<\/span><span class=\"cbb-line\">\t\t\t&lt;div { ...blockProps }&gt;\n<\/span><span class=\"cbb-line\">\t\t\t\t{ categoryId ? (\n<\/span><span class=\"cbb-line\">\t\t\t\t\t&lt;<span class=\"cbb-type\">ServerSideRender<\/span>\n<\/span><span class=\"cbb-line\">\t\t\t\t\t\tblock={ metadata.name }\n<\/span><span class=\"cbb-line\">\t\t\t\t\t\tattributes={ attributes }\n<\/span><span class=\"cbb-line\">\t\t\t\t\t\tskipBlockSupportAttributes\n<\/span><span class=\"cbb-line\">\t\t\t\t\t\/&gt;\n<\/span><span class=\"cbb-line\">\t\t\t\t) : (\n<\/span><span class=\"cbb-line\">\t\t\t\t\t&lt;<span class=\"cbb-type\">Placeholder<\/span>\n<\/span><span class=\"cbb-line\">\t\t\t\t\t\ticon=<span class=\"cbb-string\">\"list-view\"<\/span>\n<\/span><span class=\"cbb-line\">\t\t\t\t\t\tlabel={ <span class=\"cbb-function\">__<\/span>( <span class=\"cbb-string\">'Category Posts'<\/span>, <span class=\"cbb-string\">'category-posts'<\/span> ) }\n<\/span><span class=\"cbb-line\">\t\t\t\t\t\tinstructions={ <span class=\"cbb-function\">__<\/span>(\n<\/span><span class=\"cbb-line\">\t\t\t\t\t\t\t<span class=\"cbb-string\">'Choose a category in the block settings.'<\/span>,\n<\/span><span class=\"cbb-line\">\t\t\t\t\t\t\t<span class=\"cbb-string\">'category-posts'<\/span>\n<\/span><span class=\"cbb-line\">\t\t\t\t\t\t) }\n<\/span><span class=\"cbb-line\">\t\t\t\t\t\/&gt;\n<\/span><span class=\"cbb-line\">\t\t\t\t) }\n<\/span><span class=\"cbb-line\">\t\t\t&lt;\/div&gt;\n<\/span><span class=\"cbb-line\">\t\t&lt;\/&gt;\n<\/span><span class=\"cbb-line\">\t);\n<\/span><span class=\"cbb-line\">}<\/span><\/code><\/pre><\/figure>\n\n\n<p>W wersji pakietu do\u0142\u0105czonej do WordPressa 6.9 (6.9.5) <code>@wordpress\/server-side-render<\/code> eksportuje <code>ServerSideRender<\/code> ju\u017c jako eksport nazwany; eksport domy\u015blny jest oznaczony jako przestarza\u0142y i pozostaje warstw\u0105 zgodno\u015bci. Ten nazwany import jest powodem wpisu \u201eRequires at least: 6.9\u201d w nag\u0142\u00f3wku wtyczki. <code>skipBlockSupportAttributes<\/code> usuwa z \u017c\u0105dania atrybuty styl\u00f3w, poniewa\u017c zewn\u0119trzny <code>div<\/code> otrzymuje kolory i odst\u0119py ju\u017c przez <code>useBlockProps()<\/code>; w przeciwnym razie pojawi\u0142yby si\u0119 podw\u00f3jnie. Komponent do\u0142\u0105cza te\u017c do \u017c\u0105dania identyfikator bie\u017c\u0105cego wpisu, wi\u0119c kontekst <code>postId<\/code> w <code>render.php<\/code> dzia\u0142a r\u00f3wnie\u017c w edytorze. Dwa propsy <code>__next\u2026<\/code> przy kontrolkach prze\u0142\u0105czaj\u0105 starsze wersje WordPressa na aktualny rozmiar i odst\u0119py kontrolek; wed\u0142ug changelogu pakietu components oba s\u0105 ju\u017c domy\u015blne i da si\u0119 je usun\u0105\u0107, gdy starsze wersje przestan\u0105 by\u0107 obs\u0142ugiwane.<\/p>\n<figure class=\"lw-diagram\">\n<img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/diagrams\/wp-dynamischer-block-pl.png?v=20260930\" width=\"1120\" height=\"580\" alt=\"Blok dynamiczny: kt\u00f3ry plik jest czytany, gdzie i kiedy, w trzech kolumnach dla ka\u017cdego \u017c\u0105dania PHP, edytora blok\u00f3w i frontu; blocks-manifest.php rejestruje wszystkie bloki, edytor \u0142aduje index.js z podgl\u0105dem ServerSideRender, a render.php tworzy HTML z atrybut\u00f3w zapisanych w post_content\">\n<figcaption>PHP rejestruje z manifestu, edytor \u0142aduje pakiet, render.php tworzy HTML dla frontu i podgl\u0105du<\/figcaption>\n<\/figure>\n<p>Diagram podsumowuje, kto i kiedy czyta poszczeg\u00f3lne pliki: PHP czyta manifest przy ka\u017cdym \u017c\u0105daniu, edytor \u0142aduje pakiet skryptu i oba arkusze styl\u00f3w, a <code>render.php<\/code> dzia\u0142a zar\u00f3wno dla frontu, jak i dla podgl\u0105du w edytorze przez tras\u0119 REST.<\/p>\n<h2>Statycznie czy dynamicznie: co trafia do bazy danych<\/h2>\n<p>W przyk\u0142adzie baza danych zawiera wy\u0142\u0105cznie komentarz bloku. Atrybuty r\u00f3wne warto\u015bci domy\u015blnej nie s\u0105 serializowane:<\/p>\n\n\n<figure style=\"--cbb-digits:1\" class=\"cbb cbb--wrap wp-block-code-beautifier-block-code\" data-lang=\"plain\"><figcaption class=\"cbb-head\"><span class=\"cbb-lang\">Text<\/span><span class=\"cbb-actions\"><button type=\"button\" class=\"cbb-button cbb-button--wrap\" data-cbb-action=\"wrap\" aria-pressed=\"true\" title=\"Wrap long lines\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M2 4h12M2 8h9.5a2.5 2.5 0 0 1 0 5H8m0 0 1.5-1.5M8 13l1.5 1.5M2 12h3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><span class=\"cbb-sr\">Wrap long lines<\/span><\/button><button type=\"button\" class=\"cbb-button cbb-button--copy\" data-cbb-action=\"copy\" data-cbb-done=\"Copied\" data-cbb-failed=\"Press Ctrl+C to copy\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><rect x=\"5.5\" y=\"5.5\" width=\"8\" height=\"8.5\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><path d=\"M10.5 3.5V3A1.5 1.5 0 0 0 9 1.5H4A1.5 1.5 0 0 0 2.5 3v6.5A1.5 1.5 0 0 0 4 11h.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><\/svg><span class=\"cbb-button-label\" aria-live=\"polite\">Copy<\/span><\/button><\/span><\/figcaption><pre class=\"cbb-pre\" tabindex=\"0\"><code class=\"cbb-code language-plain\" style=\"counter-reset:cbb-line 0;\"><span class=\"cbb-line\">&lt;!-- wp:lw\/category-posts {\"categoryId\":7,\"count\":3} \/--&gt;<\/span><\/code><\/pre><\/figure>\n\n\n<p>Tu le\u017cy sedno r\u00f3\u017cnicy. Blok statyczny zapisuje wynik funkcji <code>save<\/code> jako HTML mi\u0119dzy ogranicznikami komentarza, a edytor przy otwieraniu wpisu por\u00f3wnuje ten HTML ze \u015bwie\u017cym wywo\u0142aniem <code>save<\/code>. Ka\u017cda zmiana kodu (dodatkowa klasa, element opakowuj\u0105cy, inny tag) psuje wi\u0119c istniej\u0105ce tre\u015bci, dop\u00f3ki tablica <code>deprecated<\/code> nie opisze starych wersji. W bloku dynamicznym <code>save<\/code> zwraca <code>null<\/code>, edytor pomija walidacj\u0119 kodu, a zmiana w <code>render.php<\/code> po kolejnym wdro\u017ceniu obejmuje ka\u017cde wyst\u0105pienie bloku. Podr\u0119cznik edytora blok\u00f3w wymienia dok\u0142adnie te dwa przypadki dla renderowania dynamicznego: tre\u015b\u0107, kt\u00f3ra ma si\u0119 zmienia\u0107 bez aktualizacji wpisu, oraz kod, kt\u00f3ry prawdopodobnie b\u0119dzie si\u0119 zmienia\u0142.<\/p>\n<p>Renderowanie dynamiczne wymaga dodatkowej pracy serwera: ka\u017cde wy\u015bwietlenie strony z blokiem uruchamia zapytanie do bazy, a ka\u017cda zmiana atrybutu w edytorze wysy\u0142a \u017c\u0105danie REST. Przy kr\u00f3tkiej li\u015bcie jest to zwykle do przyj\u0119cia. Je\u015bli generowanie wyniku jest kosztowne, wyrenderowany HTML mo\u017cna przechowa\u0107 w transiencie lub w cache obiekt\u00f3w. \u017badne z tych rozwi\u0105za\u0144 nie jest cz\u0119\u015bci\u0105 przyk\u0142adu, a na potrzeby artyku\u0142u nie wykonano pomiar\u00f3w. Blok zale\u017cy te\u017c od aktywnej wtyczki: bez niej nie renderuje niczego, poniewa\u017c <code>post_content<\/code> nie zawiera zast\u0119pczego HTML.<\/p>\n<h2>Ograniczenia i otwarte kwestie<\/h2>\n<ul>\n<li>Dokumentacja <code>ServerSideRender<\/code> okre\u015bla komponent jako mechanizm zapasowy lub przestarza\u0142y, nieodpowiedni do rozwijania nowych funkcji. Dla blok\u00f3w, kt\u00f3rych podgl\u0105d da si\u0119 zbudowa\u0107 w JavaScripcie z danych REST, zalecan\u0105 drog\u0105 jest renderowanie po stronie klienta. Przyk\u0142ad u\u017cywa komponentu celowo, aby <code>render.php<\/code> pozosta\u0142 jedynym \u017ar\u00f3d\u0142em kodu HTML; cen\u0105 jest zagnie\u017cd\u017cony element opakowuj\u0105cy w edytorze (zewn\u0119trzny <code>div<\/code> z <code>useBlockProps()<\/code> zawiera <code>div<\/code> z <code>render.php<\/code>).<\/li>\n<li>WordPress 7.0 wprowadzi\u0142 rejestracj\u0119 blok\u00f3w wy\u0142\u0105cznie w PHP: z <code>'autoRegister' =&gt; true<\/code> w <code>supports<\/code> i funkcj\u0105 <code>render_callback<\/code> blok pojawia si\u0119 w edytorze bez w\u0142asnego JavaScriptu, a dla atrybut\u00f3w typu string, integer, boolean i enum kontrolki powstaj\u0105 automatycznie. Wyb\u00f3r kategorii po nazwie zamiast numerycznego ID nie jest tym obj\u0119ty, dlatego przyk\u0142ad zachowuje w\u0142asny <code>edit.js<\/code>.<\/li>\n<li>Opisany szkielet odpowiada wersji 4.99.0 pakietu <code>@wordpress\/create-block<\/code>. Pakiet ukazuje si\u0119 cz\u0119sto; nazwy plik\u00f3w, flagi i warto\u015bci domy\u015blne mog\u0105 si\u0119 zmieni\u0107 w kolejnych wersjach.<\/li>\n<li>Plugin Check 2.1.0 nie zg\u0142asza dla przyk\u0142adu \u017cadnych b\u0142\u0119d\u00f3w w WordPressie 7.1.2 (sprawdzono 30.09.2026). Dla ka\u017cdej kopii <code>render.php<\/code> w <code>src\/<\/code> i <code>build\/<\/code> wymienia siedem ostrze\u017ce\u0144 <code>PrefixAllGlobals<\/code> oraz jedno ostrze\u017cenie dotycz\u0105ce parametru <code>exclude<\/code> w <code>get_posts()<\/code>; go\u0142e <code>echo get_block_wrapper_attributes()<\/code> zg\u0142asza jako b\u0142\u0105d.<\/li>\n<li>Cache wyrenderowanej listy i koszt podgl\u0105du przez REST nie zosta\u0142y zmierzone.<\/li>\n<\/ul>\n\n<div class=\"lw-faq\">\n<h2>Pytania i odpowiedzi<\/h2>\n\n<h3>Dlaczego zmiana w block.json nie dzia\u0142a, cho\u0107 plik zosta\u0142 zapisany?<\/h3>\n<p>Rejestracja korzysta z <code>build\/blocks-manifest.php<\/code>, a nie z <code>src\/<\/code>. Manifest powstaje podczas budowania; PHP widzi now\u0105 wersj\u0119 dopiero po <code>npm run build<\/code> lub przy dzia\u0142aj\u0105cym <code>npm start<\/code>.<\/p>\n\n<h3>Czy render.php mo\u017ce deklarowa\u0107 w\u0142asne funkcje pomocnicze?<\/h3>\n<p>Nie. WordPress wczytuje plik przez <code>require<\/code> ponownie dla ka\u017cdej instancji bloku. Przy dw\u00f3ch instancjach na stronie druga deklaracja tej samej funkcji ko\u0144czy si\u0119 b\u0142\u0119dem krytycznym. Funkcje pomocnicze z prefiksem nale\u017cy umieszcza\u0107 w g\u0142\u00f3wnym pliku wtyczki lub w osobnym pliku do\u0142\u0105czanym.<\/p>\n\n<h3>Czy blok renderowany na serwerze potrzebuje jeszcze JavaScriptu od WordPressa 7.0?<\/h3>\n<p>Niekoniecznie. Z <code>'autoRegister' =&gt; true<\/code> w <code>supports<\/code> i funkcj\u0105 <code>render_callback<\/code> blok pojawia si\u0119 w edytorze bez w\u0142asnego JavaScriptu, a kontrolki powstaj\u0105 dla atrybut\u00f3w typu string, integer, boolean i enum. Lista wyboru z nazwami kategorii lub w\u0142asny placeholder nadal wymagaj\u0105 pliku <code>edit.js<\/code>.<\/p>\n<\/div>\n\n<div class=\"lw-quellen\">\n<h2>\u0179r\u00f3d\u0142a<\/h2>\n<ul>\n<li><a href=\"https:\/\/developer.wordpress.org\/block-editor\/reference-guides\/packages\/packages-create-block\/\" target=\"_blank\" rel=\"noopener noreferrer\">@wordpress\/create-block \u2013 Block Editor Handbook<\/a> (dost\u0119p 29.09.2026)<\/li>\n<li><a href=\"https:\/\/github.com\/WordPress\/gutenberg\/tree\/trunk\/packages\/create-block\/lib\" target=\"_blank\" rel=\"noopener noreferrer\">Kod \u017ar\u00f3d\u0142owy create-block: templates.js, init-package-json.js, szablony wtyczki i bloku (GitHub)<\/a> (dost\u0119p 29.09.2026)<\/li>\n<li><a href=\"https:\/\/www.npmjs.com\/package\/@wordpress\/create-block\" target=\"_blank\" rel=\"noopener noreferrer\">@wordpress\/create-block w npm, wersja 4.99.0<\/a> (dost\u0119p 29.09.2026)<\/li>\n<li><a href=\"https:\/\/developer.wordpress.org\/block-editor\/getting-started\/tutorial\/\" target=\"_blank\" rel=\"noopener noreferrer\">Tutorial: Build your first block<\/a> (dost\u0119p 29.09.2026)<\/li>\n<li><a href=\"https:\/\/developer.wordpress.org\/block-editor\/reference-guides\/block-api\/block-metadata\/\" target=\"_blank\" rel=\"noopener noreferrer\">Metadata in block.json<\/a> (dost\u0119p 29.09.2026)<\/li>\n<li><a href=\"https:\/\/developer.wordpress.org\/block-editor\/reference-guides\/block-api\/block-api-versions\/\" target=\"_blank\" rel=\"noopener noreferrer\">Block API Versions<\/a> (dost\u0119p 29.09.2026)<\/li>\n<li><a href=\"https:\/\/developer.wordpress.org\/block-editor\/getting-started\/fundamentals\/static-dynamic-rendering\/\" target=\"_blank\" rel=\"noopener noreferrer\">Static or Dynamic rendering of a block<\/a> (dost\u0119p 29.09.2026)<\/li>\n<li><a href=\"https:\/\/developer.wordpress.org\/block-editor\/getting-started\/fundamentals\/registration-of-a-block\/\" target=\"_blank\" rel=\"noopener noreferrer\">Registration of a block<\/a> (dost\u0119p 29.09.2026)<\/li>\n<li><a href=\"https:\/\/developer.wordpress.org\/block-editor\/reference-guides\/block-api\/block-deprecation\/\" target=\"_blank\" rel=\"noopener noreferrer\">Deprecation<\/a> (dost\u0119p 29.09.2026)<\/li>\n<li><a href=\"https:\/\/developer.wordpress.org\/block-editor\/reference-guides\/packages\/packages-scripts\/\" target=\"_blank\" rel=\"noopener noreferrer\">@wordpress\/scripts<\/a> (dost\u0119p 29.09.2026)<\/li>\n<li><a href=\"https:\/\/github.com\/WordPress\/gutenberg\/blob\/trunk\/packages\/dependency-extraction-webpack-plugin\/README.md\" target=\"_blank\" rel=\"noopener noreferrer\">Dependency Extraction Webpack Plugin (README)<\/a> (dost\u0119p 29.09.2026)<\/li>\n<li><a href=\"https:\/\/developer.wordpress.org\/block-editor\/reference-guides\/packages\/packages-server-side-render\/\" target=\"_blank\" rel=\"noopener noreferrer\">@wordpress\/server-side-render<\/a> i <a href=\"https:\/\/github.com\/WordPress\/gutenberg\/blob\/trunk\/packages\/server-side-render\/CHANGELOG.md\" target=\"_blank\" rel=\"noopener noreferrer\">changelog pakietu<\/a> (dost\u0119p 29.09.2026)<\/li>\n<li><a href=\"https:\/\/developer.wordpress.org\/reference\/functions\/get_block_wrapper_attributes\/\" target=\"_blank\" rel=\"noopener noreferrer\">get_block_wrapper_attributes()<\/a> (dost\u0119p 29.09.2026)<\/li>\n<li><a href=\"https:\/\/developer.wordpress.org\/reference\/functions\/wp_register_block_types_from_metadata_collection\/\" target=\"_blank\" rel=\"noopener noreferrer\">wp_register_block_types_from_metadata_collection()<\/a> (dost\u0119p 29.09.2026)<\/li>\n<li><a href=\"https:\/\/make.wordpress.org\/core\/2024\/10\/17\/new-block-type-registration-apis-to-improve-performance-in-wordpress-6-7\/\" target=\"_blank\" rel=\"noopener noreferrer\">New block type registration APIs to improve performance in WordPress 6.7<\/a> (dost\u0119p 29.09.2026)<\/li>\n<li><a href=\"https:\/\/make.wordpress.org\/core\/2025\/03\/13\/more-efficient-block-type-registration-in-6-8\/\" target=\"_blank\" rel=\"noopener noreferrer\">More efficient block type registration in 6.8<\/a> (dost\u0119p 29.09.2026)<\/li>\n<li><a href=\"https:\/\/make.wordpress.org\/core\/2026\/03\/03\/php-only-block-registration\/\" target=\"_blank\" rel=\"noopener noreferrer\">PHP-only block registration<\/a> (dost\u0119p 29.09.2026)<\/li>\n<li><a href=\"https:\/\/github.com\/WordPress\/wordpress-develop\/blob\/7.1\/src\/wp-includes\/blocks.php\" target=\"_blank\" rel=\"noopener noreferrer\">wordpress-develop, ga\u0142\u0105\u017a 7.1: blocks.php<\/a> i <a href=\"https:\/\/github.com\/WordPress\/wordpress-develop\/blob\/7.1\/src\/wp-includes\/post.php\" target=\"_blank\" rel=\"noopener noreferrer\">post.php (get_posts)<\/a> (dost\u0119p 29.09.2026)<\/li>\n<li><a href=\"https:\/\/github.com\/WordPress\/WordPress-Coding-Standards\/blob\/develop\/WordPress\/Helpers\/EscapingFunctionsTrait.php\" target=\"_blank\" rel=\"noopener noreferrer\">WordPress Coding Standards: EscapingFunctionsTrait.php<\/a> (dost\u0119p 29.09.2026)<\/li>\n<li><a href=\"https:\/\/api.wordpress.org\/core\/version-check\/1.7\/\" target=\"_blank\" rel=\"noopener noreferrer\">API sprawdzania wersji WordPressa (aktualne wydanie 7.1.2)<\/a> (dost\u0119p 29.09.2026)<\/li>\n<\/ul>\n<\/div>","protected":false},"excerpt":{"rendered":"<p>Blok renderowany na serwerze wy\u015bwietla najnowsze wpisy z jednej kategorii: szkielet create-block, block.json, rejestracja przez manifest, render.php i podgl\u0105d w edytorze.<\/p>\n","protected":false},"author":1,"featured_media":20915,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[92646],"tags":[92271,92280,92238,92762,92286],"class_list":["post-20868","post","type-post","status-publish","format-standard","hentry","category-tutorials-pl-wordpress-plugins-tricks-pl","tag-javascript-pl","tag-php-pl","tag-tutorial-pl","tag-web-development-pl","tag-wordpress-pl"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Dynamiczny blok Gutenberga z block.json i render.php | Lukas Wojcik<\/title>\n<meta name=\"description\" content=\"Jak dzi\u015b powstaje dynamiczny blok WordPressa: create-block --variant dynamic, block.json z apiVersion 3, blocks-manifest.php oraz render.php.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.lukaswojcik.com\/blog\/pl\/wordpress-plugins-tricks-pl\/tutorials-pl-wordpress-plugins-tricks-pl\/dynamiczny-blok-block-json\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Dynamiczny blok Gutenberga z block.json i render.php | Lukas Wojcik\" \/>\n<meta property=\"og:description\" content=\"Jak dzi\u015b powstaje dynamiczny blok WordPressa: create-block --variant dynamic, block.json z apiVersion 3, blocks-manifest.php oraz render.php.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.lukaswojcik.com\/blog\/pl\/wordpress-plugins-tricks-pl\/tutorials-pl-wordpress-plugins-tricks-pl\/dynamiczny-blok-block-json\/\" \/>\n<meta property=\"og:site_name\" content=\"Lukas Wojcik - Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-10-21T18:26:00+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/09\/fi-20868-dynamiczny-blok-block-json.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Lukas Wojcik\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Lukas Wojcik\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"17 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/pl\\\/wordpress-plugins-tricks-pl\\\/tutorials-pl-wordpress-plugins-tricks-pl\\\/dynamiczny-blok-block-json\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/pl\\\/wordpress-plugins-tricks-pl\\\/tutorials-pl-wordpress-plugins-tricks-pl\\\/dynamiczny-blok-block-json\\\/\"},\"author\":{\"name\":\"Lukas Wojcik\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"headline\":\"Dynamiczny blok Gutenberga z block.json i render.php\",\"datePublished\":\"2026-10-21T18:26:00+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/pl\\\/wordpress-plugins-tricks-pl\\\/tutorials-pl-wordpress-plugins-tricks-pl\\\/dynamiczny-blok-block-json\\\/\"},\"wordCount\":2273,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"image\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/pl\\\/wordpress-plugins-tricks-pl\\\/tutorials-pl-wordpress-plugins-tricks-pl\\\/dynamiczny-blok-block-json\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/fi-20868-dynamiczny-blok-block-json.png\",\"keywords\":[\"JavaScript\",\"PHP\",\"Tutorial\",\"Web Development\",\"WordPress\"],\"articleSection\":[\"Tutorials\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/pl\\\/wordpress-plugins-tricks-pl\\\/tutorials-pl-wordpress-plugins-tricks-pl\\\/dynamiczny-blok-block-json\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/pl\\\/wordpress-plugins-tricks-pl\\\/tutorials-pl-wordpress-plugins-tricks-pl\\\/dynamiczny-blok-block-json\\\/\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/pl\\\/wordpress-plugins-tricks-pl\\\/tutorials-pl-wordpress-plugins-tricks-pl\\\/dynamiczny-blok-block-json\\\/\",\"name\":\"Dynamiczny blok Gutenberga z block.json i render.php | Lukas Wojcik\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/pl\\\/wordpress-plugins-tricks-pl\\\/tutorials-pl-wordpress-plugins-tricks-pl\\\/dynamiczny-blok-block-json\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/pl\\\/wordpress-plugins-tricks-pl\\\/tutorials-pl-wordpress-plugins-tricks-pl\\\/dynamiczny-blok-block-json\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/fi-20868-dynamiczny-blok-block-json.png\",\"datePublished\":\"2026-10-21T18:26:00+00:00\",\"description\":\"Jak dzi\u015b powstaje dynamiczny blok WordPressa: create-block --variant dynamic, block.json z apiVersion 3, blocks-manifest.php oraz render.php.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/pl\\\/wordpress-plugins-tricks-pl\\\/tutorials-pl-wordpress-plugins-tricks-pl\\\/dynamiczny-blok-block-json\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/pl\\\/wordpress-plugins-tricks-pl\\\/tutorials-pl-wordpress-plugins-tricks-pl\\\/dynamiczny-blok-block-json\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/pl\\\/wordpress-plugins-tricks-pl\\\/tutorials-pl-wordpress-plugins-tricks-pl\\\/dynamiczny-blok-block-json\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/fi-20868-dynamiczny-blok-block-json.png\",\"contentUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/fi-20868-dynamiczny-blok-block-json.png\",\"width\":1200,\"height\":630,\"caption\":\"Dynamiczny blok Gutenberga z block.json i render.php\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/pl\\\/wordpress-plugins-tricks-pl\\\/tutorials-pl-wordpress-plugins-tricks-pl\\\/dynamiczny-blok-block-json\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Dynamiczny blok Gutenberga z block.json i render.php\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\",\"name\":\"Lukas Wojcik - Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\",\"name\":\"Lukas Wojcik\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"contentUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"width\":424,\"height\":636,\"caption\":\"Lukas Wojcik\"},\"logo\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\"},\"sameAs\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Dynamiczny blok Gutenberga z block.json i render.php | Lukas Wojcik","description":"Jak dzi\u015b powstaje dynamiczny blok WordPressa: create-block --variant dynamic, block.json z apiVersion 3, blocks-manifest.php oraz render.php.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.lukaswojcik.com\/blog\/pl\/wordpress-plugins-tricks-pl\/tutorials-pl-wordpress-plugins-tricks-pl\/dynamiczny-blok-block-json\/","og_locale":"en_US","og_type":"article","og_title":"Dynamiczny blok Gutenberga z block.json i render.php | Lukas Wojcik","og_description":"Jak dzi\u015b powstaje dynamiczny blok WordPressa: create-block --variant dynamic, block.json z apiVersion 3, blocks-manifest.php oraz render.php.","og_url":"https:\/\/www.lukaswojcik.com\/blog\/pl\/wordpress-plugins-tricks-pl\/tutorials-pl-wordpress-plugins-tricks-pl\/dynamiczny-blok-block-json\/","og_site_name":"Lukas Wojcik - Blog","article_published_time":"2026-10-21T18:26:00+00:00","og_image":[{"width":1200,"height":630,"url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/09\/fi-20868-dynamiczny-blok-block-json.png","type":"image\/png"}],"author":"Lukas Wojcik","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Lukas Wojcik","Est. reading time":"17 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.lukaswojcik.com\/blog\/pl\/wordpress-plugins-tricks-pl\/tutorials-pl-wordpress-plugins-tricks-pl\/dynamiczny-blok-block-json\/#article","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/pl\/wordpress-plugins-tricks-pl\/tutorials-pl-wordpress-plugins-tricks-pl\/dynamiczny-blok-block-json\/"},"author":{"name":"Lukas Wojcik","@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"headline":"Dynamiczny blok Gutenberga z block.json i render.php","datePublished":"2026-10-21T18:26:00+00:00","mainEntityOfPage":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/pl\/wordpress-plugins-tricks-pl\/tutorials-pl-wordpress-plugins-tricks-pl\/dynamiczny-blok-block-json\/"},"wordCount":2273,"commentCount":0,"publisher":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"image":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/pl\/wordpress-plugins-tricks-pl\/tutorials-pl-wordpress-plugins-tricks-pl\/dynamiczny-blok-block-json\/#primaryimage"},"thumbnailUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/09\/fi-20868-dynamiczny-blok-block-json.png","keywords":["JavaScript","PHP","Tutorial","Web Development","WordPress"],"articleSection":["Tutorials"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.lukaswojcik.com\/blog\/pl\/wordpress-plugins-tricks-pl\/tutorials-pl-wordpress-plugins-tricks-pl\/dynamiczny-blok-block-json\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.lukaswojcik.com\/blog\/pl\/wordpress-plugins-tricks-pl\/tutorials-pl-wordpress-plugins-tricks-pl\/dynamiczny-blok-block-json\/","url":"https:\/\/www.lukaswojcik.com\/blog\/pl\/wordpress-plugins-tricks-pl\/tutorials-pl-wordpress-plugins-tricks-pl\/dynamiczny-blok-block-json\/","name":"Dynamiczny blok Gutenberga z block.json i render.php | Lukas Wojcik","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/pl\/wordpress-plugins-tricks-pl\/tutorials-pl-wordpress-plugins-tricks-pl\/dynamiczny-blok-block-json\/#primaryimage"},"image":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/pl\/wordpress-plugins-tricks-pl\/tutorials-pl-wordpress-plugins-tricks-pl\/dynamiczny-blok-block-json\/#primaryimage"},"thumbnailUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/09\/fi-20868-dynamiczny-blok-block-json.png","datePublished":"2026-10-21T18:26:00+00:00","description":"Jak dzi\u015b powstaje dynamiczny blok WordPressa: create-block --variant dynamic, block.json z apiVersion 3, blocks-manifest.php oraz render.php.","breadcrumb":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/pl\/wordpress-plugins-tricks-pl\/tutorials-pl-wordpress-plugins-tricks-pl\/dynamiczny-blok-block-json\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.lukaswojcik.com\/blog\/pl\/wordpress-plugins-tricks-pl\/tutorials-pl-wordpress-plugins-tricks-pl\/dynamiczny-blok-block-json\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.lukaswojcik.com\/blog\/pl\/wordpress-plugins-tricks-pl\/tutorials-pl-wordpress-plugins-tricks-pl\/dynamiczny-blok-block-json\/#primaryimage","url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/09\/fi-20868-dynamiczny-blok-block-json.png","contentUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/09\/fi-20868-dynamiczny-blok-block-json.png","width":1200,"height":630,"caption":"Dynamiczny blok Gutenberga z block.json i render.php"},{"@type":"BreadcrumbList","@id":"https:\/\/www.lukaswojcik.com\/blog\/pl\/wordpress-plugins-tricks-pl\/tutorials-pl-wordpress-plugins-tricks-pl\/dynamiczny-blok-block-json\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.lukaswojcik.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Dynamiczny blok Gutenberga z block.json i render.php"}]},{"@type":"WebSite","@id":"https:\/\/www.lukaswojcik.com\/blog\/#website","url":"https:\/\/www.lukaswojcik.com\/blog\/","name":"Lukas Wojcik - Blog","description":"","publisher":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.lukaswojcik.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9","name":"Lukas Wojcik","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","contentUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","width":424,"height":636,"caption":"Lukas Wojcik"},"logo":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg"},"sameAs":["https:\/\/www.lukaswojcik.com\/blog"]}]}},"_links":{"self":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/20868","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/comments?post=20868"}],"version-history":[{"count":2,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/20868\/revisions"}],"predecessor-version":[{"id":22773,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/20868\/revisions\/22773"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media\/20915"}],"wp:attachment":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media?parent=20868"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/categories?post=20868"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/tags?post=20868"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}