<?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; Tutorial</title>
	<atom:link href="http://blog.ebene7.com/kategorie/tutorial/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>Eigener Webservice in fünf Minuten</title>
		<link>https://blog.ebene7.com/2010/05/19/eigener-webservice-in-fuenf-minuten/</link>
		<comments>https://blog.ebene7.com/2010/05/19/eigener-webservice-in-fuenf-minuten/#comments</comments>
		<pubDate>Wed, 19 May 2010 05:00:03 +0000</pubDate>
		<dc:creator>Daniel</dc:creator>
				<category><![CDATA[Entwicklung]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Tutorial]]></category>
		<category><![CDATA[SOAP]]></category>
		<category><![CDATA[Webservice]]></category>

		<guid isPermaLink="false">http://blog.ebene7.com/?p=836</guid>
		<description><![CDATA[Für verschiedene Aufgaben ist es notwendig, dass mehrere Anwendungen möglichst nahtlos miteinander kommunizieren können. Dafür eignen sich Webservices sehr gut und wenn man keine Wissenschaft daraus machen möchte, kann man auch schnelle Ergebnisse erzielen Damit wir zum Testen ein paar &#8230; <a href="https://blog.ebene7.com/2010/05/19/eigener-webservice-in-fuenf-minuten/">Weiterlesen <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Für verschiedene Aufgaben ist es notwendig, dass mehrere Anwendungen möglichst nahtlos miteinander kommunizieren können. Dafür eignen sich <a href="http://de.wikipedia.org/wiki/Webservice" target="_blank">Webservices</a> sehr gut und wenn man keine Wissenschaft daraus machen möchte, kann man auch schnelle Ergebnisse erzielen</p>
<p><span id="more-836"></span>Damit wir zum Testen ein paar &#8220;lebendige&#8221; Daten haben, schreiben wir uns erstmal eine kleine Testklasse.</p>
<pre><code>&lt;?php
// myDate.php
class myDate
{
  function date($format='Y-m-d H:i:s', $timestamp = null)
  {
    return date($format, ($timestamp ? $timestamp : time()));
  }
}</code></pre>
<p>Im Prinzip kann hier eine beliebige Klasse verwendet werden.</p>
<p>Da wir nun eine Datenquelle haben, müssen wir diese auch zugänglich machen. Der Webservice dient aber nicht nur dem Datenaustausch. Objekte und Funktionen können damit über das Netzwerk fernbedient werden. Im besten Fall merkt man den Unterschied nicht, ob man mit einem lokalen Objekt oder einem Webservice arbeitet.</p>
<p>Nun aber zu unserem Server&#8230;</p>
<pre><code>&lt;?php
// server.php
require_once('myDate.php');

$server = new SoapServer(null, array('uri' =&gt; 'http://test-webservice/'));
$server-&gt;setClass('myDate');  // oder $server-&gt;setObject(new myDate());
$server-&gt;handle();</code></pre>
<p>Zuerst binden wir die Klasse <code>myDate</code> ein. Innerhald einer Anwendung wird das wahrscheinlich über Autoload geschehen. Die Instanz des Servers erzeuge ich in minimalster Form, ebenso sparsam bin ich beim Aufruf von handle(). Damit der Server arbeiten kann, müssen wir ihm den Klassennamen oder eine fertiges Objekt übergeben.</p>
<p>Wer nun mehr Code beim Client erwartet, den muss ich leider enttäuschen, es wird noch weniger.</p>
<pre><code>&lt;?php
// client.php
$url = 'http://' . $_SERVER['SERVER_NAME']
     . dirname($_SERVER['PHP_SELF']) . '/server.php';
$client = new SoapClient(null, array('location' =&gt; $url,
                                     'uri' =&gt; 'http://test-webservice/'));

echo 'Output: ' . $client-&gt;date();</code></pre>
<p>Wir erzeugen uns eine Instanz des Soap-Clients und können dann wie auf dem &#8220;echten&#8221; Objekt Methoden aufrufen. Die Methode Date funktionert wie die PHP-Funktion <code>date()</code> mit einem Standardformat.</p>
<p>Ein wenig mehr ist schon zu beachten, wenn wir den Server in einer Controller-Action verbauen wollen. In dem Fall müssen wir die Ausgabe buffern und ans das Response-Objekt übergeben.</p>
<pre><code>$server = new SoapServer(null, array('uri' =&gt; 'http://test-webservice/'));
$server-&gt;setObject($helper);

ob_start();
$server-&gt;handle(file_get_contents('php://input'));
$this-&gt;getResponse()-&gt;appendBody(ob_get_contents());
ob_clean();</code></pre>
<p>Wer mehr zu dem Thema wissen will, sollte sich das Kapitel <a href="http://de2.php.net/manual/en/book.soap.php" target="_blank">SOAP im PHP-Manual</a> ansehen.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.ebene7.com/2010/05/19/eigener-webservice-in-fuenf-minuten/feed/</wfw:commentRss>
		<slash:comments>1</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>
		<item>
		<title>Es ist ein Model und es sieht gut aus &#8211; Iterator</title>
		<link>https://blog.ebene7.com/2010/05/06/es-ist-ein-model-und-es-sieht-gut-aus-iterator/</link>
		<comments>https://blog.ebene7.com/2010/05/06/es-ist-ein-model-und-es-sieht-gut-aus-iterator/#comments</comments>
		<pubDate>Thu, 06 May 2010 05:00:37 +0000</pubDate>
		<dc:creator>Daniel</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Tutorial]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[Countable]]></category>
		<category><![CDATA[CustomLibrary]]></category>
		<category><![CDATA[Custom_Model]]></category>
		<category><![CDATA[Foreach]]></category>
		<category><![CDATA[Interface]]></category>
		<category><![CDATA[Iterator]]></category>
		<category><![CDATA[Model]]></category>
		<category><![CDATA[MVC]]></category>
		<category><![CDATA[Schleife]]></category>
		<category><![CDATA[SPL]]></category>

		<guid isPermaLink="false">http://blog.ebene7.com/?p=430</guid>
		<description><![CDATA[Nachdem sich unsere Klasse Custom_Model während der letzten Artikel gut entwickelt hat, werden wir daran heute gleich anknüpfen und das Iterator-Interface implementieren. Was kann unser Model dann, was es vorher noch nicht konnte? Durch die Verwendung des Interfaces können wir &#8230; <a href="https://blog.ebene7.com/2010/05/06/es-ist-ein-model-und-es-sieht-gut-aus-iterator/">Weiterlesen <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Nachdem sich unsere Klasse Custom_Model während der letzten Artikel gut entwickelt hat, werden wir daran heute gleich anknüpfen und das <a href="http://www.php.net/manual/de/class.iterator.php" target="_blank">Iterator-Interface</a> implementieren.</p>
<p>Was kann unser Model dann, was es vorher noch nicht konnte? Durch die Verwendung des Interfaces können wir die gespeicherten Daten in einer foreach-Schleife verarbeiten. Das kann z.B. beim erzeugen von Tabellen erforderlich sein.</p>
<p><span id="more-430"></span>Wie immer beginne ich mit einer Skizze der heutigen Erweiterungen.</p>
<pre>&lt;?php
interface Custom_Model_Interface
  extends ArrayAccess,
          Iterator,
          Countable
{
}</pre>
<p>Unser Interface Custom_Model_Interface erweitert zunächst das Iterator-Interface und auch <a href="http://www.php.net/manual/de/class.countable.php" target="_blank">Countable</a>. Dadurch lassen sich dann die Elemente des Objektes zählen und in Verbindung mit ArrayAccess auch beliebig in einer for-Schleife verarbeiten.</p>
<p>In unserer Klasse müssen wir nun die entsprechenden Methoden deklarieren.</p>
<pre>&lt;?php
class Custom_Model implements Custom_Model_Interface
{
  // ...

  public function current() {}
  public function key() {}
  public function next() {}
  public function rewind() {}
  public function valid() {}
  public function count() {}

// ...
}</pre>
<p>Jedem, der lange genug mit PHP arbeitet sollte nun was auffallen. Richtig! Wir halten unsere &#8220;Feldnamen&#8221; in einem Array und zu fast jeder Methode gibt es eine gleichnamige <a href="http://de3.php.net/manual/de/ref.array.php" target="_blank">Array-Funktion</a>.</p>
<p>Da wir aber an meistens an die Werte und nicht an die Namen der Felder wollen, müssen wir etwas tricksen.</p>
<p>Die erste Methode <code>current()</code> gibt das jeweilige Element an der Position des internen Zeigers zurück und entspricht damit der gleichnamigen Array-Funktion <a href="http://de3.php.net/manual/de/function.current.php" target="_blank"><code>current()</code></a>.</p>
<pre>public function current()
{
  return $this-&gt;_get(current($this-&gt;_keys));
}</pre>
<p>Das Gegenstück dazu stellt quasi <code>key()</code> dar, da es den Feldnamen der Position liefert.</p>
<pre>public function key()
{
  return $this-&gt;_keys[key($this-&gt;_keys)];
}</pre>
<p>Auf den ersten Blick mag diese Schreibweise vielleicht etwas verwirren, aber nur so bekommen wir den richtigen Schlüssel. <a href="http://de3.php.net/manual/de/function.key.php" target="_blank"><code>key()</code></a> gibt uns den aktuellen Schlüssel von $this-&gt;_keys zurück, wir wollen den Wert der jeweiligen Position haben.</p>
<p>Um nun in der Schleife den internen Zeiger bewegen zu können, brauchen wir <a href="http://de3.php.net/manual/de/function.next.php" target="_blank"><code>next()</code></a>.</p>
<pre>public function next()
{
  next($this-&gt;_keys);
}</pre>
<p>Wird das Objekt ein weiteres Mal mit foreach verarbeitet, so muss der interne Zeiger wieder auf die erste Position zurückgesetzt werden. Das Iterator-Interface schreibt dafür die Methode <code>rewind()</code> vor, die dann intern <a href="http://de3.php.net/manual/de/function.reset.php" target="_blank"><code>reset()</code></a> verwenden kann.</p>
<pre>public function rewind()
{
  reset($this-&gt;_keys);
}</pre>
<p>Innerhalb der Schleife wir überprüft, ob weitere Elemente vorhanden sind. Dazu wird <code>valid()</code> verwendet.</p>
<pre>public function valid()
{
  return current($this-&gt;_keys) !== false;
}</pre>
<p>Damit haben wir alles notwendige eingebaut, damit unser Model in einer foreach-Schleife ausgelesen werden kann.</p>
<pre>foreach($model as $key=&gt;$value) {
  echo $key . ' = ' . $value . '&lt;br/&gt;';
}</pre>
<p>Zuletzt gibt es dann auch noch die Methode <code>count()</code> des Countable-Interfaces, damit wir beliebig mit for-Schleifen über das Objekt iterieren können.</p>
<pre>public function count()
{
  return count($this-&gt;_keys);    
}</pre>
<p>und</p>
<pre>for($i=0; $i&lt;count($model); $i++) {
  echo $i . ' = ' . $model[$i] . '&lt;br/&gt;';
}</pre>
<p>Damit wären wir auch schon wieder am Ende. Beim nächsten Mal geht es dann weiter mit dem <a href="http://blog.ebene7.com/2010/05/07/es-ist-ein-model-und-es-sieht-gut-aus-der-observer/" target="_self">Observer-Pattern</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.ebene7.com/2010/05/06/es-ist-ein-model-und-es-sieht-gut-aus-iterator/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Verschiedene Aktionen nach Form-Submit</title>
		<link>https://blog.ebene7.com/2010/01/22/verschiedene-aktionen-nach-form-submit/</link>
		<comments>https://blog.ebene7.com/2010/01/22/verschiedene-aktionen-nach-form-submit/#comments</comments>
		<pubDate>Fri, 22 Jan 2010 09:00:55 +0000</pubDate>
		<dc:creator>Daniel</dc:creator>
				<category><![CDATA[Tutorial]]></category>
		<category><![CDATA[Bedienung]]></category>
		<category><![CDATA[Formular]]></category>
		<category><![CDATA[Header]]></category>
		<category><![CDATA[HTTP]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Weiterleitung]]></category>

		<guid isPermaLink="false">http://blog.ebene7.com/?p=275</guid>
		<description><![CDATA[Das worüber ich heute schreibe, ist weder neu, noch sonderlich aufregend, aber dennoch oftmals hilfreich. Es geht darum, über mehrere Submit-Buttons die Aktion nach dem Absenden eines HTML-Formulares steuern zu können. Angenommen, über ein Formular sollen die Stammdaten von Kunden &#8230; <a href="https://blog.ebene7.com/2010/01/22/verschiedene-aktionen-nach-form-submit/">Weiterlesen <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Das worüber ich heute schreibe, ist weder neu, noch sonderlich aufregend, aber dennoch oftmals hilfreich. Es geht darum, über mehrere Submit-Buttons die Aktion nach dem Absenden eines HTML-Formulares steuern zu können.</p>
<p><span id="more-275"></span>Angenommen, über ein Formular sollen die Stammdaten von Kunden bearbeitet werden und wir wollen dem Bearbeiter das Leben etwas leichter machen, indem er nach dem Speichern der Daten direkt die nächste Aktion ausführen kann ohne sie in der Navigation zu suchen.</p>
<p>In unserem Fall könnten die Aktionen z.B. &#8216;speichern&#8217;, &#8216;speichern und zur Liste&#8217; und &#8216;speichern und neu anlegen&#8217; sein.</p>
<p>Erstmal brauchen wir ein gewöhnliches <a href="http://de.wikipedia.org/wiki/Affenformular" target="_blank">Affenformular</a> mit den gewünschten Eingabefeldern:</p>
<pre>&lt;form action="machwas" method="post"&gt;
  // ein paar Felder
  &lt;input type="submit" value="speichern"&gt;
&lt;/form&gt;</pre>
<p>Soweit, so gut. Bislang ein ganz gewöhnliches Formular. Doch wie steuern wir denn nun was nach dem Absenden passieren soll? Ganz einfach, wir geben dem Formular noch ein paar Buttons und benennen diese.</p>
<pre>&lt;form action="machwas" method="post"&gt;
  // ein paar Felder
  &lt;input type="submit" value="speichern" name="btnSave"&gt;
  &lt;input type="submit" value="speichern und zur Liste" name="btnSaveList"&gt;
  &lt;input type="submit" value="speichern und neu anlegen" name="btnSaveNew"&gt;
&lt;/form&gt;</pre>
<p>Für diesen Zweck habe ich mir angewöhnt, den Namen das Prefix &#8216;btn&#8217; zu geben, damit sie besser zuordenbar sind.</p>
<p>Was passiert nun? Durch die Benennung der Buttons wird deren Wert (value) mit den andere Werten übertragen und wir können serverseitig damit arbeiten.</p>
<p>Theoretisch hätte ich auch alle Buttons gleich benennen können und hätte dann mit dem jeweiligen Wert weiter arbeiten können, aber dieser wäre nicht zuverlässig nutzbar. Dazu etwas später mehr.</p>
<p>Natürlich brauchen wir auch noch eine Auswertung für unser Formular:</p>
<pre>&lt;?php
// Daten prüfen, speichern und weiterleiten, wenn alles geklappt hat...

if(isset($_REQUEST['btnSave'])) {
  // ...und wieder auf das Formular weiterleiten (mit Daten anzeigen)
} else if(isset($_REQUEST['btnSaveList'])) {
  // ...und zur Liste weiterleiten
} else if(isset($_REQUEST['btnSaveNew'])) {
  // ...und wieder auf das Formular weiterleiten (ohne Daten anzeigen)
}</pre>
<p>Das Beispiel funktioniert auch mit anderen Sprachen, müsste dann aber natürlich an die Syntax angepasst werden. Persönlich finde ich nach einem POST-Request einen HTTP-<a href="http://de.wikipedia.org/wiki/Redirect" target="_blank">Redirect</a> ganz nützlich.</p>
<p>Nun noch kurz die Aufklärung, warum ich die Namen der Buttons auswerte. Es ist einfacher, da der Name sich in der Regel nicht verändert, jedoch evtl. die Beschriftung und somit der Wert, wenn die Anwendung mehrsprachig programmiert ist.</p>
<p>Ich hoffe, dass es vielleicht dem Einen oder Anderen hilft, auch wenn es eigentlich zu den Basics gehört. Viel Spaß beim Ausprobieren!</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.ebene7.com/2010/01/22/verschiedene-aktionen-nach-form-submit/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Erzeugen von Thumbnails</title>
		<link>https://blog.ebene7.com/2010/01/08/erzeugen-von-thumbnails/</link>
		<comments>https://blog.ebene7.com/2010/01/08/erzeugen-von-thumbnails/#comments</comments>
		<pubDate>Fri, 08 Jan 2010 09:00:03 +0000</pubDate>
		<dc:creator>Daniel</dc:creator>
				<category><![CDATA[Entwicklung]]></category>
		<category><![CDATA[Grundlagen]]></category>
		<category><![CDATA[Tutorial]]></category>
		<category><![CDATA[Bildbearbeitung]]></category>
		<category><![CDATA[Thumbnail]]></category>
		<category><![CDATA[Vorschaubild]]></category>

		<guid isPermaLink="false">http://blog.ebene7.com/?p=187</guid>
		<description><![CDATA[Das Erzeugen von Vorschaubildern, sogenannten Thumbnails, wird nach wie vor häufig gebraucht und eigentlich sollte es auch inzwischen jeder wissen wie es funktioniert, aber dennoch wird vereinzelt noch danach gefragt. Zunächst gibt es verschiedene Arten Vorschaubilder zu erzeugen. Die sicherlich &#8230; <a href="https://blog.ebene7.com/2010/01/08/erzeugen-von-thumbnails/">Weiterlesen <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Das Erzeugen von Vorschaubildern, sogenannten <a href="http://de.wikipedia.org/wiki/Thumbnail" target="_blank">Thumbnails</a>, wird nach wie vor häufig gebraucht und eigentlich sollte es auch inzwischen jeder wissen wie es funktioniert, aber dennoch wird vereinzelt noch danach gefragt.</p>
<p><span id="more-187"></span>Zunächst gibt es verschiedene Arten Vorschaubilder zu erzeugen. Die sicherlich primitivste ist das gegebene Bild auf die gewünschte Größe zu kopieren, denn die meisten Bilder sind original nicht quadratisch und würden dann verzerrt werden. Daher müssen wir die neuen Werte vorher berechnen, um gute Ergebnisse zu bekommen.</p>
<p>Gehen wir nun einfach mal davon aus, dass unsere Vorschaubilder eine maximale Seitenlänge haben und quadratisch sein sollen. Jetzt können wir entweder das Bild komplett kopieren und den Rest ggf. mit einer beliebigen Farbe auffüllen, oder wir kopieren einen Ausschnitt und haben dann ein vollflächiges Vorschaubild.</p>
<p>Für die proportionale Anpassung an unsere maximale Seitenlänge müssen wir zuerst das Bildformat, bzw. die Ausrichtung des Originalbildes ermitteln.</p>
<pre>if (srcHeight &lt; srcWidth) {
  // Querformat
} else {
  // Hochformat
}</pre>
<p>Danach errechnen wir die neuen Maße des Bildes. Bei der ersten Variante setzen wir unseren Maximalwert der längeren Bildseite gleich und passen die andere Seite an. Dadurch vermeiden wir, dass das Bild evtl. vergrößert würde, falls das Original kleiner als unser gewünschtes Vorschaubild ist.</p>
<pre>if (srcHeight &lt; srcWidth) {
  destWidth  = maxsize / srcHeight * srcWidth;
  destHeight = maxsize = maxsize &lt;= srcHeight ? maxsize : srcHeight;
} else {
  destWidth  = maxsize &lt;= srcWidth ? maxsize : srcWidth;
  destHeight = maxsize / srcWidth * srcHeight;
}</pre>
<p>Die Variablen srcWidth und srcHeight sollten die Werte des Originalbildes haben und destWidth und destHeight bekommen die Werte für das neue, kleinere Bild. Mit diesen Werten kann nun das Vorschaubild erzeugt werden.</p>
<p>Wenn das neue Bild nicht nur kleiner, sondern auch quadratisch und mit einer Hintergrundfarbe gefüllt sein soll, dann muss dazu ein Bild mit den gewünschten Maßen (maximale Seitenlänge im Quadrat) erzeugt und mit der Wunschfarbe gefüllt werden. Anschliessend das Originalbild verkleinert und zentriert daraufkopieren.</p>
<p>Die zweite Variante, nur einen Ausschnitt zu kopieren, funktioniert etwas anders, da wir dazu den jeweiligen Offset, also den Versatz vom Bildrand, errechnen müssen.</p>
<pre>offsetX = 0;
offsetY = 0;

if (srcHeight &lt; srcWidth) {
  offsetX = round ( (srcWidth / 2) - (srcHeight / 2) );
} else {
  offsetY = round ( (srcHeight / 2) - (srcWidth / 2) );
}</pre>
<p>Mit diesen Werten kann nun ein Quadrat mit dem entsprechenden Abstand aus dem Originalbild verkleinert kopiert werden und das war es auch schon.</p>
<p>Wie die Bilder im einzelnen bearbeitet werden und welche Klassen oder Funktionen verwendet werden können oder sollten, hängt dann von der jeweiligen Programmiersprache und den verwendeten Bibliotheken ab, daher gehe ich darauf an dieser Stelle auch nicht weiter ein.</p>
<p>Trotz zunehmender Rechenleistung empfehle ich, die erzeugten Vorschaubilder für die weitere Verwendung zu speichern und nicht jedesmal neu zu generieren.</p>
<p>Siehe auch:</p>
<ul>
<li><a href="http://de2.php.net/manual/de/book.image.php" target="_blank">Bildbearbeitung mit PHP</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>https://blog.ebene7.com/2010/01/08/erzeugen-von-thumbnails/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
