drupal 8 configuration system for coders and site builders - drupalaton 2013
DESCRIPTION
Session given at Drupalaton 2013. Overview of the configuration management system in Drupal 8. Covers the api, config entities, context system.TRANSCRIPT
Drupal 8 Configuration system for coders and site builders
Friday 16 August 13
who are we?
Kristof De Jaeger@swentel
co-maintainer of Field APILead maintainer of Display Suite
Developer @ Wunderkraut
Friday 16 August 13
who are we?
Gábor Hojtsy@gaborhojtsy
Multilingual Initiative ownerLong time Drupal Core contributor
Number one employee @ Acquia
Friday 16 August 13
Stop serving me Palinka
Friday 16 August 13
Ok, maybe one ...
Friday 16 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 16 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 16 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 16 August 13
Friday 16 August 13
The solution
• Files using the YAML specification
• Active and staging directory
• Cached in the database using a standard cache interface
Friday 16 August 13
The anatomy of a configuration file
Friday 16 August 13
system.site.yml
Friday 16 August 13
system.site.yml
Friday 16 August 13
system.site.yml
Friday 16 August 13
name: 'Configuration management'mail: [email protected]: 'makes Drupal 8 cex -y'page: 403: '' 404: '' front: node
system.site.yml
Friday 16 August 13
The API
Drupal::config()
->get()
->set()
->save()
Friday 16 August 13
Accessing data
Friday 16 August 13
name: 'Configuration management'mail: [email protected]: 'makes Drupal 8 cex -y'page: 403: '' 404: '' front: node
$site_name = Drupal::config('system.site')->get('name');
Friday 16 August 13
$page_data = Drupal::config('system.site')->get('page');
name: 'Configuration management'mail: [email protected]: 'makes Drupal 8 cex -y'page: 403: '' 404: '' front: node
Friday 16 August 13
$frontpage = Drupal::config('system.site')->get('page.front');
name: 'Configuration management'mail: [email protected]: 'makes Drupal 8 cex -y'page: 403: '' 404: '' front: node
Friday 16 August 13
$all_the_data = Drupal::config('system.site')->get();
name: 'Configuration management'mail: [email protected]: 'makes Drupal 8 cex -y'page: 403: '' 404: '' front: node
Friday 16 August 13
Saving data
Friday 16 August 13
name: 'CMI is good'mail: [email protected]: 'makes Drupal 8 cex -y'page: 403: '' 404: '' front: node
Drupal::config('system.site') ->set('name', 'CMI is good') ->save();
Friday 16 August 13
name: 'CMI is great'mail: [email protected]: '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 16 August 13
Friday 16 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 16 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 16 August 13
Config all the things!
Friday 16 August 13
Config entities
Friday 16 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 16 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 16 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 16 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 16 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 16 August 13
id: feedbackuuid: de77e4f3-f94b-41a5-ad05-5c32fa08444flabel: 'Website feedback'recipients: - ''reply: ''weight: '0'langcode: und
contact.category.feedback.yml
Friday 16 August 13
(config) entity API
• entity_load
• entity_save
• $object->any_method()
Friday 16 August 13
Deployment
Friday 16 August 13
Database
Development environment
ActiveDirectory
1
Friday 16 August 13
Database
Development environment
ActiveDirectory
1
Friday 16 August 13
Database
Development environment
ActiveDirectory
1
2
Friday 16 August 13
Database
Production environment
StagingDirectory
ActiveDirectory
3
admin/config/development/sync
Friday 16 August 13
Database
Production environment
StagingDirectory
ActiveDirectory
3
4admin/config/development/sync
Friday 16 August 13
Demo time
• No partial imports !
Friday 16 August 13
Advanced workflows
• https://drupal.org/sandbox/dereine/2057465
Friday 16 August 13
Don’t hack core
Friday 16 August 13
Don’t hack active config
Friday 16 August 13
State API
Drupal::state()->set('update.last_check', $now);//...$last_check = state()->get('update.last_check') ?: 0;
Only useful for this environment? Use state().
Friday 16 August 13
Drush integration
Friday 16 August 13
Context system,Events & Overrides
Friday 16 August 13
global $conf;
$conf['system.maintenance']['message'] = 'Sorry, our site is down now.';
Global overrides
Friday 16 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 16 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 16 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 = config('user.mail');
// Do stuff...
config_context_leave();
Friday 16 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 16 August 13
Configuration schema
Friday 16 August 13
system.maintenance.yml
enabled: '0'message: '@site is currently under maintenance. We should be back shortly. Thank you for your patience.'
Friday 16 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 16 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 16 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 16 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'
# Internal Drupal pathpath: type: string label: 'Path'
# Human readable string that can contain multiple lines of text or HTML.text: type: string label: 'Text'
Friday 16 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 16 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 16 August 13
What does all this add up to?$definition = Drupal::typedData()->getDefinition('system.maintenance');
array( 'label' => "Maintenance mode", 'class' => "\\Drupal\\Core\\Config\\Schema\\Mapping", 'mapping' => array( 'enabled' => array( 'type' => "boolean", 'label' => "Put site into maintenance mode", ), 'message' => array( 'type' => "text", 'label' => "Message to display when in maintenance mode", ), ),);
Friday 16 August 13
Config inspector module
Friday 16 August 13
Advice for module developers
• Config key names should have meaning
• Use config entities instead of tables
• Include config schema
Friday 16 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 16 August 13
Thanks@heyrocker
@webchick
@moshe_weitzman
@GaborHojtsy
@alexpott
Friday 16 August 13