Drupal 8 Configuration system for coders and site builders
Drupal 8 Configuration system for coders and site builders
Drupal 8 Configuration system for coders and site builders
You also want an ePaper? Increase the reach of your titles
YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.
What problems are wetrying to solve?DevLiveadmin/config/foonode/4admin/config/foonode/4Setting 1Setting 2textlabelSaveTESTtest test test testtest test test testtest test test testtest testSetting 1Setting 2old textlabelSaveWelcomeThis is realcontent on thelive <strong>site</strong> that endusers are viewingDatabaseDatabaseDanger!Want to bring over configurationchanges from dev, but notoverwrite live content!Saturday 16 November 13
What problems are wetrying to solve?variable_set()/variable_get()napkinsdb_select()/db_update()/db_delete()hook_update_N()drush fu$conf[...];ctools_export_object()/ctools_export_load_object()Saturday 16 November 13http://www.flickr.com/photos/bean/322616749
The solution• Files using the YAML specification• Active <strong>and</strong> staging directory• Cached in the database using a st<strong>and</strong>ardcache interface• Config directory changed via settings.php• This is all pluggableSaturday 16 November 13
The anatomy of aconfiguration fileSaturday 16 November 13
Saturday 16 November 13<strong>system</strong>.<strong>site</strong>.yml
Saturday 16 November 13<strong>system</strong>.<strong>site</strong>.yml
Saturday 16 November 13<strong>system</strong>.<strong>site</strong>.yml
<strong>system</strong>.<strong>site</strong>.ymlname: '<strong>Configuration</strong> management'mail: admin@example.comslogan: 'makes <strong>Drupal</strong> 8 cex -y'page:403: ''404: ''front: nodeSaturday 16 November 13
The API<strong>Drupal</strong>::config()->get()->set()->save()Saturday 16 November 13
Saturday 16 November 13Accessing data
$<strong>site</strong>_name = <strong>Drupal</strong>::config('<strong>system</strong>.<strong>site</strong>')->get('name');name: '<strong>Configuration</strong> management'mail: admin@example.comslogan: 'makes <strong>Drupal</strong> 8 cex -y'page:403: ''404: ''front: nodeSaturday 16 November 13
$page_data = <strong>Drupal</strong>::config('<strong>system</strong>.<strong>site</strong>')->get('page');name: '<strong>Configuration</strong> management'mail: admin@example.comslogan: 'makes <strong>Drupal</strong> 8 cex -y'page:403: ''404: ''front: nodeSaturday 16 November 13
$frontpage = <strong>Drupal</strong>::config('<strong>system</strong>.<strong>site</strong>')->get('page.front');name: '<strong>Configuration</strong> management'mail: admin@example.comslogan: 'makes <strong>Drupal</strong> 8 cex -y'page:403: ''404: ''front: nodeSaturday 16 November 13
$all_the_data = <strong>Drupal</strong>::config('<strong>system</strong>.<strong>site</strong>')->get();name: '<strong>Configuration</strong> management'mail: admin@example.comslogan: 'makes <strong>Drupal</strong> 8 cex -y'page:403: ''404: ''front: nodeSaturday 16 November 13
Saturday 16 November 13Saving data
<strong>Drupal</strong>::config('<strong>system</strong>.<strong>site</strong>')->set('name', 'CMI is good')->save();name: 'CMI is good'mail: admin@example.comslogan: 'makes <strong>Drupal</strong> 8 cex -y'page:403: ''404: ''front: nodeSaturday 16 November 13
<strong>Drupal</strong>::config('<strong>system</strong>.<strong>site</strong>')->set('name', 'CMI is great')->set('page', array(403 => 'access-denied',404 => 'not-found',front => 'user',))->save();name: 'CMI is great'mail: admin@example.comslogan: 'makes <strong>Drupal</strong> 8 cex -y'page:403: access-denied404: not-foundfront: userSaturday 16 November 13
Saturday 16 November 13
simple settings• <strong>system</strong>_settings_<strong>for</strong>m is dead• create your own submit callback• you are responsible <strong>for</strong> saving configuration• ship with default configuration fileSaturday 16 November 13
{language}{field_config}{date_<strong>for</strong>mat_type}{filter_<strong>for</strong>mat}{field_config_instance}{date_<strong>for</strong>mat_locale}{node_type}{role_permission}{role}{date_<strong>for</strong>mats}{<strong>system</strong>}{variable}{filter}Saturday 16 November 13
Saturday 16 November 13Config all the things!
Saturday 16 November 13Config entities
**Saturday 16 * November The 13categoryID.use <strong>Drupal</strong>\Core\Config\Entity\ConfigEntityBase;use <strong>Drupal</strong>\Core\Annotation\Plugin;use <strong>Drupal</strong>\Core\Annotation\Translation;/*** Defines the contact category entity.** @EntityType(* id = "contact_category",* label = @Translation("Category"),* module = "contact",* controllers = {* controller_class = "<strong>Drupal</strong>\contact\CategoryStorageController",* list = "<strong>Drupal</strong>\contact\CategoryListController",* <strong>for</strong>m = {* "add" = "<strong>Drupal</strong>\contact\CategoryFormController"* "edit" = "<strong>Drupal</strong>\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 {
**Saturday 16 * November The 13categoryID.use <strong>Drupal</strong>\Core\Config\Entity\ConfigEntityBase;use <strong>Drupal</strong>\Core\Annotation\Plugin;use <strong>Drupal</strong>\Core\Annotation\Translation;/*** Defines the contact category entity.** @EntityType(* id = "contact_category",* label = @Translation("Category"),* module = "contact",* controllers = {* controller_class = "<strong>Drupal</strong>\contact\CategoryStorageController",* list = "<strong>Drupal</strong>\contact\CategoryListController",* <strong>for</strong>m = {* "add" = "<strong>Drupal</strong>\contact\CategoryFormController"* "edit" = "<strong>Drupal</strong>\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 {
namespace <strong>Drupal</strong>\contact\Plugin\Core\Entity;use <strong>Drupal</strong>\Core\Config\Entity\ConfigEntityBase;use <strong>Drupal</strong>\Core\Annotation\Plugin;use <strong>Drupal</strong>\Core\Annotation\Translation;/*** Defines the contact category entity.** @Plugin(* id = "contact_category",* label = @Translation("Category"),* module = "contact",* controllers = {* controller_class = "<strong>Drupal</strong>\Core\Config\Entity\ConfigStorageController",* list = "<strong>Drupal</strong>\contact\CategoryListController",* <strong>for</strong>m = {* "add" = "<strong>Drupal</strong>\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.Saturday 16 November 13
**Saturday 16 * November The 13categoryID.use <strong>Drupal</strong>\Core\Config\Entity\ConfigEntityBase;use <strong>Drupal</strong>\Core\Annotation\Plugin;use <strong>Drupal</strong>\Core\Annotation\Translation;/*** Defines the contact category entity.** @EntityType(* id = "contact_category",* label = @Translation("Category"),* module = "contact",* controllers = {* controller_class = "<strong>Drupal</strong>\Core\Config\Entity\ConfigStorageController",* list = "<strong>Drupal</strong>\contact\CategoryListController",* <strong>for</strong>m = {* "add" = "<strong>Drupal</strong>\contact\CategoryFormController"* "edit" = "<strong>Drupal</strong>\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 {
*/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 <strong>for</strong> sorting).*/public $weight = 0;Saturday 16 November 13
contact.category.feedback.ymlid: feedbackuuid: de77e4f3-f94b-41a5-ad05-5c32fa08444flabel: 'Web<strong>site</strong> feedback'recipients:- ''reply: ''weight: '0'langcode: undSaturday 16 November 13
(config) entity API• entity_load• entity_save• $object->any_method()Saturday 16 November 13
Saturday 16 November 13Deployment
Development environment1DatabaseActiveDirectorySaturday 16 November 13
Development environment1DatabaseActiveDirectorySaturday 16 November 13
Development environment1Database2ActiveDirectorySaturday 16 November 13
Production environmentadmin/config/development/syncDatabase3StagingDirectoryActiveDirectorySaturday 16 November 13
Production environment4admin/config/development/syncDatabase3StagingDirectoryActiveDirectorySaturday 16 November 13
Demo time• Full import / export• Single import / export• No partial imports supported on CLI!Saturday 16 November 13
Saturday 16 November 13Drush integration
Advanced workflows• https://drupal.org/s<strong>and</strong>box/dereine/2057465• use git pre-commit hooks with definedconfig directories in settings.phpSaturday 16 November 13
Saturday 16 November 13Don’t hack core
Saturday 16 November 13Don’t hack active config
State APIOnly useful <strong>for</strong> this environment? Use state().<strong>Drupal</strong>::state()->set('update.last_check', $now);//...$last_check = <strong>Drupal</strong>::state()->get('update.last_check') ?: 0;Saturday 16 November 13
Saturday 16 November 13<strong>Configuration</strong> schema
<strong>system</strong>.maintenance.ymlenabled: '0'message: '@<strong>site</strong> is currently under maintenance. Weshould be back shortly. Thank you <strong>for</strong> your patience.'Saturday 16 November 13
<strong>system</strong>.schema.yml<strong>system</strong>.maintenance:type: mappinglabel: 'Maintenance mode'mapping:"enabled":type: booleanlabel: "Put <strong>site</strong> into maintenance mode""message":type: textlabel: "Message to display when in maintenance mode"Saturday 16 November 13
Basic scalar types from typed databoolean:label: 'Boolean'class: '\<strong>Drupal</strong>\Core\TypedData\Type\Boolean'email:label: 'Email'class: '\<strong>Drupal</strong>\Core\TypedData\Type\Email'integer:label: 'Integer'class: '\<strong>Drupal</strong>\Core\TypedData\Type\Integer'string:label: 'String'class: '\<strong>Drupal</strong>\Core\TypedData\Type\String'uri:label: 'Uri'class: '\<strong>Drupal</strong>\Core\TypedData\Type\Uri'Saturday 16 November 13
Basic data types <strong>for</strong> configurationundefined:label: 'Undefined'class: '\<strong>Drupal</strong>\Core\Config\Schema\Property'mapping:label: Mappingclass: '\<strong>Drupal</strong>\Core\Config\Schema\Mapping'sequence:label: Sequenceclass: '\<strong>Drupal</strong>\Core\Config\Schema\Sequence'Saturday 16 November 13
Simple extended data types# Human readable string that must be plain text <strong>and</strong> editablewith a text field.label:type: stringlabel: 'Label'translatable: true# Internal <strong>Drupal</strong> pathpath:type: stringlabel: 'Path'# Human readable string that can contain multiple lines of textor HTML.text:type: stringlabel: 'Text'translatable: trueSaturday 16 November 13
Complex extended data type# Mail text with subject <strong>and</strong> body parts.mail:type: mappinglabel: "Mail"mapping:"subject":type: textlabel: "Subject""body":type: textlabel: "Body"Saturday 16 November 13
Saturday 16 November 13Config inspector module
Context <strong>system</strong>,Events & OverridesSaturday 16 November 13
Global overridesglobal $conf;$conf['<strong>system</strong>.maintenance']['message'] ='Sorry, our <strong>site</strong> is down now.';Saturday 16 November 13
Global overridesclass ConfigGlobalOverrideSubscriber implementsEventSubscriberInterface {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()]);}Saturday 16 November 13
Break out of contexts// Enter the override-free context, so we can ensure nooverrides are applied.config_context_enter('config.context.free');// Get <strong>system</strong> <strong>site</strong> maintenance message text from the originalconfig.$message = config('<strong>system</strong>.maintenance')->get('message');// Leave the override-free context.config_context_leave();Saturday 16 November 13
Get into contexts// Enter a user specific context.$context = config_context_enter("<strong>Drupal</strong>\\user\\UserConfigContext");// Set the account to use on the context.$context->setAccount($account);$mail_config = <strong>Drupal</strong>::config('user.mail');// Do stuff...config_context_leave();Saturday 16 November 13
Language overridesblock.block.bartik.login.ymlid: bartik.loginuuid: 7012ebfd-7083-47ef-b...weight: '0'status: '1'langcode: enregion: sidebar_firstplugin: user_login_blocksettings:label: 'User login'module: userlabel_display: visiblecache: '-1'......locale.hu.block.block.bartik.login.ymlsettings:label: 'Belépés'locale.nl.block.block.bartik.login.ymlsettings:label: 'Inloggen'Saturday 16 November 13
ecap <strong>and</strong> 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.incSaturday 16 November 13
Please try it out!• #drupal-cmi - Dedicated IRC channel• docs - https://drupal.org/node/1667894• help along - http://drupal.org/core-mentoring-hoursSaturday 16 November 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-hoursSaturday 16 November 13
Saturday 16 November 13Questions ?
Thanks@heyrocker@webchick@moshe_weitzman@GaborHojtsy@alexpottSaturday 16 November 13