Drupal 8 for Developers
THIS IS A DRAFT
This text may not be complete.
Drupal 8 for Developers
Drupal 8 for Developers Training Materials
Copyright Notice
Copyright © 2004-2023 by NobleProg Limited All rights reserved.
This publication is protected by copyright, and permission must be obtained from the publisher prior to any prohibited reproduction, storage in a retrieval system, or transmission in any form or by any means, electronic, mechanical, photocopying, recording, or likewise.
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
- Annotations - form of DocBlock comments, borrowed from the Doctrine library
- 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
- incorporates code from many outside 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
- if cache clear is requested via 'drupal_flush_all_caches()' function
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
- we tag it with the node ID
- Custom tags
\Drupal\Core\Cache\Cache::invalidateTags($tags);
- Example: module is caching data related to a specific node content item
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) ⌘
- Drupal translations - https://localize.drupal.org/
- Related i18n API
- Related docs
Accessibility, Usability ⌘
- Accessibility
- Usability
DB Independence (database) ⌘
- Related DB API
- Related docs
Security (all user-provided input is insecure) ⌘
- Related Security API
- Related docs
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!)
- prevention for next users (of course also for us, The Blessed Creators) to 'know how'
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
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 ⌘
- Composer - packages manager, updater
- Drush - enabling extensions, administering, generating code
- Drupal Console - like drush, but mostly for generating code
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
- Make another directory and name it custom_/mm (shortcut from 'my module')
- 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
- Install the module
- What now? How can we fix it? (-:
1.2. Module file and access granting
- 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 * */
- Put this code into it
- Add file for permissions mm/mm.permissions.yml
# Permission example administer mm: title: 'Administer mm settings' description: 'If we really need more descriptions'
- Clear caches - or not (=
- Do we have to clear them now? Why?
1.3. Add WARNING description to permission from the previous exercise
- Use d8 api website to find the solution
- ..or search your drupal web folder (-;
Controller
- 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
- 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
- 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
- Why do we have to clear it now?
- 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
- 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
- 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"
- Define some static properties for one of your classes
3.3. Interface
- 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() { ... }
- Define the interface and getProperty() method (replace the Property with your business property name)
4. Make your own drupal block
Plugin
- 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
- What about now? Does it work straight away or not? Why?
5. Alter route, dynamic route
Router
- 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
- 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
- What about caches, huh?
- 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 (---;
- Correct what should be fixed, please (=
- 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
- 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 } }
- Update router /mm/mm.routing.yml
mm.form: path: '/mm-form' defaults: _title: 'mm form' _form: '\Drupal\mm\Form\mmForm' requirements: _access: 'TRUE'
- Update links /mm/mm.links.menu.yml
mm.form: title: 'Hi universe form' description: 'My own form' route_name: mm.form
Final bits
- Does it work? Why (the usual)? (=
- 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
- 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
- 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 (-:
- 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
- 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 (-:
- 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
- Write our custom hook_help()
- HINT: use token module as a reference
- 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