Shopware Sortierung im Listing erweitern

Sortierung erweitern

Sortierung erweitern

Sortierung für Shopware-Frontend erweitern.

Während man in Shopware 4 über die Einstellungen im Backend kleine SQL Snippets für die Standardsortierung angeben konnte, ist es in Shopware 5 komplett weggefallen. Mit Shopware 5 ist die Anpassung der Sortierung zwar komplexer und aufwändiger geworden, dafür aber auch sauberer.

In diesem Artikel betrachten wir anhand des „Artikelnummer“-Beispiels, wie man die Sortierung um weitere Attribute erweitern kann.

Was ist zu tun?

Wir erstellen ein PlugIn, das im Grunde zwei Aufgaben erfüllen wird:

  • Die erste und weitaus einfachere Aufgabe ist die UI-Anpassung. Die Sortierung-Combobox wird um die Option „Artikelnummer“ erweitert.
  • Die zweite, deutlich komplexere Aufgabe ist es Shopware beizubringen, wie es mit der neuen Sortieroption umgehen soll.

UI-Anpassung

Nachdem wir die Grundstruktur unseres Plugins erstellt haben, passen wir die „install“-Methode, wie folgt an:

/**
 * Plugin install function which registers all required Shopware events.
 * @return bool
 */
public function install() {
  //UI erweitern
  $this->subscribeEvent(
    'Enlight_Controller_Action_PostDispatch_Frontend_Listing',
    'onFrontendPostDispatchListing '
  );
  return true;
}

Damit haben wir uns bei dem „Listing“-Event mit der Methode „onFrontendPostDispatchListing“ registriert. Die Methode macht nichts Anderes als ein Smarty-Template zu registrieren, in dem wir unsere UI-Anpassung vornehmen:

public function onFrontendPostDispatchListing(Enlight_Event_EventArgs $arguments ) {
  $controller = $arguments->getSubject();
  $view = $controller->View ();
  $view->addTemplateDir ( $this->Path() . 'Views/' );
  $view->extendsTemplate('frontend/listing/sortByArticleNumberExtension.tpl' );
}

Im „sortByArticleNumberExtension.tpl“ erweitern wir den Block „frontend_listing_actions_sort_field_name“ und fügen die neue Option hinzu:

{block name="frontend_listing_actions_sort_field_name" append}
  <option value="100"{if $sSort eq 100} selected="selected"{/if}>{s name='ListingSortArticleNumber'}Artikelnummer{/s}
  </option>
{/block}

Der Wert 100 ist zufällig gewählt. Es kann eine beliebige andere Zahl genommen werden, Hauptsache sie ist noch nicht durch eine andere Option in Verwendung. Damit haben wir den ersten Teil der Aufgabe erfüllt.

Sortierung serverseitig implementieren

Kommen wir nun zum spannenden Teil – die serverseitige Implementierung. Was wir bisher erreicht haben, ist, dass der Wert 100 (siehe neue Combobox-Option), per Request an den Server geschickt wird, wenn die Option „Artikelnummer“ im Frontend ausgewählt wird. Wie geht Shopware damit um?

Shopware durchsucht seine Registry nach einem sogenannten „Request-Handler“, der die „Sortierung 100“ behandeln kann. Natürlich wird Shopware einen solchen Request-Handler nicht finden. Wir müssen also einen passenden Request-Handler implementieren und danach registrieren.

Request-Handler implementieren

Die Aufgabe des Request-Handlers ist es also den Request zu analysieren und eventuell darauf zu reagieren. In diesem Fall muss der Handler den Request mit dem Wert 100 verarbeiten:

class SortByArticleNumberRequestHandler implements CriteriaRequestHandlerInterface {
  const SORTING_ARTICLE_NUMBER_ASC = 100;
  
  public function handleRequest(Request $request, Criteria $criteria, ShopContextInterface $context) {
    $sort = $request->getParam ( 'sSort', $defaultSort );
    if($sort == self::SORTING_ARTICLE_NUMBER_ASC) {
      $criteria->addSorting( new ProductNumberSorting() );
    }
  }
}

Wie man hier sieht, muss der Handler den Interface „CriteriaRequestHandlerInterface“ und die Methode „handleRequest“ implementieren. Die Methode „handleRequest“ prüft ob der Request den Wert 100 liefert und falls ja, fügt es einen neuen Sortierer „ProductNumberSorting“ als Criteria hinzu. Der „Sortierer“ selbst ist ein einfaches Objekt, das zunächst gar keine Logik beinhaltet:

class ProductNumberSorting implements SortingInterface {
  public function getName() {
    return 'ProductNumber';
  }
}

Was wir jetzt erreicht haben, ist, dass Shopware nun weiß wie der Sortiert-Request 100 zu behandeln ist. Es fehlt aber noch ein weiterer Handler. Wie wir oben gesehen haben, beinhaltet der Sortierer „ProductNumberSorting“ keine Logik. Shopware verwendet sogenannte Sorting-Handler, die die eigentliche Sortierlogik beinhalten.

Shopware wird also in seiner Registry nach einem Sorting-Handler suchen, der „ProductNumberSorting“ behandeln kann. Einen solchen Sorting-Handler wird Shopware natürlich nicht finden können, weil wir „ProductNumberSorting“ gerade eben erst implementiert haben. Also müssen wir noch einen passenden Sorting-Handler implementieren und diesen Shopware bekannt machen (regiestrieren).

Sorting-Handler implementieren

Wir brauchen also einen Sorting-Handler, der den „ProductNumberSorting“-Sortierer behandeln kann und der die eigentliche Sortierlogik implementiert:

class SortByArticleNumberSortingHandler implements SortingHandlerInterface {
  public function supportsSorting(SortingInterface $sorting) {
    return ($sorting instanceof ProductNumberSorting);
  }
  
  public function generateSorting(SortingInterface $sorting, QueryBuilder $query, ShopContextInterface $context) {
    $query->addOrderBy ( 'variant.ordernumber', 'ASC' );
  }
}

Der Sorting-Handler muss den „SortingHandlerInterface“ implementieren. In der Methode „supportsSorting“ geben wir an, dass unser Handler „ProductNumberSorting“ behandelt und in der „generateSorting“ definieren wir die SQL-Order-by-Anweisung. Artikelnummer kommt aus der Tabelle „s_articles_details“ und Spalte „ordernumber“. Vielleicht fragen Sie sich aber, woher das Präfix „variant“ kommt. Dazu müssen wir uns die „$query“ aus der Methode „generateSorting“ ausgeben lassen:

SELECT  FROM s_articles product 
INNER JOIN s_articles_details variant ON variant.id = product.main_detail_id
                 AND variant.active = 1
                 AND product.active = 1 
INNER JOIN s_core_tax tax ON tax.id = product.taxID 
INNER JOIN s_articles_categories_ro productCategory ON productCategory.articleID = product.id
            AND productCategory.categoryID IN (:category) 
LEFT JOIN s_articles_avoid_customergroups avoidCustomerGroup ON avoidCustomerGroup.articleID = product.id
             AND avoidCustomerGroup.customerGroupId IN (:customerGroupIds) 
INNER JOIN s_articles_attributes productAttribute ON productAttribute.articledetailsID = variant.id
WHERE avoidCustomerGroup.articleID IS NULL 

"variant" ist also ein Kürzel für die Tabelle „s_articles_details“.

Handler Registrieren

Wir haben unsere beiden Handler implementiert. Jetzt müssen wir sie noch Shopware bekanntmachen. Dazu erweitern wir die „install“-Methode um Folgende zwei Eventlistener:

$this->subscribeEvent('Shopware_SearchBundle_Collect_Criteria_Request_Handlers', 'addCriteriaRequestHandler' );
$this->subscribeEvent('Shopware_SearchBundleDBAL_Collect_Sorting_Handlers', 'addCriteriaSortingHandler' );

Die Methoden “addCriteriaRequestHandler” und “addCriteriaSortingHandler” müssen einfach ein entsprechendes Handler-Objekt zurückgeben:

public function addCriteriaRequestHandler() {
  return new SortByArticleNumberRequestHandler ();
}
  
public function addCriteriaSortingHandler() {
  return new SortByArticleNumberSortingHandler ();
}

Damit haben wir die UI angepasst und zwei Handler implementiert und registriert. Das PlugIn kann nun installiert und das Ergebnis in Frontend Listing angeschaut werden.