Slide 1

Slide 1 text

Getting Started with Drupal 8 Module Development DrupalCamp London 2016

Slide 2

Slide 2 text

Who am I? Oliver Davies @opdavies Lead Drupal Developer at CTI Digital Drupal 7 & 8 core contributor Contrib module maintainer and contributor Symfony2 hobbyist

Slide 3

Slide 3 text

What are we going to cover? Where Drupal 8 modules are located, and how they are structured. How to build a simple module, including our own permissions and routes. How to add your own controller and service classes. What is the service/dependency injection container, and how do we use it? How we can use tools such as PhpStorm and Drupal Console to speed up the process.

Slide 4

Slide 4 text

Module location Modules live in the modules directory modules/contrib and modules/custom still works

Slide 5

Slide 5 text

Module Structure example.info.yml example.module example.permissions.yml example.routing.yml src/Controller/ExampleController.php

Slide 6

Slide 6 text

What’s New YAML replaces the INI format .module file is not always needed Permissions and routes in separate files src directory to hold PHP classes No more .inc files Namespaces / PSR-4 autoloading

Slide 7

Slide 7 text

YAML “YAML Ain’t Markup Language” Simple key-value pairs Indentation matters Must be spaces!

Slide 8

Slide 8 text

system.permissions.yml administer modules: title: 'Administer modules' administer site configuration: title: 'Administer site configuration'

Slide 9

Slide 9 text

node.permissions.yml permission_callbacks: - \Drupal\node\NodePermissions::nodeTypePermissions

Slide 10

Slide 10 text

Namespaces namespace Drupal\node; namespace Drupal\node\Controller; namespace Drupal\override_node_options;

Slide 11

Slide 11 text

Autoloading Provided by Composer Replaces the file[] syntax One class per file The filename must match the name of the class The namespace must reflect the directory structure Drupal\node\Controller == core/modules/node/src/Controller

Slide 12

Slide 12 text

Routing No more hook_menu() Uses the Symfony Routing component Defined in YAML

Slide 13

Slide 13 text

system.routing.yml system.admin: path: '/admin' defaults: _controller: '\Drupal\system\Controller\SystemController:: systemAdminMenuBlockPage' _title: 'Administration'

Slide 14

Slide 14 text

system.routing.yml requirements: _permission: 'access administration pages'

Slide 15

Slide 15 text

path.routing.yml path.delete: path: '/admin/config/search/path/delete/{pid}' defaults: _form: '\Drupal\path\Form\DeleteForm' _title: 'Delete alias' requirements: _permission: 'administer url aliases'

Slide 16

Slide 16 text

Underscores? Underscores are specified for everything which are not parameters to the controller. This is coming as a sort of "standard" from symfony. These parameters are upcasted via the param converter and passed to the controller using the controller resolver Daniel Wehner - http://drupal.stackexchange.com/a/89921

Slide 17

Slide 17 text

Creating Your Own Module

Slide 18

Slide 18 text

Creating Your Own Module Create a directory within modules or modules/custom Add a {modulename}.info.yml file Add {modulename}.module, {modulename}.routing,yml and {modulename}. permission,yml if needed

Slide 19

Slide 19 text

name: 'DrupalCamp London' core: 8.x type: module package: Custom

Slide 20

Slide 20 text

dclondon.speakers_hello: path: '/speakers/{name}' defaults: _title: 'Speakers' _controller: 'Drupal\dclondon\Controller\SpeakerController:: hello' requirements: _permission: 'access content'

Slide 21

Slide 21 text

namespace Drupal\dclondon\Controller; class SpeakerController { function hello($name) { return [ '#markup' => t('Hello, @name.', ['@name' => $name]), ]; } }

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

Changing to a Service

Slide 24

Slide 24 text

namespace Drupal\dclondon\Service; class SpeakerGreeter { public function greet($name) { // Capitalise the first letter. return ucfirst($name); } }

Slide 25

Slide 25 text

use Drupal\dclondon\Service\SpeakerGreeter; public function hello($name) { $greeter = new SpeakerGreeter(); $name = $greeter->greet($name); ... }

Slide 26

Slide 26 text

The Service Container

Slide 27

Slide 27 text

What is the Service Container? Added by the DependencyInjection component It’s an object that contains other objects and instructions on how to create them Centralised place to create all services Makes them reusable They are are only instantiated when needed Only one instance is ever instantiated of each class

Slide 28

Slide 28 text

Adding Our Service Class to the Container Add {mymodule}.services.yml Extend ControllerBase Add create() method, and return a new static class with the required parameters with $container Add properties and inject the values in __construct() Use $this->{serviceName}->{methodName}()

Slide 29

Slide 29 text

services: dclondon.speaker_greeter: class: Drupal\dclondon\Service\SpeakerGreeter arguments: []

Slide 30

Slide 30 text

use Drupal\Core\Controller\ControllerBase; class SpeakerController extends ControllerBase { private $greeter; public function __construct(GreeterInterface $greeter) { $this->greeter = $greeter; } }

Slide 31

Slide 31 text

/** * {@inheritdoc} */ public static function create(ContainerInterface $container) { $speakerGreeter = $container->get('dclondon.speaker_greeter'); return new static($speakerGreeter); }

Slide 32

Slide 32 text

public function hello($name) { $name = $this->greeter->greet($name); return [ '#markup' => t('Hello, @name.', ['@name' => $name]), ]; }

Slide 33

Slide 33 text

/** * {@inheritdoc} */ public static function create(ContainerInterface $container) { $speakerGreeter = $container->get('dclondon.speaker_greeter'); $logger = $container->get('logger.factory'); return new static($speakerGreeter, $logger); }

Slide 34

Slide 34 text

private $greeter; private $logger; public function __construct(GreeterInterface $greeter, LoggerChannelFactoryInterface $logger) { $this->greeter = $greeter; $this->logger = $logger; }

Slide 35

Slide 35 text

public function hello($name) { $name = $this->greeter->greet($name); $this->logger->get('default')->info($name); return [ '#markup' => t('Hello, @name.', ['@name' => $name]), ]; }

Slide 36

Slide 36 text

Service Parameters

Slide 37

Slide 37 text

Service Parameters Added to {modulename}.services.yml Added to __construct() as an argument, and assigned to a property Available via $this

Slide 38

Slide 38 text

parameters: dclondon.speaker_greeter.shout: false services: dclondon.speaker_greeter: class: Drupal\dclondon\Service\SpeakerGreeter arguments: - %dclondon.speaker_greeter.shout%

Slide 39

Slide 39 text

private $shout; public function __construct($shout) { $this->shout = $shout; } public function greet() { if ($this->shout) { ... } }

Slide 40

Slide 40 text

No content

Slide 41

Slide 41 text

Configuration

Slide 42

Slide 42 text

Configuration Lives in a config directory Defined in YAML config/install/* is added when the module is installed config/schema/* defines the structure of your configuration config/optional/* are for additional modules

Slide 43

Slide 43 text

config/install/dclondon.settings.yml: greeter_text: 'Hello @name' src/Controller/SpeakerController.php: \Drupal::config('dclondon.settings')->get('greeter_text');

Slide 44

Slide 44 text

Generating Forms Add a new route Add a new controller extending ConfigFormBase Add getFormId and getEditableConfigNames methods Add buildForm and submitForm methods, and use $this->config()->set()- >save();

Slide 45

Slide 45 text

Useful Tools

Slide 46

Slide 46 text

PhpStorm Auto-completion Auto-import classes Symfony plugin/Drupal bridge

Slide 47

Slide 47 text

Drupal Console CLI tool Generates code Interacts with your site - e.g. router:rebuild, cache:rebuild drupalconsole.com

Slide 48

Slide 48 text

Questions?

Slide 49

Slide 49 text

Feedback @opdavies https://www.oliverdavies.uk/talks/getting-started-with-drupal-8-module- development