<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>ebene7 &#187; Model</title>
	<atom:link href="http://blog.ebene7.com/schlagwort/model/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.ebene7.com</link>
	<description></description>
	<lastBuildDate>Tue, 04 Jun 2013 18:57:27 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>Programm to an Interface, Dude!</title>
		<link>https://blog.ebene7.com/2011/07/11/programm-to-an-interface-dude/</link>
		<comments>https://blog.ebene7.com/2011/07/11/programm-to-an-interface-dude/#comments</comments>
		<pubDate>Mon, 11 Jul 2011 04:00:12 +0000</pubDate>
		<dc:creator>Daniel</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Qualitätssicherung]]></category>
		<category><![CDATA[Data Mapper]]></category>
		<category><![CDATA[Implementierung]]></category>
		<category><![CDATA[Interface]]></category>
		<category><![CDATA[Magento]]></category>
		<category><![CDATA[Model]]></category>
		<category><![CDATA[OOP]]></category>

		<guid isPermaLink="false">http://blog.ebene7.com/?p=2891</guid>
		<description><![CDATA[Heute mag ich mal ein einfaches Thema aufgreifen, welches jedoch noch nicht wirklich seinen Weg in die PHP-Welt gefunden hat. Es geht um Interfaces. Seit Version 5 kann man in PHP viele der lange vermissten OOP-Features nutzen, aber leider machen &#8230; <a href="https://blog.ebene7.com/2011/07/11/programm-to-an-interface-dude/">Weiterlesen <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Heute mag ich mal ein einfaches Thema aufgreifen, welches jedoch noch nicht wirklich seinen Weg in die PHP-Welt gefunden hat. Es geht um Interfaces. Seit Version 5 kann man in PHP viele der lange vermissten OOP-Features nutzen, aber leider machen viele Entwickler dennoch einen großen Bogen darum.</p>
<p><span id="more-2891"></span>Als ich mich vor ein paar Jahren auf meine Java-Zertifizierung vorbereitet habe, ist mir ein Satz immer wieder begegnet: &#8220;Programm to an interface, not to an implementation!&#8221;. Klingt einfach, ist es auch.</p>
<p>Im Prinzip sagt das nur aus, dass man sich beim Entwickeln nur darauf verlassen sollte, was die jeweilig implementierten Interfaces an Funktionalität anbieten. Wie das ganze dann im Hintergrund implementiert ist, interessiert dann nicht mehr.</p>
<p>Ein kleines Beispiel aus der Praxis: In einem Shop (in dem Fall Magento) werden Bestellungen (Orders) gespeichert. Nun soll ein <a href="http://de.wikipedia.org/wiki/Enterprise_Resource_Planning" target="_blank">ERP-System</a> angebunden werden. Für beide Systeme gibt es entsprechende Models, mit denen die Daten verarbeitet werden können.</p>
<p>Soweit, so gut. Jetzt kommt der unbequeme Teil der Aufgabe. Das Magento-Order-Model erbt einfach nur von verschiedenen anderen Klassen, aber es existiert kein Interface, das dem Entwickler bestimmte Methoden garantiert. An vielen Stellen werden zudem die Parameter mit Type-Hinting überprüft, ob sie eine Magento-Order enthalten.</p>
<p>Das Model für die Anbindung an das ERP-System hat unter Umständen andere Elternklassen und somit auch erstmal keine Gemeinsamkeiten. Das bedeutet, dass wir theoretisch die Objekte gleich behandeln könnten, aber wir sie für das Type-Hinting doch wieder in Wrapper verpacken oder auf Magento-Order-Objekte mappen müssen.</p>
<p>Diese Arbeit könnte man sich komplett sparen, wenn man sich von Anfang an auf ein Interface verlassen hätte. Leider existiert dieses meines Wissens nicht und es für eigene Projekte zu nutzen würde Änderungen in den Magento-Core-Dateien fordern.</p>
<pre>&lt;?php
interface Mage_Sales_Model_Order_Interface
{
  /**
   * Retrieve customer model
   *
   * @return Mage_Customer_Model_Customer_Interface
   */
  public function getCustomer();

  // ...
}</pre>
<p>Damit könnte man sich auf verschiedene Methoden verlassen die der Typ Mage_Sales_Model_Order_Interface (!) zusagt.</p>
<pre>&lt;?php
class Mage_Sales_Model_Order
  extends Mage_Sales_Model_Abstract
  implements Mage_Sales_Model_Order_Interface
{
  // ...
}</pre>
<p>Die Klasse Mage_Sales_Model_Order könnte sich an diesen &#8220;Vertrag&#8221; halten,</p>
<pre>&lt;?php
class Erp_Sales_Model_Order
  extends Erp_Was_Auch_Immer_Model_Abstract
  implements Mage_Sales_Model_Order_Interface
{
  // ...
}</pre>
<p>sowie auch jede andere Klasse.</p>
<pre>&lt;?php
// ...
public function getEmailFromOrder(Mage_Sales_Model_Order_Interface $order)
{
  return $order-&gt;getCustomer()-&gt;getEmail();
}
// ...</pre>
<p>So können wir jede beliebige Klasse mit bestehenden Code weiterhin verarbeiten, ohne dass uns das Type-Hinting dabei im Weg steht. Was die Methode getCustomer() nun intern macht, ist uns egal, solange ein Objekt vom Typ Mage_Customer_Model_Customer_Interface zurückgegeben wird.</p>
<p>Es ist vielleicht auf den ersten Blick etwas umständlicher, für jeden Typ ein Interface zu schreiben, aber das spart man danach wieder locker ein und bleibt dazu auch in der weiteren Entwicklung seiner Anwendung flexibel.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.ebene7.com/2011/07/11/programm-to-an-interface-dude/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Faule Models und große Datenmengen</title>
		<link>https://blog.ebene7.com/2010/12/13/faule-models-und-grosse-datenmengen/</link>
		<comments>https://blog.ebene7.com/2010/12/13/faule-models-und-grosse-datenmengen/#comments</comments>
		<pubDate>Mon, 13 Dec 2010 05:00:46 +0000</pubDate>
		<dc:creator>Daniel</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[CustomLibrary]]></category>
		<category><![CDATA[Custom_Model]]></category>
		<category><![CDATA[Data Mapper]]></category>
		<category><![CDATA[Datenbank]]></category>
		<category><![CDATA[Entwurfsmuster]]></category>
		<category><![CDATA[Magento]]></category>
		<category><![CDATA[Model]]></category>
		<category><![CDATA[MVC]]></category>
		<category><![CDATA[OOP]]></category>

		<guid isPermaLink="false">http://blog.ebene7.com/?p=1536</guid>
		<description><![CDATA[Unter verschiedenen Umständen kann es vorteilhaft sein, wenn Models nicht vollständig geladen und mit Daten befüllt werden, weil das Lesen zu komplex ist (Stichwort EAV-Model) oder wir mit sehr vielen Datensätzen arbeiten und dabei Speicherplatz sparen wollen/müssen. Gleichzeitig wollen wir &#8230; <a href="https://blog.ebene7.com/2010/12/13/faule-models-und-grosse-datenmengen/">Weiterlesen <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Unter verschiedenen Umständen kann es vorteilhaft sein, wenn Models nicht vollständig geladen und mit Daten befüllt werden, weil das Lesen zu komplex ist (Stichwort EAV-Model) oder wir mit sehr vielen Datensätzen arbeiten und dabei Speicherplatz sparen wollen/müssen.</p>
<p>Gleichzeitig wollen wir aber zu jeder Zeit sicherstellen, dass wir das Model wie gewohnt vollständig abfragen und verwenden können. An dieser Stelle kommt eine Technik namens LazyLoading (lazy = engl. faul, träge) zum Einsatz.</p>
<p><span id="more-1536"></span></p>
<p>Nun konstruiere ich einfach ein Beispiel, um den Ablauf besser erklären zu können. In unserer Anwendung brauchen wir ein Model &#8220;Person&#8221;, das Informationen wie Name, Geburtstag und diverse andere speichern kann. Den Namen und den Geburtstag brauchen wir immer (z.B. in der Listenansicht) und die restlichen Felder nur wenn wir eine Detailansicht erstellen.</p>
<p>Wie kommt unser Model nun an die Daten? In der Regel wird sicherlich load() auf dem Modelobjekt aufgerufen oder eine vergleichbare Methode auf der jeweiligen Collection, dann werden die Daten über ein Resourcemodel geladen und letztendlich komplett auf die einzelnen Modelobjekte kopiert.</p>
<p>Um unser Vorhaben umzusetzen müssen wir das etwas abändern. Die Methode load() des Models funktioniert wie gewohnt und lädt alle Daten und die Collection nur die Nötigsten. Um nun sicherzustellen, dass wir trotzdem immer alles abrufen können, muss das Model etwas intelligenter werden.</p>
<p>Die Entwicklung des <a href="http://blog.ebene7.com/schlagwort/custom_model/" target="_self">Custom_Model</a>s hat der eine oder andere ja vielleicht mitbekommen und kennt den grundlegenden Aufbau. Relativ neu ist die Klasse Custom_Model_Persistent, durch die eine weitere Unterscheidung zwischen einfachen Datencontainern und speicherbaren Models möglich wird.</p>
<p>Für das automatische Nachladen muss die Modelklasse nun erweitert werden.</p>
<pre>&lt;?php
class Custom_Model_Persistent extends Custom_Model
{
  private $_isLoaded;

  // ...

  public function load($value = null, $field = 'id')
  {
    if (null === $value &amp;&amp; $this-&gt;_isset($field)) {
      $value = $this-&gt;_get($field);
    }

    $this-&gt;getResource()
         -&gt;loadModel($this, $value, $this-&gt;_mapKey($field))
         -&gt;markLoaded();

    return $this;
  }

  public function isLoaded()
  {
    return $this-&gt;_isLoaded;
  }

  public function markLoaded()
  {
    $this-&gt;_isLoaded = true;
    return $this;
  }

  protected function _get($key, $default = null)
  {
    if (!$this-&gt;_isset($key) &amp;&amp; !$this-&gt;isLoaded()) {
      $this-&gt;load();
    }
    return parent::_get($key, $default);
  }
}</pre>
<p>Die entscheidenen Stellen sind hier der überschriebene zentrale Getter, der ein das &#8220;Default-Load()&#8221; auslöst, wenn der Wert nicht gesetzt ist und die Daten auch noch nicht vollständig geladen wurden und die Methode load() mit optionalen Parameter $value.</p>
<p>Im Beispiel verlasse ich mich darauf, dass das Standardfeld schon gesetzt ist. Eine ordentliche Fehlerbehandlung muss aber unbedingt her, wenn wir das ganze produktiv einsetzen wollen.</p>
<p>Die Methoden isLoaded() bzw markLoaded() sind absichtlich public, damit sie z.B. aus einer Collection aufgerufen werden können, wenn das Model komplett geladen wurde. Eine weitere Möglichkeit wäre ein Vergleich mit einer Liste die gesetzt sein sollten.</p>
<p>Im Moment ist das noch etwas im Entwurf und sogar ungetestet. Wenn euch nun noch was dazu einfällt, dann schreibt einen Kommentar. Wo seht ihr für euch die Vor- oder evtl. auch die Nachteile?</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.ebene7.com/2010/12/13/faule-models-und-grosse-datenmengen/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Methoden-Templates mit __call() erstellen</title>
		<link>https://blog.ebene7.com/2010/09/27/methoden-templates-mit-call-erstellen/</link>
		<comments>https://blog.ebene7.com/2010/09/27/methoden-templates-mit-call-erstellen/#comments</comments>
		<pubDate>Mon, 27 Sep 2010 05:00:32 +0000</pubDate>
		<dc:creator>Daniel</dc:creator>
				<category><![CDATA[Grundlagen]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Custom_Model]]></category>
		<category><![CDATA[Magic Methods]]></category>
		<category><![CDATA[Model]]></category>

		<guid isPermaLink="false">http://blog.ebene7.com/?p=1468</guid>
		<description><![CDATA[Die magischen Funktionen bei PHP sind wie sicher vieles in der Programmierung eine Geschmacks- oder sogar fast schon eine Glaubensfrage. Die einen lieben sie, die anderen hassen sie. Je nach Verwendung können sie schonmal einen Entwickler in den Wahnsinn treiben, &#8230; <a href="https://blog.ebene7.com/2010/09/27/methoden-templates-mit-call-erstellen/">Weiterlesen <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Die magischen Funktionen bei PHP sind wie sicher vieles in der Programmierung eine Geschmacks- oder sogar fast schon eine Glaubensfrage. Die einen lieben sie, die anderen hassen sie. Je nach Verwendung können sie schonmal einen Entwickler in den Wahnsinn treiben, wenn irgendwelche Werte, irgendwie aus dem Nichts kommen und auch wieder dahin verschwinden.</p>
<p>Es gibt aber auch Fälle, in denen die magische Methode __call() den Code lesbarer machen kann und gleichzeitig eine Menge Schreibarbeit einspart.</p>
<p><span id="more-1468"></span>Stellen wir uns einfach mal vor, wir hätten ein Model mit 20 Werten und wollen es anhand eines bestimmten Feldes laden.</p>
<pre>&lt;?php
$model-&gt;load($id);  // lädt das Model mit Defaultkey = $id, oder

$model-&gt;load('Daniel', 'name');  // Feld 'name' = 'Daniel'</pre>
<p>Letzteres ist durchaus gut zu lesen und lädt bestenfalls auch die richtigen Daten. Mit __call() lässt sich das ganze ohne viel Aufwand noch etwas dekorieren, so dass wir das Model dann so laden können:</p>
<pre>&lt;?php
$model-&gt;loadByName('Daniel');</pre>
<p>Nun könnte man entweder für alle 20 Felder die jeweiligen Methoden schreiben, oder eine Art Template mit __call() und eines RegEx erstellen und den Aufruf einfach deligieren.</p>
<pre>&lt;?php
class Custom_Model
{
  public function __call($method, $args)
  {
    if (preg_match('/^loadBy([a-zA-Z]*?)$/', $method, $matches))
      return $this-&gt;load($args[0], $matches[1]);
    }
    // ...
  }
}</pre>
<p>Der Vorteil ist ganz klar die eingesparte Tipparbeit und eine übersichtlichere Klasse, da wir alleine für diese Funktionalität 20 Methoden weniger im Code haben. Jedoch ist auch der Nachteil nicht außer Acht zu lassen. Weil die Methoden nur virtuell existieren, sind sie z.B. für die IDE unsichtbar und sind daher auch für neue Entwickler nicht sofort erkennbar.</p>
<p>Es bleibt also wieder dem eigenen Geschmack überlassen, für welchen Weg man sich entscheidet.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.ebene7.com/2010/09/27/methoden-templates-mit-call-erstellen/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Stringumwandlung Underscore/CamelCase</title>
		<link>https://blog.ebene7.com/2010/07/23/stringumwandlung-underscore-camelcase/</link>
		<comments>https://blog.ebene7.com/2010/07/23/stringumwandlung-underscore-camelcase/#comments</comments>
		<pubDate>Fri, 23 Jul 2010 05:00:27 +0000</pubDate>
		<dc:creator>Daniel</dc:creator>
				<category><![CDATA[Datenbank]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Tipps und Tricks]]></category>
		<category><![CDATA[CamelCase]]></category>
		<category><![CDATA[Custom_Model]]></category>
		<category><![CDATA[Model]]></category>
		<category><![CDATA[String]]></category>
		<category><![CDATA[Umwandlung]]></category>
		<category><![CDATA[Underscore]]></category>
		<category><![CDATA[Zend Framework]]></category>

		<guid isPermaLink="false">http://blog.ebene7.com/?p=1096</guid>
		<description><![CDATA[Häufig ist beim Umgang mit Datenquellen eine Stringumwandlung zwischen der Underscore- und CamelCase-Schreibweise erforderlich. Wo das sinnvoll ist und wie man es einfach anwendet, ist das Thema des heutigen Artikels. Die Underscore-Schreibart (z.B. &#8220;user_id&#8221;) findet man sehr oft als Spaltenbezeichner &#8230; <a href="https://blog.ebene7.com/2010/07/23/stringumwandlung-underscore-camelcase/">Weiterlesen <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Häufig ist beim Umgang mit Datenquellen eine Stringumwandlung zwischen der Underscore- und CamelCase-Schreibweise erforderlich. Wo das sinnvoll ist und wie man es einfach anwendet, ist das Thema des heutigen Artikels.</p>
<p><span id="more-1096"></span>Die Underscore-Schreibart (z.B. &#8220;user_id&#8221;) findet man sehr oft als Spaltenbezeichner in Datenbanken oder auch in XML-Dateien, im PHP-Model (siehe auch mein <a href="http://blog.ebene7.com/2010/04/29/es-ist-ein-model-und-es-sieht-gut-aus/" target="_self">Artikel zum Custom_Model</a>) hingegen wird oft CamelCase (z.B. &#8220;userId&#8221;) verwendet.</p>
<p>Es wird also an der Schnittstelle zwischen dem Model und der Datenquelle eine Art Key-Mapping benötigt. Das lässt sich zwar recht einfach mit regulären Ausdrücken erschlagen, aber man kann sich auch der Zend_Filter-Klassen bedienen, wenn man in einem ZF-Umfeld arbeitet.</p>
<p>Die Anwendung ist wie so vieles beim Zend Framework recht einfach:</p>
<pre>&lt;?php
$toCamelCaseFilter = new Zend_Filter_Word_UnderscoreToCamelCase();
echo $toCamelCaseFilter-&gt;filter('user_id');

// Ausgabe: UserId</pre>
<p>Umgekehrt, wen wird es wundern, ist das natürlich genauso leicht:</p>
<pre>&lt;?php
$toUnderscoreFilter = new Zend_Filter_Word_CamelCaseToUnderscore();
echo $toUnderscoreFilter-&gt;filter('UserId');

// Ausgabe: User_Id</pre>
<p>Wenn man nicht extra eine Instanz erzeugen will, dann lassen sich die Zend Filter auch mit einem statischen Aufruf nutzen.</p>
<pre>&lt;?php
echo Zend_Filter::filterStatic('bitte_einmal_camel_case',
                               'Word_UnderscoreToCamelCase');

// Ausgabe: BitteEinmalCamelCase</pre>
<p>Das war es dann auch schon wieder für heute. Ich hoffe, dass euch der Artikel gefallen hat und ihr viele kreative Ideen zum Arbeiten mit den Filtern habt.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.ebene7.com/2010/07/23/stringumwandlung-underscore-camelcase/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Magento-Cheatsheet</title>
		<link>https://blog.ebene7.com/2010/05/19/magento-cheatsheet/</link>
		<comments>https://blog.ebene7.com/2010/05/19/magento-cheatsheet/#comments</comments>
		<pubDate>Wed, 19 May 2010 05:00:08 +0000</pubDate>
		<dc:creator>Daniel</dc:creator>
				<category><![CDATA[Notiz an mich]]></category>
		<category><![CDATA[Snippets]]></category>
		<category><![CDATA[Cheatsheet]]></category>
		<category><![CDATA[Magento]]></category>
		<category><![CDATA[Model]]></category>
		<category><![CDATA[Shop]]></category>
		<category><![CDATA[Spickzettel]]></category>

		<guid isPermaLink="false">http://blog.ebene7.com/?p=794</guid>
		<description><![CDATA[Für diverse häufig gebrauchte Funktionen mit dem doch sehr umfangreichen Shopsystem hier eine kleine Übersicht (wird zukünftig noch erweitert): Diverse Werte &#60;?php $storeId = Mage::app()-&#62;getStore()-&#62;getId(); $storeName = Mage::app()-&#62;getStore()-&#62;getName(); $storeCode = Mage::app()-&#62;getStore()-&#62;getCode(); $groupId = Mage::app()-&#62;getStore()-&#62;getGroupID(); $groupName = Mage::app()-&#62;getStore()-&#62;getGroup()-&#62;getName(); $websiteName = Mage::app()-&#62;getWebsite()-&#62;getName(); &#8230; <a href="https://blog.ebene7.com/2010/05/19/magento-cheatsheet/">Weiterlesen <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Für diverse häufig gebrauchte Funktionen mit dem doch sehr umfangreichen Shopsystem hier eine kleine Übersicht (wird zukünftig noch erweitert):<br />
<span id="more-794"></span></p>
<h3>Diverse Werte</h3>
<pre><code>&lt;?php $storeId = Mage::app()-&gt;getStore()-&gt;getId(); $storeName = Mage::app()-&gt;getStore()-&gt;getName(); $storeCode = Mage::app()-&gt;getStore()-&gt;getCode(); $groupId = Mage::app()-&gt;getStore()-&gt;getGroupID(); $groupName = Mage::app()-&gt;getStore()-&gt;getGroup()-&gt;getName(); $websiteName = Mage::app()-&gt;getWebsite()-&gt;getName();</code></pre>
<h3>Session</h3>
<pre>&lt;?php
$sessionId  = Mage::getModel('core/session')-&gt;getSessionId();
$customerId = Mage::getModel('customer/session')-&gt;getCustomerId();
$vistitorId = Mage::getModel('core/session')-&gt;getVisitorId();</pre>
<h3>Models</h3>
<pre><code>&lt;?php $customer = Mage::getModel('customer/customer'); $order = Mage::getModel('sales/order'); $product = Mage::getModel('catalog/product'); </code></pre>
<h4>Produkt-Model</h4>
<pre>$product    = Mage::getModel('catalog/product')-&gt;load($productId);
$categories = $product-&gt;getCategoryCollection();</pre>
<h3>Konfiguration</h3>
<p>auslesen</p>
<pre>$value = Mage::getStoreConfig('[MODULE]/[SECTION]/[FIELD]', $storeId);</pre>
<p>und wieder schreiben</p>
<pre>// Wert für Store 1 setzen
Mage::getConfig()-&gt;saveConfig('path/to/config/value', 12343, 'stores', 1);

// oder als Defaultwert
Mage::getConfig()-&gt;saveConfig('path/to/config/value', 12343);</pre>
<h3>Blocks</h3>
<pre><code>&lt;?php </code>$block = Mage::app()-&gt;getLayout()-&gt;createBlock('log/...');</pre>
<h3>Helpers</h3>
<pre>&lt;?php
$helper = Mage::helper('helper');</pre>
<h3>Logging</h3>
<pre><code>&lt;?php Mage::log('Schreibe das ins Logfile');</code></pre>
<h3>Sonstiges</h3>
<p>Store-Umgebung simulieren</p>
<pre><code>Mage::app()-&gt;getLocale()-&gt;emulate($storeId);</code></pre>
<p>Events auslösen</p>
<pre><code>Mage::dispatchEvent($eventName);</code></pre>
<p>Es geht weiter&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.ebene7.com/2010/05/19/magento-cheatsheet/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Implementieren einer einfachen Plugin-Schnittstelle</title>
		<link>https://blog.ebene7.com/2010/05/12/implementieren-einer-einfachen-plugin-schnittstelle/</link>
		<comments>https://blog.ebene7.com/2010/05/12/implementieren-einer-einfachen-plugin-schnittstelle/#comments</comments>
		<pubDate>Wed, 12 May 2010 05:00:26 +0000</pubDate>
		<dc:creator>Daniel</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Tutorial]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[Custom_Model]]></category>
		<category><![CDATA[Design Patterns]]></category>
		<category><![CDATA[Entwurfsmuster]]></category>
		<category><![CDATA[Interface]]></category>
		<category><![CDATA[Magic Methods]]></category>
		<category><![CDATA[Model]]></category>
		<category><![CDATA[MVC]]></category>
		<category><![CDATA[OOP]]></category>
		<category><![CDATA[Plugin]]></category>
		<category><![CDATA[SPL]]></category>
		<category><![CDATA[Strategy Pattern]]></category>

		<guid isPermaLink="false">http://blog.ebene7.com/?p=766</guid>
		<description><![CDATA[Heute geht es darum, wie wir uns eine einfache Plugin-Schnittstelle für unsere Objekte bauen können. Als Beispiel, wie sollte es auch anders sein, eignet sich unsere Custom_Model-Klasse aus meinen vergangenen Artikeln. Und schon wieder die Frage, was kann unsere Klasse &#8230; <a href="https://blog.ebene7.com/2010/05/12/implementieren-einer-einfachen-plugin-schnittstelle/">Weiterlesen <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Heute geht es darum, wie wir uns eine einfache Plugin-Schnittstelle für unsere Objekte bauen können. Als Beispiel, wie sollte es auch anders sein, eignet sich unsere Custom_Model-Klasse aus meinen <a href="http://blog.ebene7.com/2010/04/29/es-ist-ein-model-und-es-sieht-gut-aus/" target="_self">vergangenen Artikeln</a>.</p>
<p><span id="more-766"></span>Und schon wieder die Frage, was kann unsere Klasse dann mehr? Oder warum machen wir das?</p>
<p>Oftmals haben wir Objekte, die eine gewisse Menge identischer Methoden benötigen, jedoch nicht aus einer Vererbungslinie stammen.</p>
<p>Durch die Plugins können wir dann quasi Funktionen an unsere Objekte dran kleben und nutzen. Diesen Code müssen wir nur einmal schreiben, haben auch nur eine Stelle im Code zu pflegen und können dadurch sicherstellen, dass alles gleich funktioniert.</p>
<p>So erweitern wir nun also zuerst wie gewohnt unser <code>Custom_Model</code> um ein paar neue Methoden und Eigenschaften.</p>
<pre>&lt;?php
class Custom_Model implements Custom_Model_Interface
{
  // ...
  protected $_plugins;

  public function registerPlugin() {}
  public function unregisterPlugin() {}
  // ...
}</pre>
<p>Und das Interface für die Plugins.</p>
<pre>&lt;?php
interface Custom_Model_Plugin_Interface {}</pre>
<p>Beim Speichern der Plugins kommt wieder die Klasse <a href="http://de3.php.net/manual/de/class.splobjectstorage.php" target="_blank">SplObjectStorage</a> zum Einsatz, welche wir ja schon bei der <a href="http://blog.ebene7.com/2010/05/07/es-ist-ein-model-und-es-sieht-gut-aus-der-observer/" target="_self">Implementierung des Obeservers</a> verwendet haben. Demnach ist es auch nicht überraschend, dass die Registrierung an die Abmeldung der Plugins sehr einfach wird.</p>
<pre>public function registerPlugin(Custom_Model_Plugin_Interface $plugin)
{
  $this-&gt;_plugins-&gt;attach($plugin);
  return $this;
}

public function unregisterPlugin(Custom_Model_Plugin_Interface $plugin)
{
  $this-&gt;_plugins-&gt;detach($plugin);
  return $this;
}</pre>
<p>Die Models müssen das Interface <code>Custom_Model_Plugin_Interface</code> implementieren. Dieses Interface ist lediglich ein Flag- oder Marker-Interface, d.h. es schreibt keine Methoden vor.</p>
<p>Jetzt noch eine kleine Änderung an der Methode <code>__call()</code> und wir können schon mit dem Beispiel experimentieren.</p>
<pre>public function __call($method, $args)
{
  // Magie für die Getter &amp; Setter

  foreach($this-&gt;_plugins as $plugin) {
    if(method_exists($plugin, $method)) {
      array_unshift($args, $this);
      return call_user_func_array(array($plugin, $method), $args);
    }
  }

  // Fehlerbehandlung
}</pre>
<p>Durch <code>__call()</code> können wir unsere Plugin-Methoden so nutzen, als gehörten sie direkt zum Objekt. Die Art wie die Plugins verwendet werden  hat Vor-, aber auch Nachteile.</p>
<p>Die Liste der Plugins wird der Reihe nach durchlaufen und die erstbeste passende Methode wird aufgerufen. Auch die Getter oder Setter könnten wir nicht mehr überschreiben. Wenn das alles jedoch gewollt ist, dann lässt sich das aber auch mit wenigen Handgriffen erweitern.</p>
<p>Nun aber mal was praktisches zum Anschauen: Angenommen wir haben eine Model-Klasse mit Abhängigkeit zu anderen Objekten.</p>
<pre>&lt;?php
class Book extends Custom_Model
{
  protected $_id;
  protected $_title;
  protected $_ownerId;
}</pre>
<p>Nun wollen wir nicht nur die ID des Besitzers erfahren, sondern auch das Objekt das bekommen können. Dafür spendieren wir ein Plugin.</p>
<pre>&lt;?php
class GetOwnerPlugin implements Custom_Model_Plugin_Interface
{
  public function getOwner($sender)
  {
    $id = $sender-&gt;getOwnerId();

    // lade User-Objekt $owner anhand der $id

    return $owner;
  }
}</pre>
<p>Auf die Fehlerbehandlung habe ich an der Stelle mal verzichtet, sollte jedoch unbedingt stattfinden.</p>
<pre>&lt;?php
$book = new Book();
$book-&gt;registerPlugin(new GetOwnerPlugin());
$book-&gt;setOwnerId(42);
$owner = $book-&gt;getOwner();</pre>
<p>Spätestens hier fällt sicher nicht nur mir auf, dass das Konzept noch Lücken hat. Theoretisch kann es sein, dass zu verschiedenen IDs ein User-Objekt nachgeladen werden soll (<code>getAuthor()</code>, <code>getModifier()</code>, <code>getLastVisitor()</code>&#8230;) und dann bräuchten wir sämtliche Methoden in unserem Plugin oder verschiedene Plugins.</p>
<p>Dann hätten wir aber auch wieder mehrfachen Code und nichts gewonnen. Daher werde ich die Pluginschnittstelle noch etwas aufbohren und das Ergebnis dann in einem meiner nächsten Artikel vorstellen.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.ebene7.com/2010/05/12/implementieren-einer-einfachen-plugin-schnittstelle/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Es ist ein Model und es sieht gut aus &#8211; Der Observer</title>
		<link>https://blog.ebene7.com/2010/05/07/es-ist-ein-model-und-es-sieht-gut-aus-der-observer/</link>
		<comments>https://blog.ebene7.com/2010/05/07/es-ist-ein-model-und-es-sieht-gut-aus-der-observer/#comments</comments>
		<pubDate>Fri, 07 May 2010 05:00:06 +0000</pubDate>
		<dc:creator>Daniel</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Tutorial]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[CustomLibrary]]></category>
		<category><![CDATA[Custom_Model]]></category>
		<category><![CDATA[Data Mapper]]></category>
		<category><![CDATA[Design Patterns]]></category>
		<category><![CDATA[Entwurfsmuster]]></category>
		<category><![CDATA[Interface]]></category>
		<category><![CDATA[Listeners]]></category>
		<category><![CDATA[Model]]></category>
		<category><![CDATA[MVC]]></category>
		<category><![CDATA[Observable]]></category>
		<category><![CDATA[Observer]]></category>
		<category><![CDATA[SPL]]></category>

		<guid isPermaLink="false">http://blog.ebene7.com/?p=434</guid>
		<description><![CDATA[Zum Abschluss dieser Woche werden wir heute noch schnell das Observer-Pattern verbauen und dazu das passende Interface SplSubject implementieren. In dem Zusammenhang findet dann auch die Klasse SplObjectStorage eine sinnvolle Verwendung. Zuerst erweitern wir das Interface Custom_Model_Interface um SplSubject und &#8230; <a href="https://blog.ebene7.com/2010/05/07/es-ist-ein-model-und-es-sieht-gut-aus-der-observer/">Weiterlesen <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Zum Abschluss dieser Woche werden wir heute noch schnell das <a href="http://de.wikipedia.org/wiki/Observer_%28Entwurfsmuster%29" target="_blank">Observer-Pattern</a> verbauen und dazu das passende <a href="http://www.php.net/manual/de/class.splsubject.php" target="_blank">Interface SplSubject</a> implementieren. In dem Zusammenhang findet dann auch die Klasse <a href="http://www.php.net/manual/de/class.splobjectstorage.php" target="_blank">SplObjectStorage</a> eine sinnvolle Verwendung.</p>
<p><span id="more-434"></span>Zuerst erweitern wir das Interface Custom_Model_Interface um SplSubject und ein paar eigene Methoden.</p>
<pre>&lt;?php
interface Custom_Model_Interface
    extends ArrayAccess,
            Iterator,
            Countable,
            SplSubject
{
  public function mark();
  public function modified();
  public function cleanModified();
  public function modifiedKeys();
}</pre>
<p>Anschliessend kümmern wir uns um die Klassen Custom_Model und bauen alles Nötige ein.</p>
<pre>&lt;?php
class Custom_Model implements Custom_Model_Interface
{
  // ...
  protected $_observers;
  protected $_modified;

  public function attach(SplObserver $observer) {}
  public function detach(SplObserver $observer) {}
  public function notify() {}

  public function mark() {}
  public function modified() {}
  public function cleanModified() {}
  public function modifiedKeys() {}
  // ...
}</pre>
<p>So weit, so gut! Die ersten drei Methoden <code>attach()</code>, <code>detach()</code> und <code>notify()</code> werden durch das Interface SplSubject vorgeschrieben und sind die Schnittstelle, um Observer am Objekt zu registrieren, sie zu entfernen oder zu benachrichtigen.</p>
<p>Mit den vier weiteren können wir den Status des Objektes setzen oder kontrollieren. Die beiden protected Eigenschaften enthalten eine Liste von Typ SplObjectStorage für die Observer und ein einfaches Array für den jeweiligen Status eines Feldes.</p>
<p>Bevor wir das verwenden können, müssen wir es im Konstruktor initialisieren.</p>
<pre>public final function __construct()
{
  $this-&gt;_observers = new SplObjectStorage();
  $this-&gt;cleanModified();
  // ...
}</pre>
<p>Der Vorteil von SplObjectStorage ist der, dass jedes Objekt nur einmal darin vorkommen kann und wir uns um die Steuerung keine Gedanken machen müssen. Das macht dann auch das Hinzufügen und Entfernen eines Observers sehr einfach.</p>
<pre>public function attach(SplObserver $observer)
{
  $this-&gt;_observers-&gt;attach($observer);
  return $this;
}

public function detach(SplObserver $observer)
{
  $this-&gt;_observers-&gt;detach($observer);
  return $this;
}</pre>
<p>Mir zaubert das immer ein breites Grinsen ins Gesicht, wenn eine Aufgabe mit so wenig Code gelöst werden kann und der Doc-Block teilweise länger ist als die Funktion.</p>
<p>Aber nun wieder zum Thema. Bevor wir uns an die wirklich einfache Benachrichtigung machen, werde ich noch schnell auf die Sache mit der Statuskontrolle eingehen. Eine Aktion, z.B. <code>_set()</code> setzt den &#8220;modified&#8221;-Status eines Feldes, so dass jeder Schreibzugriff auf das Modell erkennbar wird.</p>
<p>Dadurch haben wir später die Möglichkeit, nur zu benachrichtigen, wenn es nötig ist oder auch nur die Felder zu speichern, die sich verändert haben.</p>
<p>Um eine Änderung bekannt zu machen verwenden wir <code>mark()</code>.</p>
<pre>public function mark($key)
{
  if($this-&gt;keyExists($key)) {
    $this-&gt;_modified[$this-&gt;_mapKey($key)] = true;
  }
  return $this;
}</pre>
<p>Die Methode erwartet den Namen eines existierenden Feldes und setzt ein Flag im Array.</p>
<p>Um den Status des Objektes oder eines einzelnen Feldes abfragen zu können haben wir dann <code>modified()</code>.</p>
<pre>public function modified($key = null)
{
  if(null !== $key) {
    return isset($this-&gt;_modified[$this-&gt;_mapKey($key)]);
  }
  return count($this-&gt;_modified) &gt; 0;
}</pre>
<p>Durch die Angabe des Parameters $key wird ein Feld abgefragt, ansonsten der Gesamtstatus. Nach dem selben Prinzip funktioniert dann auch <code>cleanModified()</code>, um den Status wieder zurück zu setzen.</p>
<pre>public function cleanModified($key = null)
{
  if(null != $key) {
    unset($this-&gt;_modified[$this-&gt;_mapKey($key)]);
  } else {
    $this-&gt;_modified = array();
  }
  return $this;
}</pre>
<p>Falls wir eine Liste der veränderten Felder brauchen, dann ist dafür <code>modifiedKeys()</code> vorgesehen.</p>
<pre>public function modifiedKeys()
{
  $keys = array();

  foreach($this-&gt;_keys as $key) {
    if($this-&gt;modified($key)) {
      $keys[] = $key;
    }
  }

  return $keys;
}</pre>
<p>Nachdem wir das alles durch haben, können wir uns nun <code>notify()</code> widmen, bevor wir unser Werk an ein paar Beispielen testen können.</p>
<pre>public function notify()
{
  if($this-&gt;modified()) {
    foreach($this-&gt;_observers as $observer) {
      $observer-&gt;update($this);
    }
  }
  $this-&gt;cleanModified();
  return $this;
}</pre>
<p>Die Methode macht im Prinzip nichts weiter, als bei allen in der Liste befindlichen Observer-Objekten <code>update()</code> aufzurufen und sich selbst als Parameter mitzugeben. Dadurch weiß die Observerklasse gleich, welches Objekt sich verändert hat.</p>
<p>Das folgende Beispiel zeigt einen einfachen Beobachter, der nur den Klassennamen von $subject ausgibt und eine Liste der geänderten Felder, wenn das Objekt vom Typ Custom_Model ist.</p>
<pre>class myObserver implements SplObserver
{
  public function update(SplSubject $subject)
  {
    echo get_class($subject) . ' hat sich verändert&lt;br/&gt;';
    if($subject instanceof Custom_Model) {
      print_r($subject-&gt;modifiedKeys());
    }
  }
}

$model = new Person();
$model-&gt;attach(new myObserver());
$model-&gt;setId(42)
      -&gt;setVorname('Bart');

$model-&gt;notify();</pre>
<p>Das interessante an dem Observer-Muster ist die lose Kopplung der Objekte und die Tatsache, dass ein Observer beliebig viele Subjekte beobachten kann und umgekehrt ein Subjekt beliebig viele Observer haben kann.</p>
<p>So, das war es dann auch schon für diese Woche und zu diesem Thema. Wie ich einst schon sagte, ist es ein sehr ergiebiges Thema, weil sich viele Techniken in sinnvoller Verbindung anwenden lassen. Bislang haben wir trotzdem erst nur einen Datenklumpen mit etwas Dekoration.</p>
<p>Richtig interessant wird es dann, wenn wir die Models über Mapper-Klassen laden und speichern können, aber das wird dann evtl. das Thema einer neuen Serie&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.ebene7.com/2010/05/07/es-ist-ein-model-und-es-sieht-gut-aus-der-observer/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>
