Magento: Neue Kundenattribute im Backend-Fomular sichtbar machen

Wer in einem Setup-Skript mittels addAttribute() der Customer-Enitität neue Attribute hinzufügen will, erlebt ab Magento 1.5 eine Überraschung. Das Setup läuft zwar fehlerfrei und die DB-Einträge in customer_eav_attribute sind anschließend vorhanden aber es findet sich im Backend kein neues Formular-Element.

Was mit anderen Entitäten wie z.B. Kategorien problemlos funktioniert und bis Magento 1.4 auch mit Kunden, benötigt hier nun eine zusätzlichen Setup-Schritt mit dem das Attribut explizit dem Admin-Formular hinzugefügt wird.

Dazu existiert eine neue Tabelle customer_form_attribute, die für jedes Attribut festlegt, in welchen Formularen es verwendet wird:

mysql> select * from customer_form_attribute;
+----------------------------+--------------+
| form_code                  | attribute_id |
+----------------------------+--------------+
| adminhtml_customer         |            1 |
| adminhtml_customer         |            3 |
| adminhtml_customer         |            4 |
| checkout_register          |            4 |
| customer_account_create    |            4 |
| customer_account_edit      |            4 |
| adminhtml_customer         |            5 |
| checkout_register          |            5 |
| customer_account_create    |            5 |
| customer_account_edit      |            5 |
| adminhtml_customer         |            6 |
| checkout_register          |            6 |
| customer_account_create    |            6 |
| customer_account_edit      |            6 |
| adminhtml_customer         |            7 |
| checkout_register          |            7 |
| customer_account_create    |            7 |
| customer_account_edit      |            7 |
[...]
+----------------------------+--------------+
88 rows in set (0.00 sec)

Aber dies nur zum Verständnis, natürlich muss man nicht selber an der Datenbank Hand anlegen, es genügen folgende Zeilen nach dem addAttribute()-Aufruf im Setup-Skript:

    Mage::getSingleton('eav/config')
        ->getAttribute('customer', $attributeCode)
        ->setData('used_in_forms', array('adminhtml_customer'))
        ->save();

wobei $attributeCode der eindeutige Code des neuen Attributs ist und 'adminhtml_customer' der Code für das Kundenverwaltungsformular im Backend. Um die Setup-Skripte übersichtlicher zu halten empfiehlt sich eine Erweiterung der Entity Setup-Klasse etwa wie folgt:

class MyNamespace_ExtendableCustomer_Model_Entity_Setup
    extends Mage_Customer_Model_Entity_Setup
{
    public function addAttributeToForm($attributeCode, $formCode)
    {
    	Mage::getSingleton('eav/config')
            ->getAttribute('customer', $attributeCode)
            ->setData('used_in_forms', (array) $formCode)
            ->save();
    }
}

Nutzt man nun diese Klasse für seine Setup-Skripte, die Kundenattribute hinzufügen, ist es ganz einfach:

$this->addAttribute('customer', 'neues_attribut', array( /* ... */);
$this->addAttributeToForm('neues_attribut', 'adminhtml_customer');

Analog kann das Attribut auch zu anderen Formularen hinzugefügt werden (Codes siehe oben).

Danke ans Magento-Forum für den entscheidenden Hinweis!

Update

Achtung, beim Speichern wird zusätzlich das Feld eav_entity_attribute.sort_order gesetzt, unabhängig davon ob dieses schon vorher spezifiziert wurde!

Siehe dazu mein Kommentar auf StackOverflow.

Weiterhin wirkt sich sort_order nur aus wenn beim Anlegen des Attributs user_defined => 0 gesetzt wird, da vor sort_order nach user_defined sortiert wird!

CSS: 3D Ribbon Generator

Inspiriert von diesem Artikel auf pvmgarage.com habe ich kürzlich einen Generator für 3D CSS Ribbons geschrieben und ihn auf css3d.net veröffentlicht. Er erstellt CSS und HTML code für einen cross-browser kompatiblen Ribbon Effekt (nicht beschränkt auf CSS 3!), basierend auf Parametern für Größen und Farben. Die roten Überschriften im untenstehenden Screenshot sind auch so generiert:

Viel Spaß damit!

Wenn Du interessiert daran bist, wie es funktioniert, besuche den obigen Link oder lies über den CSS Dreieck Trick auf css-tricks.com.

Propel 1.5.5: propel-gen schlägt fehl

Neuerdings bekam ich den Propel-Generator nicht mehr zum laufen, zunächst vermutete ich Konflikte wegen unterschiedlicher Versionen die gleichzeitig installiert waren aber auch ein Update und die Sicherstellung, nur mit der neuesten Version zu arbeiten half nicht.

Stellte sich heraus, dass Propel mit den neuesten Versionen von Phing nicht klar kommt, genau genommen allem nach 2.4.2. Es war also leider folgendes nötig:

pear install -f phing/phing-2.4.2

(-f ist der “force”-Parameter, um neuere Versionen zu überschreiben)

Und voilà, keine Fehlermeldungen mehr!

PHP: Undefined constant __COMPILER_HALT_OFFSET__

Diese Notice bekam ich gelegentlich in einer Datei mit __halt_compiler(). Es hat mich einige Zeit gekostet, bis ich das Problem entdeckt habe… der Fehler kam nur wenn ich die Seite innerhalb kurzer Zeit aktualisiert habe, so dass ich nach einer Weile den Opcode Cache als Ursache vermutete.

Tatsächlich hing es mit APC zusammen, ich habe folgenden Bug Report gefunden: http://pecl.php.net/bugs/bug.php?id=15788&edit=2

Lösung: APC updaten oder einfach andere Methoden nutzen, Daten zu speichern als am Ende eines PHP Skripts 😉

PHP-CLI Default-Werte für Kommandozeilenparameter

Heute mal ein nützliches Code-Snippet um Kommandozeilenparameter auszuwerten. Gegeben seien Standardwerte als Array in $conf['params'], beispielsweise

array(
	'narf' => 'zort',
	'foo' => false,
	'bar' => true,
);

Das Skript wollen wir so aufrufen, dass booleans als einwertige Parameter übergeben werden können:

# Standard:
php -f skript.php

# narf=puit, foo=true
php -f skript.php -- narf=puit foo

# bar=false
php -f skript.php -- bar=0

Und so wirds gemacht:

// CLI args override conf['params']
if (isset($argv)) {
	for($i=1;$i<$argc;++$i) {
		list($param,$value) = explode('=', $argv[$i], 2) + array(1=>true);
		$conf['params'][$param] = $value;
	}
}

Der Clou ist die Array-Vereinigung mittels +, der Index 1 wird hier im Gegensatz zu array_merge() nur gesetzt wenn er noch nicht vorhanden ist, somit wird foo behandelt wie foo=true

PHP Array Path

Hier mal ein kleines Snippet 1 um auf ein bestimmtes Element in verschachtelten Arrays zuzugreifen. Nützlich, wenn ein String der Form "key1.key2.key3" vorliegt, und damit auf $array['key1']['key2']['key3'] zugegriffen werden soll.

Funktionen

<?php
/**
  * @param string $spec Spezifikation in der Form 'item_1.item_2.[...].item_n=wert'
  * @param array $array Ziel-Array
  */
function insert_into_array($spec, &$array)
{
    list($path,$value) = explode('=', $spec, 2);
    $current =& $array;
    // setze Referenz $current Schritt für Schritt auf $array['item_1']['item_2'][...]['item_n']
    foreach(explode('.', $path) as $key) {
        $current =& $current[$key];
    }
    // belege dieses Array-Element mit $value
    $current = $value;
}  
/**
 * @param string $path Pfad in der Form 'item_1.item_2.[...].item_n'
 * @param array $array Ursprungs-Array
 */
function &get_from_array($path, &$array)
{
    $current =& $array;
    foreach(explode('.', $path) as $key) {
        $current =& $current[$key];
    }
    return $current;
}

Beispiel: Nutzung

$array = array();
insert_into_array('item.test.6.12134.12.12.343=4546', $array);
insert_into_array('item.test.23=foo', $array);
var_dump(get_from_array('item.test', $array));

Beispiel: Ausgabe

array(2) {
  [6] =>
  array(1) {
    [12134] =>
    array(1) {
      [12] =>
      array(1) {
        ...
      }
    }
  }
  [23] =>
  string(3) "foo"
}

Notes:

  1. Ausgehend von dieser Forumsdiskussion

CSS Top margin vor scheinbar aus dem Nichts

Ein typisches CSS problem aber ich stolpere immer wieder darüber: Die ganze Seite ist 10-20 pixel nach unten verschoben, trotz aller vorstellbaren Varianten von margin:0 in den <html> und <body> Elementen.

Was war es? Ein <h1> irgendwo im floating layout, dessen margin-top alles nach unten verschob.

Notiz an mich selbst: Immer auf die Überschriften achten!

Nützlicher Link: Margin Collapsing