Drupal 8 configuration system for coders and site builders - DrupalCamp Baltics 2013

Preview:

DESCRIPTION

Session given at DrupalCamp Baltics 2013. Overview of the configuration management system in Drupal 8. Covers the api, config entities, context system.

Citation preview

Drupal 8 Configuration system for coders and site builders

Friday 23 August 13

who am I?

Kristof De Jaeger@swentel

Co-maintainer of Field APILead maintainer of Display Suite

Developer @ Wunderkraut

Friday 23 August 13

Outline• What’s the problem

• How did we solve it

• Simple static settings

• Configuration entities

• Deployment - with demo

• Configuration schema

• Context, events and overrides

Friday 23 August 13

What problems are we trying to solve?

• Variable soup

Live

Save

textSetting 1Setting 2 label

Database Database

Dev

TESTtest test test test test test test test test test test test test test

node/4admin/config/foo

WelcomeThis is real content on the live site that end users are viewing

node/4

Save

old textSetting 1Setting 2 label

admin/config/foo

Friday 23 August 13

Live

Save

textSetting 1Setting 2 label

Database Database

Dev

TESTtest test test test test test test test test test test test test test

node/4admin/config/foo

WelcomeThis is real content on the live site that end users are viewing

node/4

Save

old textSetting 1Setting 2 label

admin/config/foo

Danger!Want to bring over configuration

changes from dev, but not overwrite live content!

What problems are we trying to solve?

Friday 23 August 13

What problems are we trying to solve?

variable_set()/variable_get()

ctools_export_object()/ctools_export_load_object()

db_select()/db_update()/db_delete()

$conf[...];hook_update_N()

drush fu

http://www.flickr.com/photos/bean/322616749

napkins

Friday 23 August 13

The solution

• Files using the YAML specification

• Active and staging directory

• Cached in the database using a standard cache interface

• Config directory changed via settings.php

Friday 23 August 13

The anatomy of a configuration file

Friday 23 August 13

system.site.yml

Friday 23 August 13

system.site.yml

Friday 23 August 13

system.site.yml

Friday 23 August 13

name: 'Configuration management'mail: admin@example.comslogan: 'makes Drupal 8 cex -y'page: 403: '' 404: '' front: node

system.site.yml

Friday 23 August 13

The API

Drupal::config()

->get()

->set()

->save()

Friday 23 August 13

Accessing data

Friday 23 August 13

name: 'Configuration management'mail: admin@example.comslogan: 'makes Drupal 8 cex -y'page: 403: '' 404: '' front: node

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

Friday 23 August 13

$page_data = Drupal::config('system.site')->get('page');

name: 'Configuration management'mail: admin@example.comslogan: 'makes Drupal 8 cex -y'page: 403: '' 404: '' front: node

Friday 23 August 13

$frontpage = Drupal::config('system.site')->get('page.front');

name: 'Configuration management'mail: admin@example.comslogan: 'makes Drupal 8 cex -y'page: 403: '' 404: '' front: node

Friday 23 August 13

$all_the_data = Drupal::config('system.site')->get();

name: 'Configuration management'mail: admin@example.comslogan: 'makes Drupal 8 cex -y'page: 403: '' 404: '' front: node

Friday 23 August 13

Saving data

Friday 23 August 13

name: 'CMI is good'mail: admin@example.comslogan: 'makes Drupal 8 cex -y'page: 403: '' 404: '' front: node

Drupal::config('system.site') ->set('name', 'CMI is good') ->save();

Friday 23 August 13

name: 'CMI is great'mail: admin@example.comslogan: 'makes Drupal 8 cex -y'page: 403: access-denied 404: not-found front: user

Drupal::config('system.site') ->set('name', 'CMI is great') ->set('page', array( 403 => 'access-denied', 404 => 'not-found', front => 'user', )) ->save();

Friday 23 August 13

Friday 23 August 13

• system_settings_form is dead

• add your own submit callback

• you are responsible for saving configuration

• ship with default configuration file

simple settings

Friday 23 August 13

{system}

{date_format_locale}

{date_formats}

{date_format_type}

{field_config}

{field_config_instance}

{filter}

{filter_format}

{node_type} {role}{role_permission}

{variable}

{language}

Friday 23 August 13

Config all the things!

Friday 23 August 13

Config entities

Friday 23 August 13

<?php

/** * @file * Definition of Drupal\contact\Plugin\Core\Entity\Category. */

namespace Drupal\contact\Plugin\Core\Entity;

use Drupal\Core\Config\Entity\ConfigEntityBase;use Drupal\Core\Annotation\Plugin;use Drupal\Core\Annotation\Translation;

/** * Defines the contact category entity. * * @EntityType( * id = "contact_category", * label = @Translation("Category"), * module = "contact", * controllers = { * controller_class = "Drupal\contact\CategoryStorageController", * list = "Drupal\contact\CategoryListController", * form = { * "add" = "Drupal\contact\CategoryFormController" * "edit" = "Drupal\contact\CategoryFormController" * } * } * uri_callback = "contact_category_uri", * config_prefix = "contact.category", * entity_keys = { * "id" = "id", * "label" = "label", * "uuid" = "uuid" * } * ) */class Category extends ConfigEntityBase implements CategoryInterface {

/** * The category ID. * * @var string */ public $id;

/** * The category UUID. * * @var string */ public $uuid;

/** * The category label. * * @var string */ public $label;

/** * List of recipient e-mail addresses. * * @var array */ public $recipients = array();

/** * An auto-reply message to send to the message author. * * @var string */ public $reply = '';

/** * Weight of this category (used for sorting). * * @var int */ public $weight = 0;

}

Friday 23 August 13

<?php

/** * @file * Definition of Drupal\contact\Plugin\Core\Entity\Category. */

namespace Drupal\contact\Plugin\Core\Entity;

use Drupal\Core\Config\Entity\ConfigEntityBase;use Drupal\Core\Annotation\Plugin;use Drupal\Core\Annotation\Translation;

/** * Defines the contact category entity. * * @EntityType( * id = "contact_category", * label = @Translation("Category"), * module = "contact", * controllers = { * controller_class = "Drupal\contact\CategoryStorageController", * list = "Drupal\contact\CategoryListController", * form = { * "add" = "Drupal\contact\CategoryFormController" * "edit" = "Drupal\contact\CategoryFormController" * } * } * uri_callback = "contact_category_uri", * config_prefix = "contact.category", * entity_keys = { * "id" = "id", * "label" = "label", * "uuid" = "uuid" * } * ) */class Category extends ConfigEntityBase implements CategoryInterface {

/** * The category ID. * * @var string */ public $id;

/** * The category UUID. * * @var string */ public $uuid;

/** * The category label. * * @var string */ public $label;

/** * List of recipient e-mail addresses. * * @var array */ public $recipients = array();

/** * An auto-reply message to send to the message author. * * @var string */ public $reply = '';

/** * Weight of this category (used for sorting). * * @var int */ public $weight = 0;

}

Friday 23 August 13

<?php

/** * @file * Definition of Drupal\contact\Plugin\Core\Entity\Category. */

namespace Drupal\contact\Plugin\Core\Entity;

use Drupal\Core\Config\Entity\ConfigEntityBase;use Drupal\Core\Annotation\Plugin;use Drupal\Core\Annotation\Translation;

/** * Defines the contact category entity. * * @Plugin( * id = "contact_category", * label = @Translation("Category"), * module = "contact", * controllers = { * controller_class = "Drupal\Core\Config\Entity\ConfigStorageController", * list = "Drupal\contact\CategoryListController", * form = { * "add" = "Drupal\contact\CategoryFormController" * } * }, * uri_callback = "contact_category_uri", * config_prefix = "contact.category", * entity_keys = { * "id" = "id", * "label" = "label", * "uuid" = "uuid" * } * ) */class Category extends ConfigEntityBase implements ContactInterface {

/** * The category ID. * * @var string */ public $id;

/** * The category UUID. * * @var string */ public $uuid;

/** * The category label. * * @var string */ public $label;

/** * List of recipient e-mail addresses. * * @var array */ public $recipients = array();

/** * An auto-reply message to send to the message author. * * @var string */ public $reply = '';

/** * Weight of this category (used for sorting). * * @var int */ public $weight = 0;

}

Friday 23 August 13

<?php

/** * @file * Definition of Drupal\contact\Plugin\Core\Entity\Category. */

namespace Drupal\contact\Plugin\Core\Entity;

use Drupal\Core\Config\Entity\ConfigEntityBase;use Drupal\Core\Annotation\Plugin;use Drupal\Core\Annotation\Translation;

/** * Defines the contact category entity. * * @EntityType( * id = "contact_category", * label = @Translation("Category"), * module = "contact", * controllers = { * controller_class = "Drupal\Core\Config\Entity\ConfigStorageController", * list = "Drupal\contact\CategoryListController", * form = { * "add" = "Drupal\contact\CategoryFormController" * "edit" = "Drupal\contact\CategoryFormController" * } * } * uri_callback = "contact_category_uri", * config_prefix = "contact.category", * entity_keys = { * "id" = "id", * "label" = "label", * "uuid" = "uuid" * } * ) */class Category extends ConfigEntityBase implements ContactInterface {

/** * The category ID. * * @var string */ public $id;

/** * The category UUID. * * @var string */ public $uuid;

/** * The category label. * * @var string */ public $label;

/** * List of recipient e-mail addresses. * * @var array */ public $recipients = array();

/** * An auto-reply message to send to the message author. * * @var string */ public $reply = '';

/** * Weight of this category (used for sorting). * * @var int */ public $weight = 0;

}

Friday 23 August 13

<?php

/** * @file * Definition of Drupal\contact\Plugin\Core\Entity\Category. */

namespace Drupal\contact\Plugin\Core\Entity;

use Drupal\Core\Config\Entity\ConfigEntityBase;use Drupal\Core\Annotation\Plugin;use Drupal\Core\Annotation\Translation;

/** * Defines the contact category entity. * * @Plugin( * id = "contact_category", * label = @Translation("Category"), * module = "contact", * controller_class = "Drupal\Core\Config\Entity\ConfigStorageController", * list_controller_class = "Drupal\contact\CategoryListController", * form_controller_class = { * "default" = "Drupal\contact\CategoryFormController" * }, * uri_callback = "contact_category_uri", * config_prefix = "contact.category", * entity_keys = { * "id" = "id", * "label" = "label", * "uuid" = "uuid" * } * ) */class Category extends ConfigEntityBase {

/** * The category ID. */ public $id;

/** * The category UUID. */ public $uuid;

/** * The category label. */ public $label;

/** * List of recipient e-mail addresses. */ public $recipients = array();

/** * An auto-reply message to send to the message author. */ public $reply = '';

/** * Weight of this category (used for sorting). */ public $weight = 0;

}

Friday 23 August 13

id: feedbackuuid: de77e4f3-f94b-41a5-ad05-5c32fa08444flabel: 'Website feedback'recipients: - ''reply: ''weight: '0'langcode: und

contact.category.feedback.yml

Friday 23 August 13

(config) entity API

• entity_load

• entity_save

• $object->any_method()

Friday 23 August 13

Deployment

Friday 23 August 13

Database

Development environment

ActiveDirectory

1

Friday 23 August 13

Database

Development environment

ActiveDirectory

1

Friday 23 August 13

Database

Development environment

ActiveDirectory

1

2

Friday 23 August 13

Database

Production environment

StagingDirectory

ActiveDirectory

3

admin/config/development/sync

Friday 23 August 13

Database

Production environment

StagingDirectory

ActiveDirectory

3

4admin/config/development/sync

Friday 23 August 13

Demo time

• No partial imports !

Friday 23 August 13

Drush integration

Friday 23 August 13

Advanced workflows

• https://drupal.org/sandbox/dereine/2057465

Friday 23 August 13

Don’t hack core

Friday 23 August 13

Don’t hack active config

Friday 23 August 13

State API

Drupal::state()->set('update.last_check', $now);//...$last_check = Drupal::state()->get('update.last_check') ?: 0;

Only useful for this environment? Use state().

Friday 23 August 13

Configuration schema

Friday 23 August 13

system.maintenance.yml

enabled: '0'message: '@site is currently under maintenance. We should be back shortly. Thank you for your patience.'

Friday 23 August 13

system.schema.yml

system.maintenance: type: mapping label: 'Maintenance mode' mapping: "enabled": type: boolean label: "Put site into maintenance mode" "message": type: text label: "Message to display when in maintenance mode"

Friday 23 August 13

Basic scalar types from typed data

boolean: label: 'Boolean' class: '\Drupal\Core\TypedData\Type\Boolean'email: label: 'Email' class: '\Drupal\Core\TypedData\Type\Email'integer: label: 'Integer' class: '\Drupal\Core\TypedData\Type\Integer'string: label: 'String' class: '\Drupal\Core\TypedData\Type\String'uri: label: 'Uri' class: '\Drupal\Core\TypedData\Type\Uri'

Friday 23 August 13

Basic data types for configuration

undefined: label: 'Undefined' class: '\Drupal\Core\Config\Schema\Property'mapping: label: Mapping class: '\Drupal\Core\Config\Schema\Mapping'sequence: label: Sequence class: '\Drupal\Core\Config\Schema\Sequence'

Friday 23 August 13

Simple extended data types# Human readable string that must be plain text and editable with a text field.label: type: string label: 'Label' translatable: true

# Internal Drupal pathpath: type: string label: 'Path'

# Human readable string that can contain multiple lines of text or HTML.text: type: string label: 'Text' translatable: true

Friday 23 August 13

Complex extended data type

# Mail text with subject and body parts.mail: type: mapping label: "Mail" mapping: "subject": type: text label: "Subject" "body": type: text label: "Body"

Friday 23 August 13

Config inspector module

Friday 23 August 13

Context system,Events & Overrides

Friday 23 August 13

global $conf;

$conf['system.maintenance']['message'] = 'Sorry, our site is down now.';

Global overrides

Friday 23 August 13

class ConfigGlobalOverrideSubscriber implements EventSubscriberInterface {

static function getSubscribedEvents() { $events['config.init'][] = array('configInit', 30); return $events; }

public function configInit(ConfigEvent $event) { global $conf;

$config = $event->getConfig(); if (isset($conf[$config->getName()])) { $config->setOverride($conf[$config->getName()]); } }}

Global overrides

Friday 23 August 13

Break out of contexts

// Enter the override-free context, so we can ensure no overrides are applied.config_context_enter('config.context.free');

// Get system site maintenance message text from the original config.$message = config('system.maintenance')->get('message');

// Leave the override-free context.config_context_leave();

Friday 23 August 13

Get into contexts

// Enter a user specific context.$context = config_context_enter("Drupal\\user\\UserConfigContext");// Set the account to use on the context.$context->setAccount($account);

$mail_config = Drupal::config('user.mail');

// Do stuff...

config_context_leave();

Friday 23 August 13

Language overridesblock.block.bartik.login.yml

id: bartik.loginuuid: 7012ebfd-7083-47ef-b...weight: '0'status: '1'langcode: enregion: sidebar_firstplugin: user_login_blocksettings: label: 'User login' module: user label_display: visible cache: '-1'......

locale.hu.block.block.bartik.login.yml

settings: label: 'Belépés'

locale.nl.block.block.bartik.login.yml

settings: label: 'Inloggen'

Friday 23 August 13

recap and advice

• key names/properties should have meaning

• Use config entities instead of tables

• Use getters/setters/methods on entities

• Include config schema (translation!)

• Upgrade functions available in update.inc

Friday 23 August 13

Please try it out!

• #drupal-cmi - Dedicated IRC channel

• docs - https://drupal.org/node/1667894

• help along - http://drupal.org/core-mentoring-hours

Friday 23 August 13

• http://groups.drupal.org/cmi - Discussion

• http://v.gd/cmi_issues - Issues

• http://groups.drupal.org/core - Core announcements

• #drupal-cmi - Dedicated IRC channel

• http://drupal.org/core-mentoring-hours

Friday 23 August 13

Questions ?

Friday 23 August 13

Thanks@heyrocker

@webchick

@moshe_weitzman

@GaborHojtsy

@alexpott

Friday 23 August 13