Shopware Backend - ExtJS-Fehler finden

Shopware Backend – Ext JS Fehlern auf die Spur kommen

Shopware Backend – Ext JS Fehlern auf die Spur kommen

Zu den mit Abstand schwierigsten Fehlern während der Shopware Entwicklung, zählen die Ext JS Fehler. Der Grund dafür ist, dass die Ausnahmen nur in den seltensten Fällen direkt im eigenen Quellcode auftreten. Typischerweise hat der selbstgeschriebene Ext JS Code eher einen konfigurativen Charakter. Dadurch treten die Fehler, als Folge einer Fehlkonfiguration, mitten im Ext JS Framework auf.

Der Bezug zum eigenen Quellcode ist oft nur schwer nachvollziehbar. In diesem Artikel werden wir eine Reihe von Methoden und Beispielen betrachten mit dem Ziel, genau diesen Bezug herzustellen und den Blick in die richtige Richtung zu lenken.

ext-all-debug.js konfigurieren

Als aller Erstes erleichtern wir uns die Interpretation der Ext JS Sourcen durch den folgenden Schritt:

  • Um lesbare Variablen- und Methodennamen zu erhalten, tauschen wir die, auf Performance optimierte ext-all.js, durch die ext-all-debug.js.
  • Da wir die Konfiguration nur temporär brauchen, machen wir die Anpassung direkt in der Datei „themes\Backend\ExtJs\backend\base\header.tpl“.
  • Im Block „backend/base/header/javascript“ ersetzen wir „ext-all.js“ durch „ext-all-debug.js“.

Stacktrace lesen

Unabhängig davon wie sich ein Ext JS Fehler im Backend äußert (z.B. Anwendung wird nur teilweise geladen, Fehlermeldungs-Popup erscheint, etc.), ist es in den meisten Fällen viel bequemer, diesen in der Entwicklungskonsole anzuschauen. Der unschlagbare Vorteil ist, dass man dort zu den betroffenen Stellen direkt springen und diese analysieren kann.

Hier ein Beispiel:

Uncaught TypeError: c is not a constructor
    at eval (eval at getInstantiator (ext-all-debug.js?201610051330:4904), :3:8)
    at Object.instantiate (ext-all-debug.js?201610051330:4876)
    at Object.create (ext-all-debug.js?201610051330:1840)
    at constructor.createCustomer (
/myShop/backend/Order/load/?f=m/OrderHistory|m/Order|m/Billing|m/Shipping|m/Tax|m/Deb…:2948)
    at constructor.createFilterForm (
/myShop/backend/Order/load/?f=m/OrderHistory|m/Order|m/Billing|m/Shipping|m/Tax|m/Deb…:2939)
    at constructor.createFieldContainer (
/myShop /backend/Order/load/?f=m/OrderHistory|m/Order|m/Billing|m/Shipping|m/Tax|m/Deb…:2685)
    at constructor.initComponent (
/myShop /backend/Order/load/?f=m/OrderHistory|m/Order|m/Billing|m/Shipping|m/Tax|m/Deb…:2669)
    at constructor.callParent (ext-all-debug.js?201610051330:3735)
    at constructor.initComponent (
/myShop /backend/Order/load/?f=m/OrderHistory|m/Order|m/Billing|m/Shipping|m/Tax|m/Deb…:2934)
    at constructor (ext-all-debug.js?201610051330:45265)

“c is not a constructor” hilft zunächst sehr wenig. Welche Zeile kann uns hier nützliche Hinweise liefern? Obwohl die eigentliche Ausnahme in der obersten Zeile stattfindet, werden wir dort unseren Fehler nicht finden. Stattdessen fangen wir die Suche in der ersten Zeile von oben, die nicht aus der „ext-all-debug.js“ kommt und die Folgenden.

In diesem Fall diese hier:

    at constructor.createCustomer (
/myShop/backend/Order/load/?f=m/OrderHistory|m/Order|m/Billing|m/Shipping|m/Tax|m/Deb…:2948)
    at constructor.createFilterForm (
/myShop/backend/Order/load/?f=m/OrderHistory|m/Order|m/Billing|m/Shipping|m/Tax|m/Deb…:2939)
    at constructor.createFieldContainer (
/myShop /backend/Order/load/?f=m/OrderHistory|m/Order|m/Billing|m/Shipping|m/Tax|m/Deb…:2685)
    at constructor.initComponent (
/myShop /backend/Order/load/?f=m/OrderHistory|m/Order|m/Billing|m/Shipping|m/Tax|m/Deb…:2669)

Beim Betrachten dieser Codestellen müssen wir zwei Dinge schaffen:

  1. Das Plugin hinter den Codestellen erkennen. Obwohl die URLs im Stacktrace uns hier keine Hinweise liefern, können wir versuchen, nach den betroffenen Funktions- oder Variablennamen in unserer Entwicklungsumgebung zu suchen.
  2. Die Fehlerursache finden. Da es nicht direkt in einer Ausnahme resultiert, wird der Fehler nicht sofort ersichtlich sein. Wir haben den Suchbereich aber zumindest sehr stark eingegrenzt.

Suchbereich grob eingrenzen

Manchmal kann es aber auch vorkommen, dass wir das fehlerhafte Plugin nicht anhand des Stacktraces identifizieren können. In diesem Fall können wir wie folgt vorgehen:

  1. Wir öffnen den Pluginmanager und deaktivieren alle Plugins, die nicht in der Shopwareinstallation enthalten waren.
  2. Wenn man davon ausgehen kann, dass der Fehler in den eigenen Plugins zu suchen ist, deaktivieren wir nur die Eigenen.
  3. Wenn der Fehler danach verschwunden ist, aktivieren wir ein Plugin nach dem anderen und bestimmen dadurch das Plugin in dem der Fehler auftritt.

JavaScript debuggen.

Wenn der Fehler immer noch nicht ersichtlich ist, können wir uns weitere Informationen durch das Debugging beschaffen. Die Entwicklertools von Chrome (Empfehlung von Shopware, die Chrome-Entwicklertools erscheinen beim Drücken von STRG+UMSCHALT+I) bieten uns hier eine hervorragende Unterstützung.

Über die Zeilen im Stacktrace gelangen wir zu den entsprechenden Stellen im Sourcefile. Das Sourcefile erscheint mit Zeilennummern auf der linken Seite. Durch einen Klick auf die Nummer können wir einen Breakpoint setzen.

Beim erneuten Ausführen der Codestelle bleibt die Anwendung an dieser Stelle stehen. Man hat nun die Möglichkeit, den Zustand aller globalen und lokalen Objekte und Variablen zu analysieren. Des Weiteren kann man mit der Ausführung Schritt für Schritt fortfahren.

Ext JS Fehler, die keine sind

Es kommt aber auch vor, dass man einen Fehler fälschlicherweise für einen Ext JS Fehler hält. Man könnte also, trotz eines 100 Prozent validen Ext JS Codes, diese Fehlermeldung bekommen:

GET http://localhost/myShop/backend/Order/load/?f=m/OrderHistory|…tch|c/Main|c/List|c/Filter|
c/Detail|c/Batch|c/Mail&no-cache=1490786765+1+1 503 (Service Unavailable)
request		@	ext-all-debug.js?201610051330:37622
request		@	base?file=bootstrap&loggedIn=1490786906:1600
(anonymous)	@	base?file=bootstrap&loggedIn=1490786906:466
each		@	ext-all-debug.js?201610051330:2153
iterate		@	ext-all-debug.js?201610051330:127
(anonymous)	@	base?file=bootstrap&loggedIn=1490786906:424
(anonymous)	@	ext-all-debug.js?201610051330:1797
Ext.Loader.require	@	base?file=bootstrap&loggedIn=1490786906:602
(anonymous)	@	ext-all-debug.js?201610051330:1840
(anonymous)	@	base?file=bootstrap&loggedIn=1490786906:2222
doProcess	@	ext-all-debug.js?201610051330:4003
(anonymous)	@	ext-all-debug.js?201610051330:6059
Ext.Loader.require	@	base?file=bootstrap&loggedIn=1490786906:572
(anonymous)	@	ext-all-debug.js?201610051330:6026
doProcess	@	ext-all-debug.js?201610051330:4003
doProcess	@	ext-all-debug.js?201610051330:4004
process		@	ext-all-debug.js?201610051330:3991
ExtClass	@	ext-all-debug.js?201610051330:3919
create		@	ext-all-debug.js?201610051330:4689
define		@	ext-all-debug.js?201610051330:5112
(anonymous)	@	/Renz/Entwicklung/php/backend/Order?file=app&no-cache=1490786765+1+1:1
(anonymous)	@	ext-all-debug.js?201610051330:528
Ext.globalEval	@	ext-all-debug.js?201610051330:529
success		@	base?file=bootstrap&loggedIn=1490786906:477
callback	@	ext-all-debug.js?201610051330:6455
onComplete	@	ext-all-debug.js?201610051330:38086
onStateChange	@	ext-all-debug.js?201610051330:38047
(anonymous)	@	ext-all-debug.js?201610051330:1816

Bei den vielen „ext-all-debug.js…“-Meldungen könnte man leicht die Meldung “503 (Service Unavailable)” übersehen und im schlimmsten Fall Stunden auf die Suche nach einem Fehler in seinen Ext JS-Klassen verwenden.

Die Meldung „503 (Service Unavailable)“ deutet daraufhin, dass bereits auf dem Server etwas schief gelaufen ist. Deshalb öffnen wir den „Network“-Tab in den Chrome Entwicklungstools, in dem alle Requests aufgelistet sind. Innerhalb dieser Liste muss nun nach dem Request gesucht werden, der die fehlerhafte URL enthält:

<h2><span class="frontend_error_exception">Ups! Ein Fehler ist aufgetreten!</span></h2>
<p>
<span class="frontend_error_exception">Die nachfolgenden Hinweise sollten Ihnen weiterhelfen.</span>
</p>
<h3>Syntax Error in template &quot;C:\xampp\htdocs\myShop\engine\Shopware\Plugins\Local\Backend\MyPlugIn\
Views\backend\order\view\list\MyExtendedOrderFilter_list.js&quot;  on line 43 &quot;
if(r){return r.data.documentId;&quot;  - 
Unexpected &quot;.&quot;, expected one of: &quot;}&quot; , &quot; &quot; , ATTR in C:\xampp\htdocs\myShop\
engine\Library\Smarty\sysplugins\smarty_internal_templatecompilerbase.php on line 657</h3>
<h3>Stack trace:</h3>
<div style="overflow:auto;">
…

Hier ist es wichtig zu erkennen, dass die Fehlermeldung von der Smarty-Engine kommt. Alle Smarty-Blöcke fangen mit einer geschweiften Klammer, gefolgt von einem Bezeichner an. Wenn wir also eine, syntaktisch und semantisch völlig korrekte Javascript-Anweisung, wie diese haben:

if(r){return r.data.documentId;}

Dann wird die Anweisung “{return r.data.documentId;}” als ein Smarty-Block interpretiert, was zu dem genannten Fehler führt. Die Lösung ist denkbar einfach – nach der geschweiften Klammer ein Leerzeichen, oder einen Zeilenumbruch einfügen.

Ext JS Fehler, die nicht danach aussehen.

Je nachdem, welche Fehlermeldung (vom Client oder vom Server) man liest, kann auch der umgekehrte Fall auftreten:

<h2><span class="frontend_error_exception">Ups! Ein Fehler ist aufgetreten!</span></h2>
<p>
<span class="frontend_error_exception">Die nachfolgenden Hinweise sollten Ihnen weiterhelfen.</span>
</p>
<h3>Unable to load template snippet 'backend/order/view/list/liste.js|
backend/order/payment_methods/controller/detail.js|
backend/order/payment_methods/view/detail/payment_methods.js|backend/order/app.js|
backend/order/view/list/extended_filter/filter.js|backend/order/view/list/ExtendedOrderFilter_list.js|
backend/order/model/ExtendedOrderFilter_order.js|backend/order/controller/float_quantity/detail.js|
backend/order/controller/float_quantity/batch.js|backend/order/model/float_quantity/position.js|
backend/order/view/detail/float_quantity/position.js' 
in C:\xampp\htdocs\myShop\engine\Library\Smarty\sysplugins\smarty_internal_templatebase.php on line 127</h3>
<h3>Stack trace:</h3>
<div style="overflow:auto;">
<pre>#0 C:\xampp\htdocs\myShop\engine\Library\Enlight\View\Default.ph(274):
Smarty_Internal_TemplateBase-&gt;fetch()
…

Hier sehen wir eine Fehlermeldung, die vom Server kommt, was den Verdacht von Ext JS schon mal weglenkt. Zusätzlich besagt die Meldung, dass Templates nicht geladen werden konnten. Die Vermutung wäre hier, dass die Templates in der Bootstrap.php falsch eingebunden wurden, was den Verdacht auf eine php-seitige Ursache nur zusätzlich erhärten würde. Tatsächlich wird der Fehler aber durch einen Fehler im Ext JS Code verursacht. Bei der Benennung von Ext JS Dateien und den darin enthaltenen Klassen gibt es Konventionen, die eingehalten werden müssen. Was uns bei der oberen Fehlermeldung hätte auffallen sollen ist der folgende Ausschnitt:

<h3>Unable to load template snippet 'backend/order/view/list/liste.js

Die Anwendung versucht ein Template zu laden, das es nicht gibt. Dieser konkrete Fall entstand durch den folgenden Vertipper in unserem Ext JS Code:

   requires: [ 'Shopware.apps.Order.view.list.Liste' ],

Die Klasse “Shopware.apps.Order.view.list.Liste” gibt es nicht, und deshalb konnte auch das entsprechende Template nicht geladen werden.

Um diesen Fehler zu vermeiden/korrigieren müssen zum einen die JS-Dateinamen und zum anderen die Angaben in „requires“, „override“ und „define“ den oben erwähnten Konventionen entsprechen.