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

Thank you

Answer: 
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 **V**endor starts with an uppercase and the **t**heme 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.