Drupal 8 for Developers: Difference between revisions

From Training Material
Jump to navigation Jump to search
Line 895: Line 895:
#** Hint: router file can be better (-;
#** Hint: router file can be better (-;
#** Don't forget to change the service too (---;
#** Don't forget to change the service too (---;
#** Reference: use 'token' module
# Play with dynamic parameter '''../mm'''
# Play with dynamic parameter '''../mm'''
<br>
<br>

Revision as of 22:02, 26 September 2023

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

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

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

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