Coding

Config vs State in Drupal 8/9

Published by Alan Saunders on Saturday, March 6, 2021 - 22:00

Reading Time: 5 minutes

Sections

Title
Introduction

Body

In this blog post, we'll look at both configuration and state in a little more detail including how to save data to and get data from configuration and state.

So the bottom line of whether you should use config or state really depends on what your use case(s), if your information would need to be deployed from your development server to your live server, it is probably configuration and not state information. In some cases you may even need to use both config and state when building a website.

I'm now going to talk about config and state in a little more detail.

    Title
    Configuration management

    Body

    In Drupal 7 most of the configuration was stored in the database, meaning that you would either need to replicate configuration changes on each environment that the website has or use the features module to export the configuration (if features was able too) that you want to set on each environment.

    When Drupal 8 came along, they made a huge change in how configuration is managed, through the Configuration Management Initiative (CMI). There is now an inbuilt method of exporting configuration like your site name, or more complex data managed with configuration entities, such as views and content types that is in the database into yaml files. These yaml files are then committed to the website git repository and on deploy to an environment the config can be imported, so that the added/ updated config gets applied and reduces human error.

    Configuration is a place to store information that you would want to synchronise from development to production. This information is often created during site-building and is not typically generated by regular users during normal site operation.

    Config can be export using the below drush command;

    drush cex

    Config can be imported using the below drush command;

    drush cim

    Also with the configuration API and the various contributed modules that help developers have better workflows when working on sites, you can do things like setup configuration so that you have one set for your local/ dev environments which would have your development tools enabled and then a set for production where development tools are disabled.

    Title
    Implementation

    Body

    Below are some helpful lines of code for interacting with the Config API.

    Without dependency injection

    Getting config

    Site name
    \Drupal::config('system.site')->get('name');
    Writing Config

    Lorem

    A single value
    \Drupal::service('config.factory')->getEditable('system.site')->set('name','value')->save();
    Removing config

    Lorem

    A single value
    $config = \Drupal::service('config.factory')->getEditable('system.performance');
    $config->clear('cache.page.max_age')->save();
    Multiple values
    \Drupal::service('config.factory')->getEditable('system.performance')->delete();

    Avoid

    \Drupal::service('config.factory')->getEditable('foo.bar')->set('foo', 'foo')->save();
    \Drupal::service('config.factory')->getEditable('foo.bar')->set('bar', 'bar')->save();

    Instead

    $config = \Drupal::service('config.factory')->getEditable('foo.bar');
    $config
      ->set('foo', 'foo')
      ->set('bar', 'bar')
      ->save();

    With dependency injection

    In some cases particularly classes that need to be registered in a [module name].services.yml file inside your module, then you will need to pass the config factory as an argument for the class in the [module name].services.yml, so that the config factory is instantiated and usable within your class.

    services:
      custom_overrides.hook_form_alter:
        class: Drupal\custom_overrides\EventSubscriber\RatlingateHookFormAlter
        arguments: ['@config.factory']
        tags:
          - { name: event_subscriber }

     

    /**
       * @var \Drupal\Core\Config\ConfigFactoryInterface
       */
      protected $configFactory;
    
      /**
       * PricesBlock constructor.
       *
       * @param array $configuration
       * @param $plugin_id
       * @param $plugin_definition
       * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
       */
      public function __construct(array $configuration, $plugin_id, $plugin_definition, ConfigFactoryInterface $config_factory) {
        parent::__construct($configuration, $plugin_id, $plugin_definition);
        $this->configFactory = $config_factory;
      }
    
      /**
       * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
       * @param array $configuration
       * @param string $plugin_id
       * @param mixed $plugin_definition
       *
       * @return \Drupal\Core\Plugin\ContainerFactoryPluginInterface|\Drupal\ratlingate_custom_blocks\Plugin\Block\PricesBlock
       */
      public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
        return new static(
          $configuration,
          $plugin_id,
          $plugin_definition,
          $container->get('config.factory')
        );
      }

     

    Getting config

    $config = $this->configFactory->get('general_settings.settings');
    $campers_price = $config->get('sandg_campers_price');
    Writing Config
    $config = $this->configFactory->get('general_settings.settings');
    $campers_price = $config->set('sandg_campers_price');

    Removing Config

    $config = $this->configFactory->get('general_settings.settings');
    $campers_price = $config->clear('sandg_campers_price');

    For form classes, we should be able to grab values from the config factory, without a lot of the code that you would need for other classes, as Drupal does the heavy lifting behind the scenes.

    Drupals FormBase class also gives us a function that helps us to grab config values from config.

    $config = $this->config('system.site');
    $site_name = $config->get('name');

     

    $this->configFactory->getEditable('config_name')
          // Set the submitted configuration setting.
          ->set('example_thing', $form_state->getValue('example_thing'))
          // You can set multiple configurations at once by making
          // multiple calls to set().
          ->set('other_things', $form_state->getValue('other_things'))
          ->save();

    For the above to work, then you should add the following function that specifies the config name(s) you want to edit, you can get away with specifying very few config names here and just specify the config names where you need to in the code. In theory the function could just return an empty array and your logic should be unaffected.

    /**
       * {@inheritdoc}
       */
      protected function getEditableConfigNames() {
        return [
          'general_settings.settings', // The name of the config you want to edit
        ];
      }

    Title
    State

    Body

    The State API is a simple system for the storage of data about the system's state. The data is stored in the database and will be lost when the database is dropped or the site is re-installed from configuration. For storing data which needs to be edited by humans or needs to be shared between environments use the Configuration API. In Drupal 7 and earlier the variables system was used to store state information.

    You should use the State API, not Configuration API, for storing local variables that shouldn't travel between instances.

    The State API provides a place for developers to store information about the system's state.

    State information differs from configuration in the following ways:

    • It is not meant to be exported.
    • It is specific to an individual environment.
    • It is not to be deployed to another environment.
    • All state information is lost when you reset the database.

    Title
    Implementation

    Body

    Below are some helpful lines of code for interacting with the State API.

    Without dependency injection

    Getting state

    A single value
    \Drupal::state()->get('key');
    Multiple key/value pairs
    \Drupal::state()->getMultiple($keys);

    Writing State

    State is written to using the set() method. To write a state value, just provide the key and the value.

    A single value
    \Drupal::state()->set('key','value');
    Multiple values
    \Drupal::state()->setMultiple($key_values);

    Removing State

    A state is deleted using the delete() method. To delete a state value, just provide the key.

    A single value
    \Drupal::state()->delete('key');
    Multiple values
    \Drupal::state()->deleteMultiple($key_values);

    With dependency injection

    /**
       * The state store.
       *
       * @var State
       */
      protected $state;
    
      /**
       * ImportantMessageForm constructor.
       * @param StateInterface $state
       */
      public function __construct(StateInterface $state) {
        $this->state = $state;
      }
    
      /**
       * {@inheritdoc}
       */
      public static function create(ContainerInterface $container) {
        // Instantiates this form class.
        return new static(
        // Load the service required to construct this class.
          $container->get('state')
        );
      }
    
    
    

    Getting state

    A single value
    $this->state->get('important_message_title');
    Multiple values
    $this->state->getMultiple($key_values);

    Writing State

    A single value
    $this->state->set('important_message_title',$title);
    Multiple values
    $this->state->setMultiple($key_values);

    Removing State

    A single value
    $this->state->delete('important_message_title');
    Multiple values
    $this->state->deleteMultiple($key_values);
    Categories:

    Related blog posts

    Kint

    Authored by Alan Saunders on Sunday, May 16, 2021 - 19:00
    Reading Time: 2 minutes

    Building a custom page in Drupal 8/9

    Authored by Alan Saunders on Sunday, December 27, 2020 - 10:00
    Reading Time: 12 minutes

    Creating a custom theme in Drupal 8/9

    Authored by Alan Saunders on Tuesday, April 13, 2021 - 23:00
    Reading Time: 7 minutes