A Kirby CMS felfedezése: egy frontend-fejlesztő szemszögéből

Mit tanultam a Kirby használatából — a marketingoldalaktól a rugalmas, tartalomvezérelt platformokig.

Az első találkozásom a Kirby CMS-sel nagyjából két éve volt, amikor egy Kirby-alapprojekt frontendjének átdolgozása lett a feladatunk — valami white-label termékféle. Az ilyen projektek önmagukban is ijesztőek tudnak lenni, hát még akkor, ha olyan rendszerre épülnek, amelyről alig tudsz valamit.

Ez az érzés szinte abban a pillanatban megváltozott, ahogy megnyitottam a Panelt. Egy minimalista, intuitív vezérlőpult fogadott, és azonnal tudtam, hol találom azt, amit módosítani szeretnék. Admin vagy szerkesztő szemmel ismerősnek hat: olyan hely, ahol el lehet navigálni és el lehet végezni a dolgokat anélkül, hogy a rendszerrel kellene küzdeni.

A Kirbyben való fejlesztés is nagyjából ugyanezt a benyomást keltette. A mappaszerkezet áttekinthető, és nagyon kevés a zaj, amikor egy adott részt szerkesztesz. A blueprintek YAML-ben írják le a tartalmi szerkezetet, a többi pedig PHP, egy jól dokumentált API-val. Nem nyomaszt egy vadonatúj stack, mielőtt bármit is elérnél.

Azóta többféle Kirby-projekten dolgoztam, az egyszerű marketingoldalaktól a bonyolultabb, generikus elrendezésű és újrahasznosítható blokkszerkezetekre épülő, teljes értékű blogokig. Két év valódi projektmunka után ezek maradtak meg leginkább.

Az első lépések

Egy dolog még a belevágás előtt: a Kirby nem teljesen nyílt forráskódú. Helyben addig futtathatod, ameddig csak akarod, de nyilvános szerveren licenc kell hozzá. Az árazás korrekt, nonprofit és oktatási kedvezmények is vannak, tehát nem egy mindenkire ráhúzott költség. Engem nem zavar ez a kompromisszum. Közvetlen módja annak, hogy támogasd azt a csapatot, amelyik az általad használt szoftvert építi.

A backend oldalán a blueprintek YAML-fájlok, amelyek leírják az oldal tartalmi modelljét és a szerkesztői felületet. Ha a TYPO3 világából érkezel, és szereted a Content Blockst, ismerős lesz az ötlet — azzal a különbséggel, hogy a Kirbyben mindenütt blueprinteket használsz.

Konzisztens mezőtípus-készletet kapsz, amelyet bárhol bevethetsz. Akár összetett adatszerkezetet definiálsz, akár egyetlen egyedi szekciót, nem kell két teljesen különböző megközelítést megtanulnod.

A DDEV-nek is van Kirby CMS quickstartja a hivatalos Starterkit alapján, így a Kirby kipróbálása és az első módosítások elvégzése alig igényel erőfeszítést.

Sablonozás és frontend

Frontend-fejlesztőként korán bele kell ásnom magam a sablonozásba. Elsőre megijesztett: nincs külön sablonnyelv. Helyette sima HTML-t írsz, köré pedig PHP-t a feltételekhez, ciklusokhoz és a kiíratáshoz.

Aztán gyorsan rájössz az előnyére: itt is nagyon kevés az új tanulnivaló.

A Kirby különféle PHP-objektumokon keresztül teszi elérhetővé a tartalmat, így a PHP a legközvetlenebb út a munkához. Kapsz mellé hasznos HTML-, URL-, fordítás- és string-segédfüggvényeket, amelyek tisztán tartják a sablonokat.

A Html::attr() segédfüggvény például egy attribútumtömbből kész, kiírható stringet csinál, és automatikusan kihagy mindent, ami false vagy null értékre oldódik fel:

php
<a <?= Html::attr([
    'href' => $url,
    'class' => 'button',
    'target' => $external ? '_blank' : null,
    'rel' => $external ? 'noopener noreferrer' : null,
]) ?>>
  <?= htmlspecialchars($label, ENT_QUOTES, 'UTF-8') ?>
</a>

Az attribútumokat dinamikusan definiálod, és a segédfüggvényre bízod, mi kerüljön kirenderelésre. Nincs kézi stringösszefűzés, és nem marad ott üres target="", ha a feltétel nem teljesül.

Az összetett logika amúgy is külön kontrollerbe való, és egy ilyet beállítani egyszerű. A kontroller ugyanazt a fájlnevet viseli, mint a hozzá tartozó sablon:

plaintext
/site/templates/article.php
/site/controllers/article.php

A kontroller által visszaadott tömb elérhetővé válik a megfelelő sablonban. Mindkét oldalon ugyanazokkal a $page, $site és $kirby objektumokkal dolgozol, tehát semmit sem kell átalakítani csak azért, hogy átlépjen ezen a határon. A sablonmotor hiánya tényleg nem világvége.

Az újrafelhasználható sablontöredékeket snippeteknek hívják. Ezek sima PHP-fájlok a snippets mappában, amelyeket a snippet() hívással emelsz be a sablonba. Az adataikat kifejezetten átadod, ahelyett hogy arra hagyatkoznál, mi van éppen a scope-ban:

php
<?php snippet('teaser', ['item' => $item]) ?>

A snippets/teaser.php fájlban közvetlenül az $item változóval dolgozol — nincs keresgélés és nincs varázslat. Ha a TYPO3 és a Fluid felől érkezel, ez nagyjából olyan, mint egy partial renderelése:

html
<f:render partial="Teaser" arguments="{item: item}" />

Az eredmény egy önálló töredék, amely csak azokat az adatokat ismeri, amelyeket átadsz neki, így újrahasznosítható anélkül, hogy észrevétlenül függene a körülötte lévő oldaltól.

Külső sablonmotorok

PHP-alapú sablonnyelvekhez is vannak közösségi integrációk. Ha Laravel felől jössz, használhatod Lukas Leitsch Blade-integrációját. Symfony-háttérrel pedig ott a JUST csapat Twig-integrációja.

Ha ezek nem elégítik ki az igényeidet, a Kirby plugin-felületén keresztül saját sablonmotort is hozhatsz. Nekünk is fő valami izgalmas ezen a téren, úgyhogy maradjatok velünk.

Hozd a saját frontendedet

A Kirbynek nincs véleménye arról, milyen frontend-keretrendszert vagy build-eszközt hozol magaddal. Ha Vitét szeretnél használni, a közösség által karbantartott kirby-vite plugin (Arnosontól) jó kiindulópont.

Az én esetemben már volt egy jól bevált Vite-integrációm TYPO3- és DDEV-környezetekhez. Mindent kiszolgál, amire nap mint nap szükségünk van, és teljes kontrollt ad a stack ezen része fölött. Meglepően kevés munka volt átültetni a Kirby plugin-rendszerére.

A Vue ismerete sokat segít, ha a Panelt akarod bővíteni, mivel a Kirby Panelje maga is Vue-ban készült, és komponensrendszert kínál a pluginoknak. Érdemes még a plugin megkezdése előtt megnézni, hogy az adott Kirby-verzió milyen Vue- és Panel-API-kat támogat, mert ez a kompatibilitás itt többet nyom a latban, mint a publikus frontenden.

A teljesen headless irány is választható. A Kirby nem kényszerít arra, hogy HTML-t rendereljen; a headless beállítás cookbookja jó kiindulópont, ha erre indulsz.

Számomra viszont az az érdekesebb, hogy nem kell teljesen elköteleződnöd egyetlen megközelítés mellett. A Kirby ugyanazt a tartalmat a kéréstől függően különböző formátumokban tudja megjeleníteni. Egy oldal renderelhető HTML-ként a böngészőnek és JSON-ként egy API-fogyasztónak anélkül, hogy a tartalmi modellt duplikálnád.

A szokásos page.php sablon mellé elhelyezhetsz egy page.json.php reprezentációt, és a Kirby a kért kiterjesztés alapján választja ki a megfelelőt. Ugyanaz a blueprint, ugyanazok a mezők, más reprezentáció kifelé. Kellemes középút, amikor egy projekt egyik része normál weboldalt igényel, egy másik pedig mobilalkalmazást vagy külön frontendet szolgál ki.

Panel, blueprintek és a backend

A Panellel való napi munka megerősíti, amit az első benyomás ígért. Akkor is intuitív marad, amikor valódi projektet építesz, nem csak nézelődsz.

Ebben nagy szerepe van a blueprintek konzisztenciájának. Ha egyet megírtál, gyakorlatilag mindegyik formáját láttad — akár egy teljes oldalt, akár egyetlen fület, akár egy blokkot írsz le egy elrendezés-építőn belül.

Vegyünk egy fület az oldalszintű céges beállításokhoz a site.yml fájlban:

site.yml
company:
  label: Company Info
  fields:
    companyname:
      label: Company Name
      type: text
    logo:
      label: Logo
      type: files
      max: 1

Most hasonlítsd ezt össze egy idézetblokkal a blocks/quote.yml fájlban:

blocks/quote.yml
name: Quote
icon: quote-right
fields:
  text:
    label: Quote
    type: textarea
  author:
    label: Author
    type: text

Ugyanaz a fields kulcs, ugyanazok a címkék és típusok, és ugyanazok a meződefiníciók bukkannak fel az egész rendszerben. Egy text mező text mező marad, akár globális fülön, akár egy szokásos oldalon, akár egy blokkba ágyazva van.

A jogosultságok ugyanezt a jól olvasható megközelítést követik. Az, hogy egy szerkesztő szerepkör mit tehet és mit nem, szintén egy YAML-fájl:

yaml
title: Editor
permissions:
  files:
    changeName: false
  pages:
    delete: false

Ha egy mező már létezik egy blueprintben, akkor a sablonban úgy éred el, hogy metódusként hívod meg az oldalobjektumon. Egy Field objektumot kapsz vissza, olyan segédfüggvényekkel, mint a kirbytext() a KirbyTexthez és a html() az escape-elt egyszerű szöveghez.

A tartalom természeténél fogva opcionális — valaki mindig üresen hagy egy mezőt —, így az isNotEmpty() hívással ellenőrizheted, mielőtt bármit kiírnál:

php
<?php if ($page->text()->isNotEmpty()): ?>
  <?= $page->text()->kirbytext() ?>
<?php endif; ?>

Nem kell !empty() hívásokat szórnod a sablonba, és nem kell találgatnod, hogy egy hiányzó mező null vagy üres string értéket ad-e vissza. A Field objektum ezt már tudja, és meg is mondja.

Rugalmasság — és mikor érdemes korlátozni

A Kirby engedi, hogy annyira generikus légy, amennyire csak akarsz, és pont itt tanultam meg óvatosnak lenni.

Csábító egyetlen mega-rugalmas blokk- vagy mezőszerkezetet létrehozni, amely elméletileg bármilyen elrendezést kezel, amit egy ügyfél kitalálhat. A gyakorlatban ez a rugalmasság ellened dolgozhat. A szerkesztők a végén opcionális mezők falát bámulják, és nem tudják, melyik számít azon az oldalon, amelyet éppen szerkesztenek. A fejlesztők pedig védekező sablonkódot írnak olyan kombinációkra, amelyek soha nem fognak előfordulni.

Bizonyos korlátok mindkét oldalt jobban szolgálják. Döntsd el előre, mire való egy szekció, ahelyett hogy minden ajtót nyitva hagynál.

Ahol a Kirby rugalmassága igazán megtérül, az a Panel elrendezése. A blueprintekkel szabályozhatod a szekciók szélességét, fülekbe csoportosíthatod a mezőket, és megjeleníthetsz tartalmat rácsként, listaként, táblázatként vagy kártyákként. Ehhez semmilyen új megközelítés nem kell; csupán több konfiguráció azok köré a mezők köré, amelyeket amúgy is használsz.

Két összetartozó mezőt egymás mellé tenni egyszerű width beállítás. Ez például egy esemény kezdő- és záródátumánál hasznos, ahol tényleg számít, hogy egyszerre látod mindkettőt:

yaml
fields:
  startdate:
    label: Start Date
    type: date
    width: 1/2
  enddate:
    label: End Date
    type: date
    width: 1/2

Egy blueprint fülekre bontása nagyjából ugyanígy működik:

yaml
tabs:
  content:
    label: Content
    fields:
      text:
        type: textarea
  seo:
    label: SEO
    fields:
      metatitle:
        type: text

Beállíthatsz hasznos előnézeteket is, hogy a szerkesztőknek ne kelljen minden elemet megnyitniuk csak azért, hogy megtalálják a megfelelő képet vagy címsort. Egy structure mező például kártyákat használhat előnézeti képpel és felirattal:

yaml
fields:
  team:
    type: structure
    layout: cards
    image:
      query: item.photo.toFile
    info: "{{ item.role }}"
    fields:
      name:
        type: text
      role:
        type: text
      photo:
        type: files
        max: 1

Ez az elrendezési munka azonban nem légüres térben zajlik. Megéri az időt leülni a szerkesztőkkel, és végigvenni velük néhány lehetőséget, mielőtt elköteleződnél egy mellett. Ami a fejlesztőnek hatékonynak tűnik, nem mindig természetes annak, aki nap mint nap kitölti. Egy ötperces beszélgetés az elején rengeteg utómunkát spórolhat.

A Kirby bővítése

A Kirby plugin-API-ja egyszerű, ha egyszer belejössz. Az egyetlen mindenes hook helyett világos bővítési pontok vannak az egész rendszerben. Regisztrálhatsz blueprinteket, hozzáadhatsz saját metódusokat, reagálhatsz eseményekre, vagy biztosíthatsz sablonokat és snippeteket — mindezt egy plugin részeként csomagolva, így az eredmény nem tűnik utólag ráaggatottnak.

A Panel bővítése elsőre más tészta. Némi ásás kell hozzá, hogy pontosan kiderüljön, mit tesz elérhetővé, mire leesik a tantusz. Utána viszont egy Panel-komponens megírása nagyjából olyan, mint a szokásos Vue, a Kirby komponenseivel és segédfüggvényeivel megfejelve.

A Kirby Panel Lab és a UI-dokumentáció itt különösen hasznos. A Panel-bővítmények szorosan kötődnek ahhoz a Kirby-verzióhoz, amelyet céloznak, ezért komolyabb plugin-munka előtt mindig megnézném a hozzá tartozó komponens-dokumentációt.

Dokumentáció, ökoszisztéma és közösség

A Kirby dokumentációja dicséretet érdemel. Óriási segítség volt a kezdeteknél: property-listákkal és működő példákkal a rendszer szinte minden részének rendereléséhez vagy használatához. Az első napjaim jó részét a referenciában töltöttem, ügyelve arra, hogy ne találjak fel újra valamit, ami már létezik.

Persze attól, hogy minden opció dokumentálva van, még előfordul, hogy rossz eszközért nyúlsz. Itt mutatja meg az értékét a cookbook. Tele van valós példákkal, amelyek lefedik a gyakori és jó néhány ritkább forgatókönyvet is, így ritkán tart sokáig kideríteni, melyik irányt részesíti előnyben a Kirby.

Ha a dokumentáció és a cookbook valamit nem fed le, gyakran a közösség igen. A Kirby fórum elég aktív ahhoz, hogy a legtöbb problémámra már volt ott válasz — néha egyenesen a core csapattól.

A plugin-ökoszisztéma is jól áll. Egészséges arányban vannak ingyenes és fizetős lehetőségek, köztük robusztus SEO- és űrlap-pluginok az igényesebb projektekhez. A plugin-katalógus jól szervezett, így a böngészés nem válik nyűggé.

Ez még mindig csak a felszín. Már önmagában a dokumentáció és a pluginok terén is rengeteg felfedeznivaló akad, és néhányukra vissza fogunk térni a későbbi cikkekben.

Záró gondolatok

Néhány év Kirby-használat után ez lett az egyik olyan CMS, amelyhez természetes módon nyúlok. Nem lóg a lábad alatt, amikor egy projekt egyszerű, de elég rugalmasságot ad, amikor a tartalmi modell és a frontend ambiciózusabbá válik.

Amit a legjobban értékelek benne, az a konzisztencia. A blueprintek, a mezők, a sablonok és a pluginok ugyanannak a rendszernek a részeiként hatnak, nem pedig idővel összegyűjtött funkciókként. Ettől lesz a Kirby kellemesen tanulható — és ami még fontosabb, kellemes vele dolgozni az első projekt után is.

Ha a következő weboldalatokhoz fontolgatjátok a Kirbyt, nemcsak a frontend kialakításában tudunk segíteni, hanem a mögötte álló tartalmi architektúrában és szerkesztői élményben is. Vegyétek fel velünk a kapcsolatot a Digital Zombiesnál, és nézzük meg együtt, hogy a Kirby illik-e ahhoz, amit építetek.