Step 3: Database setup

Version

6.0.0 or newer

Table of contents

The plugin is fully setup, yet it's not doing anything as of now.

A first step to get your plugin rolling would probably be to setup the necessary database table first. The bundles are going to be saved in a database table, but which data do you need?

Almost every table in Shopware 6 has a binary ID column, so you'll need that. All ID columns are saved as an UUID. For those of you who wonder what an UUID even is:

A universally unique identifier (UUID) is a 128-bit number used to identify information in computer systems.

A UUID is basically almost guaranteed to be unique, even though there's the smallest chance for it to be duplicated. They look like this, maybe you stumbled upon them already: 9a0b44d5-eec2-4ad9-86b1-14d034035fb1 Those are not automatically generated by your storage, such as MySQL, so you would normally have to take care of that yourself. But don't worry, Shopware 6 is taking care of that for you. A big advantage of using UUIDs is the ability to foresee a new entry's id before even creating the entry and thus granting the ability to keep on working with this id. Otherwise you'd have to create another query to fetch the automatically created id before being able to work with it.

Since the main reason for customers to buy a bundle, is the discount that comes with a bundle, so you'll have to integrate discounts. Some shop managers prefer absolute discounts, some would rather use percentage discounts, so a bundle would need a discount_type field. Also, you need to provide the actual discount value, so you'll need a discount column. Note: For simplicities' sake this bundle example does not support staggered prices.

Per default, every entity also receives an created_at and an updated_at column, so you'll need those in your table as well.

Here's what the SQL for this looks like:

CREATE TABLE IF NOT EXISTS `swag_bundle` (
  `id` BINARY(16) NOT NULL,
  `discount_type` VARCHAR(255) NOT NULL,
  `discount` DOUBLE NOT NULL,
  `created_at` DATETIME(3) NOT NULL,
  `updated_at` DATETIME(3) NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

First of all the name of the table is swag_bundle, which consists of the vendor prefix swag plus the actual table name. While the naming is up to you, we highly recommend you to prefix your tables with your vendor name. If you don't do that, you're taking the risk of breaking with other plugins, that created the same table. By providing a unique table name, you prevent that possible issue.

Setting up a migration

Shopware 6 uses a migration system to install and update a plugin's database setup. Make sure to always use the migration system when setting up database tables or updating their structure!

Migrations are executed when installing and updating your plugin. In order to create a new table, you'll need one of those migrations. They are automatically loaded and executed, if you place them in the proper directory. By default, those are expected to be in a Migration directory, relative to your plugin's base class. Since your base class' location is the src directory, that's also where the Migration directory has to be in: <plugin root>/src/Migration

After creating the directory, you can use symfony's bin/console to create boilerplate migration files.

bin/console database:create-migration --plugin SwagBundleExample --name Bundle
`--plugin`
specifies the plugin's name in order to create the file in the correct directory
`--name`
specifies an arbitrary suffix for the migration file helping you to quickly see, which migration file is doing what

The actual PHP class name has to match the filename, so Migration1554708925Bundle it is. Migrations have to extend from the class Shopware\Core\Framework\Migration\MigrationStep.

Every migration must implement three methods: getCreationTimestamp, update and updateDestructive

getCreationTimestamp
Simply returns the timestamp here, that's used in the class name. Return value must be an integer. This timestamp will be saved into the database table `migration`, so Shopware 6 can keep track of which migrations were executed already and which ones are still necessary.
update
Updates done in here have to be reversible, like creating a database table or a nullable column.
updateDestructive
This reflects the counter part to the `update` method - all non-reversible updates have to be made here. Examples for destructive changes are dropping a table or removing columns.

Since you're just going to create a new table, you can use the update method. Both update-methods have access to a DBAL connection in order to actually execute their database actions.

So this is, what your Migration should look like now, including the SQL statement to create the table:

<?php declare(strict_types=1);

namespace Swag\BundleExample\Migration;

use Doctrine\DBAL\Connection;
use Shopware\Core\Framework\Migration\MigrationStep;

class Migration1554708925Bundle extends MigrationStep
{
    public function getCreationTimestamp(): int
    {
        return 1554708925;
    }

    public function update(Connection $connection): void
    {
        $connection->executeQuery('
            CREATE TABLE IF NOT EXISTS `swag_bundle` (
              `id` BINARY(16) NOT NULL,
              `discount_type` VARCHAR(255) NOT NULL,
              `discount` DOUBLE NOT NULL,
              `created_at` DATETIME(3) NOT NULL,
              `updated_at` DATETIME(3) NULL,
              PRIMARY KEY (`id`)
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
        ');
    }

    public function updateDestructive(Connection $connection): void
    {
    }
}

The updateDestructive method is empty, since you don't have to execute any destructive changes. The database will be setup once you install your plugin. If you've done that already, you'll have to reinstall your plugin.

Head over to the next step to introduce your new database tables to Shopware 6.