Rethinking Drupal Configuration – MidCamp 2015



Rethinking Drupal Configuration – MidCamp 2015

0 0


presentation-rethinking-drupal-config

A reveal.js presentaiton.

On Github sreynen / presentation-rethinking-drupal-config

Rethinking Drupal Configuration

MidCamp 2015

  • You are here.

Scott Reynen

@scottr

aten.io

  • I'm Scott ...

What I Did

groups.drupal.org @font-your-face
  • This is how you know me.

What I Do Now

Config in Code (Parenting)
  • This is what I've been doing more recently.

Opinions are Cool!

  • And I have a lot of opinions on config. You should too.
  • They don't need to be the same as my opinions on config.
  • I brought you a config opinion starter pack.

1. Config is ... Everything

Saved Decisions

  • Everything starts as custom.
  • Start auto-generating in different ways.
  • Config is how we save those decisions.

2. Config is not Interface

Fields are not Fields UI

Views are not Views UI

  • Config exists independent of how you make it.

3. Interface is Important

Laziness is a Virtue

  • More intuitive interface helps you do more faster.
  • Easy to ignore slowness of what you already learned.

The Dark Ages

(Before Features)

  • A long time ago (2009) ...
Create
Read
Update
Delete
Browser UI
  • We did everything in the browser.
  • It was easy until....
Create
Read
Update
Delete
Manage
Browser UI
  • On launch, you can copy database.
  • Post-launch you can't, and end up repeating everything between dev and prod.

1. Config is ... Everything

  • Not a big deal with Drupal was doing less.
  • Didn't scale as Drupal grew more complicated.

Features is

Good

  • Features started a new era of Drupal config thinking.
  • Config management was important.
  • This is what Features looks like.
  • Exports to a module as PHP code.
Manage
Browser UI Features
  • Config Management was standard now. Yay!
  • Everything is awesome? Not quite.
Create
Read
Update
Delete
Manage
Browser UI Features
  • Features can't delete configuration.
  • Updating configuration is complicated.
  • Features are PHP code, so only work as well as that code works.

Features is

Not

Good

Enough

  • Features is good.
  • But not good enough.

2. Config is not Interface

  • And this picture is even less happy as we remember the first point: Config is not Interface.
  • This about donut ordering, which you do in dozens.
  • Need a "donut type" field with 12 values.
  • How do you do this?
  • You can't. Not in the browser UI.
  • This is reasonable for the standard need, but not for your special case.
  • Config can say 12 values; only UI cannot.
Create
Read
Update
Delete
Manage
Browser UI Features
  • So the Browser UI is less than it could be, and Features inherits these problems.

Drupal 8 Config is

Better

  • Drupal 8 makes everything better, including config.
  • Drupal 8 has a standard configuration structure.
  • Somewhat like Features, you can export it.
  • But it exports as YML files, not PHP.
  • Those files are organized like this.
langcode: en
status: true
dependencies: {  }
name: 'Basic page'
type: page
description: 'Use <em>basic pages</em> for your static content, such as an ''About us'' page.'
help: ''
title_label: Title
settings:
  node:
    options:
      status: true
      promote: false
      sticky: false
      revision: false
    preview: 1
    submitted: false
  • This is what YAML looks like.
  • Very simple.
  • You can also export individual configurations.
  • Also have Features in D8, for bundles.
Manage
Browser UI Features Drupal 8
  • Drupal 8 makes config management better.
Create
Read
Update
Delete
Manage
Browser UI Features Drupal 8
  • Standard config format makes D8 updates better.
  • Still no delete.

Drupal 8 Config is

Not

Better

Enough

  • Delete is one reason D8 config is not better enough.
  • But we're also missing opportunities still.

3. Interface is Important

  • Config interfaces are improved in Drupal 8, but ...
  • We haven't adjusted our thinking about how config UI should work.

Core Interfaces are for Everyone

You're not Everyone

  • Now that config is cleanly separated from UI, there's little reason we should all be using the same UI.
  • This entire interface is focused on finding the module you want.
  • You know that already, just need to enable it.
  • Many of us don't use this interface at all, use Drush instead.
  • That's a custom interface for a specific approach to the same config.

Custom Interfaces are Hard, Right?

No, Not Anymore

  • And the difficulty of making custom UI is no longer a reason either, because it's pretty easy now.
\Drupal::service('config.factory')->getEditable('node.type.page')
  ->set('type', 'page')
  ->set('name', 'Basic page')
  ->set('description', 'Use <em>basic pages</em> for your static content, such as an ''About us'' page.')
  ->save();

Easy!

  • This is how to make content type in D8.
  • This is faster than using the default UI for coders.
  • And for everyone else, knowing coders can do it faster means you can encourage coders to make your ideal UI.

Config in Code

(CINC)

drupal.org/project/cinc

  • The Config in Code (CINC) module let's you use close to the same code interface in D7.
CINC::init(‘ContentType’)->machine_name('page')
  ->set('name', 'Basic page')
  ->set('description', 'Use <em>basic pages</em> for your static content, such as an ''About us'' page.')
  ->create();

Easier!

  • Very similar to D8 API, but a little more human-readable.
$text_field = CINC::init('TextField')->machine_name('field_text')
  ->create();
CINC::init(‘ContentType’)->machine_name('page')
  ->add_field($text_field);

Easiest!

  • And CINC goes beyond just replicating the D8 system.
  • It makes each config object smart, so they can do more than edit raw config.
  • For example, each field type sets different defaults.
  • And adding a field to a content type is simply passing the field object into the content type object.
            uuid: b071f334-ec2e-40e3-a809-9d436780386e
langcode: en
status: true
dependencies:
  module:
    - node
    - user
id: content
label: Content
module: node
description: 'Find and manage content.'
tag: default
base_table: node
base_field: nid
core: 8.x
display:
  default:
    display_options:
      access:
        type: perm
        options:
          perm: 'access content overview'
      cache:
        type: none
      query:
        type: views_query
      exposed_form:
        type: basic
        options:
          submit_button: Filter
          reset_button: true
          reset_button_label: Reset
          exposed_sorts_label: 'Sort by'
          expose_sort_order: true
          sort_asc_label: Asc
          sort_desc_label: Desc
      pager:
        type: full
        options:
          items_per_page: 50
      style:
        type: table
        options:
          grouping: {  }
          row_class: ''
          default_row_class: true
          override: true
          sticky: true
          caption: ''
          summary: ''
          description: ''
          columns:
            node_bulk_form: node_bulk_form
            title: title
            type: type
            name: name
            status: status
            changed: changed
            edit_node: edit_node
            delete_node: delete_node
            dropbutton: dropbutton
            timestamp: title
          info:
            node_bulk_form:
              align: ''
              separator: ''
              empty_column: false
              responsive: ''
            title:
              sortable: true
              default_sort_order: asc
              align: ''
              separator: ''
              empty_column: false
              responsive: ''
            type:
              sortable: true
              default_sort_order: asc
              align: ''
              separator: ''
              empty_column: false
              responsive: ''
            name:
              sortable: false
              default_sort_order: asc
              align: ''
              separator: ''
              empty_column: false
              responsive: priority-low
            status:
              sortable: true
              default_sort_order: asc
              align: ''
              separator: ''
              empty_column: false
              responsive: ''
            changed:
              sortable: true
              default_sort_order: desc
              align: ''
              separator: ''
              empty_column: false
              responsive: priority-low
            edit_node:
              sortable: false
              default_sort_order: asc
              align: ''
              separator: ''
              empty_column: false
              responsive: ''
            delete_node:
              sortable: false
              default_sort_order: asc
              align: ''
              separator: ''
              empty_column: false
              responsive: ''
            dropbutton:
              sortable: false
              default_sort_order: asc
              align: ''
              separator: ''
              empty_column: false
              responsive: ''
            timestamp:
              sortable: false
              default_sort_order: asc
              align: ''
              separator: ''
              empty_column: false
              responsive: ''
          default: changed
          empty_table: true
      row:
        type: fields
      fields:
        node_bulk_form:
          id: node_bulk_form
          table: node
          field: node_bulk_form
          label: ''
          exclude: false
          alter:
            alter_text: false
          element_class: ''
          element_default_classes: true
          empty: ''
          hide_empty: false
          empty_zero: false
          hide_alter_empty: true
          plugin_id: node_bulk_form
          entity_type: node
        title:
          id: title
          table: node_field_data
          field: title
          label: Title
          exclude: false
          alter:
            alter_text: false
          element_class: ''
          element_default_classes: true
          empty: ''
          hide_empty: false
          empty_zero: false
          hide_alter_empty: true
          link_to_node: true
          plugin_id: node
          entity_type: node
          entity_field: title
        type:
          id: type
          table: node_field_data
          field: type
          label: 'Content Type'
          exclude: false
          alter:
            alter_text: false
          element_class: ''
          element_default_classes: true
          empty: ''
          hide_empty: false
          empty_zero: false
          hide_alter_empty: true
          link_to_node: false
          machine_name: ''
          plugin_id: node_type
          entity_type: node
          entity_field: type
        name:
          id: name
          table: users_field_data
          field: name
          relationship: uid
          label: Author
          exclude: false
          alter:
            alter_text: false
          element_class: ''
          element_default_classes: true
          empty: ''
          hide_empty: false
          empty_zero: false
          hide_alter_empty: true
          link_to_user: true
          overwrite_anonymous: false
          anonymous_text: ''
          format_username: true
          plugin_id: user_name
          entity_type: user
          entity_field: name
        status:
          id: status
          table: node_field_data
          field: status
          label: Status
          exclude: false
          alter:
            alter_text: false
          element_class: ''
          element_default_classes: true
          empty: ''
          hide_empty: false
          empty_zero: false
          hide_alter_empty: true
          type: published-notpublished
          type_custom_true: ''
          type_custom_false: ''
          not: false
          plugin_id: boolean
          entity_type: node
          entity_field: status
        changed:
          id: changed
          table: node_field_data
          field: changed
          label: Updated
          exclude: false
          alter:
            alter_text: false
          element_class: ''
          element_default_classes: true
          empty: ''
          hide_empty: false
          empty_zero: false
          hide_alter_empty: true
          date_format: short
          custom_date_format: ''
          timezone: ''
          plugin_id: date
          entity_type: node
          entity_field: changed
        operations:
          id: operations
          table: node
          field: operations
          relationship: none
          group_type: group
          admin_label: ''
          label: Operations
          exclude: false
          alter:
            alter_text: false
            text: ''
            make_link: false
            path: ''
            absolute: false
            external: false
            replace_spaces: false
            path_case: none
            trim_whitespace: false
            alt: ''
            rel: ''
            link_class: ''
            prefix: ''
            suffix: ''
            target: ''
            nl2br: false
            max_length: 0
            word_boundary: true
            ellipsis: true
            more_link: false
            more_link_text: ''
            more_link_path: ''
            strip_tags: false
            trim: false
            preserve_tags: ''
            html: false
          element_type: ''
          element_class: ''
          element_label_type: ''
          element_label_class: ''
          element_label_colon: true
          element_wrapper_type: ''
          element_wrapper_class: ''
          element_default_classes: true
          empty: ''
          hide_empty: false
          empty_zero: false
          hide_alter_empty: true
          destination: true
          plugin_id: entity_operations
      filters:
        status_extra:
          id: status_extra
          table: node_field_data
          field: status_extra
          operator: '='
          value: false
          plugin_id: node_status
          group: 1
          entity_type: node
        status:
          id: status
          table: node_field_data
          field: status
          relationship: none
          group_type: group
          admin_label: ''
          operator: '='
          value: true
          group: 1
          exposed: true
          expose:
            operator_id: ''
            label: Status
            description: ''
            use_operator: false
            operator: status_op
            identifier: status
            required: false
            remember: false
            multiple: false
            remember_roles:
              authenticated: authenticated
          is_grouped: true
          group_info:
            label: 'Published status'
            description: ''
            identifier: status
            optional: true
            widget: select
            multiple: false
            remember: false
            default_group: All
            default_group_multiple: {  }
            group_items:
              1:
                title: Published
                operator: '='
                value: '1'
              2:
                title: Unpublished
                operator: '='
                value: '0'
          plugin_id: boolean
          entity_type: node
          entity_field: status
        type:
          id: type
          table: node_field_data
          field: type
          relationship: none
          group_type: group
          admin_label: ''
          operator: in
          value: {  }
          group: 1
          exposed: true
          expose:
            operator_id: type_op
            label: Type
            description: ''
            use_operator: false
            operator: type_op
            identifier: type
            required: false
            remember: false
            multiple: false
            remember_roles:
              authenticated: authenticated
              anonymous: '0'
              administrator: '0'
            reduce: false
          is_grouped: false
          group_info:
            label: ''
            description: ''
            identifier: ''
            optional: true
            widget: select
            multiple: false
            remember: false
            default_group: All
            default_group_multiple: {  }
            group_items: {  }
          plugin_id: bundle
          entity_type: node
          entity_field: type
        title:
          id: title
          table: node_field_data
          field: title
          relationship: none
          group_type: group
          admin_label: ''
          operator: contains
          value: ''
          group: 1
          exposed: true
          expose:
            operator_id: title_op
            label: Title
            description: ''
            use_operator: false
            operator: title_op
            identifier: title
            required: false
            remember: false
            multiple: false
            remember_roles:
              authenticated: authenticated
              anonymous: '0'
              administrator: '0'
          is_grouped: false
          group_info:
            label: ''
            description: ''
            identifier: ''
            optional: true
            widget: select
            multiple: false
            remember: false
            default_group: All
            default_group_multiple: {  }
            group_items: {  }
          plugin_id: string
          entity_type: node
          entity_field: title
        langcode:
          id: langcode
          table: node_field_data
          field: langcode
          relationship: none
          group_type: group
          admin_label: ''
          operator: in
          value: {  }
          group: 1
          exposed: true
          expose:
            operator_id: langcode_op
            label: Language
            description: ''
            use_operator: false
            operator: langcode_op
            identifier: langcode
            required: false
            remember: false
            multiple: false
            remember_roles:
              authenticated: authenticated
              anonymous: '0'
              administrator: '0'
            reduce: false
          is_grouped: false
          group_info:
            label: ''
            description: ''
            identifier: ''
            optional: true
            widget: select
            multiple: false
            remember: false
            default_group: All
            default_group_multiple: {  }
            group_items: {  }
          plugin_id: language
          entity_type: node
          entity_field: langcode
      sorts: {  }
      title: Content
      empty:
        area_text_custom:
          id: area_text_custom
          table: views
          field: area_text_custom
          empty: true
          content: 'No content available.'
          plugin_id: text_custom
      arguments: {  }
      relationships:
        uid:
          id: uid
          table: node_field_data
          field: uid
          admin_label: author
          required: true
          plugin_id: standard
      show_admin_links: false
      filter_groups:
        operator: AND
        groups:
          1: AND
      display_extenders: {  }
    display_plugin: default
    display_title: Master
    id: default
    position: 0
  page_1:
    display_options:
      path: admin/content/node
      menu:
        type: 'default tab'
        title: Content
        description: ''
        menu_name: admin
        weight: -10
        context: ''
      tab_options:
        type: normal
        title: Content
        description: 'Find and manage content'
        menu_name: admin
        weight: -10
      display_extenders: {  }
    display_plugin: page
    display_title: Page
    id: page_1
    position: 1
          

YAML Views

So Easy!

  • View has a very complex, general-purpose UI.
  • YAML is 8 pages!
CINC::init('View')->machine_name('random_instructor')
    ->set('human_name', 'Random Instructor')
    ->add_block_display()
    ->set_row_style('node')
    ->set_view_mode('teaser')
    ->add_filter('published')
    ->add_node_type_filter('instructor')
    ->limit_items(1)
    ->add_sort('random')
    ->create();

CINC Views

  • CINC does Views simpler.

Sheet2Module

cinc.io

  • CINC makes it easy to create interfaces that work exactly how you want.
  • I've made a few of those interfaces for myself.
  • Aten uses spreadsheets to describe content structure before we build it.
  • Sheet2Module uses these spreadsheets.

80% Less Time

on Content Types

  • The result is eliminating most of the work of creating content types.

Fast Content Type UI

  • Fast Content Type UI is another options.
  • It creates all your content types on one screen.
  • Can also see/change comment settings all on one screen.

1. Config is ... Everything

2. Config is not Interface

3. Interface is Important

  • All of this adds up to the best approach to Drupal config...

Roll Your Own is

Best

  • You should build the perfect interface for you.
  • We should have 100 or 1000 interfaces for managing content types, and all other config.
  • Each interface should be focused on unique approaches to the same config.
Create
Read
Update
Delete
Manage
Browser UI Features Drupal 8 Roll Your Own
  • All smiles. Yay!

Feedback!

joind.in/13821

Twitter: @scottr

#MidCamp

  • Everything is awesome for real this time.
  • Let's talk more.