Reading and writing to drupal (8) config.
Tags: drupal, php, api, twig
Published: 2022-07-21 08:30:06

Drupal 8 provides several apis for storing your application data, these include;[entity api](https://www.drupal.org/docs/drupal-apis/entity-api/introduction-to-entity-api-in-drupal-8), [configuration api](https://www.drupal.org/docs/drupal-apis/configuration-api/configuration-api-overview) and [the state api](https://www.drupal.org/docs/8/api/state-api/overview).
There are several ways to determine what api to use depending on what data you want to store, and in this blog we will look at [the config api](https://www.drupal.org/docs/drupal-apis/configuration-api/overview-of-configuration-vs-other-types-of-information).

We're going to see how we could define default values on module install, and later edit the same via a config form, and finally we will use these settings in a controller to render items on a page including pictures of cats, from _ [the cats api](https://thecatapi.com/) _. (I choose this api because it requires a key and we need that to demo the capabilities of drupal config api ).

[Code for this blog on github here](https://github.com/flaircore/rw_config);

Before we get to the code, I will outline the folders and files structures required first, then get into what goes into each file.
Let's name the module rw_config, which will also be the name of the module folder.

At the root of this folder we will have:
- config (folder to contain default settings on install and schema file)
- src (folder that will contain our module classes)
- templates (folder to contain this modules templates files)
- rw_config.info.yml
- rw_config.module
- rw_config.routing.yml

The config sub folder will contain two sub folders; 'install' and 'schema' and each of these folders will each contain one file namely 'rw_config.settings.yml' and 'rw_config.schema.yml', respectively. For the src folder, we will go into what files and folders goes in there later when need be. We will get direct into what goes into
each of the files now.

**Below is an illustration of how the rw_config module should be structured.**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/605gfvr819ind5wu9zt6.PNG)

 **##Below is the code that goes into rw_config.info.yml. and defines our module.**

```yml
# see https://www.drupal.org/docs/drupal-apis/configuration-api/configuration-api-overview

name: 'Read Write Config'
type: module
description: 'Example on how to write and read to drupal config'
core: 8.x
package: 'Dev Examples'
```

In the 'rw_config.module' file, we will just define/register the template we will be using for this project, so other classes (Controller in our case) can use the same.

## Below is the code that goes into rw_config.module

```php
<?php
 
/**
 * Implements hook_theme().
 */
function rw_config_theme($existing, $type, $theme, $path) {
 
  return [
    'rw_config_listing' => [
      'render element' => 'children',
      'template' => 'rw-config-listing', #name of the template file +.html.twig
      'variables' => [
        'data' => FALSE
      ]
    ]
  ];
}
```
The 'rw_config.routing.yml' will contain two routes, one to render a config form (edit/update the configs), and another to render a page that will output data while relying on our custom config settings. This file relies on everything we will add in the src folder, but on on the src folder later.

**##Below is the code that goes into rw_config.routing.yml**

```yml
#see src\Controller\ConController class
rw_config.r_test:
 path: '/rwconfig/tests/page'
 defaults:
   _controller: '\Drupal\rw_config\Controller\ConController::showCats'
   _title: 'Rw Config Test Page'
 requirements:
   _permission: 'access content'

#see src\Form\RwConfigSettingsForm class
rw_config.config_form:
 path: '/rw_config/config_settings'
 defaults:
   _form: '\Drupal\rw_config\Form\RwConfigSettingsForm'
   _title: 'Rw Config Test Page'
 requirements:
   _permission: 'access content' # should be a defined better permission
```

Remember our two files in the config sub folder 'congif\install\rw_config.settings.yml' and 'config\schema\rw_config.schema.yml'. As their name suggests, rw_config.schema.yml defines our config object schema, while rw_config.settings.yml defines default values on module install, and can be updated or deleted. (NB: as of Drupal 8 we don't need to define the schema structure for the configuration api, you can ignore the rw_config.schema.yml and your module should work fine.)

**## Below is the code that goes into each file.**

```yml
# ---- Start of rw_config.settings.yml file --------------

# Contains the default values on install and can be
# overridden in our config settings form, accessible
# on route `<yoursite.com>/rw_config/config_settings`

api_key: 'Test default api_key on install'
app_title: 'Test default app title on install'
show_pictures: false
 
 
# ---- End of rw_config.settings.yml file --------------
```

```yml
# ---- Start of rw_config.schema.yml file --------------
# Defines rw_config.settings schema
rw_config.settings:
  type: config_object
  label: 'Read Write Config Example'
  mapping:
    api_key:
      type: string
      label: 'API key'
    app_title:
      type: string
      label: 'The app title'
    show_pictures:
      type: boolean
      label: 'Control if picture display on test page'
 
# ---- End of rw_config.schema.yml file --------------
```

Remember our routing file and our src folder, let's create what we need and join the remaining pieces. We need two more files in the src sub folder namely; 'src\Form\RwConfigSettingsForm.php' and 'src\Controller\ConController.php' which defines our configuration form/page and our apps display page respectively.

**## Below is the code that goes into each file;**

```php
<?php
// RwConfigSettingsForm.php start ------------------------------------------------

namespace Drupal\rw_config\Form;


use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;

/**
* Class RwConfigSettingsForm
*
* @package Drupal\rw_config\Form
*
* see https://www.drupal.org/docs/drupal-apis/configuration-api/working-with-configuration-forms
*/
class RwConfigSettingsForm extends ConfigFormBase {

 protected function getEditableConfigNames() {
   return ['rw_config.settings'];
 }

 public function getFormId() {
   return 'rw_config_app_settings';
 }

 public function buildForm(array $form, FormStateInterface $form_state) {
   $config = $this->config('rw_config.settings');

   $form['api_key'] = [
     '#default_value' => $config->get('api_key'),
     '#description' => $this->t('Your api key give at https://thecatapi.com/'),
     '#maxlength' => 40,
     '#required' => TRUE,
     '#title' => $this->t('API key'),
     '#type' => 'textfield',
   ];

   $form['app_title'] = [
     '#default_value' => $config->get('app_title'),
     '#description' => $this->t('Example page title'),
     '#maxlength' => 40,
     '#required' => TRUE,
     '#title' => $this->t('App title'),
     '#type' => 'textfield',
   ];

   $form['show_pictures'] = [
     '#default_value' => $config->get('show_pictures'),
     '#description' => $this->t('Show pictures '),
     '#title' => $this->t('Control if picture display on test page : ..../rwconfig/tests/page'),
     '#type' => 'checkbox',
   ];


   return parent::buildForm($form, $form_state);
 }


 public function submitForm(array &$form, FormStateInterface $form_state) {

   # save to config and clear cache
   $config = $this->config('rw_config.settings');
   $config
     ->set('api_key', $form_state->getValue('api_key'))
     ->set('app_title', $form_state->getValue('app_title'))
     ->set('show_pictures', $form_state->getValue('show_pictures'))
     ->save();

   // clear cache
   drupal_flush_all_caches();

   parent::submitForm($form, $form_state);
 }

}


// RwConfigSettingsForm.php End ------------------------------------------------
```

```php
<?php
// ConController.php Start ------------------------------------------------
namespace Drupal\rw_config\Controller;


use Drupal\Component\Serialization\Json;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Controller\ControllerBase;
use GuzzleHttp\Client;
use Symfony\Component\DependencyInjection\ContainerInterface;

class ConController extends ControllerBase {


 /**
  * @var \Drupal\Core\Config\ConfigFactoryInterface
  */
 protected $configFactory;

 /**
  * @var \GuzzleHttp\Client
  */
 private $client;

 public function __construct(ConfigFactoryInterface $configFactory, Client $client) {
   $this->configFactory = $configFactory;
   $this->client = $client;
 }

 public static function create(ContainerInterface $container) {
   return new static(
     # see https://www.drupal.org/docs/drupal-apis/configuration-api/simple-configuration-api#s-interacting-with-configuration
     $container->get('config.factory'),
     # see  https://www.drupal.org/docs/contributed-modules/http-client-manager
     $container->get('http_client')
   );
 }

 public function showCats(){
   $config = $this->configFactory->getEditable('rw_config.settings');
   $api_url = 'https://api.thecatapi.com/v1/images/search?limit=20&page=1&order=Desc';

   $options = [
     'headers' => [
       'Content-Type' => 'application/json',
       'x-api-key' => $config->get('api_key')
     ]
   ];

   $data = $this->client->request('GET', $api_url, $options);

   $items = Json::decode($data->getBody()->getContents());

   return [
     '#theme' => 'rw_config_listing', # remember template definition in rw_config.module
     '#data' => [ # data to the template file
       'app_title' => $config->get('app_title'),
       'items' => $items,
       'show_pictures' => $config->get('show_pictures'),

     ]
   ];
 }

}

// ConController.php End ------------------------------------------------
```

Finally, before we test the module, let's create the last file named 'rw-config-listing.html.twig' in our templates folder (see rw_config_theme() in rw_config.module file ).

**## Below is the code that goes into this file;**

```twig
<h1> App Name: {{ data['app_title'] }}</h1>
<hr/>
{% for key, item in data['items'] %}
  <div style="width: 300px">
    <hr/>
    <h1> Picture id: {{ item['id'] }}</h1>
    {% if data['show_pictures'] %}
 
      Show Picture:
      <img src={{ item['url'] }} alt={{ item['url'] }}>
    {% else %}
      <h4> Picture display set to off!!!</h4>
    {% endif %}
  </div>
 
 
{% endfor %}
```

Now if we enable the module and head to url "<yoursitehomepage>/rw_config/config_settings", the config form we defined in 'src\Form\RwConfigSettingsForm.php will be rendered and the form fields will be populated with the default values we defined in the 'congif\install\rw_config.settings.yml'.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d0m81juw5n72cdqwa892.PNG)

Now head to https://thecatapi.com/ and sign up to have an api key sent to your email, and use the same on the config form above, make sure to check 'Show pictures' and give your app a title maybe (you can come back to this page and change your settings), before heading to '<yoursitehomepage>/rwconfig/tests/page' to see the config settings in action.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/swgrylm7cpvjqhrn3tox.PNG)

**CONCLUSION:**
Configuration definitions can be [exported as files between sites](https://www.drupal.org/docs/configuration-management/workflow-using-drush) , if doing this and working with sensitive keys, you might consider treating your config files with care(privacy).

**RESOURCES:**

- Configuration API overview https://www.drupal.org/docs/drupal-apis/configuration-api/configuration-api-overview
- Yaml docs https://yaml.org/spec/1.2/spec.html#id2760395
- custom twig templates for custom module https://www.drupal.org/docs/theming-drupal/twig-in-drupal/create-custom-twig-templates-for-custom-module
- Managing drupal site configuration https://www.drupal.org/docs/configuration-management/managing-your-sites-configuration