Eigene SQL-Sortierung in Product Repository

Ich möchte in einem Plugin eine hartkodierte Liste an Produkt-Nummern zur Sortierung einer Produktliste übergeben. Normalerweise würde man einem Criteria ein FieldSorting übergeben. Das funktioniert aber soweit ich weiß nur mit definierten Feldnamen, wie zB „productNumber“. Ich würde da gern etwas SQL Code übergeben (CASE ... END), was aber so nicht funktioniert:

$hardCodesProductNumbers = ['123','ABC'];
$criteria = (new Criteria())
	->addFilter(new EqualsFilter('active', '1'))
	->addFilter(new EqualsAnyFilter('productNumber', $hardCodesProductNumbers));
$criteria->addSorting(new FieldSorting('CASE productNumber WHEN "123" THEN 1 WHEN "ABC" THEN 2 ELSE 3 END', FieldSorting::ASCENDING));
$products = $this->productRepository->search($criteria, $context);

Hier steht wohl beschrieben wie man eine eigene Sortierung erzeugt:

Aber so eine Sortierung würde dem Benutzer auch per Dropdown angeboten, was ich gar nicht will. Stattdessen will ich die Sortierung nur einmal intern verwenden. Meine hartkodierte Liste ändert sich häufiger, daher sollte die Sortierung auch nicht gespeichert werden.

Es gibt nur einen Sortierungs-Typ in core/Framework/DataAbstractionLayer/Search/Sorting. Dieser Sortiert nur nach einzelnen Feldern.

Ich würde an deiner Stelle ein numerisches custom field in der products entity anlegen. Dieses füllst du dann mit Werten entsprechend der Priorität und kannst es dann mit dem einfachen FieldSorting sortieren. Ist was die Performance angeht auf jeden Fall besser.

1 „Gefällt mir“

Diese hartkodierte Liste ändert sich häufig. Das würde bedeuten dass ich die Werte erst zur Laufzeit in ein Custom Field schreiben muss, um danach zu sortieren.

Kann ich ansonsten das Ergebnis (also ein EntitySearchResult) im Nachhinein noch sortieren? Habe mal versucht auf ein array zu casten und dann per usort()… aber als Array lässt sich das nicht mehr in Twig’s verwenden.

Wenn die Liste nicht besonders groß ist, kannst die die auch loopen und einzelnd nacheinander suchen, dann wären sie wie die Ausgangsliste sortiert.

Außerdem ist EntitySearchResult.elements doch ein Array was man sortieren kann?

Oder du kannst das ScoreQuery benutzen um den Ergebnissen einen Score zu geben. Aber da weiß ich nicht ob das gut performt oder wie genau das ist. In diesem Beispiel hat es funktioniert:


        $hardCodesProductNumbers = ['511090138','511280129','511480038'];
        $criteria = (new Criteria())
            ->addFilter(new EqualsFilter('active', '1'))
            ->addFilter(new EqualsAnyFilter('productNumber', $hardCodesProductNumbers));

        $criteria->addQuery(
            new ScoreQuery(new ContainsFilter('productNumber', '511480038'), 3000)
        );
        $criteria->addQuery(
            new ScoreQuery(new ContainsFilter('productNumber', '511280129'), 2000)
        );
        $criteria->addQuery(
            new ScoreQuery(new ContainsFilter('productNumber', '511090138'), 1000)
        );

        $products = $this->productRepository->search($criteria, $context);

Der ScoreQuery scheint bei mir auch zu funktionieren, und ist mit wenig Aufwand generiert. Die Performance ist bei mir nicht messbar unterschiedlich zur Abfrage ohne ScoreQuery.

Werde das mal beobachten, sieht aber schon mal nach einer guten Lösung aus. Vielen Dank!

Also Array-Sortierung war eigentlich auch ne gute Idee, denn EntitySearchResult hat u.a. eine sort() Methode der man ein Closure übergeben kann:

$hardCodesProductNumbers = ['123','ABC'];
$criteria = (new Criteria())
	->addFilter(new EqualsFilter('active', '1'))
	->addFilter(new EqualsAnyFilter('productNumber', $hardCodesProductNumbers));
$products = $this->productRepository->search($criteria, $context);
$sortClosure = function($a, $b) use($hardCodesProductNumbers): int {
	$idxA = array_search($a->productNumber, $hardCodesProductNumbers);
	$idxB = array_search($b->productNumber, $hardCodesProductNumbers);
	if($idxA == $idxB) {
		return 0;
	}
	else {
		return ($idxA < $idxB) ? -1 : 1;
	}
};
$products->sort($sortClosure);
1 „Gefällt mir“

nice, gut zu wissen!