Ga naar de inhoud
Craft CMS

Craft CMS best practices: sneller, duidelijker en beter te onderhouden

Luremo

Craft CMS studio

Een ontwikkelaar ordent contentkaarten naast een laptop op een donkere werktafel.
Een contentmodel uitwerken voordat de templates worden gebouwd. Beeld: AI-gegenereerd.

Een goede Craft-site begint bij een helder contentmodel. Daarna maak je templates efficiënt, zet je caching gericht in en houd je beheer veilig. Zeven praktische keuzes voor Craft CMS 5.

Ontwerp het contentmodel vanuit het werk van de redacteur

Voordat je een template schrijft, wil je weten welke informatie de redacteur steeds opnieuw invoert. Een klantcase heeft bijvoorbeeld een klant, diensten, resultaten en beelden. Maak van informatie die elders terugkomt een eigen entry met relaties. Zo kan dezelfde dienst op een case, dienstenpagina en overzicht verschijnen zonder drie losse kopieën.

Gebruik Matrix voor de volgorde van paginaonderdelen, zoals tekst, beeld en een quote. In Craft 5 zijn die onderdelen geneste entries. Beperk het aantal bloktypen tot onderdelen die echt een andere invoer of presentatie nodig hebben; tientallen bijna gelijke varianten maken het beheer moeilijker. Test het model met iemand die er straks mee werkt: kan diegene een case toevoegen, een dienst koppelen en een afbeelding vervangen zonder uitleg?

Een contentmodel is geen eenmalige technische exercitie. Leg veldnamen, verplichte velden en vertaalregels vast, zodat de invoer ook over een jaar begrijpelijk blijft.

Zo doen we dit bij Luremo: _patterns/matrix.twig kiest per Matrix-type een eigen template. _patterns/matrix/cards.twig haalt de geneste card entries en hun afbeeldingen op en geeft voorbereide waarden door aan card-collection.twig. Die presentatiepartial hoeft daardoor zelf geen contentquery te doen.

Herken de N+1-query in een lijst

Een overzicht met twaalf artikelen en een afbeelding per artikel lijkt eenvoudig. Maar als de template voor elk artikel apart het gekoppelde beeld ophaalt, kan één query voor de artikelen uitgroeien tot nog eens twaalf queries voor de beelden. Voeg je categorieën of auteurs toe, dan groeit dat verder. Dit heet het N+1-probleem.

Craft 5 biedt lazy eager loading. Zet .eagerly() op de relationele query die je binnen de lijst gebruikt. Craft kan de beelden dan gezamenlijk laden wanneer ze nodig zijn:

Zo doen we dit bij Luremo: het blogoverzicht vraagt maximaal twaalf artikelen per pagina op en laadt hun afbeeldingen vooraf met .with(['image']). In de for-loop die artikelen naar cards mapt, gebruikt article.image.first() de vooraf geladen relatie. Daardoor is er geen extra image query per artikel.

Twig · Craft 5
{% set articles = craft.entries().section('blog').limit(12).all() %}

{% for article in articles %}
    {% set image = article.image.eagerly().one() %}
    <article>
        <h2>{{ article.title }}</h2>
        {% if image %}
            {{ image.getImg() }}
        {% endif %}
    </article>
{% endfor %}

Kies bewust tussen .eagerly() en .with()

.eagerly() past goed bij herbruikbare partials: de plek die de relatie gebruikt, geeft zelf aan dat gezamenlijk laden zinvol kan zijn. Weet je in de hoofdquery al zeker welke relaties nodig zijn, dan is .with(['image']) ook een goede keuze. Voor sommige ingebouwde relaties en voor vooraf te laden afbeeldingstransformaties blijft .with() nodig. Beide zijn gereedschap, geen regel die je blind op iedere query plakt.

Hetzelfde geldt voor Matrix: toon je in een lijst zowel geneste blokken als hun afbeeldingen, kijk dan naar beide lagen. Controleer daarna in de Debug Toolbar of het aantal queries en de tijd werkelijk verbeteren. De blog van nystudio107 laat zien waarom meten vóór en na optimalisatie nuttiger is dan op gevoel optimaliseren; gebruik voor de actuele Craft 5-syntax de officiële documentatie.

Zo doen we dit bij Luremo: in _patterns/matrix/cards.twig gebruiken we block.cardItems.eagerly().all() voor de geneste kaarten en card.image.eagerly().one() voor hun beelden. Het blogoverzicht gebruikt juist .with(['image']), omdat daar al bij de hoofdquery vaststaat dat iedere kaart een beeld kan tonen.

Cache de artikelpagina en ververs na een wijziging

Ook een artikelpagina kan meerdere relaties, Matrix-blokken en afbeeldingen renderen. In dit project staat het Twig-cacheblok in de gedeelde entrytemplate. De sleutel combineert entry.id met entry.dateUpdated.timestamp. Zodra de artikelentry wordt aangepast en opnieuw opgeslagen, verandert de datum en gebruikt Craft een nieuwe sleutel. De volgende aanvraag bouwt de artikeluitvoer opnieuw op.

De Matrix-query staat binnen het cacheblok. Daardoor ziet Craft ook welke geneste elementen bij de uitvoer horen en kan het de fragmentcache ongeldig maken als die inhoud wijzigt. Het vereenvoudigde voorbeeld hieronder volgt de opzet van deze site: de entry is al door Craft geladen, de blokken worden eager-loaded en een patroon rendert de inhoud. Er wordt geen blogoverzicht opgehaald of gecachet.

Houd formulieren, CSRF-tokens en gebruikersafhankelijke uitvoer buiten dit blok. Een eigen cachesleutel blijft bestaan nadat templatecode verandert; verhoog dan bewust de sleutelversie of wis de templatecache. Meet of het fragment op jouw pagina voordeel oplevert.

Zo doen we dit bij Luremo: _layouts/entry.twig gebruikt precies deze sleutel voor de artikelinhoud en haalt de Matrix-blokken binnen het cacheblok op. In de lokale ontwikkelomgeving staat templatecaching uit; het deployscript wist de templatecache na craft up, zodat gewijzigde Twig-code direct zichtbaar wordt.

Twig · artikelcache
{% cache using key 'pages:' ~ entry.id ~ ':' ~ entry.dateUpdated.timestamp %}
    <h1>{{ entry.title }}</h1>
    {% set blocks = entry.matrix.eagerly().all() %}
    {% include '_patterns/matrix.twig' with {
        entry: entry,
        blocks: blocks,
    } only %}
{% endcache %}

Lever afbeeldingen op het formaat van hun plek

Een grote upload hoeft niet op iedere kaart in origineel formaat naar de browser. Gebruik image transforms voor de uitsnede en afmetingen die het ontwerp nodig heeft, en bied met srcset passende varianten aan. Geef afbeeldingen een zinvolle alt-tekst wanneer ze informatie overbrengen; voor puur decoratief beeld kan die leeg blijven. Reserveer breedte en hoogte waar mogelijk, zodat de pagina niet verspringt terwijl het beeld laadt.

De eerste aanvraag van een nieuwe transform kan extra werk vragen. Test daarom ook een koude pagina en de achtergrondwachtrij na een release. Een afbeelding die snel laadt in je eigen browser na een warm cachebezoek vertelt niet het hele verhaal.

Zo doen we dit bij Luremo: _patterns/entry-image.twig kiest aparte transformaties voor mobiel, tablet en desktop. _patterns/image.twig levert srcset, sizes, afbeeldingsafmetingen en alt-tekst; het gebruikt ook het ingestelde focuspunt voor de uitsnede.

Behandel configuratie als onderdeel van de code

Craft bewaart secties, velden, entrytypes en andere systeeminstellingen in Project Config. Versioneer die YAML samen met templates en composer.lock, en pas wijzigingen via Craft toe. Bewerk de gegenereerde YAML niet handmatig. Bij een deployment past php craft up migraties en openstaande configuratiewijzigingen toe.

Bewaar wachtwoorden, sleutels en omgevingsafhankelijke URL's in omgevingsvariabelen. Zo kun je dezelfde code naar test en productie brengen zonder geheimen in Git te zetten. Plan een contentwijziging apart van een schemawijziging: Project Config verplaatst geen artikelen of uploads mee.

Zo doen we dit bij Luremo: de schema-instellingen staan in config/project/ en de PHP-afhankelijkheden in composer.lock. Bij een deployment installeert .docker/provision.sh eerst de vastgelegde afhankelijkheden en voert daarna php craft up --interactive=0 uit.

Maak veiligheid en onderhoud een vaste routine

Een goede release eindigt niet bij een werkende homepage. Houd Craft, plugins, PHP en de server actueel; maak en controleer back-ups; geef beheerders alleen de rechten die ze nodig hebben. Craft adviseert in productie devMode uit te zetten, allowAdminChanges uit te schakelen en alleen de publieke map als webroot te gebruiken. Beveilig het beheerscherm met HTTPS en houd geheimen buiten publiek bereik. Dit zijn onderdelen van Crafts beveiligingsrichtlijnen.

Leg vast wie een beveiligingsupdate beoordeelt, wie hem uitrolt en hoe je een mislukte release terugdraait. Controleer na iedere wijziging ten minste een artikelpagina, een overzicht, afbeeldingen, formulieren en de wachtrij. Zo wordt ‘onderhoudbaar’ een proces in plaats van een belofte.

Zo doen we dit bij Luremo: config/general.php zet devMode en allowAdminChanges alleen aan in de ontwikkelomgeving. Updates verlopen via Composer en alleen public/ is de webroot. De wachtrij draait in een aparte container in plaats van tijdens een paginaverzoek.