5 Minute Tips: Conference Talks

The learning coffee break is back! Today with a few tips I collected from others about preparing and giving conference talks:

On Scope

To me, a good talk is one where I learn something new or come away with some change in perspective. The more I learn / the more the speaker makes me think differently about something, the better. So, to give a good talk, you need to say a new-to-your-audience thing in an understandable way.

I don’t mind too much how many times you say “um” or how nice your slides are.

Julia Evans – Ideas for making better conference talks & conferences

Continue reading “5 Minute Tips: Conference Talks”

Usability for Programmers – UX Is Not Just Frontend!

This Tweet in 2015 blew my mind. I learned a bit about designing interactive systems and psychology in design, so I could relate immediately. It inspired me to a talk “Usability for Programmers” at WebCon Aachen, that unfortunately never went public because the event was cancelled, and I am yet waiting for a new chance to deliver it.

But I find the content too interesting to leave it hidden any longer, so here’s a blog post!

Continue reading “Usability for Programmers – UX Is Not Just Frontend!”

TDD Kata 01 – The Bowling Game

One of my goals for 2017 is to make TDD katas a part of my daily routine. What is a Kata? Just as in martial arts it is training through repetition, practicing the same exercise again and again until you can do it in your sleep.

Some would say, it is the only way to learn TDD. This makes sense for several reasons: you start in a small, protected scope and can apply pure test driven development. This way you are able to train the different mindset (the red-green-refactor cycle) without frustration. It means, you will not have to learn working around the quirks of a framework at the same time.
Continue reading “TDD Kata 01 – The Bowling Game”

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”

What to mock in a Magento 2 unit test

You write unit tests for a Magento 2 module and need to mock a core class, like the store model. This class has a few dependencies which have more dependencies and it’s getting complicated to mock. Although PHPUnit 5.4 has a createMock() method that automatically stubs methods and let them return new stubs if they have a return type.

Or: you mock a service contract like getList() in the product repository, which takes a search criteria instance as parameter, which must be returned by a search criteria factory. Then it returns a product search result, which has a getItems() method, which returns a array of products. So you end up mocking countless dependendencies for a single method call

Do these situations sound familiar? It does not need to be like that, though! Let me show you a sane way to deal with Magento dependencies.

Continue reading “What to mock in a Magento 2 unit test”

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";}