Türchen 18: URL Rewrite Indexierung

Da gibt es so einen unscheinbaren Menüpunkt, denn viele Shop-Betreiber gar nicht kennen und dem Programmierer zu schaffen machen kann. Die URL-Rewrites.

Bei kleineren bis mittleren Shops ist auch alles ok, man kann ganz schnell neue Umleitungen definieren.
Möchten man jedoch einen Shop mit ca. 240.000 Produkten in 220 Kategorien und 8 Store-Views nächtlich mit einem Import beglücken und danach den Index neu aufbauen, schaut man schon und überlegt, was macht er da solange.

Er legt mal so einige Millionen neue Records an. Für jedes Produkt in jeden Store (egal ob Aktiviert oder nicht) für den Kategorie-Teil-Baum und jedem Produkt in der entsprechenden Kategorie.Damit kann man sicherlich fast alle SEO-Wünsche erfüllen. Sprachabhängige "suchmaschinenfreundliche" URLs mit und ohne dem Kategorie-Baum. Ob man es brauch oder nicht muss jeder selber entscheiden, für uns war die Index-Bildung einfach zu lang.

Was kann man machen, bzw. haben wir für diesen Projekt umgesetzt. Man kann sicherlich auch nur die Rewrites aktualisieren, die durch den Import geändert wurden sind. Macht aber einiges an Logik notwendig und eine manuelle Indexierung dauert immer noch so lange.

Zuerst einmal gibt es die Option "Kategoriepfad für Artikel URLs verwenden", wenn diese Option auf "Nein" gestellt wird, dann gibt es nur noch URLs in der Form "http://domain.de/product-a.html ", dies hilft besonders bei Produkten, die in mehreren Kategorien zu finden sind.

Die weitere Einsparung geht leider nicht ohne Programmierung. Da die URLs, nie sprachabhängig gebraucht werden (in unserem Projekt), und somit in allen Store-Views gleich aussehen, haben wir den Faktor "Store Views" auf ein Store-View pro Website verringert.

Zuerst muss die Klasse" Mage_Catalog_Model_Url" überlagert werden und denn Loop über alle Store-Views mit einem über alles Websites und den Aufruf mit dem Default-Store der Website ersetzt werden:

<?php

class AuIt_Cfc_Model_Rewrite_Catalog_Url extends Mage_Catalog_Model_Url
{
    protected function _refreshProductRewrite(Varien_Object $product, Varien_Object $category)
    {
        if ($this->getStoreRootCategory($category->getStoreId())->getId() != $category->getId()) {
            return $this;
        }
        return parent::_refreshProductRewrite($product, $category);
    }

    public function refreshRewrites($storeId = null)
    {
        if (is_null($storeId)) {
            foreach (Mage::app()->getWebsites() as $website) {
                if ($website->getDefaultStore())
                    $this->refreshRewrites($website->getDefaultStore()->getId());
            }
            return $this;
        }
        return parent::refreshRewrites($storeId);
    }

    public function refreshCategoryRewrite($categoryId, $storeId = null, $refreshProducts = true)
    {
        if (is_null($storeId)) {
            foreach (Mage::app()->getWebsites() as $website) {
                if ($website->getDefaultStore())
                    $this->refreshCategoryRewrite($categoryId, $website->getDefaultStore()->getId(), $refreshProducts);
            }
            return $this;
        }
        return parent::refreshCategoryRewrite($categoryId, $storeId, $refreshProducts);
    }

    public function refreshProductRewrite($productId, $storeId = null)
    {
        if (is_null($storeId)) {
            foreach (Mage::app()->getWebsites() as $website) {
                if ($website->getDefaultStore())
                    $this->refreshProductRewrite($productId, $website->getDefaultStore()->getId());
            }
            return $this;
        }
        return parent::refreshProductRewrite($productId, $storeId);
    }

    public function clearStoreInvalidRewrites($storeId = null)
    {
        if (is_null($storeId)) {
            foreach (Mage::app()->getWebsites() as $website) {
                if ($website->getDefaultStore())
                    $this->clearStoreInvalidRewrites($website->getDefaultStore()->getId());
            }
            return $this;
        }
        return parent::clearStoreInvalidRewrites($storeId);
    }

}

Da aber alle Anfragen des Systems immer über den Store-View kommen müssen wir die Anfrage eines Store-Views wieder zum Default-Store der Website zuordnen.

class AuIt_Cfc_Model_Rewrite_Core_Url_Rewrite extends Mage_Core_Model_Url_Rewrite
{
    public function loadByIdPath($path)
    {
        $storeId = $this->getStoreId();
        $this->setStoreId(Mage::app()->getWebsite()->getDefaultStore()->getId());
        parent::loadByIdPath($path);
        $this->setStoreId($storeId);
        return $this;
    }

    public function loadByRequestPath($path)
    {
        parent::loadByRequestPath($path);
        if ( !$this->getData('request_path') )
        {
            $storeId = $this->getStoreId();
            $this->setStoreId(Mage::app()->getWebsite()->getDefaultStore()->getId());
            parent::loadByRequestPath($path);
            $this->setStoreId($storeId);
        }
        return $this;
    }
}

Damit die URLs richtig aufgebaut werden, muss noch die Methode " joinUrlRewrite" wieder entsprechend an den Default-Store der Website angepasst werden.

class AuIt_Cfc_Model_Rewrite_Resource_Category_Collection extends Mage_Catalog_Model_Resource_Category_Collection
{
    /**
     * Joins url rewrite rules to collection
     *
     * @return Mage_Catalog_Model_Resource_Category_Collection
     */
    public function joinUrlRewrite()
    {
        //$storeId = Mage::app()->getStore()->getId();
        $storeId = Mage::app()->getWebsite()->getDefaultStore()->getId();
        $this->joinTable(
            'core/url_rewrite',
            'category_id=entity_id',
            array('request_path'),
            "{{table}}.is_system=1"
                . " AND {{table}}.product_id IS NULL"
                . " AND {{table}}.store_id='{$storeId}'"
                . " AND id_path LIKE 'category/%'",
            'left'
        );
        return $this;
    }
}

Setzt man die Flat-Tabellen für die Kategorien ein, so müssen die Methoden

Mage_Catalog_Model_Resource_Category_Flat:: getParentCategories,
Mage_Catalog_Model_Resource_Category_Flat:: _loadNodes,
Mage_Catalog_Model_Resource_Category_Flat_Collection::addUrlRewriteTo

noch angepasst werden. Dabei einfach nach "url_rewrite" suchen und die Store-ID mit der Store-ID des Default-Store-Views ersetzen. Gegenüber früheren Zeiten ist

Mage_ImportExport_Model_Import_Entity_Product, 
Mage_ImportExport_Model_Import_Entity_Customer, 
Mage_ImportExport_Model_Import_Entity_Abstrac

(Vorgestellt im letzten Adventskalender http://www.webguys.de/magento/turchen-19-produktimport-mit-der-importexport-schnittstelle/), in einer annehmbaren Zeit möglich, wenn danach nicht die Indexierung laufen müsste. Mit diesen vorgestellten, relativ kleinen Maßnahmen, konnten wir die Zeit der URL-Rewrite-Indexierung um ca. 6/8 minimieren. Vieleicht gibt es auch noch andere Möglichkeiten …



Ein Beitrag von Michael Augsten
Michael's avatar

Michael Augsten ist seit über 20 Jahren selbstständiger Informatiker aus München. Er ist begeistert von der Anpassbarkeit von Magento und realisiert in seiner Firma Au-IT, seit der Magento Version 1.1.6 vor gut 3 Jahren, fast ausschließlich Projekte die auf dem Open Source System aufbauen. Vom klassischen Online Shop bis hin zum Marketing-Portal für eine firmenweite Nutzung. Sie erreichen Ihn über seine Webseite oder per E-Mail

Alle Beiträge von Michael

Kommentare
Timo Schwarz am

Hallo, unser Shop 1.7.0.2 umfasst 12 Storeviews (verschiedene Sprachen) mit 42.000 Artikeln pro Store. Da wir den Shop momentan nur an Händler bereitstellen und nicht dem Endkunden, sind die SEO Fragen für uns nicht so relevant. Kann man dies nicht deaktivieren damit der Katalog URL Rewrites Neuaufbau nicht so lange dauert, oder gar ganz wegfällt?

Der Neuaufbau dauert bei uns momentan 6 Stunden. Was komplett vom Stern ist :-(

Gibt es da eine Möglichkeit?

Gruß

Timo

Volker am

@Simon: Vielen Dank für den Tipp (http://www.magentocommerce.com/magento-connect/dn-d-patch-index-url-1364.html). Der catalog_url Index hat mir ein paar schlaflose Nächte beschert, mit dem Modul läuft er wieder!

willneueshandy am

hallo, und vielen Dank für diesen wirklich aufschlussreichen Artikel zum thema url-rewrite indexierung bei magento. kann die tipps sehr gut gebrauchen. nochmals danke und der blog wird gebookmarked.

Michael am

Danke für die Tips. Werde ich mir mal über Weihnachten anschauen.

Simon am

store codes in urls meine ich natürlich ---

Simon am

Hallo,

wir haben in einem SHop (6 Views, 170k Produkte) momentan u.a. diese Extension hier aktiv:

http://www.magentocommerce.com/magento-connect/dn-d-patch-index-url-1364.html

Die Rewrites funktionieren gut, man muss nur ein bisschen aufpassen, welche Produkte deaktiviert/unsichtbar sind.

Und natürlich: topp Artikel & schön umfangreich, kommt auf jeden Fall mal bei einem der nächsten Projekte mal rein :) Wobei ich aus SEO-Gründen eher dazu tendiere, Shop-Rewrites zu erlauben, um für jede Sprache eine eigene URL zu haben.

Alexander am

Danke für den Tipp von Matthias.

Matthias Zeis am

Hallo Michael, danke auch von mir.

Zur Ergänzung zwei Extensions, die der URL-Rewrite-Indexierung Beine machen:

https://github.com/IvanChepurnyi/EcomDev_UrlRewrite/ http://www.dnd.fr/2012/09/magento-patch-how-to-optimize-re-index-processing-time-for-url-rewrite/ Die Ansätze sind etwas unterschiedlich, beide Extensions sollten aber auf ihre Art Verbesserungen bringen.

Ich habe heute die Extension von Ivan in einem Shop eingerichtet. Da sie keine Klassen-Rewrites verwendet, kann man sie ziemlich bedenkenlos einbauen. Man muss natürlich überprüfen, ob die URLs unverändert geblieben sind und zuerst in nicht-produktiven Systemen testen. Der Shop hat ca. 250.000 Produkt-URL-Rewrites. Mit Magento-Bord-Mitteln dauerte die Indizierung länger als 5 Stunden => Abbruch des Prozess aufgrund maximaler Laufzeit in der Shell. Mit Ivans Erweiterung dauerte es im ersten Durchlauf 8 Minuten, im zweiten Durchlauf 2 Minuten.

Vinai am

Ich schließe mich Andreas an - klasse Beitrag, danke!

Andreas von Studnitz am

Hallo Michael, danke für den guten Beitrag. Mit den URL-Rewrites habe ich mich bisher nicht in der Tiefe beschäftigt, daher ist das ein guter Einblick. Eine Ergänzung: Wenn der Produktimport über FastSimpleImport läuft (meinen Array-Adapter für Mage_ImportExport), gibt es die Möglichkeit, partielle Indizierung zu aktivieren, sodass nur die aktualisierten Produkte indiziert werden. Die Syntax dazu ist unter https://github.com/avstudnitz/AvS_FastSimpleImport#features beschrieben.

Dein Kommentar