문제

How can we assign a theme in Magento 2.1 to a store using setup scripts?

Thank you

도움이 되었습니까?

해결책

Create a new InstallData (or UpgradeData) script in either a new module or a fitting pre-existing module.

Here is a simple example for a complete InstallData script. Simply replace the THEME_NAME constant found at the top of the class with your own theme name. For example, if you wanted to assign the default Magento 2 luma theme, you would write:

const THEME_NAME = 'Magento/luma';

(Make sure the Vendor starts with an uppercase and the theme with a lower case letter!)

You can find your theme name by simply looking at the registration.php in the directory of your theme (found in app/design/frontend/), it's probably easiest to copy paste it from here to prevent any typos:

\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::THEME,
    'frontend/Vendor/theme',
    __DIR__
);

The full InstallData.php script looks like this, alternatively you can add it to an UpgradeData.php as well, you mainly need the assignTheme() method from the bottom and inject all the required classes into the constructor:

<?php
namespace Medline\Theme\Setup;

use Magento\Framework\Setup\InstallDataInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Store\Model\Store;

/**
 * @codeCoverageIgnore
 * @SuppressWarnings(PHPMD)
 */
class InstallData implements InstallDataInterface
{
    const THEME_NAME = 'Vendor/theme';

    /**
     * @var \Magento\Theme\Model\Config
     */
    private $config;

    /**
     * @var \Magento\Theme\Model\ResourceModel\Theme\CollectionFactory
     */
    private $collectionFactory;

    /**
     * InstallData constructor.
     * @param \Magento\Theme\Model\ResourceModel\Theme\CollectionFactory $collectionFactory
     * @param \Magento\Theme\Model\Config $config
     */
    public function __construct(
        \Magento\Theme\Model\ResourceModel\Theme\CollectionFactory $collectionFactory,
        \Magento\Theme\Model\Config $config
    ) {
        $this->collectionFactory = $collectionFactory;
        $this->config = $config;
    }


    /**
     * @param ModuleDataSetupInterface $setup
     * @param ModuleContextInterface $context
     */
    public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
    {
        $setup->startSetup();

        $this->assignTheme();

        $setup->endSetup();
    }

    /**
     * Assign Theme
     *
     * @return void
     */
    protected function assignTheme()
    {
        $themes = $this->collectionFactory->create()->loadRegisteredThemes();
        /**
         * @var \Magento\Theme\Model\Theme $theme
         */
        foreach ($themes as $theme) {
            if ($theme->getCode() == self::THEME_NAME) {
                $this->config->assignToStore(
                    $theme,
                    [Store::DEFAULT_STORE_ID],
                    ScopeConfigInterface::SCOPE_TYPE_DEFAULT
                );
            }
        }
    }
}

다른 팁

On Magento 2.1 and later (2.2 and 2.3 coming out) it is recommended to use service contract so I tried doing this without calling method in the model directly.

Unfortunately at this point there is no straight forward way in the core Magento_Theme module service contract so we still have to make do with using some factory class.

First we need to inject the following classes to the setup script contructor:

use Magento\Theme\Api\DesignConfigRepositoryInterface;
use Magento\Theme\Model\Data\Design\ConfigFactory;
use Magento\Theme\Model\ResourceModel\Theme\CollectionFactory;

public function __construct(
        DesignConfigRepositoryInterface $designConfigRepository,
        CollectionFactory $themeCollectionFactory,
        ConfigFactory $themeConfigFactory) {...}

Next we will try to load the theme ID from it's path. Then we create a Design Config object by using factory class (hopefully Magento core will provide a service contract to do this in the future). Finally we use the ConfigRepository to save the value.

In this example my theme is for the frontend area, and has path MyVendorName/MyThemeName.

if (version_compare($context->getVersion(), '1.0.0', '<')) {
    $themeCollection = $this->themeCollectionFactory->create();
    $theme = $themeCollection->getThemeByFullPath('frontend/MyVendorName/MyThemeName');
    $themeId = $theme->getId();

    $designConfigData = $this->themeConfigFactory->create('default', 0, ['theme_theme_id' => $themeId]);

    $this->designConfigRepository->save($designConfigData);
}

Notes: the designConfigRepository should take care of reindexing the grid.

Using this approach you can also change other theme configuration, for example:

$designConfigData = $this->themeConfigFactory->create('default', 0, [
'theme_theme_id' => $themeId,
'head_default_title' => 'My Default Title',
'head_demonotice' => 1 // Display demo store notice
]);

Obviously you can replace 'default' and '0' with relevant store scope. Hope this helps.

Above solutions work but have one shortcoming. They assume that magento has already registered the theme, before setup script execution. This is not always the case. If you revert entries in setup_module table, you discover that error is "magically fixed".

To avoid it, make sure that themes are registered before setup script execution. One way to do it is

\Magento\Theme\Model\Theme\Registration::register()

Better to use \Magento\Framework\View\Design\Theme\ListInterface instead of \Magento\Theme\Model\ResourceModel\Theme\CollectionFactory. ListInterface have method getThemeByFullPath, so your code would be:

<?php
namespace Vendor\Module\Setup;

use Magento\Framework\Setup\InstallDataInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Store\Model\Store;

/**
 * Class InstallData
 */
class InstallData implements InstallDataInterface
{
    const THEME_FULL_PATH = 'frontend/Vendor/theme';

    /**
     * @var \Magento\Theme\Model\Config
     */
    private $config;

    /**
     * @var \Magento\Framework\View\Design\Theme\ListInterface $themeList
     */
    private $themeList;

    /**
     * InstallData constructor.
     *
     * @param \Magento\Framework\View\Design\Theme\ListInterface $themeList
     * @param \Magento\Theme\Model\Config $config
     */
    public function __construct(
        \Magento\Framework\View\Design\Theme\ListInterface $themeList,
        \Magento\Theme\Model\Config $config
    ) {
        $this->themeList = $themeList;
        $this->config = $config;
    }


    /**
     * @param ModuleDataSetupInterface $setup
     * @param ModuleContextInterface $context
     * @return void
     */
    public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
    {
        $setup->startSetup();

        $this->assignTheme();

        $setup->endSetup();
    }

    /**
     * Assign Theme
     *
     * @return void
     */
    protected function assignTheme()
    {
        /** @var \Magento\Framework\View\Design\ThemeInterface $theme */
        $theme = $this->themeList->getThemeByFullPath(self::THEME_FULL_PATH);

        if ($theme->getId()) {
            $this->config->assignToStore($theme, [Store::DEFAULT_STORE_ID], ScopeConfigInterface::SCOPE_TYPE_DEFAULT);
        }
    }
}

And you should add di.xml to your module

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Vendor\Module\Setup\InstallData">
        <arguments>
            <argument name="themeList" xsi:type="object">Magento\Theme\Model\ResourceModel\Theme\Collection</argument>
        </arguments>
    </type>
</config> 
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 magento.stackexchange
scroll top