Drupal 8 for Developers

From Training Material
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

THIS IS A DRAFT

This text may not be complete.


title
Drupal 8 for Developers
author
Lukasz Sokolowski

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

Overview Con't ⌘

Major subsystems in Drupal

  • Routing, Menus
  • Entities, Fields, Views, Forms
  • Configuration, Plugins
  • Theme system, Caching
  • All Drupal APIs
  • Related docs

Routing ⌘

  • Handles interactions
    • user (or system) accessing a certain path (or resource)
    • translates into a route, which maps that resource to a flow
    • returns a response back - success or graceful failure
  • Replacement for hook_menu - Symfony Routing component
  • Route mapped to a controller which renders drupal page
  • Related Routing API
  • Related docs

Menus ⌘

  • Navigation
    • provides details about how the site itself is organized
    • keeps a structure of how content is related
  • Provide APIs to generate, retrieve, and modify elements of the site structure
  • Hierarchical - have a tree-like structure
  • Related Menu API
  • Related docs


Entities ⌘

  • Powerful way of modeling data and content in Drupal
  • Node, taxonomy, user, comment, file, media, etc - or our custom one
  • Can have multiple bundles
    • different variations of the same entity type
    • can have different fields on them (while sharing some base fields)
  • New type of entity - configuration
    • exportable items, to be reused/shared in environments
  • Related Entity API
  • Related docs

Fields ⌘

  • Entity bundles can have various fields
  • Are responsible for holding data
  • Two main types - base and configurable
    • Base - defined in code for each entity type
    • Configurable - usually created and configured in the UI
      • attached to a bundle of that entity type
      • exported via configuration

Fields con't ⌘

  • Can also be of multiples types, depending on the data they store
    • string (or text) fields, numeric, date, email, and so on
  • We can create our own field types
    • with its own data input widget
    • and output formatter
  • Related Field API
  • Related docs


Views ⌘

  • Listing content and data
  • Allows the creation of configurable listings
  • Includes - filters, sorts, display options, and many other features
  • Extendable via custom plugins
  • Related Views API

Forms ⌘

  • Capture user input
  • Powerful Form API
    • securely and efficiently rendering and processing the submitted data
    • abstraction over having to output our own form elements and deal with posted values
    • allows to define our own form definition in OOP and handle validation and submission in a logical way
  • Related Form API
  • Related docs

Configuration ⌘

  • Centralized configuration system
  • Can be stored in: database (default), files in a specific dir, other storage back-ends
  • Exportable into YAML files (to be re-imported later)
  • Two kinds
    • simple - always singular (one instance only) - site name, email address, etc
    • complex - follows a certain schema and we can have multiple definitions - View, Entity, etc
  • Related Config API
  • Related docs

Plugins ⌘

  • Encapsulating functionality - very widely used in D9
  • Components of reusable code - used and managed by a central system
  • System handles a task in a certain way (plugin X)
    • other modules to provide different ways (plugin Y or Z).
  • Opposite to entities: not used for data storage, but for functionality
  • Discoverability - usually via
    • Annotations - form of DocBlock comments, borrowed from the Doctrine library
      • can describe classes(Drupal), methods, and even properties with certain metadata
    • YAML files - for example menu links
  • Related Plugin API
  • Related docs

The theme system ⌘

  • Is spread out over Drupal core, modules, and themes
  • Both modules and themes can theme data or content
  • Best practice - to ensure that modules are able to theme their data
    • themes can then come into play to style the output or override whats needed
  • D8 moved to the open source Twig templating system (twig.symfony.com)
    • separation of logic from a presentation layer
    • makes front-end developers' jobs much easier and secured
  • Related Theme API
  • Related docs

Caching ⌘

  • Improves the performance of building pages and rendering data
  • Cache backend - stores the results of complex data calculations
    • cache invalidation - something requires the calculations to be redone
  • Render cache - wraps output with metadata that describes when the cache of that output needs to be invalidated
  • Related Cache API
  • Related docs

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
  • procedural-code-based system transformed into a mostly object-oriented
    • 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 (Not Yet In D9, works only in D8) ⌘

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

Exercises:

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

render_example

theming_example

Separation of: Content, Configuration, State Data ⌘

  • Content - Entities, Bundles, Fields, data in db
  • Configuration - setups, YAML files, definitions
  • State - stored in db, per system, temporary (dies with db)
    • in D7 handled by variables
    • common keys examples: system.cron_last, install_time
    • $pairs = \Drupal::state()->getMultiple($keys);
    • Related docs

i18n (internationalization) ⌘

Accessibility, Usability ⌘

DB Independence (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 Context or Layout Builder modules, which allow much more flexibility on block placement
  • Database queries
    • Use Views core 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

Exercises:

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

Command liners ⌘

Other Useful Modules ⌘

Errors logging ⌘

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

/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
    • What now? How can we fix it? (-:

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 - or not (=
    • Do we have to clear them now? Why?

1.3. Add WARNING description to permission from the previous exercise

  1. Use d8 api website to find the solution
    • ..or search your drupal web folder (-;


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
    

Clearing Caches, playing around

  1. Why do we have to clear it now?
  2. What about the path, do you see anything interesting about it?
    • Change it accordingly to other links in that section - clear caches again, what do we see now (or how) and why?


3. Drupal 8 OOP specifics

The module with examples works only in D8, not yet in D9

In D9+ download the module only and don't install it

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!'),
        );
      }
    }
    

Clearing Caches

  1. What about now? Does it work straight away or not? Why?


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 }
    

Final steps

  1. What about caches, huh?
  2. Do we follow here all the best practices?
    • Correct what should be fixed, please (=
      • Hint: router file can be better (-;
      • Don't forget to change the service too (---;
      • Reference: use 'token' module
  3. Play with dynamic parameter ../mm


6. Create own form

Extend the mm module

Use drush gen form command, but only to see it's example in the command line

  • do it with --dry-run option

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
    

Final bits

  1. Does it work? Why (the usual)? (=
  2. Rewrite it into mm.links.task.yml. How?
    • Hint: Use pathauto module as a reference

6.2. Validate the form

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

6.3. Submit handler

Handle form submission

  1. Use messenger() and put Franchisee name in message via replacement variable (for example @frname) in t() function

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)
    • Discuss the best way to solve it (-:
  2. Create a new plugin type related to your business
    • You need: plugin manager, plugin interface, base class, and plugin definition.
    • Implement it


8. Custom Field

  1. Fix me - in samples/licence_plate example
    • Add licence plate custom field to Article content type
    • Add new content of Article type
    • Data is not saved for some reason - find out why
      • In the section Manage form display (admin/structure/types/manage/article/form-display)
      • HINT: use link core field to answer that (-:
    • In the section Manage display (admin/structure/types/manage/article/display)
      • There is no cog button there - fix it (How?)
      • There is also missing short summary of format settings - add it
      • HINT: use comment core field to find the answers (-:
  2. New file /mm/src/Plugin/Field/FieldType/RealName.php
    <?php
    /**
     * @file
     * Contains \Drupal\mm\Plugin\Field\FieldType\RealName.
     */
    
    namespace Drupal\mm\Plugin\Field\FieldType;
    
    use Drupal\Core\Field\FieldItemBase;
    use Drupal\Core\Field\FieldStorageDefinitionInterface;
    use Drupal\Core\TypedData\DataDefinition;
    
    /**
    * Plugin implementation of the 'realname' field type.
    *
    * @FieldType(
    *   id = "realname",
    *   label = @Translation("Real name"),
    *   description = @Translation("This field stores a first and last name."),
    *   category = @Translation("General"),
    *   default_widget = "string_textfield",
    *   default_formatter = "string"
    * )
    */
    class RealName extends FieldItemBase {
    
    
      /**
       * {@inheritdoc}
       */
      public static function schema(\Drupal\Core\Field\FieldStorageDefinitionInterface $field_definition){
        return array(
          'columns' => array(
            'first_name' => array(
              'description' => 'First name.',
              'type' => 'varchar',
              'length' => '255',
              'not null' => TRUE,
              'default' => '',
            ),
            'last_name' => array(
              'description' => 'Last name.',
              'type' => 'varchar',
              'length' => '255',
              'not null' => TRUE,
              'default' => '',
            ),
          ),
          'indexes' => array(
            'first_name' => array('first_name'),
            'last_name' => array('last_name'),
          ),
        );
      }
    
      /**
       * {@inheritdoc}
       */
      public static function propertyDefinitions(\Drupal\Core\Field\FieldStorageDefinitionInterface $field_definition) {
        $properties['first_name'] = \Drupal\Core\TypedData\DataDefinition::create('string')->setLabel(t('First name'));
        $properties['last_name'] = \Drupal\Core\TypedData\DataDefinition::create('string')->setLabel(t('Last name'));
    
        return $properties;
      }
    
    }
    

Add new field of type RealName to Article content type

  • Create content Article - what can we say?
  • Fix it!
    • Hint: compare it with code from field_example


9. Hooks

  1. Write our custom hook_help()
    • HINT: use token module as a reference
  2. Add custom token from our module
    • HINT1: use hook_token_info() and hook_tokens()
    • HINT2: use one of hook_token_info() implementations described in it's docs