Listing mit Infinite Scrolling auf den View zugreifen geht nicht mehr ab der 5.3 RC1

Hallo Community,

ich habe bisher mit dem subscribe auf Widgets Listing einen weiteren Wert aus der Datenbank jedem Artikel in einem Listing zugewiesen. Das hat bis SW 5.3 auch super geklappt, sodass auch bei nachgeladenen Elementen der zusätzliche Wert im Template assigned und im Listing je Artikel genutzt werden konnte - also auch für die nachgeladenen Artikel beim infinite Scrolling.

 

Das hier hat bis 5.3 funktioniert:

$this->subscribeEvent(
    'Enlight_Controller_Action_PostDispatchSecure_Widgets_Listing',
    'addXY'
);

public function addXY(Enlight_Event_EventArgs $arguments)
    {

		$subject = $arguments->getSubject();

        $view = $subject->View();

        $sArticles = $view->getAssign('sArticles');

        if(is_array($sArticles)) {
            foreach ($sArticles as &$article) {
                $sqlArticleId = $article['articleID'];
                $sql = "SELECT xy FROM s_articles_details WHERE articleID = $sqlArticleId";
                $fields = Shopware()->Db()->fetchOne($sql);
                $article['xy'] = $fields;
            }
            unset($article);
        }

        $view->assign('sArticles', $sArticles);

    }

 

Mit der Version 5.3 RC1 funktioniert das nun nicht mehr. Ich habe bisher herausgefunden, dass ich auf ein weiteres Event subscriben muss, bzw. auf die Action davon um auch für die nachgeladenen Elemente den neuen Wert zur Verfügung zu stellen. Folgender subscribe Funktioniert für alle nachgeladenen Objekte:

$this->subscribeEvent(
    'Enlight_Controller_Action_Widgets_Listing_listingCount',
    'addXY'
);

Leider komme ich aber nun mit obigem Code nicht mehr an die Elemente. Der zusätzliche Wert wird nicht assigned und ich kann mir auch nicht den view bzw. $sArticles ausgeben lassen. Folgender Code erzeugt einen Fehler in der Console: Uncaught TypeError: Cannot read property ‘trim’ of undefined

public function addXY(Enlight_Event_EventArgs $arguments)
    {

		$subject = $arguments->getSubject();

		$view = $subject->View();

		$sArticles = $view->getAssign('sArticles');

		echo 'Ausgabe:';
		print_r($sArticles);
		echo '';
		die('');

	}

 

 

Hat jemand eine Idee oder Rat?

 

 

// Edit: Mir ist auch aufgefallen, dass bei 5.3 keine Template Vars mehr ausgeben werden, nach dem im Infinite Scrolling des Listings die weiteren Artikel geladen wurden:

Ich habe mal nach dem Fehler gesucht (Uncaught TypeError: Cannot read property ‚trim‘ of undefined). Das besagt im Grunde, dass ich eine Variable printen will, die es so nicht gibt. 

Das sagt mir also, dass ich aus dem Event _Enlight_Controller_Action_Widgets_Listing_listingCount _nicht auf die view zugreifen kann, bzw. nichts assigned wird. Dafür spricht auch die Ausgabe aus Firephp, wo zu sehen ist, dass keine neue Variablen assigned werden, nachdem eine weitere Seite / weitere Artikel im Infinite Scrolling geladen wurden.

Wieso macht Shopware das jetzt so und wie komme ich jetzt bloß an die Artikel heran  Thumb-down

 

 

//Edit: in der listingCountAction() steht als kommentar:

/**
     * Loads the listing count for the provided listing parameters.
     * Sets a json response with: `facets`, `totalCount` and `products`.
     */

heißt das nun, dass die nachgeladenen Artikel nicht mehr wie in allen Version bis 5.3 RC dem view als Objekte assigned werden, sondern nur noch ein langer json string übergeben wird?!

Ich hänge hier mal eine reduzierte Version der Bootstrap.php an. Vll. wird das Problem dann etwas deutlicher: 

Frage: 

  1. Wieso funktioniert die Funktion  addXYListing nicht bei dem Event Enlight_Controller_Action_Widgets_Listing_listingCount?
  2. Weshalb gibt es nach dem Event Shopware_Controllers_Widgets_Listing::listingCountAction keine Template Vars in der Console, sondern nur einen JSON-String (siehe Bilder oben)
  3. Was muss ich tun, damit ich auch bei den nachgeladenen Objekten den Wert an assignen kann?

 

subscribeEvent(
			'Enlight_Controller_Action_PostDispatchSecure_Frontend_Listing',
			'onFrontendPostDispatch'
		);

		/* 5.3 init onFrontendPostDispatch on infinite Scroll */
		$this->subscribeEvent(
			'Enlight_Controller_Action_Widgets_Listing_listingCount',
			'onFrontendPostDispatch'
		);


        /* add Event to get article addXY data on listing */
        $this->subscribeEvent(
        	'Enlight_Controller_Action_PostDispatchSecure_Frontend_Listing',
			'addXYListing'
		);

		/* 5.3 - add Event to get article addXY data on infinite Scroll */
		$this->subscribeEvent(
			'Enlight_Controller_Action_Widgets_Listing_listingCount',
			'addXYListing'
		);

        return true;
    }

    /**
    * Plugin activation
     * @return array
     */
    public function enable()
    {
		return ['success' => true, 'invalidateCache' => $this->getInvalidateCache()];
    }

	/* Plugin Deinstallation */
	public function uninstall()
	{

		return ['success' => true, 'invalidateCache' => $this->getInvalidateCache()];
	}

	/**
	 * Helper method to return all the caches, that need to be cleared after uninstalling/enabling/disabling a plugin
	 *
	 * @return array
	 */
	private function getInvalidateCache()
	{
		return ['frontend', 'backend', 'theme'];
	}

    public function addXYListing(Enlight_Event_EventArgs $arguments)
    {

		$subject = $arguments->getSubject();

        $view = $subject->View();

        $sArticles = $view->getAssign('sArticles');

        if(is_array($sArticles)) {
            foreach ($sArticles as &$article) {
                $sqlArticleId = $article['articleID'];
                $sql = "SELECT meta_description FROM s_articles_supplier WHERE articleID = $sqlArticleId";
                $fields = Shopware()->Db()->fetchOne($sql);
                $article['meta_description'] = $fields;
            }
            unset($article);
        }

        $view->assign('sArticles', $sArticles);

	}

    public function onFrontendPostDispatch(Enlight_Event_EventArgs $args)
    {

		/** @var \Enlight_Controller_Action $controller */
		$controller = $args->get('subject');

		$view = $controller->View();

		$view->addTemplateDir(
			__DIR__. '/Views'
		);

	}
}

 

Hi,

mit 5.3 haben wir den neuen Filter modus implementiert.

Damit jedoch Plugin Entwickler sich nicht auf dutzende Routen registrieren müssen um Produkt Listings zu erweitern haben wir hier das Infinite Scrolling und den Listing Count call zusammen gelegt. Das zusammen legen dieser Routen hatte zudem auch Performance Gründe, da sonst für Filter Kombinationen und Listing nachladen zwei Suchen ausgeführt werden müssten.

Damit du die Daten weiterhin erweitern kannst solltest du nach dem neuem Pattern von Shopware 5 arbeiten und den ListProductService dekorieren:

https://developers.shopware.com/developers-guide/shopware-5-core-service-extensions/#decorating-an-existing-service

Damit deine Template Anpassungen bei der Route greifen solltest du das Template Verzeichnis einfach über den PreDispatch registrieren:

 'Enlight\_Controller\_Action\_PreDispatch\_Widgets'

Hier kannst du einfach über “Shopware()->Container()->get(‘template’)->addTemplateDir()” das Template hinzufügen.

Dann noch ein kleiner Tipp am Rande: 

Im obigen Beispiele sehe ich, dass du mit einem Foreach über die Aritkel gehst und für jeden Artikel ein SQL Query absendest. Dies verursacht natürlich ziemlich viel last auf dem Server auch wenn die query über einen Foreign Key läuft. Wenn du statt eines " id = X" queries ein “WHERE id IN (X, Y)” benutzen würdest, kannst du einiges an performance einsparen und die query würde besser skalieren und weniger last auf dem Server ausführen. Hier eine optimierte Version deines Source codes:

$ids = array_column($sArticles, 'id');

$query = Shopware()->Container()->get('dbal_connection')->createQueryBuilder();
$query->select(['articleID', 'meta_description']);
$query->from('s_articles_supplier');
$query->where('articleID IN (:ids)');
$query->setParameter(':ids', $ids, \Doctrine\DBAL\Connection::PARAM_INT_ARRAY);

$descriptions = $query->execute()->fetchAll(PDO::FETCH_KEY_PAIR);

foreach ($sArticles as &$article) {
    $id = $article['articleID'];

    if (!array_key_exists($id, $descriptions)) {
        continue;
    }

    $article['meta_description'] = $descriptions[$id];
}

 

1 Like

Jop, Beispielimplementierung siehe im alten Thread hier: https://forum.shopware.com/discussion/comment/197936/#Comment_197936

1 Like

Hallo @derwunner‍

Danke für euren input. Ich habe mir auf https://developers.shopware.com/developers-guide/shopware-5-core-service-extensions/#decorating-an-existing-service das Textplugin heruntergeladen und versucht es einigermaßen nachzuvollziehen. 

Meine Bootstrap.php sieht nun folgendermaßen aus: 

subscribeEvent(
			'Enlight_Controller_Action_PreDispatch_Widgets',
			'onFrontendPostDispatch'
		);

	    /* add Event to decorate ListProductService */
        $this->subscribeEvent(
        	'Enlight_Bootstrap_AfterInitResource_shopware_storefront.list_product_service',
			'registerListProductService'
		);

        return true;
    }

	public function afterInit()
	{
		$this->get('Loader')->registerNamespace(
			'ShopwarePlugins\prefixAddXY',
			$this->Path()
		);
	}

    /**
    * Plugin activation
     * @return array
     */
    public function enable()
    {
		return ['success' => true, 'invalidateCache' => $this->getInvalidateCache()];
    }

	/* Plugin Deinstallation */
	public function uninstall()
	{

		return ['success' => true, 'invalidateCache' => $this->getInvalidateCache()];
	}

	/**
	 * Helper method to return all the caches, that need to be cleared after uninstalling/enabling/disabling a plugin
	 *
	 * @return array
	 */
	private function getInvalidateCache()
	{
		return ['frontend', 'backend', 'theme'];
	}


	public function registerListProductService()
	{
		$coreService = Shopware()->Container()->get('shopware_storefront.list_product_service');
		$listService = new ListProductService($coreService);
		Shopware()->Container()->set('shopware_storefront.list_product_service', $listService);
	}

    public function onFrontendPostDispatch(Enlight_Event_EventArgs $args)
    {

		/** @var \Enlight_Controller_Action $controller */
		$controller = $args->get('subject');

		$view = $controller->View();

		$view->addTemplateDir(
			__DIR__. '/Views'
		);

	}
}

 

im Ordner StoreFrontBundle habe ich dann noch eine ListProductService.php

service = $service;
	}

	public function getList(array $numbers, Struct\ProductContextInterface $context)
	{
		$products = $this->service->getList($numbers, $context);

		$ids = array_map(
			function (ListProduct $product) {
				return $product->getId();
			},
			$products
		);

		echo 'Ausgabe:';
		var_dump($ids);
		echo '';

		$query = Shopware()->Container()->get('dbal_connection')->createQueryBuilder();
		$query->select(['articleID', 'meta_description']);
		$query->from('s_articles_supplier');
		$query->where('articleID IN (:ids)');
		$query->setParameter(':ids', $ids, Connection::PARAM_INT_ARRAY);

        $descriptions = $query->execute()->fetchAll(\PDO::FETCH_KEY_PAIR);

		foreach ($products as $product) {
			$productId = $product->getId();

        if (!array_key_exists($productId, $descriptions)) {
        		continue;
        	}

        $product['meta_description'] = $descriptions[$productId];

		}

		return $products;

    }

    public function get($number, Struct\ProductContextInterface $context)
	{
		$products = $this->getList([$number], $context);
		return array_shift($products);
	}
}

Hier bin ich jetzt soweit, dass ich unter $products alle Artikel des Listings habe und mir deren IDs in einer Schleife in das Frontend printen lassen.

Jetzt muss ich doch „nur“ noch aus der Datenbank in s_articles_supplier die meta_description je ID raussuchen und dann das ganze an den view assignen, sodass ich mir im Template mit $sArticle.meta_description mir diese ausgeben lassen kann?

  1. Bin ich damit auf dem richtigen Weg?

  2. Wie assigne ich mit $product[‚meta_description‘] = $descriptions[$productId]; den Inhalt aus $descriptions[$productId] an den view, sodass ich mir die description über $sArticle.meta_description holen kann? Die Zeile erzeugt einen Error 500. Auch Versuche über $product->addAttribute(‚meta_description‘, $attribute); waren leer und hat meta_description in die Attribute des Artikels verschoben, sodass ein Aufruf über $sArticle.meta_description ohnehin nicht passen würde.

 

@mdsw‍ hast du eine Lösung dafür gefunden? Unser Plugin funtkioniert seit 5.3 ebenfalls nicht mehr beim Infinite Scrolling.

@Misengo‍

probier mal dieses Event:

Enlight_Controller_Action_Widgets_Listing_listingCount

 

@mdsw‍  leider nicht - war mein erster guess da der Call ja http://URL/widgets/listing/listingCount/sCategory/2472?p=3&c=2472&o=1&n=12&loadProducts=1 lautet.

Hast du es so in SW 5.3 gelöst?

@Misengo‍

 

Ich nutze mehrere Events:

Listing allgemein

Enlight_Controller_Action_PostDispatchSecure_Frontend

Topseller

Enlight_Controller_Action_PostDispatchSecure_Widgets_Listing

Emotion Elemente

Enlight_Controller_Action_PostDispatchSecure_Widgets_Emotion

Listing Count

Enlight_Controller_Action_Widgets_Listing_listingCount

 

@mdsw‍ ich habe dazu mal einen Beitrag verfasst. Eventuell auch noch hilfreich für dich https://the-cake-shop.de/shopware-plugin-101-service-extensions/

Ich werde in Zukunft bei sowas immer direkt über das dekorieren von services gehen - ist halt am Anfang eine Umstellung, aber lohnt sich dann zum Ende hin.

1 Like