Eigene Formularelemente im Produkt erstellen, gestalten und benutzen

In Magento lassen sich über die Attribute-Sets ohne weiteres Felder zum Produkt hinzufügen. Die Typen der Felder sind auf Text Field, Text Area, Date, Yes/No, Multiple Select, Dropdown, Price, Gallery, Media Image und Fixed Product Tax beschränkt. Im folgenden möchte ich zeigen wie ein eigenes Datenfeld gestaltet und und implementiert wird. Das Ziel soll es sein dem Shopbetreiber die Möglichkeit zu geben beliebig viele Datenblätter zu einem Produkt hochzuladen.

Magento-Admin_1263750033896-499x148

1. Attribute anlegen

Zuerst müssen wir ein einfaches Attribute anlegen. Dazu wählen wir Catalog/ Attributes/ Manage Attributes. Ich gehe bei dem neuen Attribute davon aus das als Code “datenblatt” gewählt wurde. Der Einfachhalt halber verzichte ich darauf die Datenblätter in einer separaten Kreuztabelle zu speichern – stattdessen werden die Zuordnungen direkt in das Datenbankfeld geschrieben. Aus diesem Grund sollte der Typ des Attributes auf “TextArea” stehen. Zusätzlich muss das neue Attribute, damit es sichtbar ist, noch dem Default-Set zugeordnet werden.

2. Datenbank anpassen

Magento baut die Formulare teilweise anhand der Felder in der Datenbank zusammen. Es nutzt dafür ein sogenanntes EAV-Model. Dieses Model sorgt salopp gesagt dafür das wir fast beliebig viele neue Felder schaffen können ohne unser Datenbank-Layout zu verändern. Zudem haben wir die Möglichkeit anzugeben wie die Formularfelder erstellt und gespeichert werden. Hierzu gibt es den Frontend-Input-Renderer (Feldname: . Dieser kümmert sich vollständig um die Darstellung der Formularelemente. Das Gegenstück hierzu ist das Backend-Model (Feldname: ) welches sich um die Speicherung und Validierung der Eingaben kümmert.

Um nun unser Feld “datenblatt” optisch und funktional anzupassen gehen wir wie folgt vor:

Zuerst Datenbank z.B. im phpMyAdmin öffnen. Dann zur Tabelle eav_attribute wechseln und dort den Datensatz mit attribute_code = “datenblatt” heraussuchen und bearbeiten:

backend_model: test/product_backend_datenblatt
frontend_input_renderer: Test_Test_Renderer_Datenblatt

In diesem Fall weisen wir Magento an das Model Product/Backend/Datenblatt aus dem Modul Test zum Speichern unserer Daten zu verwenden. Im zweiten geben wir an das die Darstellung über die Class Test_Test_Renderer_Datenblatt erfolgt. Wichtig ist das, warum auch immer, das Backend-Model auf ein Model verweist – es muss also vor dem Slash das Modul stehen. Der Frontend-Input-Renderer dagegen erwartet direkt eine Class!

3. Frontend-Input-Renderer erstellen

Hierzu erstellen wir in app/code/local/Test/Test/Renderer die Datei Datenblatt.php mit folgendem Inhalt:

Datei: app/code/local/Test/Test/Renderer/Datenblatt.php

<?php
class Test_Test_Renderer_Datenblatt extends Varien_Data_Form_Element_File
{
	 /**
	 * Retrieve Element HTML fragment
	 *
	 * @return string
	 */
	 public function getElementHtml()
	 {
		 $html = '';

		 // Value lesen und Zeilenweise trennen
		 $data = $this->getData('value');
		 $lines = explode("\n", $data);

		 $html .= '<table>';

		 $html .= '<col width="110" /> ';
		 $html .= '<col width="150" /> ';
		 $html .= '<col /> ';

		 $html .= '    <thead>    <tr> <th>Datenblatt-Nr.</th> <th>Aktuelle Datei</th>  <th>Neue Datei</th> </tr> </thead> ';

		 $html .= '    <tbody>';

		 $i = 0;
		 foreach( $lines AS $cur ) {
		 $i++;

		 // pro Zeile wird genau ein Datensatz getrennt mit einem Gleich-Zeichen erwartet
		 $tmp = explode('=', $cur);
		 if ( count($tmp) != 2 ) {
		 	continue;
		 }
		 list($nr, $file)  = $tmp;

		 $nr = trim($nr);
		 $file = trim($file);

		 // Eine leere Datenblatt-Nummer akzeptieren wir nicht
		 if ( $nr == '' ) {
			 continue;
		 }

		 // HTML-Table aufbauen
		 $html .= '    <tr>';

		 $html .= '        <td> <input type="text" name="'.$this->getName().'[nr]['.$i.']" style="width: 100px" value="'.$this->_escape( $nr ).'" /> </td> ';

		 $html .= '        <td> '.$file;
		 $html .= '            <input type="hidden" name="'.$this->getName().'[filename]['.$i.']" value="'.$this->_escape($file).'" />';
		 $html .= '         </td> ';

		 $html .= '        <td> <input type="file" name="datenblatt_file_'.$i.'" style="width: 100px" /> </td> ';

		 $html .= '    </tr>';
		 }
		 $html .= '    </tbody>';

		 $html .= '    <tr>';
		 $html .= '        <td> <input type="text" name="'.$this->getName().'[nr][new]" style="width: 100px" /> </td> ';
		 $html .= '        <td> Neues Datenblatt </td>';
		 $html .= '        <td> <input type="file" name="datenblatt_file_new" style="width: 100px" /> </td> ';
		 $html .= '    </tr>';

		 $html .= '</table>';

		 // und ein kleiner Hinweis wie man Datenblätter löschen kann
		 $html .= '<small>Entfernen Sie die Datenblatt-Nr. um das Datenblatt zu löschen.</small>';

		 return $html;
	 }
}

4. Backend-Model zum Speichern der Daten

Wie sich unschwer erkenne lässt können wir unsere Daten nun hübsch darstellen. Was fehlt ist der Teil unserer Anwendung der sich darum kümmert das die Daten in Datenbank bzw. Dateisystem abgelegt werden. Hier kommt das Backend-Model ins Spiel: Datei: app/code/local/Test/Test/Renderer/Datenblatt.php

<?php

class Test_Test_Model_Product_Backend_Datenblatt extends Mage_Eav_Model_Entity_Attribute_Backend_Abstract
{

	public function beforeSave($object)
	{
		$path = Mage::getBaseDir('media') . DS . 'catalog' . DS . 'datenblatt';
		$name = $this->getAttribute()->getName();

		$inputs = $object->getData( $name );

		if ( !is_array($inputs['nr']) ) {
			return;
		}

		$res = array();

		foreach( $inputs['nr'] AS $i => $nr ) {

			if ( $nr == '' ) {
				continue;
			}

			try {
				$uploader = new Varien_File_Uploader( 'datenblatt_file_'.$i );
				//$uploader->setAllowedExtensions(array('pdf'));
				$uploader->setAllowRenameFiles(true);
				$uploader->save($path);

				$res[] = $nr.'='.$uploader->getUploadedFileName();

			} catch (Exception $e) {
				if( isset($inputs['filename'][$i]) ) {
					$res[] = $nr.'='.$inputs['filename'][$i];
				}
			}

		}

		$object->setData($this->getAttribute()->getName(),  join("\n", $res) );
		return;
	}

}

Fazit

In Magento ist es sehr einfach Produkte um weitere Standard-Eingabefelder zu erweitern. Sollen diese Eingaben jedoch besondere Eigenschaften bzw. Features aufweisen wird die Aufgabe ein wenig komplexer ist jedoch durchaus zu bewältigen. Der Aufwand lohnt sich in der Regel jedoch: das Pflegen von Produkten wird durch spezialisierte Eingaben deutlich einfacher und ist weniger fehleranfällig.



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
Tobias Vogt am

Hallo Hannes,

das Tutorial ist mittlerweile über 2 Jahre alt. Da kann es durchaus sein das sich Dinge ändern. Erstellt wurde es damals mit der Magento Version 1.3 und hat auch einmal grob funktioniert.

Dennoch wünsche ich dir viel Erfolg und und zwinge ja auch niemanden das hier zu lesen :)

Liebe Grüße

Tobi

Hannes am

Nochmal ich... sorry aber das Tutorial steckt so voller Fehler, ich weiss gar nicht wo ich anfangen soll. Es fehlen die

Hannes am

Hi, schönes Tutorial! Bin grade dabei das mal testweise nachzubauen. Mir ist allerdings aufgefallen, dass du dich bei den Ordnern etwas vertan hast, ich nehme mal an der Pfad für die zweite Datei soll über /Model/ laufen und nicht über /Renderer/, sonst hättest du oben zwei unterschiedliche Inhalte für die selbe Datei beschrieben - oder müssen dort noch zusätzlich wie vom Klassenamen abzuleiten die Ordner Test/Test/Model/Product/Backend/Datenblatt.php angelegt werden?

Tobias Vogt am

Genau akleur. In den System-Ordner gehören auch keine eigenen Dateien. Im Beispiel oben sollte dies eigentlich auch im Modul-Ordner liegen. Mittels Namensräumen (der String vor dem Slash) kannst du Magento sagen in welcher Extension dein Modul zu finden ist. Leider ist das ganze nicht mal so salopp nebenbei in einem Kommentar erklärt sondern erfordert schon einiges an Arbeit. Eventuell helfen wir aber das Buch Magento, das Handbuch für Entwickler oder auch der Artikel von Matthias Zeis: http://www.matthias-zeis.com/archiv/tipps-entwicklung-mit-magento-teil-1

akleur am

Anscheinend kann man die Renderer Datei "Pdf.php" lokal im Ordner "app\code\local\Varien\Data\Form\Element" ablegen... Man braucht sie nicht im Systemordner "lib/Varien/Data/Form/Element" abzulegen..

akleur am

Ich habe gerade gesehen dass die Seite mir die Codezeilen von der config.xml gefressen hat... Hier nochmal den Inhalt der Datei mit eckigen Klammern: [?xml version="1.0"?] [config] [modules] [Meinefirma_Datenblatt] [version]0.1.0[/version] [/Meinefirma_Datenblatt] [/modules] [global] [models] [datenblatt] [class]Model_Product_Backend_Datenblatt[/class] [/datenblatt] [/models] [/global] [/config]

akleur am

Vielen Dank für dieses Tutorial! Damit es bei mir funktioniert (Magento 1.4.1.1)müsste ich folgendes machen:

Mein Modul heisst "Datenblatt" und liegt im Ordner "Meinfirma"

So sieht meine config.xml Datei (im Ordner Meinfirma/Datenblatt/etc):

    0.1.0

        Model_Product_Backend_Datenblatt

Mein Backend-Model Datei "Datenblatt.php" liegt im Ordner Meinefirma/Datenblatt/Model/Product/Backend

MySQL: in der Tabelle "eav_attribute" habe ich lediglich das Feld "backend_model" ausgefüllt: meinefirma_datenblatt_model_product_backend_datenblatt Als "frontend_input" habe ich folgendes eingetragen: pdf

Mein Renderer liegt in einer Datei mit dem Namen "Pdf.php" im Ordner lib/Varien/Data/Form/Element und beinhaltet folgende Klasse: class Varien_Data_Form_Element_Pdf extends Varien_Data_Form_Element_Abstract Diese Klasse hat 2 Methoden: public function construct($data) { parent::construct($data); $this->setType('file'); } public function getHtml() { // Hier den Inhalt der Funktion getElementHtml() von weiter oben übernehmen }

Ich hoffe es hilft jemanden. Über eine Alternative wie ich die Renderer Datei in einem anderen Ordner als /lib/Varien hinkriege wäre ich dankbar.

matthias am

hi Tobias,

könntest du evtl. noch deine config.xml mal posten. Ich hab leider das Problem, dass ich die anscheinend nicht korrekt hinbekommen. Bei mir erscheint, wenn ich die Produkt-Detailseite im Backend aufrufe immer folgender Fehler.

Invalid backend model specified: test/product_backend_datenblatt

Mein XML sieht folgend aus:

        1.0.0

            Test_Test_Model_Product_Backend_Datenblatt
Tobias Vogt am

Hallo Philipp,

leider gibt es im Moment keine Magento-Installation wo du dir das näher anschauen könntest. Das Beispiel läuft ähnlich wie hier auf einer Kundenseite ;) Eine Step-By-Step Anleitung ist eigentlich nicht im Rahmen des Blocks der mehr unter dem Motto "Hilfe-Zur-Selbsthilfe" steht. Ich studiere neben meinen Job noch, muss noch eine Facharbeit schreiben und ähnliches - da ist es mir im Moment leider nicht möglich mich hinzusetzen und eine deutliche Anleitung zu schreiben, sorry!

Ich hoffe du verstehst das!

Tobi

Philipp Sälzer am

Hallo, ich bekomme es trotzdem nicht zum Laufen! Hast du eine Magento Installation wo ich mir das mal LIVE anschauen kann. Oder kannst du mir eine richtige step by step Anleitung geben! Ist für mich sehr Wichtig das ich das zum Laufen bekomme!

Grüße aus HH

Phil

Tobias Vogt am

Hallo Phillip,

zu Frage 1: Nein "local" ist richtig. In locale liegen Übersetzungen, in local dagegen eigene Erweiterungen von Magento. Der Pfad im Artikel ist jedoch falsch: Richtig ist app/code/local. Das habe ich soeben korrigiert.

Zusätzlich musst du noch eine config.xml für das Modul erstellen. Sonst ist das Modul nicht aktiviert bzw. der Namespace nicht aktiv. Wie das mit dem Modulen geht findest du z.B. dort: http://rackspeed.de/forum/magento-faq-extensions/modulerstellung-erstelle-modul-funktion-ueberschreiben-19

Zur Frage 2: Ich habe den Artikel um einen Pfad ergänzt. Du kannst in der Regel am Class-Namen den Pfad ableiten. Ein Unterstrich (_) stellt hier immer einen Ordnerwechsel dar. Ein Slash öffnet immer den Namespace eines Moduls.

schönen Gruß und vielen Dank fürs Lob :)

Tobi

P.S. Zur Artikel schreiben muss ich erst mit meinem Mitschreiber sprechen. Da haben wir uns noch keine echten Gedanken zu gemacht :)

Philipp Sälzer am

alles klar kein Problem! Ach übrigens, du hattest mir auch einen Link zu dem Tut mit den Attributsets geschickt im Magento Forum! Danke, hatte ich schon selber gelöst siehe hier :

http://shop.isetsolar.eu/index.php/inverters/sma/sunny-boy-1200.html

Aber danke für deinen Link, mach weiter so, zwar bis jetzt nicht viele Beiträge aber dafür sehr gute! Wenn du magst, würde mich vllt auch an dem Füllen von Beiträgen hier beteiligen! Sag mal bescheid

Tobias Vogt am

Hallo Philipp,

ich werde schauen das ich mir deine Frage morgen im Laufe des Tages ein wenig näher anschaue - heute ist es schon zu spät :)

Lg, Tobi

Philipp Sälzer am

Hallo,

erstmal danke für das tolle Tutorial! Ich habe aber ledier Probleme beim Einbau!

2 Fragen:

1.app/local/Test/Test/Renderer

Bei mir gibt es unter apps nur einen Ordner der locale heißt aber nicht local ! Meisnt du diesen Ordner?

Den zweiten Code für das Backend Model, wo muss ich den einfügen bzw. wo muss ich Dateien und Ordner anlegen? Würde mic hsehr über Antwort von Dir freuen.

LG

cominaction

Dein Kommentar