<?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>Mage::log()</title>
	<atom:link href="http://magelog.de/feed/" rel="self" type="application/rss+xml" />
	<link>http://magelog.de</link>
	<description>Magento-Entwickler Notizen</description>
	<lastBuildDate>Tue, 20 Sep 2011 16:32:52 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Was tun bei einer &#8220;Integrity constraint violation&#8221;?</title>
		<link>http://magelog.de/2011/09/20/was-tun-bei-einer-integrity-constraint-violation/</link>
		<comments>http://magelog.de/2011/09/20/was-tun-bei-einer-integrity-constraint-violation/#comments</comments>
		<pubDate>Tue, 20 Sep 2011 16:27:30 +0000</pubDate>
		<dc:creator>jkuensebeck</dc:creator>
				<category><![CDATA[Anderes]]></category>
		<category><![CDATA[oneliner]]></category>
		<category><![CDATA[quickie]]></category>
		<category><![CDATA[sql]]></category>
		<guid isPermaLink="false">http://magelog.de/?p=177</guid>
		<description><![CDATA[Ihr habt Mist gebaut und die Indexe werden nicht mehr richtig aufgebaut: Beim reindexieren der Indexe &#8220;catalog_product_attribute&#8221; oder &#8220;cataloginventory_stock&#8221; landet nur der Fehler SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '18263-1-1' for key 1' im Log. Dann habt ihr wahrscheinlich doppelte Attributewerte in den Produkten, also hat z.B. das Produkt mit der ID 16 für [...]]]></description>
			<content:encoded><![CDATA[<p>Ihr habt Mist gebaut und die Indexe werden nicht mehr richtig aufgebaut: Beim reindexieren der Indexe &#8220;catalog_product_attribute&#8221; oder &#8220;cataloginventory_stock&#8221; landet nur der Fehler<br />
<code>SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '18263-1-1' for key 1' </code><br />
im Log.<br />
Dann habt ihr wahrscheinlich doppelte Attributewerte in den Produkten, also hat z.B. das Produkt mit der ID 16 für das Attribut &#8220;Status&#8221; (im Beispiel mit der ID 272) gleich zweimal im selben Store (mit der ID 0) einen Wert gespeichert. In der Datenbank sähe das z.B so aus:</p>
<pre>SELECT * FROM catalog_product_entity_int LIMIT 2;
+----------+----------------+--------------+----------+-----------+-------+
| value_id | entity_type_id | attribute_id | store_id | entity_id | value |
+----------+----------------+--------------+----------+-----------+-------+
|        1 |             10 |          272 |        0 |        16 |     0 |
|        2 |             10 |          272 |        0 |        16 |     1 |
+----------+----------------+--------------+----------+-----------+-------+</pre>
<p>Das Attribut mit der ID 272 ist also für das Produkt doppelt belegt, einmal mit dem Wert 0 und einmal mit dem Wert 1 (man hat aber auch das selbe Problem, wenn die Werte gleich sind).</p>
<p>Um die problematischen Werte zu finden, helfen folgende SQL Zeilen um Duplikate in den catalog_product_entity_&#8230; Tabellen zu finden:</p>
<pre class="brush: sql">
SELECT eav_table.* FROM catalog_product_entity_int AS eav_table GROUP BY entity_id, attribute_id, store_id HAVING COUNT(*) &amp;gt; 1;
SELECT eav_table.* FROM catalog_product_entity_varchar AS eav_table GROUP BY entity_id, attribute_id, store_id HAVING COUNT(*) &amp;gt; 1;
SELECT eav_table.* FROM catalog_product_entity_text AS eav_table GROUP BY entity_id, attribute_id, store_id HAVING COUNT(*) &amp;gt; 1;
SELECT eav_table.* FROM catalog_product_entity_datetime AS eav_table GROUP BY entity_id, attribute_id, store_id HAVING COUNT(*) &amp;gt; 1;
SELECT eav_table.* FROM catalog_product_entity_decimal AS eav_table GROUP BY entity_id, attribute_id, store_id HAVING COUNT(*) &amp;gt; 1;
</pre>
<p>Um dann die doppelten Werte zu löschen (vorher bitte unbedingt anschauen, was doppelt ist und ob es Widerspüchlichkeiten gibt):</p>
<pre class="brush: sql">
DELETE FROM catalog_product_entity_int WHERE value_id IN (SELECT value_id FROM (SELECT value_id FROM catalog_product_entity_int GROUP BY entity_id, attribute_id, store_id HAVING COUNT(*) &amp;gt; 1) AS temp);
DELETE FROM catalog_product_entity_varchar WHERE value_id IN (SELECT value_id FROM (SELECT value_id FROM catalog_product_entity_varchar GROUP BY entity_id, attribute_id, store_id HAVING COUNT(*) &amp;gt; 1) AS temp);
DELETE FROM catalog_product_entity_text WHERE value_id IN (SELECT value_id FROM (SELECT value_id FROM catalog_product_entity_text GROUP BY entity_id, attribute_id, store_id HAVING COUNT(*) &amp;gt; 1) AS temp);
DELETE FROM catalog_product_entity_datetime WHERE value_id IN (SELECT value_id FROM (SELECT value_id FROM catalog_product_entity_datetime GROUP BY entity_id, attribute_id, store_id HAVING COUNT(*) &amp;gt; 1) AS temp);
DELETE FROM catalog_product_entity_decimal WHERE value_id IN (SELECT value_id FROM (SELECT value_id FROM catalog_product_entity_decimal GROUP BY entity_id, attribute_id, store_id HAVING COUNT(*) &amp;gt; 1) AS temp);
</pre>
<p>Die eigentliche Frage ist, wie man überhaupt zu so einer EAV-Struktur kommen konnte. Das kann <strong>eigentlich</strong> nicht passieren, denn es gibt einen Unique-Index über die Felder (attribute_id, entity_id, store_id). Per SQL kann man diese aber über mit &#8220;SET  UNIQUE_CHECKS=0&#8243; umgehen, was zum Beispiel am Anfang eines phpMyAdminDumps immer gemacht wird. Der &#8220;Trick&#8221; wird auch von eingen Extensions benutzt, um schneller in die Datenbank zu schreiben.</p>
]]></content:encoded>
			<wfw:commentRss>http://magelog.de/2011/09/20/was-tun-bei-einer-integrity-constraint-violation/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Controller Action mit Events überschreiben</title>
		<link>http://magelog.de/2011/07/27/controller-action-mit-events-uberschreiben/</link>
		<comments>http://magelog.de/2011/07/27/controller-action-mit-events-uberschreiben/#comments</comments>
		<pubDate>Wed, 27 Jul 2011 19:36:17 +0000</pubDate>
		<dc:creator>datenbrille</dc:creator>
				<category><![CDATA[Anderes]]></category>
		<guid isPermaLink="false">http://magelog.de/?p=149</guid>
		<description><![CDATA[In meinem Projekt verwenden wir momentan einen Rewrite des Checkout Controllers um unsere Modifikation vorzunehmen. Irgendwie gefiel mir das nicht und ich habe nach einer anderen Methode gesucht. Nach einigen Hinweisen von den Webguys habe ich eine Event-Observer Idee entwickelt. Das ganze ist möglich weil Magento in Mage_Core_Controller_Varien_Action in der Methode dispatch ein Flag abfragt [...]]]></description>
			<content:encoded><![CDATA[<p>
In meinem Projekt verwenden wir momentan einen Rewrite des Checkout Controllers um unsere Modifikation vorzunehmen. Irgendwie gefiel mir das nicht und ich habe nach einer anderen Methode gesucht. Nach einigen Hinweisen von den <a href="http://www.webguys.de/">Webguys</a> habe ich eine Event-Observer Idee entwickelt.
</p>
<p>
Das ganze ist möglich weil Magento in <strong>Mage_Core_Controller_Varien_Action</strong> in der Methode dispatch ein Flag abfragt ob es überhaupt einen Dispatch geben soll.
</p>
<pre class="brush: php">
//Mage_Core_Controller_Varien_Action-&gt;dispatch()
...
if ($this-&gt;getRequest()-&gt;isDispatched()) {
    /**
     * preDispatch() didn&#039;t change the action, so we can continue
     */
    if (!$this-&gt;getFlag(&#039;&#039;, self::FLAG_NO_DISPATCH)) {
        $_profilerKey = self::PROFILER_KEY.&#039;::&#039;.$this-&gt;getFullActionName();
        Varien_Profiler::start($_profilerKey);
        $this-&gt;$actionMethodName();
        Varien_Profiler::stop($_profilerKey);
        Varien_Profiler::start(self::PROFILER_KEY.&#039;::postdispatch&#039;);
        $this-&gt;postDispatch();
        Varien_Profiler::stop(self::PROFILER_KEY.&#039;::postdispatch&#039;);
    }
}
...
</pre>
<p>
Setzt man nun diesen Flag in Controller oder sonst wo, dann wird Magento nicht die Action des Controller ausführen. Damit war ich meinem Ziel schon etwas näher.<br />
Aber wie kann ich mich in eine beliebige Controller Action einhängen? Auch hier bietet Magento einen Hook. Dieser befindet sich ebenfalls in Mage_Core_Controller_Varien_Action, aber dieses mal in der Methode preDispatch. Dort werden verschiede Events geworfen und das zu einem sehr frühen Zeitpunkt in Request-Response Zyklus von Magento. Erst dadurch ist es möglich eine Action vollständig zu überschreiben.
</p>
<pre class="brush: php">
Mage::dispatchEvent(&#039;controller_action_predispatch&#039;, array(&#039;controller_action&#039;=&gt;$this));
Mage::dispatchEvent(
    &#039;controller_action_predispatch_&#039;.$this-&gt;getRequest()-&gt;getRouteName(),
    array(&#039;controller_action&#039;=&gt;$this)
);
Varien_Autoload::registerScope($this-&gt;getRequest()-&gt;getRouteName());
Mage::dispatchEvent(
    &#039;controller_action_predispatch_&#039;.$this-&gt;getFullActionName(),
    array(&#039;controller_action&#039;=&gt;$this)
);
</pre>
<p>
Wie ihr seht werden hier viele verschiedene Events gefeuert. Ich kann einfach auf alle Controller preDispatches hören oder auch nur auf bestimmte. Zur Demonstration habe ich einfach mal alle Actions überschrieben.
</p>
<pre class="brush: xml">
//XML in aus der Config XML eines Moduls
&lt;controller_action_predispatch&gt;
  &lt;observers&gt;
    &lt;updateflags&gt;
      &lt;type&gt;singleton&lt;/type&gt;
      &lt;class&gt;magelog_teaser/observer&lt;/class&gt;
      &lt;method&gt;stopAction&lt;/method&gt;
    &lt;/updateflags&gt;
  &lt;/observers&gt;
&lt;/controller_action_predispatch&gt;
</pre>
<p>Die Methode im Oberserver macht nichts anderes als ein Hello in die Response zu schreiben. Will man nun eine Methode im Checkout überschreiben, ersetzt man das Hello durch eine JSON Rückgabe und wählt den Event weniger allgemein.</p>
<pre class="brush: php">
public function stopAction(Varien_Event_Observer $observer) {
        //controller aus dem event laden
        /** @var $controller Mage_Core_Controller_Varien_Action */
        $controller = $observer-&gt;getData(&#039;controller_action&#039;);
        //hier passiert die Magie, und wird der Dispatch unterbrochen
        $controller-&gt;setFlag(
             $controller-&gt;getRequest()-&gt;getActionName()
             ,Mage_Core_Controller_Varien_Action::FLAG_NO_DISPATCH
             ,true
         );
        //jetzt kann man beliebigen Output erzeugen
        $controller-&gt;getResponse()-&gt;setBody(&quot;Hello&quot;);
}
</pre>
<p>Viel Spaß damit und ich werde berichten wie weit wir dieses Konzept in unserem neuen Checkout getrieben haben. Ach ja, das ganze hat bei uns auch schon einen Namen: &#8220;Das schöne Magento die()&#8221;.</p>
]]></content:encoded>
			<wfw:commentRss>http://magelog.de/2011/07/27/controller-action-mit-events-uberschreiben/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Warum man in Update Scripten keine Entitäten löscht!</title>
		<link>http://magelog.de/2011/07/27/warum-man-in-update-schippten-keine-entitaten-loscht/</link>
		<comments>http://magelog.de/2011/07/27/warum-man-in-update-schippten-keine-entitaten-loscht/#comments</comments>
		<pubDate>Wed, 27 Jul 2011 18:40:35 +0000</pubDate>
		<dc:creator>datenbrille</dc:creator>
				<category><![CDATA[Magento]]></category>
		<category><![CDATA[SQL]]></category>
		<guid isPermaLink="false">http://magelog.de/?p=147</guid>
		<description><![CDATA[Ich habe mit einem neueren Release von meinen Shop auch mal die Datenbank aufgeräumt und überflüssige Kategorien und Attribute gelöscht. Was ich nicht wusste ist, das Magento bei jedem Script oder Durchgang die Foreign Key Contraints abschaltet. Ich hatte mich bei der Entwicklung auch gefragt warum in Magento 1.5 es nicht mehr erlaubt ist Category [...]]]></description>
			<content:encoded><![CDATA[<p>Ich habe mit einem neueren Release von meinen Shop auch mal die Datenbank aufgeräumt und überflüssige Kategorien und Attribute gelöscht. Was ich nicht wusste ist, das Magento bei jedem Script oder Durchgang die Foreign Key Contraints abschaltet.</p>
<p>Ich hatte mich bei der Entwicklung auch gefragt warum in Magento 1.5 es nicht mehr erlaubt ist Category zu löschen, wenn man nicht im Backend eingelogt ist. Gibt es da einen Zusammenhang? Ich weiß es leider nicht, aber vielleicht ihr.</p>
<p>Ich konnte das Problem auch nicht mit <a href="http://gauravsohoni.wordpress.com/2009/03/09/mysql-disable-foreign-key-checks-or-constraints/">MySQL-Mitteln</a> lösen.</p>
]]></content:encoded>
			<wfw:commentRss>http://magelog.de/2011/07/27/warum-man-in-update-schippten-keine-entitaten-loscht/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Translations in der Datenbank und das gute alte Locale</title>
		<link>http://magelog.de/2011/07/27/translations-in-der-datenbank-und-das-gute-alte-locale/</link>
		<comments>http://magelog.de/2011/07/27/translations-in-der-datenbank-und-das-gute-alte-locale/#comments</comments>
		<pubDate>Wed, 27 Jul 2011 18:31:35 +0000</pubDate>
		<dc:creator>datenbrille</dc:creator>
				<category><![CDATA[Magento]]></category>
		<category><![CDATA[Upgrade]]></category>
		<guid isPermaLink="false">http://magelog.de/?p=142</guid>
		<description><![CDATA[Die Translations aus der Datenbank werden in neueren Magento Versionen (> 1.3.2.4) mit einem Locale geladen. Das kann zu Problemen führen, wenn die Translations mit en_US in der DB stehen, aber man z.B. deutsche Übersetzungen eingefügt hat.]]></description>
			<content:encoded><![CDATA[<p>Kleiner Tipp bei der Migration von älteren Magento Versionen auf die Version 1.5.X.X. Die Translations werden nun aus der DB mit einem Locale Parameter geladen. Soweit kein Problem, solange das Locale zu den Übersetzungen passt. Bei mir war es leider nicht so.</p>
<p>Code aus Magento 1.5.1.0:</p>
<pre class="brush: php">
    protected function _loadDbTranslation($forceReload = false)
    {
        $arr = $this-&gt;getResource()-&gt;getTranslationArray(null, $this-&gt;getLocale());
        $this-&gt;_addData($arr, $this-&gt;getConfig(self::CONFIG_KEY_STORE), $forceReload);
        return $this;
    }
</pre>
<p>Code aus Magento 1.3.2.4. Man beachte den fehlenden Locale Parameter bei getTranslationArray().</p>
<pre class="brush: php">
    protected function _loadDbTranslation($forceReload = false)
    {
        $arr = $this-&gt;getResource()-&gt;getTranslationArray();
        $this-&gt;_addData($arr, $this-&gt;getConfig(self::CONFIG_KEY_STORE), $forceReload);
        return $this;
    }
</pre>
]]></content:encoded>
			<wfw:commentRss>http://magelog.de/2011/07/27/translations-in-der-datenbank-und-das-gute-alte-locale/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Fehlende Tabellen nach Magento Upgrade nachrüsten.</title>
		<link>http://magelog.de/2011/07/04/fehlende-tabellen-nach-magento-upgrade-nachrusten/</link>
		<comments>http://magelog.de/2011/07/04/fehlende-tabellen-nach-magento-upgrade-nachrusten/#comments</comments>
		<pubDate>Mon, 04 Jul 2011 18:40:55 +0000</pubDate>
		<dc:creator>datenbrille</dc:creator>
				<category><![CDATA[Magento]]></category>
		<category><![CDATA[Upgrade]]></category>
		<category><![CDATA[update]]></category>
		<guid isPermaLink="false">http://magelog.de/?p=129</guid>
		<description><![CDATA[Wie kann ich den Fehler: Base table or view not found: 1146 Table 'core_file_storage' beheben.]]></description>
			<content:encoded><![CDATA[<p>Ich führe gerade ein Upgrade einer Magento 1.3 Installation auf die Version 1.5 durch. Dabei ist mir im System Log folgendes aufgefallen:</p>
<p>PHP Fatal error:  Uncaught exception &#8216;PDOException&#8217; with message &#8216;SQLSTATE[42S02]: Base table or view not found: 1146 Table &#8216;core_file_storage&#8217; doesn&#8217;t exist&#8217;.</p>
<p>Nach ein wenig suchen im Magento Core ist mir aufgefallen, dass diese Tabellen &#8220;on the fly&#8221; anlegt werden. Jedoch nur wenn die Datenbank als Cache-Backend verwendet wird. Der Fehler hat bei mir nur das Log voll geschrieben. Der Shop war nicht beeinträchtigt.</p>
<p>Die Lösung ist folgender Schnippsel:</p>
<pre class="brush: php">
/** @var $model Mage_Core_Model_File_Storage_Directory_Database */
$model = Mage::getModel(&#039;core/file_storage_directory_database&#039;);
$model-&gt;prepareStorage();
/** @var $model Mage_Core_Model_File_Storage_Database */
$model = Mage::getModel(&#039;core/file_storage_database&#039;);
$model-&gt;prepareStorage();
</pre>
<p>Wichtig ist dabei die Reihenfolge! Die zweite Tabelle erstellt Foreign Keys auf die erste. Das ganze habe ich in ein Update Script eines der Module gepackt und nun werden sie einfach einmalig erstellt.</p>
]]></content:encoded>
			<wfw:commentRss>http://magelog.de/2011/07/04/fehlende-tabellen-nach-magento-upgrade-nachrusten/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Unterschiedliche Toolbars in der Produktliste</title>
		<link>http://magelog.de/2010/08/22/unterschiedliche-toolbars-in-der-produktliste/</link>
		<comments>http://magelog.de/2010/08/22/unterschiedliche-toolbars-in-der-produktliste/#comments</comments>
		<pubDate>Sun, 22 Aug 2010 12:54:57 +0000</pubDate>
		<dc:creator>jkuensebeck</dc:creator>
				<category><![CDATA[Magento-Templates]]></category>
		<category><![CDATA[layout.xml]]></category>
		<category><![CDATA[template]]></category>
		<guid isPermaLink="false">http://magelog.de/?p=84</guid>
		<description><![CDATA[Das Problem Ich habe gerade dieses Design auf dem Tisch. Eine Produktliste, oben eine Toolbar mit Sortieroptionen, unten eine Toolbar mit Pager, bestimmt kein Problem: Die Template-Engine von Magento ist doch wie gemacht für so etwas. Schauen wir mal ins Template: &#60;div class=&#34;category-products&#34;&#62; &#60;?php echo $this-&#62;getToolbarHtml(); ?&#62; ... Produktlisten-Ausgabe ... &#60;?php echo $this-&#62;getToolbarHtml(); ?&#62; &#60;/div&#62; [...]]]></description>
			<content:encoded><![CDATA[<h2>Das Problem</h2>
<p><a href="http://magelog.de/wp-content/uploads/2010/08/split-toolbar.png"><img class="alignright size-medium wp-image-85" title="split-toolbar" src="http://magelog.de/wp-content/uploads/2010/08/split-toolbar-196x300.png" alt="2 verschiedene Toolbars in Magento" /></a><br />
Ich habe gerade dieses Design auf dem Tisch. Eine Produktliste, oben eine Toolbar mit Sortieroptionen, unten eine Toolbar mit Pager, bestimmt kein Problem: Die Template-Engine von Magento ist doch wie gemacht für so etwas.<br />
<span id="more-84"></span><br />
<br style="clear: both;" /></p>
<p>Schauen wir mal ins Template:</p>
<pre class="brush: php">
&lt;div class=&quot;category-products&quot;&gt;
&lt;?php echo $this-&gt;getToolbarHtml(); ?&gt;
... Produktlisten-Ausgabe ...
&lt;?php echo $this-&gt;getToolbarHtml(); ?&gt;
&lt;/div&gt;
</pre>
<h2>Die Nicht-Lösung</h2>
<p>$this-&gt;getToolbarHtml() ruft lediglich  $this-&gt;getChildHtml(&#8216;toolbar&#8217;) auf, also werde ich einfach zwei Mage_Catalog_Block_Product_List_Toolbar-Blöcke mit verschiedenen Templates erstellen und mit $this-&gt;getChildHtml(&#8216;product_list_toolbar&#8217;) und  $this-&gt;getChildHtml(&#8216;product_list_toolbar2&#8242;) ins Template einfügen, à la:</p>
<pre class="brush: xml">
...
&lt;catalog_category_default&gt;
&lt;reference name=&quot;content&quot;&gt;
&lt;block type=&quot;catalog/category_view&quot; name=&quot;category.products&quot; template=&quot;catalog/category/view.phtml&quot;&gt;
&lt;block type=&quot;catalog/product_list&quot; name=&quot;product_list&quot; template=&quot;catalog/product/list.phtml&quot;&gt;
&lt;block type=&quot;catalog/product_list_toolbar&quot; name=&quot;product_list_toolbar&quot; template=&quot;catalog/product/list/toolbar-sort.phtml&quot; &gt;
&lt;block type=&quot;page/html_pager&quot; name=&quot;product_list_toolbar_pager&quot;/&gt;
&lt;/block&gt;
&lt;block type=&quot;catalog/product_list_toolbar&quot; name=&quot;product_list_toolbar2&quot; template=&quot;catalog/product/list/toolbar-pager.phtml&quot;/&gt;
&lt;block type=&quot;page/html_pager&quot; name=&quot;product_list_toolbar_pager&quot;/&gt;
&lt;/block&gt;
&lt;/block&gt;
&lt;/block&gt;
&lt;/reference&gt;
...
&lt;/catalog_category_default&gt;
...
</pre>
<p>Super Lösung, ausser, dass sie nicht funktioniert: Die Toolbar wird gesondert behandelt, in Mage_Catalog_Block_Product_List_Toolbar::_beforeToHtml() wird der Toolbar-Block on the fly unter dem Name &#8220;toolbar&#8221; erstellt, mit den nötigen Daten (der Produkt-Collection und den Pager- und Sortieroptionen) befüllt und dann erst gerendert.</p>
<h2>Die Lösung: 2 verschiedene Toolbar-Templates?</h2>
<p>Die obere Toolbar behandeln wir einfach wie von Magento gedacht, sie wird per $this-&gt;getToolbar() in das Template eingefügt, dafür müssen wir im Layout-XML noch den Toolbar-Block-Namen registrieren:</p>
<pre class="brush: xml">
...
&lt;catalog_category_default&gt;
&lt;reference name=&quot;content&quot;&gt;
&lt;block type=&quot;catalog/category_view&quot; name=&quot;category.products&quot; template=&quot;catalog/category/view.phtml&quot;&gt;
&lt;block type=&quot;catalog/product_list&quot; name=&quot;product_list&quot; template=&quot;catalog/product/list.phtml&quot;&gt;
&lt;block type=&quot;catalog/product_list_toolbar&quot; name=&quot;product_list_toolbar&quot; template=&quot;catalog/product/list/toolbar-sort.phtml&quot;&gt;
&lt;block type=&quot;page/html_pager&quot; name=&quot;product_list_toolbar_pager&quot;/&gt;
&lt;/block&gt;
&lt;action method=&quot;setToolbarBlockName&quot;&gt;&lt;name&gt;product_list_toolbar&lt;/name&gt;&lt;/action&gt;
&lt;block type=&quot;catalog/product_list_toolbar&quot; name=&quot;product_list_toolbar2&quot; template=&quot;catalog/product/list/toolbar-pager.phtml&quot;&gt;
&lt;block type=&quot;page/html_pager&quot; name=&quot;product_list_toolbar_pager&quot;/&gt;
&lt;/block&gt;
&lt;/block&gt;
&lt;/block&gt;
&lt;/reference&gt;
...
&lt;/catalog_category_default&gt;
...
</pre>
<p>Ausserdem haben wir eine weiteren Toolbar-Block unter dem Namen &#8220;product_list_toolbar2&#8243; im XML angelegt. Damit wir diesen benutzen können, muss der Block aus dem PHP-Template heraus per $this-&gt;setToolbarBlockName(&#8216;product_list_toolbar2&#8242;) als der Toolbar-Block registriert und per &#8220;$this-&gt;_beforeToHtml()&#8221; mit Daten befüllt werden.<br />
Für die eigentlich Ausgabe kann dann $this-&gt;getChildHtml(&#8216;toolbar&#8217;,false) aufgerufen werden, der zweite Parameter bewirkt, dass der Block-Cache umgangen wird (im Cache ist sonst noch die obere Toolbar hinterlegt):</p>
<pre class="brush: php">
&lt;?php
$this-&gt;setToolbarBlockName(&#039;product_list_toolbar2&#039;);
$this-&gt;_beforeToHtml();
echo $this-&gt;getChildHtml(&#039;toolbar&#039;,false);
?&gt;
</pre>
<p>ich bin nicht so wirklich zufrieden mit der Lösung: Gut ist, dass sie funktioniert, schlecht ist, dass sie den Layout-XML-Mechanismus und das Block-Caching umgeht, das Template vollmüllt, und es evtl. Seiteneffekte durch den doppelten Aufruf von _beforeToHtml() gibt.</p>
<p>Kennt jemand einen besseren Ansatz?</p>
]]></content:encoded>
			<wfw:commentRss>http://magelog.de/2010/08/22/unterschiedliche-toolbars-in-der-produktliste/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Model-&gt;load($id) vs. Model-&gt;getCollection()-&gt;getItemById($id)</title>
		<link>http://magelog.de/2010/08/20/model-loadid-vs-model-getcollection-getitembyidid/</link>
		<comments>http://magelog.de/2010/08/20/model-loadid-vs-model-getcollection-getitembyidid/#comments</comments>
		<pubDate>Fri, 20 Aug 2010 07:00:05 +0000</pubDate>
		<dc:creator>jkuensebeck</dc:creator>
				<category><![CDATA[Magento]]></category>
		<category><![CDATA[don'ts]]></category>
		<category><![CDATA[einzeiler]]></category>
		<category><![CDATA[performance]]></category>
		<guid isPermaLink="false">http://magelog.de/?p=79</guid>
		<description><![CDATA[Neulich bin ich in einer älteren Version einer in Deutschland sehr beliebten Extension über einen lustigen kleinen Performance-Killer gestolpert: Es sollte ein Quote-Object anhand der ID geladen werden, das sah im Code folgendermaßen aus: $quote = Mage::getModel(&#039;sales/quote&#039;)-&#62;getCollection()-&#62;getItemById($quote_id); harmlos, oder? Nein! Diese eine Zeile brauchte ca. 30 Sekunden, und das auf einem wirklich performanten Kundenserver. Wie [...]]]></description>
			<content:encoded><![CDATA[<p>Neulich bin ich in einer älteren Version einer in Deutschland sehr beliebten Extension über einen lustigen kleinen Performance-Killer gestolpert: Es sollte ein Quote-Object anhand der ID geladen werden, das sah im Code folgendermaßen aus:</p>
<pre class="brush: php">
$quote = Mage::getModel(&#039;sales/quote&#039;)-&gt;getCollection()-&gt;getItemById($quote_id);
</pre>
<p>harmlos, oder? Nein!</p>
<p>Diese eine Zeile brauchte ca. 30 Sekunden, und das auf einem wirklich performanten Kundenserver.<br />
Wie kommt&#8217;s?<br />
<span id="more-79"></span><br />
In lib/Varien/Data/Collection.php finden wir folgendes:</p>
<pre class="brush: php">
    /**
     * Retrieve item by id
     *
     * @param   mixed $idValue
     * @return  Varien_Object
     */
    public function getItemById($idValue)
    {
        $this-&gt;load();
        if (isset($this-&gt;_items[$idValue])) {
            return $this-&gt;_items[$idValue];
        }
        return null;
    }
</pre>
<p>Zuerst wird die gesamte Collection geladen, und dann per Array-Lookup das richtige Element zurückgegeben. Die gesamte Collection laden bedeutete in diesem Fall 50.000 Quotes aus der Datenbank abholen, 50.000 mal den Konstruktor der Klasse aufrufen, und dann den EINEN verdammten Eintrag zurückgeben.<br />
Wir haben diese Zeile dann durch das gute alte Mage::getModel(&#8216;sales/quote&#8217;)->load($id) ersetzt und konnten so die Performance um den Faktor 50.000 erhöhen;)<br />
Ich frage mich was die Idee hinter dieser Methode ist und warum eine dermaßen nicht skalierende Methode überhaupt im Magento-Core vorhanden ist. Oder gibt es da vernünftige Einsatzbereiche?</p>
]]></content:encoded>
			<wfw:commentRss>http://magelog.de/2010/08/20/model-loadid-vs-model-getcollection-getitembyidid/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Unübersetzte Strings finden</title>
		<link>http://magelog.de/2010/08/17/unubersetzte-strings-finden/</link>
		<comments>http://magelog.de/2010/08/17/unubersetzte-strings-finden/#comments</comments>
		<pubDate>Tue, 17 Aug 2010 17:36:15 +0000</pubDate>
		<dc:creator>jkuensebeck</dc:creator>
				<category><![CDATA[Magento]]></category>
		<category><![CDATA[einzeiler]]></category>
		<category><![CDATA[übersetzung]]></category>
		<guid isPermaLink="false">http://magelog.de/?p=55</guid>
		<description><![CDATA[Als dirty Hack einfach die Mage::log(&#8230;) Zeile zwischen DEN BEIDEN SCHREIENDEN KOMMENTAREN!!! in app/code/core/Mage/Core/Model/Translate.php einfügen: /** * Return translated string from text. * * @param string $text * @param string $code * @return string */ protected function _getTranslatedString($text, $code) { $translated = &#039;&#039;; if (array_key_exists($code, $this-&#62;getData())) { $translated = $this-&#62;_data[$code]; } elseif (array_key_exists($text, $this-&#62;getData())) { [...]]]></description>
			<content:encoded><![CDATA[<p>Als dirty Hack einfach die Mage::log(&#8230;) Zeile zwischen DEN BEIDEN SCHREIENDEN KOMMENTAREN!!! in app/code/core/Mage/Core/Model/Translate.php einfügen:</p>
<pre>
<pre class="brush: php">
/**
 * Return translated string from text.
 *
 * @param string $text
 * @param string $code
 * @return string
 */
 protected function _getTranslatedString($text, $code)
 {
   $translated = &#039;&#039;;
   if (array_key_exists($code, $this-&gt;getData())) {
     $translated = $this-&gt;_data[$code];
   }
   elseif (array_key_exists($text, $this-&gt;getData())) {
     $translated = $this-&gt;_data[$text];
   }
   else {
     $translated = $text;
     // THE LINE THAT SAVES THE DAY FOLLOWS:
     Mage::log(&#039;UNTRANSLATED STRING: &quot;&#039;.$code.&#039; - &#039;.$text.&#039;&quot;&#039;);
     // THAT WAS THE LINE THAT SAVED THE DAY
   }
   return $translated;
}
</pre>
</pre>
<p>und anschliessend sich die fehlenden Übersetzungen per </p>
<pre class="brush: shell">
grep &#039;UNTRANSLATED STRING&#039; var/log/system.log
</pre>
<p>anzeigen lassen.</p>
<p>Vielleicht gibt es das später auch noch mal in schön, mal sehen&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://magelog.de/2010/08/17/unubersetzte-strings-finden/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>HOW-TO: Der Autovervollständigung auf die Sprünge helfen und die eigene Produktivität um ca. 170% steigern</title>
		<link>http://magelog.de/2010/08/17/autocomplete-auf-die-sprunge-helfen/</link>
		<comments>http://magelog.de/2010/08/17/autocomplete-auf-die-sprunge-helfen/#comments</comments>
		<pubDate>Mon, 16 Aug 2010 22:40:32 +0000</pubDate>
		<dc:creator>jkuensebeck</dc:creator>
				<category><![CDATA[Magento]]></category>
		<category><![CDATA[Magento-Templates]]></category>
		<category><![CDATA[einzeiler]]></category>
		<category><![CDATA[netbeans]]></category>
		<category><![CDATA[PHPDoc]]></category>
		<category><![CDATA[produktivität]]></category>
		<guid isPermaLink="false">http://magelog.de/?p=36</guid>
		<description><![CDATA[Da ich der Meinung bin, dass das Lesen von Dokumentationen Verschwendung meiner wertvollen Zeit ist, lasse ich das lieber von anderen erledigen, z.B. meiner IDE (d.h. momentan Netbeans, mit PDT für Eclipse funktioniert der ganze Spass aber genauso). Die Autovervollständigung der IDE Meistens funktioniert die Autovervollständigung bei der Magento-Entwicklung ja recht gut, wenn ich z.B. [...]]]></description>
			<content:encoded><![CDATA[<p>Da ich der Meinung bin, dass das Lesen von Dokumentationen Verschwendung meiner wertvollen Zeit ist, lasse ich das lieber von anderen erledigen, z.B. meiner IDE (d.h. momentan Netbeans, mit PDT für Eclipse funktioniert der ganze Spass aber genauso).</p>
<h2>Die Autovervollständigung der IDE</h2>
<p>Meistens funktioniert die Autovervollständigung bei der Magento-Entwicklung ja recht gut, wenn ich z.B. in einem eigenen Controller<span id="more-36"></span></p>
<pre class="brush: php">
$this-&gt;getRequest()-&gt;getP[CTRL+SPACE]
</pre>
<p>tippe, schlägt mir netbeans, wie es sich gehört,</p>
<pre class="brush: php">
$this-&gt;getRequest()-&gt;getParam($key, $default);
</pre>
<p>vor.</p>
<h2>und wo sie versagt:</h2>
<p>Das funktioniert aber nicht immer: Wenn netbeans den Kontext, in dem ich mich befinde nicht analysieren kann, gibt es keine ober nur sehr allgemeine Vorschläge:</p>
<pre class="brush: php">
$product = Mage::getModel(&#039;catalog/product&#039;);
$product-&gt;get [CTRL+SPACE]
</pre>
<p>liefert magere 13 Möglichkeiten, obwohl es weitaus mehr Methoden in der Klasse <a title="PHPDoc der Klasse Mage_Catalog_Model_Product" href="http://docs.magentocommerce.com/Mage_Catalog/Mage_Catalog_Model_Product.html">Mage_Catalog_Model_Product</a> gibt, wie man sich schnell mit einem Blick in den Sourcecode überzeugen kann. Netbeans erkennt am Rückgabewert der Methode getModel(), dass es sich bei der zurückgegebenen Instanz um eine von Mage_Core_Model_Abstract abgeleitete Klasse handelt, welche aber genau, kann Netbeans natürlich nicht ableiten.</p>
<h2>Type-Hints for the rescue</h2>
<p><a href="http://magelog.de/wp-content/uploads/2010/08/netbeans-weiss-bescheid1.png"><img class="alignleft size-medium wp-image-41" title="netbeans-weiss-bescheid" src="http://magelog.de/wp-content/uploads/2010/08/netbeans-weiss-bescheid1-300x171.png" alt="Netbeans weiss Bescheid" width="300" height="171" /></a></p>
<p>Hier helfen wir mit einem <a title="Erklärung der Type-Hint Syntax auf zend.com" href="http://files.zend.com/help/PDT/working_with_code_assist.htm#Class_Type_Hints" target="_blank">Type-Hint-Kommentar</a> nach. Type-Hint-Kommentare sind eine spezielle <a title="Zur Dokumentation von PHPDoc" href="http://manual.phpdoc.org/HTMLframesConverter/default/phpDocumentor/tutorial_phpDocumentor.pkg.html">PHPDoc</a>-Syntax um den Typ einer lokalen Variablen explizit anzugeben. </p>
<p>Der äußerst gewünschte Nebeneffekt ist, dass Netbeans nun auf Basis des angegebenen Typs die Autovervollständigung generiert:</p>
<pre class="brush: php">
$product = Mage::getModel(&#039;catalog/product&#039;);
/* @var $product Mage_Catalog_Model_Product */
$product-&gt;get [CTRL+SPACE]
</pre>
<p>Und tada: Netbeans weiss, was wir wollen!</p>
<h2>Templates schreiben lassen</h2>
<p>Noch viel nützlicher ist die Methode bei der Magento-Template-Erstellung: Hier haben die Magento-Entwickler ja beinahe mitgedacht, und haben meistens oben im Template das zugehörige Block-Model in einem @see-Kommentar notiert. Wenn man in einem Template gerade nicht weiss, welche Methoden, einem zur Verfügung stehen, ergänzte man einfach den @see-Kommentar um einen Type-Hint-Kommentar. Dass die gehintete Variable zufällig $this ist, stört Netbeans nicht weiter:</p>
<pre class="brush: php">
&lt;?php
/**
* Shopping cart template
*
* @see Mage_Checkout_Block_Cart
*/
/* @var $this Mage_Checkout_Block_Cart */
?&gt;
&lt;div&gt;
&lt;h3&gt;&lt;?php echo $this-&gt;getQ [CTRL+SPACE] ?&gt;&lt;/h3&gt;
// ...
</pre>
<h2>Fazit</h2>
<p>Den minimalen zusätzlichen Aufwand, lokale Variablen zu kommentieren, hat man spätestens nach dem zweiten Editieren der Datei wieder raus. Außerdem hilft der Type-Hint nicht nur der Entwicklungsumgebung, sondern auch dem Entwickler, den Überblick zu behalten.</p>
<p>Also: Machen! Immer!</p>
]]></content:encoded>
			<wfw:commentRss>http://magelog.de/2010/08/17/autocomplete-auf-die-sprunge-helfen/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Magento SQL Profiler</title>
		<link>http://magelog.de/2010/08/07/magento-sql-profiler/</link>
		<comments>http://magelog.de/2010/08/07/magento-sql-profiler/#comments</comments>
		<pubDate>Sat, 07 Aug 2010 11:00:36 +0000</pubDate>
		<dc:creator>jkuensebeck</dc:creator>
				<category><![CDATA[Magento]]></category>
		<category><![CDATA[einzeiler]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[profiler]]></category>
		<category><![CDATA[sql]]></category>
		<guid isPermaLink="false">http://magelog.de/?p=13</guid>
		<description><![CDATA[Magento (bzw. wir das Zend-Framework) hat einen eingebauten SQL-Profiler im Gepäck, um diesen zu aktivieren einfach in der local.xml im Connection-Knoten folgende Zeile ergänzen: &#60;resources&#62; &#60;db&#62; &#60;table_prefix&#62;&#60;![CDATA[]]&#62;&#60;/table_prefix&#62; &#60;/db&#62; &#60;default_setup&#62; &#60;connection&#62; &#60;host&#62;&#60;![CDATA[localhost]]&#62;&#60;/host&#62; &#60;username&#62;&#60;![CDATA[root]]&#62;&#60;/username&#62; &#60;password&#62;&#60;![CDATA[]]&#62;&#60;/password&#62; &#60;dbname&#62;&#60;![CDATA[magento_test]]&#62;&#60;/dbname&#62; &#60;active&#62;1&#60;/active&#62; &#60;profiler&#62;1&#60;/profiler&#62; &#60;/connection&#62; &#60;/default_setup&#62; &#60;/resources&#62; Nachdem man einmal den Cache gelöscht hat erscheint unterhalb des regulären Profilers ein neuer Bereich mit [...]]]></description>
			<content:encoded><![CDATA[<p>Magento (bzw. wir das Zend-Framework) hat einen eingebauten SQL-Profiler im Gepäck, um diesen zu aktivieren einfach in der local.xml im Connection-Knoten folgende Zeile ergänzen:</p>
<pre>&lt;resources&gt;
 &lt;db&gt;
 &lt;table_prefix&gt;&lt;![CDATA[]]&gt;&lt;/table_prefix&gt;
 &lt;/db&gt;
 &lt;default_setup&gt;
 &lt;connection&gt;
 &lt;host&gt;&lt;![CDATA[localhost]]&gt;&lt;/host&gt;
 &lt;username&gt;&lt;![CDATA[root]]&gt;&lt;/username&gt;
 &lt;password&gt;&lt;![CDATA[]]&gt;&lt;/password&gt;
 &lt;dbname&gt;&lt;![CDATA[magento_test]]&gt;&lt;/dbname&gt;
 &lt;active&gt;1&lt;/active&gt;
<strong> &lt;profiler&gt;1&lt;/profiler&gt;</strong>
 &lt;/connection&gt;
 &lt;/default_setup&gt;
 &lt;/resources&gt;
</pre>
<p><span id="more-13"></span>Nachdem man einmal den Cache gelöscht hat erscheint unterhalb des regulären Profilers ein neuer Bereich mit ein paar wenigen Infos, unter anderem wird auch die langsamste SQL-Query ausgegeben. Wie nützlich das in der Praxis ist muss man sehen, wem die Ausgabe zu langweilig ist, der kann sich per</p>
<pre>$profiler = Mage::getSingleton('core/resource')-&gt;getConnection('core_write')-&gt;getProfiler();
</pre>
<p>auch das ganze <a title="Dokumentation von Zend_Db_Profiler" href="http://framework.zend.com/manual/en/zend.db.profiler.html" target="_blank">Zend_Db_Profiler</a> &#8211; Objekt mit allen SQL-Queries und Laufzeiten heranholen und die Infos selber besser aufbereiten, z.B im einfachsten Fall so</p>
<pre>print_r($profiler-&gt;getQueryProfiles());</pre>
]]></content:encoded>
			<wfw:commentRss>http://magelog.de/2010/08/07/magento-sql-profiler/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

