Galerie in Craft: 439 SQL-Abfragen für eine Seite
Die Galerieseite mit 82 Bildern, jedes mit srcset aus vier Varianten, hatte in der ersten Fassung 439 SQL-Befehle und eine TTFB von 106 ms, mehr als WordPress mit Elementor. .eagerly() lädt die Assets mit einer Abfrage, aber $asset->getUrl('transform') sucht die fertige Variante in der Tabelle imagetransformindex mit einer eigenen Abfrage für jedes Bild und jede Variante. Eine Zeile im Template lädt den Variantenindex für die ganze Galerie auf einmal:
{%- set images = block.images.eagerly().all() -%}
{%- do craft.app.imageTransforms.eagerLoadTransforms(images, ['medium', 'large', 'thumbnail', 'mlarge']) -%}
WordPress hat in derselben Galerie seine eigene Form des N+1-Problems: zwei Abfragen pro Bild (SELECT * FROM wp_posts WHERE ID = … und eine Abfrage der Metadaten). Ein Vorladen der Galeriebilder senkt sie im Test, ohne das Theme des Kunden zu ändern:
add_action( 'elementor/frontend/widget/before_render', function ( $widget ) {
if ( 'theme-gallery' === $widget->get_name() ) { // Name des Galerie-Widgets im Theme
_prime_post_caches( wp_list_pluck( (array) $widget->get_settings( 'gallery' ), 'id' ), false, true );
}
} );
| Galerie mit 82 Bildern (Website 3) | TTFB | SQL-Befehle |
| WordPress + Elementor, wie übergeben | 66,8 ms | 260 |
| WordPress, Bilder vorgeladen (Test) | 61,7 ms | 98 |
| Craft, erste Fassung des Templates | 106,0 ms | 439 |
| Craft, Varianten mit einer Abfrage | 58,2 ms | 33 |
Auf Website 1 dasselbe: die Projektliste von 67 auf 34 Befehle, die Galerie von 62 auf 33. Das Template „funktioniert“ in beiden Systemen auch ohne das.
Dieselbe Qualität, andere Bytes
Varianten aus denselben WebP-Originalen, in denselben Maßen und mit derselben Qualität (80 und 82), sind in Craft 8 bis 11 % größer (Website 1: 4,3 und 4,8 MB für alle Varianten, Website 3: 47,8 und 51,6 MB). WordPress verkleinert mit dem Filter Triangle und leichtem Nachschärfen, Craft (Bibliothek Imagine) mit dem Standardfilter von Imagick. Dazu komprimiert Craft eine Datei, die nicht über das Control Panel hochgeladen wird, standardmäßig neu: Alle 32 Originale von Website 1 hatten nach dem Import andere Bytes als in WordPress, bis wir das abgeschaltet haben:
$asset->sanitizeOnUpload = false; // Dateien aus WordPress sind bereits bereinigt; sonst schreibt Craft das WebP neu
Imagick passt nicht ins memory_limit
Der Speicher von ImageMagick zählt nicht zum PHP-Limit und wird in einem Prozess zwischen den Bildern nicht freigegeben. Der Import von 308 Bildern und der erste Aufruf der Galerie (Varianten beim Anzeigen erzeugt, Option generateTransformsBeforePageLoad) endeten im Container mit 512 MB mit einem hart beendeten Prozess, ohne Antwort an curl:
exit 137
Bei rund 1 100 Varianten in einem Prozess meldete ImageMagick:
cache resources exhausted ... @ error/cache.c/OpenPixelCache/3931
WordPress erzeugt die Bildgrößen beim Hochladen, ein Bild pro Anfrage. In Craft erzeugen wir die Varianten vorab mit einem eigenen Befehl in einem einmaligen CLI-Container mit höherem Limit:
docker run --rm -m 2g ... cs-cms-php:8.3 craft site/import/transforms # einmaliger CLI-Container, dasselbe Image
WordPress-Theme ohne Elementor: fataler Fehler
Bei der Messung „WordPress ohne Plugins“ lieferte Website 1 den Status 500:
PHP Fatal error: Uncaught Error: Class "Elementor\Plugin" not found in /var/www/html/wp-content/themes/<theme>/inc/elementor.php:161
Schuld war der Hook wp_print_footer_scripts, der das JavaScript von Elementor auf Seiten ohne Interaktion abschaltet. Ein Theme, das für einen Page Builder gebaut ist, hängt enger an ihm, als das Backend erkennen lässt; für die Messung haben wir diesen einen Hook mit einer temporären Datei in mu-plugins entfernt.
Erneutes schema/apply in Craft 5 löschte den Inhalt
Craft 5 speichert Feldwerte in der JSON-Spalte elements_sites.content unter der UID des Feldlayout-Elements, nicht des Feldes selbst. Ein Schemaskript, das bei jedem Lauf neue Layout-Elemente anlegte, „verlor“ nach dem zweiten Durchlauf alle Texte: Sie waren in der Datenbank, aber unter den alten UIDs, und resave/entries machte den Verlust dauerhaft. Die Korrektur: die UIDs der vorhandenen Layout-Elemente übernehmen. Wichtig für alle, die das Schema von Craft im Code halten.
Text aus Elementor ist nicht der Text auf der Seite
Elementor speichert rohes HTML, und WordPress schickt es bei der Ausgabe durch wptexturize: Ein Bindestrich zwischen Wörtern wird zum Halbgeviertstrich (–), gerade Anführungszeichen werden zu typografischen. Ein Import aus den Elementor-Daten hätte andere Zeichen ergeben als auf der Live-Website, deshalb haben wir Texte und Titel von Website 2 aus dem gerenderten HTML übernommen.
Die Suche liefert bei gleichem Inhalt andere Ergebnisse
WordPress sucht per LIKE in post_content, wo Elementor eine HTML-Kopie der Seite samt Alternativtexten und Dateinamen der Bilder ablegt. Craft sucht in einem Wortindex der Felder, die als durchsuchbar markiert sind, und in Craft 5 ist ein Feld standardmäßig nicht durchsuchbar: Die Suche fand nur Titel. Nach dem Einschalten des Index liefert WordPress für dasselbe Wort 6 Seiten, Craft 4; in zwei davon stand das Wort nur in Dateinamen von Bildern.