Archive for the ‘Magento’ Category

Magento: Problem mit addFieldToFilter

29. Januar 2016

Nach dem Einspielen einiger Patches in Magento 1.9.0.1 machte eine Extension einer Drittpartei plötzlich Probleme, beim Aufrufen der zugehörigen Seite warf Magento folgende Fehlermeldung aus:

  1. SQLSTATE[42S22]: Column not found: 1054 Unknown column 'CAST(CONCAT(year, "-", month, "-01") AS DATE)' in 'where clause'

das hatte vorher einwandfrei funktioniert und weil Magento super ist, habe ich anstatt ewig zu googeln einfach in Mage und Varien gegraben und die Ursache in lib/Varien/Data/Collection/Db.php gefunden. In der Extension der Drittpartei gab es folgendes Konstrukt:

  1. $entry->addFieldToFilter('CAST(CONCAT(year, "-", month, "-01") AS DATE)', array('from' => $fromDate, 'to' => $toDate));

Seit dem Patch macht addFieldToFilter aber etwas anders, es setzt nämlich alle übergebenen Werte erstmal in Quotes, und dann sieht die Query so aus:

  1. SELECT * FROM table WHERE `CAST(CONCAT(year, "-", month, "-01") AS DATE)` ...

Durch die Quotes nimmt MySQL dann an, dass es sich um eine Spalte handelt und kann die natürlich nicht finden. Man muss also dafür sorgen, dass addFieldToFilter so einen Wert nicht in Quotes packt, und dafür gibt es eine Möglichkeit. Bevor ein Wert in Quotes gesetzt wird, wird überprüft, ob in $this->_map nicht vielleicht ein Eintrag für diesen Wert drin ist, und wenn hier was drinsteht, dann werden die Anführungszeichen weggelassen. Weil es recht eilig war, habe ich mir also hiermit beholfen:

  1. $entry->addFilterToMap('CAST(CONCAT(year, "-", month, "-01") AS DATE)', 'CAST(CONCAT(year, "-", month, "-01") AS DATE) '); //mit einem leerzeichen danach
  2. $entry->addFieldToFilter('CAST(CONCAT(year, "-", month, "-01") AS DATE)', array('from' => $fromDate, 'to' => $toDate));

Und damit klappt’s dann auch. Ich bin sicher, wie ich das handhabe ist es nicht optimal, aber zwischenzeitlich hats mir den Arsch gerettet.

Magento: Produktseiten werfen beim Aufrufen manchmal einen 404er error

09. Dezember 2015

Ein merkwürdiges Problem, das mir nun schon bei 2 Magento installationen (Version 1.9.x.x) begegnet ist: ab und zu passiert es, dass Produktseiten nach dem Login nicht mehr erreichbar sind und stattdessen ein 404 error angezeigt wird. Im log (var/log/exception.log) steht dann

  1. Next exception 'Zend_Db_Statement_Exception' with message 'SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'xxxxx-xxx' for key 'UNQ_REPORT_VIEWED_PRODUCT_INDEX_CUSTOMER_ID_PRODUCT_ID'' in /path/to/shop/lib/Zend/Db/Statement/Pdo.php:234

Dieser Bugreport sieht ganz nach dem selben Problem aus, es scheint als wäre das ein bekanntes Problem von Magento < 2. Der Bugfix steht dann praktischerweise auch gleich mit drin, ich empfehle die Lösung von andrr ganz unten, und zwar dass man in das Magento core eingreift und folgende Datei bearbeitet: app/code/core/Mage/Reports/Model/Resource/Product/Index/Abstract.php in der save() Methode ergänzt man folgendes:

  1. unset($data[$this->getIdFieldName()]);
  2. //start fix
  3. if(Mage::getSingleton(‘customer/session’)->isLoggedIn()){
  4. $this->updateCustomerFromVisitorByProductId($object);
  5. }
  6. //end fix
  7. $matchFields = array(‘product_id’, ‘store_id’);

Und fügt der Klasse die Methode updateCustomerFromVisitorByProductId hinzu:

  1. public function updateCustomerFromVisitorByProductId(Mage_Reports_Model_Product_Index_Abstract $object)
  2. {
  3. /**
  4. * Do nothing if customer not logged in
  5. */
  6. if (!$object->getCustomerId() || !$object->getVisitorId() || !$object->getProductId()) {
  7. return $this;
  8. }
  9. $adapter = $this->_getWriteAdapter();
  10. $select = $adapter->select()
  11. ->from($this->getMainTable())
  12. ->where('visitor_id = ?', $object->getVisitorId())
  13. ->where('product_id = ?', $object->getProductId());
  14. $rowSet = $select->query()->fetchAll();
  15. foreach ($rowSet as $row) {
  16. $select = $adapter->select()
  17. ->from($this->getMainTable())
  18. ->where('customer_id = ?', $object->getCustomerId())
  19. ->where('product_id = ?', $row['product_id']);
  20. $idx = $adapter->fetchRow($select);
  21. if ($idx) {
  22. /* If we are here it means that we have two rows: one with known customer, but second just visitor is set
  23. * One row should be updated with customer_id, second should be deleted
  24. */
  25. $adapter->delete($this->getMainTable(), array('index_id = ?' => $row['index_id']));
  26. $where = array('index_id = ?' => $idx['index_id']);
  27. $data = array(
  28. 'visitor_id' => $object->getVisitorId(),
  29. 'store_id' => $object->getStoreId(),
  30. 'added_at' => Varien_Date::now(),
  31. );
  32. } else {
  33. $where = array('index_id = ?' => $row['index_id']);
  34. $data = array(
  35. 'customer_id' => $object->getCustomerId(),
  36. 'store_id' => $object->getStoreId(),
  37. 'added_at' => Varien_Date::now()
  38. );
  39. }
  40. $adapter->update($this->getMainTable(), $data, $where);
  41. }
  42. return $this;
  43. }

Bei mir hats damit dann funktioniert!

Magento Admin: speichern einer Entity klappt nur teilweise

14. September 2015

Ich habe im Magento Admin Bereich einen neuen Navigationspunkt mit einer neuen Entity mit Hilfe dieses großartigen Tutorials erstellt. Als ich die Felder meiner Entity erweitern wollte, stieß ich auf ein Problem: ein Feld wurde einfach nicht gespeichert. Obwohl ich die erstellte Tabelle in der Datenbank händisch gelöscht habe und auch unter core_resource den Eintrag entfernt, damit Magento meine Tabelle neu erstellt. Nichts hat geholfen, bis ich – trotz eigentlich deaktivierter Caches – auf die Idee kam, den Cache zu löschen. Und tatsächlich, der Button “Cache Lagerung leeren” hat geholfen!

Magento Newsletter einrichten

04. Mai 2011

Magento bietet von Haus aus eine simple Newsletter Funktion. Da ich noch keine kostenfreie Extension dafür gefunden habe, habe ich mich mal bemüht, mit den vorhandenen Mitteln einen einigermaßen präsentablen, wiederverwendbaren Newsletter aufzusetzen, den auch Outlook anzeigt. Der Newsletter hat einen Header und Footer, im Inhalt werden die neuesten Produkte angezeigt. Das Layout ist mit Tabellen aufgebaut, da wird ja alle wissen, dass CSS Styles in einem Newsletter von den meisten Mailprogrammen nicht akzeptiert werden.

Schritt 1: anlegen der footer und header Templates

Ich habe meine templates footer.phtml und header.phtml genannt. Header hat eine Zeile beliebigen Inhalts und umschließt den gesamten Content mit einer Tabelle, Footer beendet diese Tabelle und hat nochmal eine abschließende Zeile, in die man beispielsweise disclaimer oder den unsubscribe Link hineinschreiben kann. Die beiden Dateien bekommen ein neues Verzeichnis im template Ordner des aktuellen Designs, und dieses Verzeichnis heißt “email”.
Die header Datei wird gespeichert unter /app/design/frontend/default/THEMENAME/template/email/header.phtml und sieht so aus:

  1. <table width="500" cellpadding="10">
  2. <tr height="70">
  3. <td bgcolor="#FF0000">
  4. ...header inhalt...
  5. </td>
  6. </tr>
  7. <tr>
  8. <td>

footer befindet sich unter /app/design/frontend/default/THEMENAME/template/email/footer.phtml und sieht so aus:

  1. </td>
  2. </tr>
  3. <tr height="70">
  4. <td bgcolor="#FF00FF">...footer inhalt...</td>
  5. </tr>
  6. </table>

Schritt 2: eine neue Newsletter Vorlage erstellen

Felder wie Absender, Betreff, etc. sind selbsterklärend und werden nach Wunsch ausgefüllt. In Vorlageninhalt kann man nun seine Inhalte einfügen und über die Magento Syntax den Footer, den Header und die neuen Produkte einfügen:

  1. {{block type="core/template" template="email/header.phtml"}}
  2. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim.
  3. {{block type="catalog/product_new" name="home.catalog.product.new" alias="product_homepage" template="catalog/product/new_nl.phtml"}}
  4. {{block type="core/template" template="email/footer.phtml"}}

In der ersten und letzten Zeile werden header und footer eingefügt und können so in neuen Vorlagen wiederverwendet werden. Der mittlere Aufruf ist für das Anzeigen neuer Produkte.

Schritt 3: Anzeige der neuen Produkte

Standardmäßig werden die neuen Produkte als Listen angezeigt, für einen Newsletter ist es besser, das ganze in einer Tabelle darzustellen, deshalb habe ich eine neue Datei in catalog/product/ angelegt, new_nl.phtml. Ich habe mir den Inhalt von new.phtml übernommen und meinen Vorstellungen entsprechend modifiziert:

  1. <?php if (($_products = $this->getProductCollection()) && $_products->getSize()): ?>
  2. <h2><?php echo $this->__('New Products') ?></h2>
  3. <?php $_columnCount = $this->getColumnCount(); ?>
  4. <table>
  5. <?php $i=0; foreach ($_products->getItems() as $_product): ?>
  6. <?php if ($i++%$_columnCount==0): ?>
  7. <tr>
  8. <?php endif ?>
  9. <td>
  10. <a href="<?php echo $_product->getProductUrl() ?>" title="<?php echo $this->htmlEscape($_product->getName()) ?>"><img src="<?php echo $this->helper('catalog/image')->init($_product, 'small_image')->resize(135) ?>" width="135" height="135" alt="<?php echo $this->htmlEscape($_product->getName()) ?>" /></a>
  11. <h3><a href="<?php echo $_product->getProductUrl() ?>" title="<?php echo $this->htmlEscape($_product->getName()) ?>"><?php echo $this->htmlEscape($_product->getName()) ?></a></h3>
  12. <?php echo $this->getPriceHtml($_product, true, '-new') ?><br/>
  13. </td>
  14. <?php if ($i%$_columnCount==0 || $i==count($_products)): ?>
  15. </tr>
  16. <?php endif ?>
  17. <?php endforeach; ?>
  18. </table>
  19. <?php endif; ?>

Damit werden die Daten in Tabellenzellen gepackt und so von den gängigen Mailprogrammen korrekt dargestellt.

aheadWorks blog auf der Startseite

02. Mai 2011

Ich fange gerade erst damit an, mich mit Magento zu beschäftigen. Für ein Projekt brauche ich auf der Startseite die Anzeige der neuesten Blogeinträge (beim Blog handelt es sich um die Blog Extension von aheadWorks) und dies geht offenbar folgendermaßen:

  1. <block type="blog/blog" name="blog" template="aw_blog/blog.phtml"></block>

In CMS – Seiten verwalten – Home Page – Gestaltung – XML für Layoutänderung.