MageTestFest – A Unique Conference and One Time Opportunity

If you are interested in Software Testing and/or Magento development, the most interesting event of the year is approaching: MageTestFest in Amerfoort (NL)!

  • Nov 15: Workshop PHPUnit (Sebastian Bergmann)
  • Nov 16: Workshop DDD (Mathias Verraes)
  • Nov 17: Conference Day (Agenda)
  • Nov 18: Magento Contribution Day (Hackathon)

Continue reading “MageTestFest – A Unique Conference and One Time Opportunity”

Collection Pipelines in PHP

If you read the book “Refactoring to Collections” or saw screencasts and talks by Adam Wathan about collection pipelines, but do not work with Laravel, you might have asked yourself how to apply these techniques to other PHP applications, for example Magento.

If you did not, let me explain collection pipelines first. Better, I’ll let Martin Fowler give the definition:

A collection pipeline lays out a sequence of operations that pass a collection of items between them. Each operation takes a collection as an input and emits another collection (except the last operation, which may be a terminal that emits a single value). The individual operations are simple, but you can create complex behavior by stringing together the various operations, much as you might plug pipes together in the physical world, hence the pipeline metaphor.

Continue reading “Collection Pipelines in PHP”

Learn Refactoring to Framework Independent Code

I’m proud to annouce that I will present the Nomad Mage session of January 2017. Nomad Mage is the Magento offspring of Nomad PHP and describes itself as:

Nomad Mage® is a virtual user group for Magento developers who understand that they need to keep learning to grow professionally. We meet online monthly to hear some of the best speakers in the community share what they’ve learned.

My topic: “Keep Magento Out of Your Magento Extensions – Refactoring to Framework Independent Code”

To port an existing Magento 1 extension to Magento 2, it can be helpful to first extract the business logic into a reusable library. This also makes for more testable and future-proof code. But how can it be done?

On Nomad Mage I’m going to walk through real examples to show you how such a refactoring can be approached. Although we will focus on refactoring existing Magento 1 extensions, the patterns you will learn are also useful for new extensions, Magento 1 or Magento 2.

Continue reading “Learn Refactoring to Framework Independent Code”

Column not found: 1054 Unknown column ‘sales_bestsellers_aggregated_yearly.product_type_id’ in ‘field list

Column not found: 1054 Unknown column ‘sales_bestsellers_aggregated_yearly.product_type_id’ in ‘field list

You might receive this error message in a fresh Magento 1.9.3 installation with sample data when trying to log into the admin panel. I got it after installing https://github.com/andreaskoch/dockerized-magento and found a few reports online, but no solution.

I don’t know the root cause yet, but to be able to log in again, you can set a different admin startup page:

n98-magerun config:set admin/startup/page system/config
n98-magerun cache:clear config

If you don’t have magerun installed (you should!), change the value in the core_config_data table instead and delete the cache.

Now delete the adminhtml cookie in your browser and you can log in again. Just don’t try to open the dashboard.


Full Stack Trace

a:5:{i:0;s:698:"SQLSTATE[42S22]: Column not found: 1054 Unknown column 'sales_bestsellers_aggregated_yearly.product_type_id' in 'field list', query was: SELECT COUNT(*) FROM (SELECT MAX(DATE_FORMAT(period, '%Y-%m-%d')) AS `period`, SUM(qty_ordered) AS `qty_ordered`, `sales_bestsellers_aggregated_yearly`.`product_id`, MAX(product_name) AS `product_name`, MAX(product_price) AS `product_price`, `sales_bestsellers_aggregated_yearly`.`product_type_id` FROM `sales_bestsellers_aggregated_yearly` WHERE (EXISTS (SELECT 1 FROM `catalog_product_entity` AS `existed_products` WHERE (sales_bestsellers_aggregated_yearly.product_id = existed_products.entity_id))) AND (store_id IN(0)) GROUP BY `product_id` LIMIT 5) AS `t`";i:1;s:4693:"#0 /var/www/html/web/lib/Varien/Db/Statement/Pdo/Mysql.php(110): Zend_Db_Statement_Pdo->_execute(Array)
#1 /var/www/html/web/app/code/core/Zend/Db/Statement.php(291): Varien_Db_Statement_Pdo_Mysql->_execute(Array)
#2 /var/www/html/web/lib/Zend/Db/Adapter/Abstract.php(480): Zend_Db_Statement->execute(Array)
#3 /var/www/html/web/lib/Zend/Db/Adapter/Pdo/Abstract.php(238): Zend_Db_Adapter_Abstract->query('SELECT COUNT(*)...', Array)
#4 /var/www/html/web/lib/Varien/Db/Adapter/Pdo/Mysql.php(504): Zend_Db_Adapter_Pdo_Abstract->query('SELECT COUNT(*)...', Array)
#5 /var/www/html/web/lib/Zend/Db/Adapter/Abstract.php(828): Varien_Db_Adapter_Pdo_Mysql->query(Object(Varien_Db_Select), Array)
#6 /var/www/html/web/lib/Varien/Data/Collection/Db.php(225): Zend_Db_Adapter_Abstract->fetchOne(Object(Varien_Db_Select), Array)
#7 /var/www/html/web/lib/Varien/Data/Collection.php(225): Varien_Data_Collection_Db->getSize()
#8 /var/www/html/web/lib/Varien/Data/Collection.php(211): Varien_Data_Collection->getLastPageNumber()
#9 /var/www/html/web/lib/Varien/Data/Collection/Db.php(522): Varien_Data_Collection->getCurPage()
#10 /var/www/html/web/lib/Varien/Data/Collection/Db.php(569): Varien_Data_Collection_Db->_renderLimit()
#11 /var/www/html/web/app/code/core/Mage/Reports/Model/Resource/Report/Collection/Abstract.php(285): Varien_Data_Collection_Db->load(false, false)
#12 /var/www/html/web/app/code/core/Mage/Adminhtml/Block/Widget/Grid.php(550): Mage_Reports_Model_Resource_Report_Collection_Abstract->load()
#13 /var/www/html/web/app/code/core/Mage/Adminhtml/Block/Dashboard/Tab/Products/Ordered.php(66): Mage_Adminhtml_Block_Widget_Grid->_prepareCollection()
#14 /var/www/html/web/app/code/core/Mage/Adminhtml/Block/Widget/Grid.php(643): Mage_Adminhtml_Block_Dashboard_Tab_Products_Ordered->_prepareCollection()
#15 /var/www/html/web/app/code/core/Mage/Adminhtml/Block/Widget/Grid.php(649): Mage_Adminhtml_Block_Widget_Grid->_prepareGrid()
#16 /var/www/html/web/app/code/core/Mage/Core/Block/Abstract.php(922): Mage_Adminhtml_Block_Widget_Grid->_beforeToHtml()
#17 /var/www/html/web/app/code/core/Mage/Adminhtml/Block/Dashboard/Grids.php(64): Mage_Core_Block_Abstract->toHtml()
#18 /var/www/html/web/app/code/core/Mage/Core/Block/Abstract.php(297): Mage_Adminhtml_Block_Dashboard_Grids->_prepareLayout()
#19 /var/www/html/web/app/code/core/Mage/Core/Model/Layout.php(456): Mage_Core_Block_Abstract->setLayout(Object(Mage_Core_Model_Layout))
#20 /var/www/html/web/app/code/core/Mage/Adminhtml/Block/Dashboard.php(75): Mage_Core_Model_Layout->createBlock('adminhtml/dashb...')
#21 /var/www/html/web/app/code/core/Mage/Core/Block/Abstract.php(297): Mage_Adminhtml_Block_Dashboard->_prepareLayout()
#22 /var/www/html/web/app/code/core/Mage/Core/Model/Layout.php(456): Mage_Core_Block_Abstract->setLayout(Object(Mage_Core_Model_Layout))
#23 /var/www/html/web/app/code/core/Mage/Core/Model/Layout.php(472): Mage_Core_Model_Layout->createBlock('adminhtml/dashb...', 'dashboard')
#24 /var/www/html/web/app/code/core/Mage/Core/Model/Layout.php(239): Mage_Core_Model_Layout->addBlock('adminhtml/dashb...', 'dashboard')
#25 /var/www/html/web/app/code/core/Mage/Core/Model/Layout.php(205): Mage_Core_Model_Layout->_generateBlock(Object(Mage_Core_Model_Layout_Element), Object(Mage_Core_Model_Layout_Element))
#26 /var/www/html/web/app/code/core/Mage/Core/Model/Layout.php(210): Mage_Core_Model_Layout->generateBlocks(Object(Mage_Core_Model_Layout_Element))
#27 /var/www/html/web/app/code/core/Mage/Core/Controller/Varien/Action.php(344): Mage_Core_Model_Layout->generateBlocks()
#28 /var/www/html/web/app/code/core/Mage/Core/Controller/Varien/Action.php(269): Mage_Core_Controller_Varien_Action->generateLayoutBlocks()
#29 /var/www/html/web/app/code/core/Mage/Adminhtml/Controller/Action.php(275): Mage_Core_Controller_Varien_Action->loadLayout(NULL, true, true)
#30 /var/www/html/web/app/code/core/Mage/Adminhtml/controllers/DashboardController.php(40): Mage_Adminhtml_Controller_Action->loadLayout()
#31 /var/www/html/web/app/code/core/Mage/Core/Controller/Varien/Action.php(418): Mage_Adminhtml_DashboardController->indexAction()
#32 /var/www/html/web/app/code/core/Mage/Core/Controller/Varien/Router/Standard.php(254): Mage_Core_Controller_Varien_Action->dispatch('index')
#33 /var/www/html/web/app/code/core/Mage/Core/Controller/Varien/Front.php(172): Mage_Core_Controller_Varien_Router_Standard->match(Object(Mage_Core_Controller_Request_Http))
#34 /var/www/html/web/app/code/core/Mage/Core/Model/App.php(365): Mage_Core_Controller_Varien_Front->dispatch()
#35 /var/www/html/web/app/Mage.php(692): Mage_Core_Model_App->run(Array)
#36 /var/www/html/web/index.php(83): Mage::run('', 'store')
#37 {main}";s:3:"url";s:70:"/index.php/admin/dashboard/index/key/8565326cb6d7ae31c17ba1e04f2631bc/";s:11:"script_name";s:10:"/index.php";s:4:"skin";s:5:"admin";}

The week on StackExchange #40/2016

Since the last “Weeks on StackExchange” post got quite long, I’ll get back to the weekly schedule:

Hot Magento 2 answers

Open questions

The weeks on StackExchange #37-39/2016

Here we go again, a summary of interesting posts from the last weeks:

Magento 2 CLI

Magento 2 Extension Development

Magento 2 Translation

Magento Product Import

Magento 1 Best Practices

Magento 1 Tipps

PHPUnit

Isolating Domain Logic in Magento Customizations

Lately I’ve been advocating decoupling business logic from the framework (i.e. Magento) a lot.

This has multiple advantages:

  • Benefit from test driven development (TDD) without having to mock a bunch of core classes.
  • Possible reuse in different applications (for example Magento 1 and Magento 2)
  • Having separated bounded contexts helps to view parts of the domain isolated and without distraction.

Even in chirurgic modifications that we often have to do in Magento projects, it is worth identifying the actual logic and extracting it from the actual Magento classes.

Let me demonstrate it with a real-world example:

Read more at integer-net.com

Stop using Helpers

“Helpers” are often used as convenient collection of functions. They are also a sign of bad design, and I want you to stop writing them. I’ll quote myself

In general, having classes named “Helper”, “Util” or similar just says “I have some functions that I don’t know where to put” and don’t make much sense as a class.

It’s not very object oriented. Not at all to be frank. The idea of object oriented programming is that there are objects that send each others messages. They have an active role in the system and are not just containers for data and code, which would be a very procedural way to see them.

So what would be the role of a helper? A butler maybe, that does not act on its own and will do anything you tell him. But to do that, he needs access to your whole household, your bank account and your car.

Continue reading “Stop using Helpers”

The week on StackExchange #36/2016

And again some new posts from Magento StackExchange that I’d like to highlight:

Magento 2

Magento 1