Shopware

Höhere Produktivität durch LLM am Arsch

Das unterstreicht auch wieder meine Ansicht, dass LLM beim Entwickeln die Produktivität keinesfalls steigert: Vibecoding: nothing more than meowing nuns

Junge! Junge! Vergangene Woche hatte ich auch wieder so eine Sache, bei der mich drei verschiedene LLM in den Wahnsinn getrieben haben: Ich wollte einfach nur wissen, wie ich unter Shopware SEO-URLs für eine vierte Root-Navigation bekomme, wenn diese nicht als einer der drei Einstiegspunkte in einem SalesChannel hinterlegt ist.

Neben der nicht programmatischen Antwort, einfach einen weiteren – unbenutzten – SalesChannel anzulegen und die Navigation dort einzutragen, kamen natürlich auch diverse Vorschläge, das Ganze mit ein wenig Code zu lösen.

Ganz entzückend, wenn der Agent dabei auf Events und Interfaces zugreift, die nie und zu keiner Version jemals existiert haben. Nach der frustrierten Anmerkung, dass es das nicht gibt, kamen im besten Fall wieder Lösungsansätze mit halluzinierten Komponenten heraus. Sehr knuffig fand ich auch den Hinweis, dass ich das einfach mit dem Command sales-channel:add-category machen könnte. Unnötig zu erwähnen, dass es das Command nicht gibt. Oder?

Wie auch immer. In den gut anderthalb Stunden hat mir kein LLM eine brauchbare Lösung anbieten können. Und ich musste doch wieder selbst denken. Also dank LLMs wieder Zeit in den Wind gerotzt, die ich hätte sinnvoller nutzen können.

Überschreiben existierender Shopware Storefront Javascript Plugins

Trotz der Info Each JavaScript plugin can only be overridden once. If two Shopware plugins try to override the same plugin, only the last one of them will actually work. (Override Existing Javascript) hat mich irgendein Gedanke geritten, das könnte mehrfach gemacht werden.

Und so wollte ich einfach das OffCanvasCartPlugin aus der Storefront ein klein wenig erweitern und die Eingabe einer Postleitzahl im Offcanvas Cart ermöglichen - manchmal zur Lieferkostenberechnung unabdingbar – und dabei die Infrastruktur zum Updaten von Feldern nutzen, die das Plugin schon mitbringt. Also einfach kurz das src/plugin/offcanvas-cart/offcanvas-cart.plugin extended, meine paar Methoden hinzugefügt und fertig. Dachte ich.

PluginManager.override('OffCanvasCart', 
    ZipExtensionOffcanvasCartPlugin, '[data-offcanvas-cart]'
);

Ich war wohl nicht der Einzige, der das dachte. Beispielsweise im SwagAmazonPay Plugin wird das Offcanvas Dingelchen auch schon überschrieben. Und gemäß dem Motto "Die Letzten werden die Ersten sein" gewinnt natürlich das Plugin, das die letzte Überschreibung durchführt. Das war nicht meins.

Letztlich bleibt einem nichts anderes übrig, die eigene Erweiterung als neues Plugin zu registrieren. Allerdings muss man dann auch darauf achten, indem selbst registrierte Plugins nicht noch einmal alle Events erstellt werden, sodass der Offcanvas zweimal geöffnet wird etc. In diesem speziellen Fall mit dem Offcanvas Plugin war das einfach, indem ich die init() überschrieben habe, ohne einen Aufruf von super.init()

export default class ZipExtensionOffcanvasCartPlugin
        extends OffCanvasCartPlugin {
    init() {
        this.client = new HttpClient()
        this._registerZipTriggerEvents()
    }

    _registerZipTriggerEvents() {
        this.$emitter.subscribe('offCanvasOpened', () => {
            const forms = DomAccess.querySelectorAll(
                document, '.js-offcanvas-cart-add-zip', false
            )
            if (forms) {
                Iterator.iterate(forms, 
                    form => form.addEventListener(
                        'submit', this._onAddZip.bind(this))
                    )
            }
        });
    }

    _onAddZip(event) {
        event.preventDefault()
        const form = event.target
        const selector = '.js-offcanvas-cart-zip'

        this.$emitter.publish('onAddZip')

        super._fireRequest(form, selector)
    }
}

Die Zip in die ShippingLocation über einen SalesChannelContextFactoryDecorator in den SalesChannelContexts zu bekommen und der vernünftige Umgang damit im Checkout war dann im Vergleich zu dem Javascript-Theater der wesentlich entspanntere Teil des Ganzen. Und ist vielleicht später mal einen Post wert.

Unknown database type enum requested

Bei der Installation einer Shopware App, die Entities (Resources/entities.xml) enthält, wird mir bei Shopware 6.4 mitunter ein lapidares Unknown database type enum requested, MariaDb1027Platform may not support it. auf der Konsole entgegen gehustet. Das tritt auf, wenn man MariaDB benutzt und Plugins installiert sind, die Datenbanktabellen erstellen, die Felder des Typs EUNUM haben. Shopware scheint sich hier wohl Doctrine zu bedienen, um Entities aus der XML Datei der App anzulegen.

Wenn es sich um eine fremdes Plugin mit den betroffenen Tabellen handelt, mache ich es mir idR. einfach und wandle die ENUM Spalten in ausreichend große VARCHAR Spalten um. Und dann klappt auch der Spaß mit der App Aktivierung.

Es ist per SQL Query relativ einfach die "schuldigen" Tabellen und Spalten zu finden:

select col.table_schema as database_name,
     col.table_name,
     col.ordinal_position as column_id,
     col.column_name,
     col.data_type,
     trim(leading 'enum' from col.column_type) as enum_values
from information_schema.columns col
join information_schema.tables tab 
    on tab.table_schema = col.table_schema
        and tab.table_name = col.table_name
        and tab.table_type = 'BASE TABLE'
where col.data_type in ('enum')
    and col.table_schema not in (
        'information_schema', 
        'sys',
        'performance_schema', 
        'mysql'
    )
    and col.table_schema = 'Meine_Shopware_Datenbank' 
order by col.table_schema,
    col.table_name,
    col.ordinal_position;

Meine_Shopware_Datenbank muss natürlich durch den entsprechenden Namen der Shopware DB ausgetauscht werden.