April 05, 2021 - 7 min read
As I said in my previous article, at elgentos have been working with Shopware 6 close to a year now. Let’s do a quick run-through on how we experienced Shopware 6, coming from a Magento background.
Magento 1 was basically a monolith. Some external packages (mainly Zend Framework - now called Laminas) that were hard-wired into the core of Magento. Unless you really drilled down, you’d never even see a mention of Zend Framework packages anywhere. Magento 2 improved on that a lot; it includes packages from at least 15 different vendors, still leveraging Laminas heavily but also using a couple of Symfony packages amongst others. But Magento still is largely its own framework and its own platform - over 75% of the code base is Magento-specific code.
Shopware is competely built as a platform on the Symfony framework - starting from Shopware 6.4, they even upgraded to Symfony 5, which was released in November 2019. That’s pretty quick for such a big platform! Shopware leverages standard Symfony packages and best practices more than Magento does with Laminas, which makes Symfony developers feel at home very, very quickly. Around 45% of the code base is Shopware-specific code. The downside for developers who are not familiar with both Shopware and Symfony is that it is hard to gauge whether a certain piece of code or functionality is Symfony-wide or Shopware-specific. Where does Symfony end, and Shopware start?
Magento generally uses constructor injection to do DI (although argument injection is also possible). This is pretty easy; you inject the needed classes into your __construct
method and Magento takes care of the rest. In Shopware - or rather, Symfony - this isn’t that easy. Symfony requires a lot more explicit configuration than Magento does, so you’ll need to inject both the class into the constructor and define the injection in services.xml
. If this isn’t your thing, you can also set up autowiring - which we prefer to do.
This confused me no to end when I started working with Shopware. Magento has a concept called configurable products. A t-shirt could be a configurable product; it might have two super attributes size and color. So when the t-shirt has 3 colors available and 3 sizes, it would have 3x3=9 simple products. It would also have a tenth product - the configurable product - to ‘glue’ these 9 products together. The 9 simple products would generally be hidden from the storefront and only the configurable product would be shown - all descriptions, images, attributes, etc would be configured only on the configurable product.
Shopware flips this around; Shopware has parents and variants which you could loosely consider as a configurable and its children (simple) products. Except that the parent product doesn’t have its own entity on the storefront. Instead, the product variants are exposed on the storefront and clicking a different color on the product detail page leads you to a different URL - the variant you clicked on. This means that all relevant product data needs to be configured on all variants and not just on the parent! These pages do not count as duplicate content since all variants use the canonical URL of one (seemingly randomly chosen) variant.
There is no right or wrong in this one - the approaches are just different. However, there is a huge problem when you use the migration assistant to migrate from Magento 1 to Shopware 6. Magento 1 store owners generally only fill out product data on the configurable product, and Shopware 6 does not have such an entity as such. So the migration assistant only migrates the product data from the simple products to the variants in Shopware. But those are generally only empty shells, causing missing data on the Shopware side after the migration finished, leaving store owners befuddled where their data went.
As a Magento 2 developer, we’re used to requiring Magento extensions through composer. The alternative (copying them manually into app/code) is considered ugly, dirty and a bad practice. It baffled us to learn that in the Shopware world, there is no official way to require composer packages into the Shopware environment. I’ve written about static plugins vs custom static plugins before but the main takeaway from that article is; you can still require composer packages into Shopware unofficially, through the Friends of Shopware Packages repository. As to why this isn’t made an official part of Shopware, I have no clue.
This is a cool feature Magento doesn’t have (by default) - when a variant (in Magento terms; one of a configurable product’s child products, see above) is opened in the admin, the Storefront Presentation button reveals an option called Expand property values in product listings. This allows the store owner to show not just one variant of the parent (configurable) product in the category listing, but to split it up on one or more of the property values axes (Magento term; super attributes), such as color or size.
Product data in Magento is stored in attributes - these can be anything like images, (multi-)selects, input fields, textareas, dates, booleans, etc. For some inexplicable reason, Shopware divided product data up in two distinct systems - custom fields & properties. At first, it wasn’t clear to us when to use custom fields and when to use properties. This blog by Shyim does a good job at explaining the differences. My summary:
One final thing to note is that custom fields by default don’t show up on the product page! You’ll have to add custom code to make them show on the frontend.
Basically, these are translations. You can add a snippet file to your custom extension. Magento uses full string translation where the original base language string (usually English) serves as the string identifier (<?=__('Go to home'); ?>
). Shopware makes use of references, where each string gets its own internal identifier ({{ "general.homeLink"|trans }}
). The nice thing about Snippets in Shopware 6 is that the merchant can manually edit them from the admin under Settings > Snippets.
I’ve blogged about the weird CMS system in Shopware 6 before, so I won’t go into depth here. tl;dr; it’s weird now, but should be improved with the Site Builder in Shopware 6.4.
Internally, Shopware 6 uses binary UUID’s to identify database rows. UUID’s have certain advantages of which the biggest one is that you can generate the UUID before saving the entity and safely assume that will be the identifier. One huge downside (at least, for us Magento developers) is that it becomes pretty hard to do queries in the database like we’re used to with Magento to track down data. My tip is to use a decent IDE such as PhpStorm and use the database tool to query the Shopware database - you can copy & paste a UUID so you know you’ve got the right one.
The media system in Shopware 6 is built in a very abstract manner - the local file system has no one on one relation to the file system within Shopware 6. The folder and file structure within Shopware 6 is stored in the database. This allows us to easily save the files elsewhere (such as an S3 bucket) but it also adds an extra layer of complexity when dealing with images.
Shopware is a great platform and certainly worth considering as an alternative to Magento. Moving to Shopware from Magento requires a shift in thinking, both in code as in features and approaches. I hope this article helped you clarify some things we ran into when we first started working with Shopware 6.
Written by Peter Jaap Blaakmeer @PeterJaap