Shopware: Model / Entity beim Update eines Plugins modifizieren

Mischpult

Wenn bei einem Plugin eigene Models (= Tabellen die nicht zum Shopware Core gehören) zum Einsatz kommen musst Du deren Struktur möglicherweise bei der Weiterentwicklung aktualisieren. Dieser Artikel zeigt Dir, wie Du solche Updates mit dem Doctrine SchemaTool durchführen kannst.

Am Einfachsten lässt sich die Anwendung hier anhand eines Beispiels zeigen. Angenommen Dein Plugin benötigt zusätzliche Tabellen in der Shopware Datenbank um Discounts und Firmendaten zu speichern, kannst Du diese in der Install-Methode des Plugins bequem anlegen.

Achtung: Es gibt Hinweise, dass es bei bestimmten Systemkonfigurationen zu Problemen mit der Kollation der Tabellen kommen kann (Danke an max für den Hinweis). Details dazu findest Du im Shopware-Forum: https://forum.shopware.com/discussion/comment/197711/#Comment_197711

Das zusätzliche Company Model:

<?php

namespace PluginNamespace\Models;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\Table(name="myplugin_company")
 */
class Company
{

    /**
     * @var integer
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

    /**
     * @var string
     * @ORM\Column(name="name", type="string", length=255, nullable=false, options={"comment":"Name of the company."})
     */
    private $name = '';

    /**
     * @return int
     */
    public function getId(): int
    {
        return $this->id;
    }

    /**
     * @param int $id
     */
    public function setId(int $id)
    {
        $this->id = $id;
    }

    /**
     * @return string
     */
    public function getName(): string
    {
        return $this->name;
    }

    /**
     * @param string $name
     */
    public function setName(string $name)
    {
        $this->name = $name;
    }

}

Erstellen der Tabellen bei der Plugin-Installation:

/**
 * @param InstallContext $context
 * This method is called on plugin installation
 */
public function install(InstallContext $context)
{
    $tool = new SchemaTool(Shopware()->Container()->get('models'));
    $schemas = [
        Shopware()->Container()->get('models')->getClassMetadata(Discount::class),
        Shopware()->Container()->get('models')->getClassMetadata(Company::class)
    ];
    
    /** @var MySqlSchemaManager $schemaManager */
    $schemaManager = Shopware()->Container()->get('models')->getConnection()->getSchemaManager();
    foreach($schemas as $class) {
        if (!$schemaManager->tablesExist($class->getTableName())) {
            $tool->createSchema([$class]);
        }else{
            $tool->updateSchema([$class], true); //true - saveMode and not delete other schemas
        }
    }

    return parent::install($context);
}

Anhand der Annotations die bei den beiden Models Discount und Company hinterlegt sind, baut das Doctrine Schema Tool die nötigen Tabellen auf. Um Konflikte bei einer Neuinstallation des Plugins zu vermeiden, prüfe ich über den Schemamanager auch, ob die Tabelle für das Model bereits existiert und führe gegebenenfalls nur ein Update durch.

Angenommen es besteht nun der Bedarf das Company Model in einer neuen Version des Plugins um eine Property additionalInfo zu erweitern, stehen wir vor einem kleinen Problem. Dieses Attribut soll automatisch hinzugefügt werden, wenn der Anwender im Plugin-Manager auf das Update-Icon klickt und keine Neuinstallation ausgeführt wird. Bestandsdaten dürfen dabei keinesfalls verändert werden.

Die nötigen Werkzeuge um diese Problem zu lösen hat Shopware (bzw. Doctrine) bereits an Board. Füge die neue Property additionalInfo mit der entsprechenden ORM Annotation in Dein Company Model ein. Anschließend musst Du beim Update des Plugins (in diesem Fall auf Version 1.1.0) dem Schema-Tool nur noch sagen, dass er die Tabelle entsprechend der Modeldefinition aktualisieren soll:

Das modifizierte Company Model:

<?php

namespace PluginNamespace\Models;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\Table(name="myplugin_company")
 */
class Company
{

    /**
     * @var integer
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

    /**
     * @var string
     * @ORM\Column(name="name", type="string", length=255, nullable=false, options={"comment":"Name of the company."})
     */
    private $name = '';

    /**
     * @var string
     * @ORM\Column(name="additional_info", type="string", length=255, nullable=false, options={"comment":"Some additional info for the company."})
     */
    private $additionalInfo = '';

    /**
     * @return int
     */
    public function getId(): int
    {
        return $this->id;
    }

    /**
     * @param int $id
     */
    public function setId(int $id)
    {
        $this->id = $id;
    }

    /**
     * @return string
     */
    public function getName(): string
    {
        return $this->name;
    }

    /**
     * @param string $name
     */
    public function setName(string $name)
    {
        $this->name = $name;
    }

     /**
     * @return string
     */
    public function getAdditionalInfo(): string
    {
        return $this->additionalInfo;
    }

    /**
     * @param string $additionalInfo
     */
    public function setAdditionalInfo(string $additionalInfo)
    {
        $this->additionalInfo = $additionalInfo;
    }

}

Aktualisieren der Company Tabelle bei Plugin-Update:

/**
 * @param UpdateContext $context
 * This method is called on update of the plugin
 */
public function update(UpdateContext $context)
{

    // Updates with Version 1.1.0
    if (version_compare($context->getCurrentVersion(), '1.1.0', '<')) {

        // Update Company model schema
        $tool = new SchemaTool(Shopware()->Container()->get('models'));
        $class = Shopware()->Container()->get('models')->getClassMetadata(Company::class);

        /** @var MySqlSchemaManager $schemaManager */
        $schemaManager = Shopware()->Container()->get('models')->getConnection()->getSchemaManager();

        if (!$schemaManager->tablesExist($class->getTableName())) {
            $tool->createSchema([$class]);
        }else{
            $tool->updateSchema([$class], true); //true - saveMode and not delete other schemas
        }

    }

    $context->scheduleClearCache(InstallContext::CACHE_LIST_ALL);
    return parent::update($context);

}

Ein weiterer Vorteil dieser Methode ist, dass auch bei einer Neuinstallation automatisch die neue Spalte mit angelegt wird und Du diesen Fall nicht gesondert abdecken musst.

1 Kommentar

In diesem Beitrag kannst Du Kommentare verfassen.

Kommentar verfassen

Folge mir auf Twitter

Hol Dir kostenlos Tipps und Tricks zu Shopware, E-Commerce und andere Open-Source Produkte.

Folge @synonymousrocks