You are here

class ConfigDevelCommands in Configuration development 8

Drush integration for the Configuration Development module.

Hierarchy

Expanded class hierarchy of ConfigDevelCommands

1 string reference to 'ConfigDevelCommands'
drush.services.yml in ./drush.services.yml
drush.services.yml
1 service uses ConfigDevelCommands
config_devel.commands in ./drush.services.yml
\Drupal\config_devel\Commands\ConfigDevelCommands

File

src/Commands/ConfigDevelCommands.php, line 18

Namespace

Drupal\config_devel\Commands
View source
class ConfigDevelCommands extends DrushCommands {

  /**
   * The module handler.
   *
   * @var \Drupal\Core\Extension\ModuleHandlerInterface
   */
  protected $moduleHandler;

  /**
   * The theme handler.
   *
   * @var \Drupal\Core\Extension\ThemeHandlerInterface
   */
  protected $themeHandler;

  /**
   * The parser for info.yml files.
   *
   * @var \Drupal\Core\Extension\InfoParserInterface
   */
  protected $infoParser;

  /**
   * The configuration object factory.
   *
   * @var \Drupal\Core\Config\ConfigFactoryInterface
   */
  protected $configFactory;

  /**
   * The config importer and exporter.
   *
   * @var \Drupal\config_devel\ConfigImporterExporter
   */
  protected $configImportExport;

  /**
   * The event subscriber that listens to config change events.
   *
   * @var \Drupal\config_devel\EventSubscriber\ConfigDevelAutoImportSubscriber
   */
  protected $autoImportSubscriber;

  /**
   * The file system service.
   *
   * @var \Drupal\Core\File\FileSystemInterface
   */
  protected $fileSystem;

  /**
   * Constructs a new ConfigDevelCommands object.
   *
   * @param \Drupal\Core\Extension\ModuleHandlerInterface $moduleHandler
   *   The module handler.
   * @param \Drupal\Core\Extension\ThemeHandlerInterface $themeHandler
   *   The theme handler.
   * @param \Drupal\Core\Extension\InfoParserInterface $infoParser
   *   The parser for info.yml files.
   * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
   *   The configuration object factory.
   * @param \Drupal\config_devel\ConfigImporterExporter $autoExportSubscriber
   *   The event subscriber that listens to config change events, and happens to
   *   contain some code that we depend on which should be factored out into a
   *   separate service.
   * @param \Drupal\config_devel\EventSubscriber\ConfigDevelAutoImportSubscriber $autoImportSubscriber
   *   The event subscriber that listens to config change events, and happens to
   *   contain some code that we depend on which should be factored out into a
   *   separate service.
   * @param \Drupal\Core\File\FileSystemInterface $fileSystem
   *   The file system service.
   */
  public function __construct(ModuleHandlerInterface $moduleHandler, ThemeHandlerInterface $themeHandler, InfoParserInterface $infoParser, ConfigFactoryInterface $configFactory, ConfigImporterExporter $config_import_export, ConfigDevelAutoImportSubscriber $autoImportSubscriber, FileSystemInterface $fileSystem) {
    parent::__construct();
    $this->moduleHandler = $moduleHandler;
    $this->themeHandler = $themeHandler;
    $this->infoParser = $infoParser;
    $this->configFactory = $configFactory;
    $this->configImportExport = $config_import_export;

    // @todo We should not depend on event subscribers directly.
    // @see https://www.drupal.org/node/2388253
    $this->autoImportSubscriber = $autoImportSubscriber;
    $this->fileSystem = $fileSystem;
  }

  /**
   * Write back configuration to module's config directory.
   *
   * List which configuration settings you want to export in the module's info
   * file by listing them under 'config_devel', as shown below:
   *
   * config_devel:
   *   install:
   *     - entity.view_display.node.article.default
   *     - entity.view_display.node.article.teaser
   *     - field.instance.node.article.body
   *   optional:
   *     - field.instance.node.article.tags
   *
   * @command config:devel-export
   * @param string $extension Machine name of the module, profile or theme to export.
   * @usage drush config-devel-export MODULE_NAME
   *   Write back configuration to the specified module, based on .info file.
   * @aliases cde,cd-em,config-devel-export
   *
   * @throws \Exception
   *   Thrown when the passed in extension
   */
  public function export($extension) {

    // Determine the type of extension we're dealing with.
    $type = $this
      ->getExtensionType($extension);
    if (!$type) {
      throw new \Exception("Couldn't export configuration. The '{$extension}' extension is not enabled.");
    }

    // Get the config.
    $config = $this
      ->getExtensionConfig($type, $extension);
    if (empty($config)) {
      throw new \Exception(sprintf("Couldn't export configuration. There is no config available for %s.", $extension));
    }

    // Export the required config.
    if (isset($config['install'])) {
      $this
        ->exportConfig($config['install'], $type, $extension, InstallStorage::CONFIG_INSTALL_DIRECTORY);
    }

    // If we have any optional configuration, export that as well.
    if (isset($config['optional'])) {
      $this
        ->exportConfig($config['optional'], $type, $extension, InstallStorage::CONFIG_OPTIONAL_DIRECTORY);
    }
  }

  /**
   * Import configuration from module's config directory to active storage.
   *
   * List which configuration settings you want to export in the module's info
   * file by listing them under 'config_devel', as shown below:
   *
   * config_devel:
   *   install:
   *     - entity.view_display.node.article.default
   *     - entity.view_display.node.article.teaser
   *     - field.instance.node.article.body
   *   optional:
   *     - field.instance.node.article.tags
   *
   * @command config:devel-import
   * @param string $extension Machine name of the module, profile or theme.
   * @usage drush config-devel-import MODULE_NAME
   *   Import configuration from the specified module, profile or theme into the active storage, based on .info file.
   * @aliases cdi,cd-im,config-devel-import
   *
   * @throws \Exception
   *   Thrown when the passed in extension is not enabled.
   */
  public function import($extension) {

    // Determine the type of extension we're dealing with.
    $type = $this
      ->getExtensionType($extension);
    if (!$type) {
      throw new \Exception("Couldn't import configuration. The '{$extension}' extension is not enabled.");
    }

    // Get the config
    $config = $this
      ->getExtensionConfig($type, $extension);
    if (empty($config)) {
      throw new \Exception(sprintf("Couldn't import configuration. There is no config available for %s.", $extension));
    }

    // Import config
    if (isset($config['install'])) {
      $this
        ->importConfig($config['install'], $type, $extension, InstallStorage::CONFIG_INSTALL_DIRECTORY);
    }

    // Import optional config
    if (isset($config['optional'])) {
      $this
        ->importConfig($config['optional'], $type, $extension, InstallStorage::CONFIG_OPTIONAL_DIRECTORY);
    }
  }

  /**
   * Import a single config item into active storage.
   *
   * List which configuration settings you want to export in the module's info
   * file by listing them under 'config_devel', as shown below:
   *
   * config_devel:
   *   install:
   *     - entity.view_display.node.article.default
   *     - entity.view_display.node.article.teaser
   *     - field.instance.node.article.body
   *   optional:
   *     - field.instance.node.article.tags
   *
   * @command config:devel-import-one
   * @param string $path Config file name.
   * @usage drush config-devel-import-one system.site.yml
   *   Import the contents of system.site.yml into the config object system.site.
   * @usage drush config-devel-import-one system.site
   *   Import the standard input into the config object system.site. Helpful for scripting copying to remote.
   * @aliases cdi1,cd-i1,config-devel-import-one
   *
   * @throws \Exception
   *   Thrown when the given file was not found.
   */
  public function importSingle($path) {
    $contents = '';
    if (!file_exists($path)) {
      if (substr($path, -4) != '.yml') {
        $contents = file_get_contents('php://stdin');
      }
      elseif (!empty($_SERVER['PWD'])) {
        $path = $_SERVER['PWD'] . '/' . trim($path, '/');
      }
    }
    if ($contents || file_exists($path)) {
      $new_hash = $this->configImportExport
        ->importConfig($path, '', $contents);
      if ($new_hash) {
        $this
          ->output()
          ->writeln('Imported config from file ' . $path . '.');
      }
    }
    else {
      throw new \Exception("File '{$path}' not found.");
    }
  }

  /**
   * Returns the type for the given extension.
   *
   * @param string $extension
   *   The extension name.
   *
   * @return string
   *   Either 'module', 'theme', 'profile', or NULL if no valid extension was
   *   provided.
   */
  protected function getExtensionType($extension) {
    $type = NULL;

    // Check the profile first, as profiles are treated as modules by
    // moduleExists()!
    if (\Drupal::installProfile() === $extension) {
      $type = 'profile';
    }
    elseif ($this->moduleHandler
      ->moduleExists($extension)) {
      $type = 'module';
    }
    elseif ($this->themeHandler
      ->themeExists($extension)) {
      $type = 'theme';
    }
    return $type;
  }

  /**
   * Returns the config for the given extension.
   *
   * @param string $type
   *   Either 'module', 'theme' or 'profile'.
   * @param string $extension
   *   The name of the extension for which to return the config.
   *
   * @return array
   *   An array containing install and optional config.
   */
  protected function getExtensionConfig($type, $extension) {
    $filename = drupal_get_path($type, $extension) . '/' . $extension . '.info.yml';
    $info = $this->infoParser
      ->parse($filename);
    $config = [];
    if (isset($info['config_devel'])) {

      // Keep backwards compatibility for the old format. This has config names
      // listed directly beneath 'config_devel', rather than an intermediate
      // level for 'install' and 'optional'.
      // Detect the old format based on whether there's neither of these two
      // keys.
      if (!isset($info['config_devel']['install']) && !isset($info['config_devel']['optional'])) {
        $info['config_devel']['install'] = $info['config_devel'];
      }
      foreach ([
        'install',
        'optional',
      ] as $type) {
        if (isset($info['config_devel'][$type])) {
          $config[$type] = $info['config_devel'][$type];
        }
      }
    }
    return $config;
  }

  /**
   * Exports a list of configuration entities.
   *
   * @param array $config_list
   *   An array of configuration entities.
   * @param string $type
   *   The type of extension we're exporting, one of 'module', 'profile' or
   *   'theme'.
   * @param string $extension
   *   The name of the extension we're exporting.
   * @param string $directory
   *   The subdirectory within the extension that we're exporting to. One of
   *   \Drupal\Core\Config\InstallStorage\InstallStorage::CONFIG_INSTALL_DIRECTORY
   *   or \Drupal\Core\Config\InstallStorage\InstallStorage::CONFIG_OPTIONAL_DIRECTORY.
   *
   * @throws \Exception
   *   Thrown when the directory to export to is missing and could not be
   *   created.
   */
  protected function exportConfig($config_list, $type, $extension, $directory) {
    $config_path = drupal_get_path($type, $extension) . "/{$directory}";

    // Ensure the directory always exists.
    if (!file_exists($config_path) && !$this->fileSystem
      ->mkdir($config_path, NULL, TRUE)) {
      throw new \Exception(sprintf('The %s directory could not be created', $config_path));
    }

    // Use loadMultiple() rather than get(), as get creates a new config object
    // if it doesn't manage to load one, and we don't want that to happen.
    $configs = $this->configFactory
      ->loadMultiple($config_list);
    foreach ($config_list as $name) {
      if (!isset($configs[$name])) {
        $this
          ->logger()
          ->warning("No config found for '{$name}', skipping.");
        continue;
      }
      $file_names = [
        $config_path . '/' . $name . '.yml',
      ];
      $written_files = $this->configImportExport
        ->writeBackConfig($configs[$name], $file_names);
      foreach ($written_files as $written_file_name) {
        $this
          ->output()
          ->writeln('Exported config file ' . $written_file_name . '.');
      }
    }
  }

  /**
   * Imports a list of configuration entities.
   *
   * @param array $config_list
   *   An array of configuration entities.
   * @param string $type
   *   The type of extension we're importing, one of 'module'. 'profile' or
   *   'theme'.
   * @param string $extension
   *   The module, theme or install profile we're importing.
   * @param string $directory
   *   The subdirectory within the extension that we're importing from. One of
   *   \Drupal\Core\Config\InstallStorage\InstallStorage::CONFIG_INSTALL_DIRECTORY
   *   or \Drupal\Core\Config\InstallStorage\InstallStorage::CONFIG_OPTIONAL_DIRECTORY.
   */
  protected function importConfig($config_list, $type, $extension, $directory) {
    $config_path = drupal_get_path($type, $extension) . "/{$directory}";
    foreach ($config_list as $name) {
      $file_name = $config_path . '/' . $name . '.yml';
      $this
        ->importSingle($file_name);
    }
  }

}

Members

Namesort descending Modifiers Type Description Overrides
ConfigDevelCommands::$autoImportSubscriber protected property The event subscriber that listens to config change events.
ConfigDevelCommands::$configFactory protected property The configuration object factory.
ConfigDevelCommands::$configImportExport protected property The config importer and exporter.
ConfigDevelCommands::$fileSystem protected property The file system service.
ConfigDevelCommands::$infoParser protected property The parser for info.yml files.
ConfigDevelCommands::$moduleHandler protected property The module handler.
ConfigDevelCommands::$themeHandler protected property The theme handler.
ConfigDevelCommands::export public function Write back configuration to module's config directory.
ConfigDevelCommands::exportConfig protected function Exports a list of configuration entities.
ConfigDevelCommands::getExtensionConfig protected function Returns the config for the given extension.
ConfigDevelCommands::getExtensionType protected function Returns the type for the given extension.
ConfigDevelCommands::import public function Import configuration from module's config directory to active storage.
ConfigDevelCommands::importConfig protected function Imports a list of configuration entities.
ConfigDevelCommands::importSingle public function Import a single config item into active storage.
ConfigDevelCommands::__construct public function Constructs a new ConfigDevelCommands object.