Configurable Product: Attribute als Tabelle darstellen

Magento bietet von Haus aus nicht die Möglichkeit ein Configurable Product tabellarisch anzuzeigen - auch die Module auf Magento Commerce sind hierzu rar gesät. Also entwickeln wir doch einfach selbst ein Modul :)

Fertiges Modul gesucht?

Das Modul ist nun in ähnlicher Fassung, mit Warenkorb-Funktion, als Paid-Modul für 99 € verfügbar. Jetzt Bestellen!

Das Ziel

Kein Modul ohne eine klare Zieldefinition - kaum etwas ist schlimmer als einfach ins Blaue zu entwickeln. Unser Ziel soll es also sein alle zu einem Configurable Product zugewiesenen Simple Products in einer Tabelle anzuzeigen. Die Produkte sollten für das Erste ausschließlich dargestellt werden - ein direktes Hinzufügen zum Warenkorb per z.B. Radio-Auswahl ist nicht vorgesehen.

Unser Ergebnis sollte also ungefähr so aussehen:

SKU Farbe Größe ... ... Preis
1234-5 Rot M ... ... 0,55 €
1234-5 Blau M ... ... 0,67 €
1234-5 Rot S ... ... 0,75 €
1234-5 Blau S ... ... 0,85 €

Die Attribute-Werte sollen dabei alphabetisch sortiert sein, zuerst nach Attribute 1, dann Attribute 2 usw. Die Tabelle erweitert sich automatisch auf die benötige Anzahl an Attributen. Angezeigt wird das ganze auf der Produkt-Detail-Seite (catalog/product/view.phtml)

Was brauchen wir?

Um unser Ziel zu erreichen benötigen wir im groben nur ein Template und einen Block. Der Block soll die benötigten Informationen zusammenfassen und an das Template übergeben. Das Template, wie sollte es auch anders sein, gibt die Daten als HTML aus.

Woher bekommen wir die Daten?

Nachdem Google und das Magento-Forum bei dieser Aufgabe leider keine große Hilfe waren musste ich mich näher dem Magento-Kern in Bezug auf Configurable Products widmen. Dabei stellen sich folgende Fragen:

  • Welche Attribute des Configurable-Products sind konfigurierbar?
  • Welche Attributewerte kann das Attribute annehmen?
  • Wie lässt sich Anhand der ausgewählte Attribute ermitteln welches Simple-Product zutreffend ist?

Die ersten Fragen ließen sich relativ einfach klären - bei der letzten dauerte es leider etwas länger bis die richtige Stelle gefunden war.

Grundlagen schaffen

Bevor wir ordentlich anfangen sollten wir den benötigen Block anlegen, das Template mit einer Hallo Welt-Ausgabe erstellen und alles im catalog.xml einbinden. So sehen wir direkt, welche Auswirkungen unsere Änderungen haben und können kontrollieren, ob wir da eigentlich das richtiges tun - oder auch nicht ;)

Block erstellen

Um uns ein bisschen Arbeit beim Anlegen abzunehmen, arbeiten wir mit unseren Block im Ordner app/local/Mage - das gehört zwar nicht unbedingt zum guten Ton, bläht diesen Beitrag aber nicht unnötig auf. Erstellen wir also unseren Block:

Datei: app/code/local/Mage/Catalog/Block/Product/View/Configurabletable.php

class Mage_Catalog_Block_Product_View_Configurabletable extends Mage_Core_Block_Template
{
/*
Die Funktionen bauen wir nun Stück für Stück auf
*/
}

Template anlegen

Das Template soll, wie beschrieben, erstmal nur "Hallo Welt" ausgeben. So können wir relativ einfach kontrollieren, ob unser Template über den Block richtig ausgegeben wird und sich das Layout mit dem dazugehörigen XML wie gewünscht verhält.

Datei: app/design/frontend/default/default/template/catalog/product/view/configurabletable.phtml

Hallo Welt

Layout-XML anpassen

Und nun müssen wir unseren Block mit dem Template noch dem Layout bekannt machen: "Hallo Catalog,xml ich bin der Block Tableview. Darf ich Ihnen meine Frau configurabletable.phtml vorstellen? Sie ist ein Template!"

Datei: app/design/frontend/default/default/layout/catalog.xml

<catalog_product_view translate="label">
	...
	<reference name="content">
		...
		<block type="catalog/product_view" name="product.info" template="catalog/product/view.phtml">
			...
			<block type="catalog/product_view_description" name="product.description" as="description" template="catalog/product/view/description.phtml"/>

			<!-- Folgende Zeile einfügen -->
			<block type="catalog/product_view_configurabletable" name="catalog.product.configurabletable" as="configurabletable" template="catalog/product/view/configurabletable.phtml"/>
			<!-- Bis hier einfügen -->

			<block type="catalog/product_view_configurabletable" name="catalog.product.configurabletable" as="configurabletable" template="catalog/product/view/configurabletable.phtml"/>
			...
		</block>
		...
	</reference>
	...
</catalog_product_view>

So, nun kennen sich Layout, Block und Template. Jedoch wird unser "Hallo Welt" noch an keiner Stelle ausgegeben. Dazu müssen wir app/design/frontend/default/default/template/catalog/product/view.phtml noch anpassen und an der gewünschten Stelle ein

<?php echo $this->getChildHTML('configurabletable');?>

einfügen. Ich habe mich in meinen Beispiel dazu entschieden das genau unter der ShortDescription zu tun. Aussehen wird das ganze also bis jetzt wie folgt:

Anashria-Womens-Premier-Leather-Sandal_1271246044044-500x267

Block erstellen

Nachdem wir nun unseren leeren Block mit einem einfachen Template vorbereitet haben und das ganze auch korrekt ausgegeben wird, benötigen wir noch einige Daten zum Configurable-Product. Dazu gehören die Attribute, die Attributwerte und die Kombination aus verschiedenen Attributen in Hinblick auf den Preis bzw. die SKU.

Zuerst benötigen wir eine Funktion die uns sagt welches Produkt überhaupt dargestellt werden soll. Diese Information ist über die Mage-Registry durch den Product-View-Controller bereits gesetzt.

protected function getProduct() {
    return Mage::registry('current_product');
}

Die Funktion ist natürlich Bestandteil der Class "Mage_Catalog_Block_Product_View_Configurabletable".

Sobald wir wissen, um welches Configurable-Product es sich handelt können wir recht einfach ermitteln, welche Simple-Products dem Configurable-Product zugewiesen sind. Auch das kapseln wir wieder in einer Funktion.

public function getAllowProducts() {
	$products = array();
	$allProducts = $this->getProduct()->getTypeInstance(true)
							->getUsedProducts(null, $this->getProduct());
	foreach ($allProducts as $product) {
		if ($product->isSaleable()) {
			$products[] = $product;
		}
	}
	return $products;
}

Jetzt wird es Zeit für einen ersten Test. Rufen wir aus dem Template doch einmal die getAllowProducts auf und stellen die zugeordneten SKUs dar.

Datei: configurabletable.phtml

Hallo Welt
<?php foreach( $this->getAllowProducts() AS $_product ) : ?>
	<?php echo $_product->getSku(); ?>
<?php endforeach; ?>

Bei dem Magento Demo-Produkt "Zolof The Rock And Roll Destroyer: LOL Cat T-shirt (Shirts T)" (SKU=zol) erhalten wir damit folgende Ausgabe:

Hallo Welt zol_r_sm zol_g_sm zol_r_med zol_g_med zol_r_lrg zol_g_lrg

Wir sehen also, die erste Funktion tut was sie soll. Nun liegt es an uns herauszufinden welche Attribute bei dem Configurable Product denn überhaupt konfigurierbar sind. Auch hierzu basteln wir uns wieder eine Funktion bzw. klauen sie aus dem Magento-Kern.

public function getAllowAttributes() {
    return $this->getProduct()->getTypeInstance(true)
        ->getConfigurableAttributes($this->getProduct());
}

Fehlt nur noch eine kleine Testausgabe in unserem Template:

[.. siehe oben ..]
<hr/>
<?php foreach( $this->getAllowAttributes() AS $_attribute ) : ?>
	<?php echo $_attribute->getProductAttribute()->getAttributeCode() ?>
<?php endforeach; ?>

Als Rückmeldung erhalten wir "shirt_size color" - das entspricht genau den Namen (Codes) der Attributen die konfigurierbar sind.

Wir wissen nun also welche Attribute konfigurierbar sein sollen und welche Simple Products dem Configurable Product zugewiesen sind. Was hält uns also davon ab unsere Simple Products durchzulaufen und uns einen Attribute-Tabelle zu bauen an dessen Ende unser Simple Product steht?

Ein Beispiel:

Array
(
    [0] => Array
        (
            [sku] => zol_r_sm
            [product] => 'Für das Beispiel entfernt'
            [items] => Array
                (
                    [0] => Small
                    [1] => Red
                )

        )

    [1] => Array
        (
            [sku] => zol_g_sm
            [product] => 'Für das Beispiel entfernt'
            [items] => Array
                (
                    [0] => Small
                    [1] => Green
                )

        )

    [.. gekürzt ..]

)

Wenn wir einen Schritt weiter denken hilft uns diese Struktur nachher sehr bei der Darstellung von Simple-Products in einer Tabelle. Jeder Eintrag in unserem Array ist quasi eine Reihe in unserer späteren Tabelle. In der Tabelle stellen wir dann zunächst alle Werte aus [items] nacheinander da um dann mit SKU und Preis aus [product] zu enden. Fehlt nur noch die Funktion die uns ein solches Array liefert:

public function getTableData() {
	$data = array();

	foreach( $this->getAllowProducts() AS $product ) {
		// Zuerst alle Simple-Products durchlaufen

		$items = array();

		foreach( $this->getAllowAttributes() AS $attribute ) {
			// Dann zu jedem Simple Product die Attributewerte in Items speichern
			$attrValue = $product->getResource()->getAttribute( $attribute->getProductAttribute()->getAttributeCode() )->getFrontend();
			$items[] = $attrValue->getValue($product);
		}

		// Um letztlich dem Tabellenbody noch Informationen zum Product mitzugeben
		if ( $items > 0 ) {
			$data[] =  array( 	'sku' 		=> $product->getSku(),
								'items' 	=> $items,
								'product' 	=> $product
								);
		}

	}

	return $data;
}

Dann passen wir einmal unser Template an und stellen schon einmal eine vereinfachte Tabelle dar:

Datei: configurabletable.phtml

<table>
	<?php foreach( $this->getTableData() AS $tablerow ) : ?>
		<tr>
			<?php foreach( $tablerow['items'] AS $attribute ) : ?>

				<td> <?php echo $this->htmlEscape( $attribute ); ?> </td>

			<?php endforeach; ?>

			<td> <?php echo $this->htmlEscape( $tablerow['product']->getSku() ); ?> </td>
			<td> <?php echo $this->htmlEscape( $tablerow['product']->getPrice() ); ?> </td>
		</tr>
	<?php endforeach; ?>
</table>

Beim Aufruf des Beispiel-Produktes gibt es damit folgende Ausgabe im Browser:

Mal abgesehen von einer schönen Formatierung, die wir besser den Grafikern bzw. CSS-Gurus überlassen wollen: Was fehlt noch? Richtig, eine ordentlich Tabellenüberschrift! Dabei können wir uns sehr einfach bereits bestehenden Funktionen bedienen - so müssen wir nur unser Template anpassen:

Datei: configurabletable.phtml

<table>

	<tr>
		<?php foreach( $this->getAllowAttributes() AS $attribute ) : ?>
			<th>
				<?php echo $this->htmlEscape( $attribute->getLabel() ); ?>
			</th>
		<?php endforeach; ?>

		<th><?php echo $this->__('SKU')?></th>
		<th><?php echo $this->__('Preis')?></th>

	</tr>

	<?php foreach( $this->getTableData() AS $tablerow ) : ?>
		<tr>
			<?php foreach( $tablerow['items'] AS $attribute ) : ?>

				<td> <?php echo $this->htmlEscape( $attribute ); ?> </td>

			<?php endforeach; ?>

			<td> <?php echo $this->htmlEscape( $tablerow['product']->getSku() ); ?> </td>
			<td> <?php echo $this->htmlEscape( $tablerow['product']->getPrice() ); ?> </td>

		</tr>
	<?php endforeach; ?>
</table>

Mit ein ganz bisschen Formatierung sieht unser Endergebnis wie folgt aus:

Zolof-The-Rock-And-Roll-Destroyer-LOL-Cat-T-shirt_1271275886017-500x382

Fazit

Tolles Ergebnis für ein paar Zeilen Code - die Hauptarbeit lag darin die Funktionalität von Simple und Configurable-Products zu recherchieren. Einmal verstanden ging es quasi wie von selbst :)

Abschließend noch einmal den vollständigen Configurabletable Block:

<?php

class Mage_Catalog_Block_Product_View_Configurabletable extends Mage_Core_Block_Template
{

	protected function getProduct() {
		return Mage::registry('current_product');
	}

	public function getAllowProducts() {
		$products = array();
		$allProducts = $this->getProduct()->getTypeInstance(true)
								->getUsedProducts(null, $this->getProduct());
		foreach ($allProducts as $product) {
			if ($product->isSaleable()) {
				$products[] = $product;
			}
		}
		return $products;
	}

	public function getAllowAttributes() {
        return $this->getProduct()->getTypeInstance(true)
            ->getConfigurableAttributes($this->getProduct());
    }

	public function getTableData() {
		$data = array();

		foreach( $this->getAllowProducts() AS $product ) {
			// Zuerst alle Simple-Products durchlaufen

			$items = array();

			foreach( $this->getAllowAttributes() AS $attribute ) {
				// Dann zu jedem Simple Product die Attributewerte in Items speichern
				$attrValue = $product->getResource()->getAttribute( $attribute->getProductAttribute()->getAttributeCode() )->getFrontend();
				$items[] = $attrValue->getValue($product);
			}

			// Um letztlich dem Tabellenbody noch Informationen zum Product mitzugeben
			if ( $items > 0 ) {
				$data[] =  array( 	'sku' 		=> $product->getSku(),
									'items' 	=> $items,
									'product' 	=> $product
									);
			}

		}

		return $data;
	}

}



Ein Beitrag von Tobias Vogt
Tobias's avatar

Tobias Vogt arbeitet seit 2008 mit Magento und ist seit 2011 durch Magento zertifizierter Entwickler. Seit 2016 ist er Mitgründer und CTO bei der connect-io GmbH, einer Magento-Agentur mit Sitz im idyllischen Paderborn-Salzkotten. Er gehört zum Gründer-Team der Webguys und ist seit November 2011 Bachelor of Science (Wirtschaftsinformatik). Sie erreichen Ihn per E-Mail unter tobi@webguys.de.

Alle Beiträge von Tobias

Kommentare
Wolle am

Hey Tobias,

super Sache, vielen Dank für die ausführliche Beschreibung - hat auf Anhieb geklappt...

Leider bin ich nicht der Magento-Crack geschweige denn fit genug im Magento Coding...

Wie würde es aussehen, wenn ich anstatt des Attribute-Textes (Storeview) die dazugehörige Adminbezeichnungen benötigen würde?

Habe mir heute schon die Finge wund gegoogelt und auch ein paar Lösungen gefunden, die mir zumindest bei einer angelegten Attribut-Option die dazugehörige Admin-Bezeichnug ausgeben würden. Idealerweise wäre für mich aber eine Lösung die eine gemischte wäre...

Denke bitte nicht, dass ich zu faul bin rumzuprobieren, ich komme aber einfch noch nicht mit der Magento-Code klar... Hättest du mir da evt einen guten Tipp bzw ein entsprechendes Snippet parat...

Vielen Dank im Voraus...

LG W

Tobias Vogt am

Hey Nino,

vielen Dank für dein Kommentar. Die Configurable-Matrix funktioniert in der Standard-Version nur genau mit zwei Attributen. Es wäre aber denkbar zu dem Produkt weitere individuelle Attribute zu hinterlegen. Das ist technisch z.Z. aber noch nicht vorgesehen.

Wenn du möchtest schauen wir gerne mal wie Aufwändig deine Anforderung wäre. Melde dich doch einfach einmal bei mir (tobi@webguys.de) per E-Mail.

Tobi

Nino am

Hallo, vielen Dank für die Anleitung und die Extension. Ich habe einen Frage, ist es möglich mit Eurer Extension darzustellen, dass der Kunde T-Shirts bestellen kann in unterschiedlichen Größen (über eure Tabellenextension) vorher das T-Shirt aber noch mit einer Auswahl von Attributen konfiguriert? So z.B. Grundpreis des Shirt liegt bei 10 € zzgl. die Position des Aufdruckes durch ein Drop Down Menü (Aufdruck vorne 10 € oder hinten groß 15 €)

Also der Einzelpreis liegt dann bei 20 € und der Kunde wählt dann die unterschiedlichen Größen in der Tabelle.

rub am

Hi Tobias,

Ich finde diesen Beitrag absolut super! Leider bin ich noch nicht sehr weit in der Materie um von diesem Projekt, auf die Lösung meines eigenen zu stoßen. Bei mir sind die Hauptbilder immer die Selben, jedoch würde ich der Beschreibung des Produkts gerne die Beschreibung des einfachen Produkts, auf welches nach Auswahl der Optionen verwiesen wird, hinzufügen. Hast Du dafür bereits auch schon etwas entwickelt?

Gruß

rub

Tobias Vogt am

Hey Kathrin,

schau dir mal eine Beta-Extension von mir an. Du findest Sie unter http://dev.webguys.de/sizeselector/apparel/shoes/womens/steven-by-steve-madden-pryme-pump.html - hier werden anstelle von Dropdowns klickbare Flächen angezeigt und zusätzlich, bei Auswahl, unten weitere Informationen zum Artikel ausgewiesen.

Wenn dir das hilft schreib mir doch kurz eine E-Mail, dann schauen wir mal :)

Tobi

Kathrin am

: ) Ich versuch es noch mal: ein konfigurierbares Produkt in diesem Fall ein Bildmotiv ist die Hülse von 3 Single Products = unterschiedlichen Größen. Jedes Single Produkt hat eine unterschiedliche verfügbare Menge bei meinen Bildern. Die großen gibt es nur 10 mal die mittleren nur 5 und die kleinen sind unlimitiert. In einer Tabelle möchte ich die verfügbare Menge auf Produktebene anzeigen. Es gibt hier einige Anleitungen aber das haut nicht hin oder ich verstehe es nicht ganz. Vielleicht könntest du mir helfen? http://www.magentocommerce.com/boards/viewthread/25393/P15/ Ich möchte aber die Menge nicht im Drop Down einspielen, sondern unter Kurzbeschreibung.

Danke dir und viele Grüße, Kathrin

Tobias Vogt am

Sorry Kathrin, die Frage hab ich nicht verstanden :(

Kathrin am

Hallo Tobias,

klasse Anleitung die ich lokal auch umgesetzt habe. Hast du eine Ahnung wie ich die jeweilige Menge für die einzelnen Single Products mit in die Tabelle einlesen lassen kann. Meine Bilder soll es in 3 Größen aber limitiert geben. Mit getQty kommen keine Werte. Über einen Tipp wäre ich dankbar!

Besten Gruß, Kathrin

Tobias Vogt am

Hallo Brigitte,

das klingt für mich danach als ob du auf den falschen Produkt-Typ setzt. Hast du schon einmal das Grouped Product probiert?

Schau mal hier: http://www.magentocommerce.com/blog/magento-screencast-creating-grouped-products/

Tobi

Brigitte am

Hallo Tobias,

bin durch Zufall hierhin gestoßen, und glaube dein "Tutorial" könnte die Lösung meines Problems sein. Und zwar möchte ich meinen Kunden die Möglichkeit geben mehrere Shirts in versch, Größen zu kaufen. Dafür müsste noch eine Spalte mit einer Eingabe Maske hinzugefügt werden ( 3x grün, 2x blau...) verstehst du was ich meine?? wäre so etwas möglich?? könntest du mir auf die Sprünge helfen??

Tobias Vogt am

Gibt es die wirklich? Ui ui ui... Bau ich die Tage noch mal ein, danke :)

heiko am

nicht vergessen, den code in app/code/local/Mage/Catalog/Block/Product/View/Configurabletable.php mit den tags zu umschliessen, es soll ja leute geben, die ohne nachzudenken den code nur kopieren und die sich dann über komische fehlermeldungen wundern... :-/

Tobias Vogt am

Das habe ich nicht zu 100% Verstanden aber ich denke das es recht problemlos möglich sein dürfte das Array dementsprechend umzubauen :)

Björn Jacob am

Hehe, WordPress ist ja nett. Also du hast im catalog.xml den einen Block durch Kommentare besonders hervorgehoben. Der geht bei mir nicht. Bei mir (1.3.x.x) musste der Type so heißen: "catalog/product_view_configurabletable"

Andere Frage. Kann man die Klasse auch noch so erweitern, dass er mir von allen / einem bestimmten Attribute die verfügbaren Optionen aggregiert? Also ich habe ein Configurable Product mit 10 Simple Products. Die haben ein Attribut Farbe. Es sind zwei Ausprägungen verfügbar. Ich wüsste nun gerne welche. Z.B. schwarz und rot...

Tobias Vogt am

Hey Björn,

ich dachte eigentlich ich hätte meine Beispiele getestet aber an der Qualität muss ich wohl noch ein wenig schrauben. Vielen Dank für die Qualitätssicherung :) Dein Block-Beispiel hat Wordpress leider verschluckt aber ich denke ich konnte es in deinem Sinne im Beitrag ergänzen - schau doch noch einmal drauf? escapeHTML habe ich durch durch htmlEscape ausgetauscht - zumindest in Magento 1.4.x wird der Aufruf von escapeHTML bei mir keinen Fehler - ein wenig komisch finde ich es jedoch schon.

schönen Gruß und viel Erfolg noch bei der Darstellung :)

Tobias

Björn Jacob am

Danke dir Tobias für deine Antwort. Ich habe den Fehler gefunden. Es liegt am catalog.xml. Der Block Type stimmt nicht. Es muss so heißen:

Sobald ich das korrigiert hatte, konnte ich den Rest deines Beitrags abarbeiten. Klappt soweit super. Nur im phtml ist bei mir noch ein Fehler aufgetreten. Du verwendest die Methode escapeHTML() - es heißt aber htmlEscape(). Der Rest läuft prima! Danke nochmals.

Tobias Vogt am

Danke für das erste Kommentar erst einmal :) Ich freu mich! Die unterschiedlichen Klassennamen habe ich soeben im Beitrag korrigiert. "Mage_Catalog_Block_Product_View_Configurabletable" war der richtige Name. Wenn du im "Hallo Welt"-Template ein einbaust welche Meldung gibt dir Magento dann? Eigentlich müsstest du, wenn du das Layout richtig konfiguriert hast, ein "Mage_Catalog_Block_Product_View_Configurabletable" bekommen.

Vlt. ist es auch etwas ganz einfaches: Den Magento-Cache hast du deaktiviert so dass er das Layout immer wieder neu zusammenbaut?

Björn Jacob am

Danke für diese tolle Anleitung. Ich war auf der Suche nach einer solchen Möglichkeit und bin auf deinen Code gestoßen. Allerdings bereitet mir die Adaption Probleme. Zum einen unterscheidet sich der Klassenname innerhalb dieses Beitrags. Zu Beginn leitest du das ganze als "class Mage_Block_Configurable_Tableview extends Mage_Core_Block_Template" ein. Bis unten heißt deine Klasse dann anders.

Zum anderen komme ich mit diesem Code eigentlich nur bis zum "Hallt Welt". Naja, wenigstens schon etwas. Aber sobald ich die erste foreach im Template einbaue bekomme ich einen typischen Magento Error Report (Invalid argument supplied for foreach()).

Woran kann das liegen?

Dein Kommentar