Drupal 8 for Developers

From Training Material
Jump to: navigation, search

THIS IS A DRAFT

This text may not be complete.


Title

Drupal 8 for Developers
Author
Lukasz Sokolowski
Subfooter

Drupal 8 for Developers          Lukasz Sokolowski


Contents

Drupal 8 for Developers

Drupal 8 for Developers Training Materials

Overview of Drupal ⌘

  • What Is Drupal?
  • Drupal Core
  • Drupal Add-Ons: Modules, Themes, Distributions, and Translations

The Evolution of Drupal ⌘

Changes introduced in Drupal 8:

  • improved the usability and accessibility of the administrative UI
  • the administrative UI and the core themes work well on mobile devices
  • transformation of procedural-code-based system into a mostly object-oriented system that incorporates code from many outside open-source projects
    • use and improve code from other projects where possible
      • don't invent everything itself
    • some of the core systems of Drupal were rewritten as portable PHP classes without Drupal-specific dependencies
      • usable by other open-source projects

Changes in internal systems and APIs ⌘

  • URL request handler from the Symfony
  • Swappable services (also from Symfony)
    • many aspects of Drupal core have been converted to services (allowing contributed modules to replace them)
  • OO plugin system (from Symfony and Doctrine)
    • many of the hooks have been converted to use plugins
    • reduction of procedural code for more OO code
  • Some informational hooks have been converted to use YAML-formatted files
  • Storing configuration information has new API, easy to export and import
    • to share configuration between sites,
    • or store it in a revision control system (git, etc)
  • Templating system from the Twig
  • Class loader from the Composer
  • Views in core, Poll out of the core, etc

Handling HTTP Requests ⌘

  • The main Drupal index.php file is loaded and executed by the server to handle the request
  • Drupal 8 uses Symfony to handle HTTP requests
    • The dynamic class loader and error handlers are started (db connections, etc)
    • Loading and executing of settings.php file (can be multi-site setup, additional sites.php file required)
    • Drupal kernel starts up the Dependency Injection Container
      • defines services provided by classes, configuration system requests
    • Initialization of PHP session variables and cookies
    • For anonymous users page cache is checked (doesn't apply to 'logged-in' users)
    • Various files are loaded and executed (core include files and enabled modules' .module files)
    • Symfony handles the req and returns result to browser

Symfony HTTP request system in Drupal 8 ⌘

  • Modules can register routes, which map URLs and conditions (context) to controller classes
    • Controller classes: produce generic page output, present forms and process form submissions, etc
  • The best match to the context is determined by Symfony and it chooses the controller method to execute
  • Controller returns a Symfony response object containing HTTP header and content information
  • Symfony returns the response information to browser
  • event subscribers can be also registered by modules
    • they can intercept and override lots of steps in response process
    • examples of use in core modules
      • translating URL aliases to system URL paths
      • enabling maintenance mode
      • handling language selection
      • providing dynamic URL routes

Cache in Drupal ⌘

  • System, which allows modules to precalculate data or output and store it
    • the next time it is needed it doesn’t have to be calculated again
    • saves a lot of time on page loads
    • expense is more complexity
      • when data is invalidated (changes in dependent data), any module that uses caching needs to clear it

Examples of cached information ⌘

  • Page output for anonymous users (can be disabled on Performance config page)
  • Block output (can't be turned off in Drupal 8)
  • Data collected from hooks
  • Lists with available plugins (plugin managers)
  • Form arrays
  • Theme information (list of theme regions, theme-related info from modules and themes)

Cache API in Drupal 8 ⌘

  • Services do the job
    • Different sets of cached data (or bins) can use different storage mechanisms
    • Allways call, to get correct cache class
      \Drupal::cache($bin)
      
    • Instance of cache class implements
      \Drupal\Core\Cache\CacheBackendInterface
      
    • Use '$container' as an alternative
      // The name of the service for a particular cache bin is
      // 'cache.' . $bin.
      $cache_class = $container->get('cache.' . $bin);
      
    • Available methods
      • set(), get(), invalidate(), etc

Clearing caches ⌘

  • When we use default core cache bins, it will be flushed automatically
  • When custom cache system in use
    • if cache clear is requested via 'drupal_flush_all_caches()' function
      • flash data with custom 'hook_cache_flush()'
    • custom 'hook_rebuild()' may be also needed

Related D8 api/doc references:

Modules to enable: Examples for Developers, Cache example

Exercises:

  • Clear cache with
    • performance section in admin config
    • webprofiler (additional module from devel package)
    • drush
    • in your own module via hook (we'll do it later in another exercise)

Tagging mechanism ⌘

  • To flush parts of the cache when data they’re related to is changed or removed
    • Example: module is caching data related to a specific node content item
      • we tag it with the node ID
        // $cid is a unique cache ID key for your data, which is $data.
        // $nid is the ID of the node whose data is cached.
        $cache_class->set($cid, 
                          $data, 
                          CacheBackendInterface::CACHE_PERMANENT, 
                          array('node:' . $nid));
        
      • when calls to the core Node module modify this node content item, it will call cache methods to invalidate all cached data that was tagged with this node ID
    • Custom tags
      \Drupal\Core\Cache\Cache::invalidateTags($tags);
      

Automatic Class Loading in Drupal ⌘

  • Automatic loading of files containing PHP class, interface, and trait declarations
    • Integrate with PHP's native class-auto-loading system (functions can be registered as class loaders)
    • Reduce the load on the server by only loading files containing class definitions if those classes are being used in a specific page request
    • Reduce the burden on developers by automatically loading files containing class definitions (instead of making them load the classes explicitly in code)

Drupal 8 Specific Way ⌘

  • Incorporates the class loader from the Composer
    • based on the PSR-0 and PSR-4 standards, and PHP namespaces.
  • How does it work
    • Classes are declared to be in a PHP namespace with a PHP namespace declaration
    • Drupal uses namespaces beginning with the name \Drupal ,
      • Namespace(s) for a specific module mymodule should begin with \Drupal\mymodule
    • Files that depend on classes outside their own namespace have PHP use declarations for the outside classes
    • Each class declaration is in its own file
    • Drupal tells Symfony(to it's class loading system) about its namespace and directory conventions
      • for instance: look only in enabled modules' src dirs (omit those disabled)
    • If needed, class is defined via Symfony class loader, which automatically locates and loads the related include file
    • The directory that each class file goes in depends on its namespace (examples in the next slide)

Dirs and namespaces, Examples ⌘

Relations between dirs and namespaces

  • Add-on module’s class
'\Drupal\mymodule\subnamespace\Foo' 
    will go in file in dir
'mymodule/src/subnamespace/Foo.php'
  • 'core/lib' dir for core (but not part of specific D8 core module)
class '\Drupal\Component\Datetime\DateTimePlus'
    lives in file 
'core/lib/Drupal/Component/Datetime/DateTimePlus.php'
  • 'vendor/*' - classes adopted from outside projects
class '\Symfony\Component\DependencyInjection\Container'
    lives in file
'vendor/symfony/dependency-injection/Container.php'

Drupal 8 OOP Examples ⌘

Modules to enable: oop_examples, oop_design_patterns

Exercise:

Drupal Rules, Programming ⌘

  • Alterability
  • Separation of: Content, Configuration, State Data
  • i18n (internationalization)
  • Accessibility, Usability
  • DB Independency (database)
  • Security (all user-provided input is insecure)
  • Tests, Documentation

Alterability ⌘

  • Drupal core and contributed modules are almost fully alterable
    • they provide mechanisms that you can use to customize and add to their behaviour and output
  • The basic idea is that instead of editing downloaded code, you should create an add-on module or theme, which uses the existing alteration mechanisms
  • Drupal Alteration Mechanisms
    • Hooks
    • Plugins
    • Dependency Injection
    • Symfony-based routing
    • YAML-based menu

Drupal Hooks ⌘

  • PHP file or function you can put into a module or theme
  • Will be invoked (called or included) at an appropriate time by a Drupal core or contributed module
    • in order to let your module or theme alter (or add to) behaviour and output
  • Types: generic hooks, alter hooks, theme hooks (they use theme functions or template files '.html.twig')
  • Theme hooks: theme preprocessing hooks and theme processing hooks

Modules to enable: Examples for Developers, Hooks example

Drupal Plugins ⌘

  • Replaced many of the generic hooks from Drupal 7 with an object-oriented system
    • modules can add classes that define additions to Drupal behavior

Modules to enable: Examples for Developers, Plugin type example

Exercises:

Drupal Dependency Injection ⌘

  • Allows modules to replace fundamental core systems of Drupal (or services)
    • for example the mechanism for storing cached data

Drupal Routing ⌘

  • Symfony-based routing system
    • allows modules to define URLs and their output by defining routes and controllers
    • allows to alter what happens at various points during an HTTP request by defining event subscribers

Drupal Links ⌘

  • YAML-based system
  • For defining
    • default menu links
    • contextual links
    • and related data for the menu system

Drupal Module Themeable, Output ⌘

  • Render arrays should be returned from all functions that return output
    • these will be rendered by calls to internal functions;
    • the 'theme()' function does not exist in Drupal 8 for modules to call directly
    • calling 'drupal_render()' directly is also discouraged

https://training-course-material.com/training/Drupal_8_Themes

https://www.drupal.org/node/2216195

Separation of: Content, Configuration, State Data ⌘

i18n (internationalization) ⌘

Accessibility, Usability ⌘

DB Independency (database) ⌘

Security (all user-provided input is insecure) ⌘

Tests, Documentation ⌘

  • Should we bother at all, huh?
    • Yes, everything should be tested
    • Yes, everything should be documented

Everything should be tested ⌘

  • SimpleTest, PHPUnit - both adopted in D8, 1st deprecated (new tests should be written with 2nd)
  • All new changes in core must pass all of existing automated tests
  • All new core functionalities or bug-fixes must have new tests
  • Adopted also by lots of contributed modules (to some extent, at least)

Everything should be documented ⌘

  • standards for in-code documentation
    • each distinct code item (function, class, file, method, etc.) should include a documentation header
    • later these documentation headers are parsed to create the Drupal API reference site (api.drupal.org)
  • No change should be committed without its accompanying documentation being complete,
    • critical-priority bug if documentation omitted
  • Write README files, API documentation, and end-user documentation
    • prevention for next users (of course also for us, The Blessed Creators) to 'know how'
      • and 'Do Not Ask Over and Over The Same Boring Questions' (that's the lighter version, for sure!)

Drupal Mistakes, Programming ⌘

  • Programming Too Much
  • Over-Executing Code
  • Saving PHP Code in the Database
  • Working Alone

Programming Too Much ⌘

  • Avoiding Custom Programming with Fielded Data
  • Defining Theme Regions for Block Placement

Over-Executing Code ⌘

  • Executing Code on Every Page Load
  • Using an Overly General Hook

Saving PHP Code in the Database ⌘

  • It's hacking
    • Security risk
    • Risk of bugs
    • Hard to debug
    • Hard to track

Alternatives for php code in db ⌘

  • Control block visibility
    • Use the Context module or the Panels module, which allow you much more flexibility on block placement
  • Database queries
    • Use the Views module.
  • Custom page or block output
    • Create your block or page in a module.
  • Change how something is displayed
    • Override a theme function or template.
  • Calculations for a field
    • Create your own custom field or formatter.

Working Alone ⌘

  • Participating in Groups and IRC
  • Reporting Issues and Contributing Code to the Drupal Community
  • Contributing to the Drupal Community in Other Ways

Programming Examples ⌘

  • Module skeleton
  • Registering for URLs and Displaying Content
  • Using the Drupal Form API
  • Programming with Ajax in Drupal
  • Programming with Entities and Fields

Module skeleton ⌘

Exercises:

Registering for URLs and Displaying Content ⌘

  • Registering for a URL in Drupal 8
    • mm.routing.yml, HiUnivController.php, mm.permissions.yml
  • Providing administrative links
    • mm.links.menu.yml, mm.links.task.yml, mm.links.action.yml
    • to alter: hook_menu_links_discovered_alter(), hook_menu_local_tasks_alter(), hook_menu_local_actions_alter(), hook_menu_local_tasks()
  • Altering Routes and Providing Dynamic Routes in Drupal 8
    • mm.services.yml, mmRouting.php, mm.info.yml
  • Registering a Block in Drupal 8
  • Creating Render Arrays for Page and Block Output
  • Generating paged output

Page callback, menu link ⌘

Related D8 api/doc references:

Drupal 8 Interconnections example:

Modules to enable: Examples for Developers, Page example

Exercises:

Block, plugin, annotation ⌘

Related D8 api/doc references:

Modules to enable: Examples for Developers, Block Example

Exercises:

Using the Drupal Form API ⌘

  • Form Arrays, Form State Arrays, and Form State Objects
  • Basic Form Generation and Processing in Drupal 8
  • Creating Confirmation Forms
  • Adding Auto-Complete to Forms
  • Altering Forms

Programming with Ajax in Drupal ⌘

  • Setting Up a Form for Ajax
  • Wrapper-based Ajax Callback Functions
  • Command-based Ajax Callback Functions in Drupal 8

Form, validation, submit, ajax ⌘

Modules to enable: Examples for Developers, Form API Example

Exercises:

Programming with Entities and Fields ⌘

  • Terminology of Entities and Fields
  • Defining a Content Entity Type in Drupal 8
  • Defining a Configuration Entity Type in Drupal 8
  • Querying and Loading Entities in Drupal 8
  • Defining a Field Type
  • Programming with Field Widgets
  • Programming with Field Formatters

Entity, field ⌘

Modules to enable: Examples for Developers, Config entity example, Content Entity Example, Field Example, Field Permission Example

Programming Tools and Tips ⌘

  • Where to Find More Information
    • Drupal Site Building and General Drupal Information
    • Drupal Programming Reference and Background
    • PHP Resources
    • Database Resources
    • Other Web Technology Resources
  • Drupal Development Tools Devel
  • Discovering Drupal API Functions and Classes
  • Other Programming Tips and Suggestions

Errors logging ⌘

Enable logging all errors (only on testing envy!):

/admin/config/development/logging
Edit settings.php and add at the end of it:
/**
 *
 * Enable all errors reporting
 *
 */
error_reporting(E_ALL);
ini_set('display_errors', TRUE);
ini_set('display_startup_errors', TRUE);

Exercises ⌘

1. Create your own module

Make new folder modules/custom_

1.1. Config file

  1. Make another directory and name it custom_/mm (shortcut from 'my module')
  2. Provide some metadata about the project
    • Create file mm/mm.info.yml
    • Add entries in mm.info.yml as shown below
      # Required 1 line
      name: My Own Module
      # 2 optional lines
      description: Here I will learn how to code with Drupal 8
      package: Custom
      
      # Required 2 lines
      type: module
      core: 8.x
      
      # Version number. If not private module, then it will be added automatically by the packager on drupal.org
      version: VERSION
      
  3. Install the module

1.2. Module file and access granting

  1. Create file mm/mm.module
    • Put this code into it
      <?php
      
      /**
       * @file
       * My own module.
       *
       * This file can be omitted if we do not need to define any functions or implement hooks
       *
       */
      
  2. Add file for permissions mm/mm.permissions.yml
    # Permission example
    administer mm:
      title: 'Administer mm settings'
      description: 'If we really need more descriptions'
    
  3. Clear caches

1.3. Add WARNING description to permission from the previous exercise

  1. Use d8 api website to find the solution

2. Add simple page and link in menu

Controller

  1. Create file mm/src/Controller/HiUnivController.php
    <?php
    
    namespace Drupal\mm\Controller;
    
    use Drupal\Core\Controller\ControllerBase;
    
    class HiUnivController extends ControllerBase {
    
      /**
       * Display the markup.
       *
       * @return array
       */
      public function hiuniv() {
        return array(
          '#type' => 'markup',
          '#markup' => $this->t('Hi, Universe!'),
        );
      }
    }
    

Router

  1. Another file /mm/mm.routing.yml
    mm.hiuniv:
      path: '/hiuniv'
      defaults:
        _controller: '\Drupal\mm\Controller\HiUnivController::hiuniv'
        _title: 'Hi Universe'
      requirements:
        _permission: 'access content'
    

Menu link

  1. Almost there /mm/mm.links.menu.yml
    mm.admin:
      title: 'Hi universe simple page example'
      description: 'My module settings - link to hiuniv page'
      parent: system.admin_config_development
      route_name: mm.hiuniv
      weight: 99
    

3. Drupal 8 OOP specifics

3.1. Class

  1. Create simple module which will prepare OO skeleton for 3 specific "real life" things from your company/website, etc
    • Use example 04 or 05
    • Example of business scenario:
      NobleProg Services
        Training
        Consultancy
      

3.2. Subclass with properties

  1. Extend the module from 3.1.
    • Use example 06 and 07
    • Example of business scenario:
      • Define some static properties for one of your classes
        Training
          Venue
          Number of delegates
        
      • Provide a method to wrap one property within a string
        Number of delegates 
          "There will be 6 people on this training"
        

3.3. Interface

  1. Extend the module from 3.2.
    • Use example 08 and 09
    • Example of business scenario:
      • Define the interface and getProperty() method (replace the Property with your business property name)
          getVenue();
        
      • Implement getProperty() method in both classes, one should return literal value, second should use property's value
        Training
          getVenue() { ... }
        Consultancy
          getVenue() { ... }
        

4. Make your own drupal block

Plugin

  1. New file /mm/src/Plugin/Block/HiUnivBlock.php
    <?php
    namespace Drupal\mm\Plugin\Block;
    
    use Drupal\Core\Block\BlockBase;
    
    /**
     * Provides a 'Hi Universe' Block.
     *
     * @Block(
     *   id = "hiuniv_block",
     *   admin_label = @Translation("Hi Universe block"),
     * )
     */
    class HiUnivBlock extends BlockBase {
    
      /**
       * {@inheritdoc}
       */
      public function build() {
        return array(
          '#markup' => $this->t('Hi, Universe!'),
        );
      }
    }
    

5. Alter route, dynamic route

Router

  1. New file /mm/src/Routing/mmRouting.php
    <?php
    
    /**
     * @file
     * Contains \Drupal\mm\Routing\mmRouting.
     */
    
    namespace Drupal\mm\Routing;
    
    use Drupal\Core\Routing\RouteSubscriberBase;
    use Symfony\Component\Routing\Route;
    use Symfony\Component\Routing\RouteCollection;
    
    /**
     * Provides dynamic route and route alter.
     *
     */
    class mmRouting extends RouteSubscriberBase {
      /**
       * {@inheritdoc}
       */
      protected function alterRoutes(RouteCollection $collection) {
        // Alter the title of the mm module page (drupal/hiuniv).
        $route = $collection->get('mm.hiuniv');
        $route->setDefault('_title', 'Altered title');
    
        // Add a dynamic route at 'hiuniv/mm'
        $path = $route->getPath();
        // Constructor parameters: path, defaults, requirements
        // like in a routing.yml file.
        $newroute = new Route($path . '/mm', array(
            '_controller' => '\Drupal\mm\Controller\HiUnivController::hiuniv',
            '_title' => 'New page title',
          ), array(
            '_permission' => 'administer_mm',
          ));
    
        $collection->add('mm.newroutename', $newroute);
      }
    }
    

Service

  1. New file /mm/mm.services.yml
    # Service definitions for module.
    services:
      # Machine name of the service.
      mm.mm_routing:
        # Class providing the service.
        class: Drupal\mm\Routing\mmRouting
        # Service tags. This service is an event subscriber.
        tags:
        - { name: event_subscriber }
    

6. Create own form

Extend the mm module

6.1. Provide class form

  1. New file /mm/src/Form/mmForm.php
    <?php
    /**
    * @file
    * Contains \Drupal\mm\Form\mmForm.
    **/
    namespace Drupal\mm\Form;
    
    use Drupal\Core\Form\FormBase;
    use Drupal\Core\Form\FormStateInterface;
    
    class mmForm extends FormBase {
      /**
      * {@inheritdoc}
      */
      public function getFormId() {
        return 'mm_mm_form';
      }
    
      /**
      * {@inheritdoc}
      */
      public function buildForm(array $form, FormStateInterface $form_state) {
        // Return array of Form API elements.
        $form['franchisee_name'] = array(
          '#type' => 'textfield',
          '#title' => $this->t('Franchisee name'),
        );
    
        $form['submit'] = array(
          '#type' => 'submit',
          '#value' => $this->t('Save'),
        );
    
        return $form;
      }
    
      /**
      * {@inheritdoc}
      */
      public function validateForm(array &$form, FormStateInterface $form_state) {
        // Validation required to satisfy interface
      }
    
      /**
      * {@inheritdoc}
      */
      public function submitForm(array &$form, FormStateInterface $form_state) {
        // Submit required to satisfy interface
      }
    
    }
    
  2. Update router /mm/mm.routing.yml
    mm.form:
      path: '/mm-form'
      defaults:
        _title: 'mm form'
        _form: '\Drupal\mm\Form\mmForm'
      requirements:
        _access: 'TRUE'
    
  3. Update links /mm/mm.links.menu.yml
    mm.form:
      title: 'Hi universe form'
      description: 'My own form'
      route_name: mm.form
    

6.2. Validate the form

Check if not empty and if it is less than 3 characters

6.3. Submit handler

Handle form submission

/mm/config/install/mm.schema.yml

6.4. Altering the form

Alter mm form with new email field ( use proper hook: hook_form_FORM_ID_alter() )

7. Custom Plugin

  1. Create a new plugin type that will work with units of measurement and conversions (ft to m, etc)
  2. Create a new plugin type related to your business
    • You need: plugin manager, plugin interface, base class, and plugin definition.
    • Implement it