+++
title = "When There's Not a Module For That"
outputs = ["Reveal"]
+++

# When There's Not a Module For That

###### [mid.camp/6337](https://mid.camp/6337)

<img alt="Agaric" src="images/o-midcamp.png" class="plain" />

{{% note %}}
mid-air camp
{{% /note %}}

---

###### Presented by

**Benjamin Melançon** & **Mauricio Dinarte**

###### AKA

**[mlncn](https://agaric.coop/mlncn)** & **[dinarcon](https://agaric.coop/dinarcon)**

---


### Together,

## We are

#### ⅓

#### of


---


<img alt="Agaric" src="images/agaric-logo-stacked.png" class="plain" />

**ask@agaric.coop**

---


<img alt="Agaric" src="images/o-midcamp.png" class="plain" width="47%" />

{{% note %}}
mid-air camp
{{% /note %}}


---


> When building a Drupal site, “there’s a module for that” can be the sweetest words you can hear.


{{% note %}}
{{% /note %}}


---

What do you do

## When there's not a module for that

?

{{% note %}}
You make your own.  It's easy.
{{% /note %}}


---


![Regular node add form with Save button](images/create-frankenstein-unmodified.png)


{{% note %}}
Say you're creating a site where people can relive great literature that may have been inspired by a famine.

You've already created an excellent content type under Structure » Content.
{{% /note %}}

---


`frankenstein.info.yml`

```yaml
name: Frankenstein
type: module
description: "Rename save button for Frankenstein's content."
core: 8.x
```
&nbsp;  


`frankenstein.module`

```php
<?php
function frankenstein_form_node_frankenstein_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state) {
  $form['actions']['submit']['#value'] = t('Re-animate!');
}
```

---

### Where to put these files

![Folder hierarchy of custom module in Drupal](images/frankenstein-location-in-ranger.png)


---


![Regular node add form with Save button](images/create-frankenstein-unmodified.png)


---


**Enable your module:**

`drush -y en frankenstein`


---


*That slide was the most important you're going to be shown.*

# Enable your module


---


![Regular node add form with Save button as reanimate](images/create-frankenstein-reanimate.png)

{{% note %}}
Tadaaa!  Now you know how to make a module!
{{% /note %}}


---

![Adam Scott staring at the camera](images/adam-scott-conan-stare.gif)

{{% note %}}
So you're all sitting* there thinking ... sure, that's easy if you know the exact words and symbols to *put* in that file.  And you're absolutely right.

And we're going to tell you how you can figure out all of that.
{{% /note %}}

---

`frankenstein.info.yml`

```yaml
name: Frankenstein
type: module
description: "Rename save button for Frankenstein's content."
core: 8.x
core_version_requirement: ^8 || ^9
```
&nbsp;  


`frankenstein.module`

```php
<?php
function frankenstein_form_node_frankenstein_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state) {
  $form['actions']['submit']['#value'] = t('Re-animate!');
}
```

{{% note %}}
   We added the core_version_requirement line to make the module recognizeable to Drupal 9 as well as Drupal 8.
{{% /note %}}

---


A few basic approaches give a lot of power.

<img alt="Screwdriver with extensive collection of driver tips." src="images/screwdriver-tip-set.jpg" width="70%" />

{{% note %}}
The examples with the what will tell you a lot about the how.

A few specific examples will give you a lot of tools.

But first, two secrets.

One's good news, second is bad news.
{{% /note %}}

---


### 1. Now you know where to paste

![How do I exit the Vim editor? viewed 2.1 million times](images/stack-overflow-helping-devs-quit-vim.png)

{{% note %}}
And knowing where to paste unleashes the power of Stack Overflow / Stack Exchange sites.

(yes, the same family of sites that has helped a couple million developers figure out how to quit vim.)

Knowing where to put this code unleashes the power of Stack Overflow.

You now know enough to be dangerous.
{{% /note %}}


---


### 2. That simple form alter has hidden gotchas

&nbsp;

* As written, it only applies to the create (node/add) form— not the edit form.
* There's a dozen variations of the humble form alter hook, and all are valid.

{{% note %}}
With our dirty secrets acknowledged, on with the show.
{{% /note %}}

---


## The Show

- Act One: **How Not To Make a Module**
- Act Two: **How Not To Have to Make a Whole Module**
- Act Three: **How To Make a Module**
- Act Four: **How To Figure Out How To Make a Module**
- Encore

{{% note %}}
With our dirty secrets acknowledged, on with the show.
{{% /note %}}


---

How to make a module

## We'll cover 

* Hooks
  * Form alter
  * Node insert
* Plugins
  * Block
  * Formatter
* Services
  * Route subscriber
  * Event subscriber

{{% note %}}

the three main ways to mess with Drupal 8 and 9: hooks, plugins, and services, with a couple examples in each.

both examples of what technically to do and how to do general things

For example:

Doing something with a form?  You want hook_form_alter() or one of its variants.
{{% /note %}}


---

How to figure out how to make a module

## Learning to learn

* Drupal documentation
* Drupal contributed modules
* Drupal core and *its* modules
* Drupal forums and issues
* Drupal.StackExchange.com
* The rest of the Internet
* Debugger
* Code search / grep

{{% note %}}
Where to start if i don't cover it in this session
{{% /note %}}


---


#### When not to make a module?

[![Turtle with golf ball on spring contraption on back.](images/rube-goldberg-tortoise.jpg)](https://www.koreus.com/video/machine-rube-goldberg-lente.html)

{{% note %}}
You can do anything with a module!  Why wouldn't you make one every day?
{{% /note %}}


---


#### …when there's an easier way

![Ginger Howard completing her swing after hitting a golf ball](images/ginger-howard-golf-swing.jpg)

{{% note %}}
find a contrib module that does it

Should it be in a template?  If it's very specific to the display on your site, you should probably do it in the theme layer.  But if it's at all data and you might ever want to switch themes, or want to allow site builders or site managers to disable or change settings, get that in a module.
{{% /note %}}

---

![Modules link](images/download-extend-modules-menu.png)

{{% note %}}
{{% /note %}}

---

![Modules search](images/drupal-download-extend-modules-search.png)

{{% note %}}
I definitely recommend changing core compatibility to something reasonable, 8.x
And Status to "All projects", not just Full, non-sandbox projects, if you're ready to write a module.  A sandbox may be a great start.
Then put in your key words and search.
{{% /note %}}

---


##### There's a module that *almost* does exactly what you want

* Contribute a patch!
* Port it to Drupal 8/9!

{{% note %}}
Even if your patch is not accepted nor is there any way given to build on the module, you can fork the module— maintain your own copy.
{{% /note %}}


---

[![David submitting issue with patch](images/david-filing-issue-with-patch.png)](https://www.drupal.org/project/comment_notify/issues/2850935)

---


[![David fixing a reported comment notify issue all himself.](images/david-fixing-comment-notify-issue-alone.png)](https://www.drupal.org/project/comment_notify/issues/2976478)

{{% note %}}
This strategy is not without risks.

David was made a maintainer of Comment Notify, and now routinely fixes issues there all himself.

Still, that's generally better for *everyone* than to make your own module with duplicate functionality.
{{% /note %}}


---

### When to make a module


![Lego heart with a missing piece](images/lego-heart-missing-piece.jpg)

{{% note %}}
You've identified to the best of your ability that there's definitely not something out there that does what you want to do.

I personally do not advocate introspection at this point:  Why am i trying to do something that no one else is trying to do?

Credit: lego heart is credited to Llama-Muffin-Kelly on DeviantArt but only see it on Pinterest
{{% /note %}}


---


#### Step 1: Write up what you plan to do

[groups.drupal.org/contributed-module-ideas](https://groups.drupal.org/contributed-module-ideas)

{{% note %}}
The module does not exist.


{{% /note %}}


---

![image](images/copyright-block.png)

---

```yaml
name: "Project Copyright"
type: module
description: "Generate a block with the copyright leyend."
core: 8.x
core_version_requirement: ^8 || ^9
```

---

```php
<?php

namespace Drupal\copyright\Plugin\Block;

use Drupal\Core\Block\BlockBase;

/**
 * Provides a block with the copyright  legend.
 *
 * @Block(
 *   id = "copyright_block",
 *   admin_label = @Translation("Copyright"),
 * )
 */
class Copyright extends BlockBase {

  /**
   * @inheritDoc
   */
  public function build() {
    return [
      '#markup' => $this->t('Copyright © @year All rights reserved', ['@year' => date('Y')]),
    ];
  }

}
```


---

## Resources

Most of these links go to a page on [Drupal's API reference which is also a great place to go for an overview](https://api.drupal.org/api/drupal/9.0.x) of what tools are available to you as you stretch and .

---

### Key hooks

* [hook form_alter](https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Form%21form.api.php/function/hook_form_alter/9.0.x ) 
* [hook_entity_extra_field_info](https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Entity%21entity.api.php/function/hook_entity_extra_field_info/9.0.x ) + a 'field' site builders can use in entity view modes, such as to combine entity data and format it for display.  Or you can replace those three hooks with one plugin by using the [Extra field module](https://www.drupal.org/project/extra_field )


{{% note %}}
A note on all these links: I've linked to the Drupal 9.0 version.  These hooks all work in Drupal 8.x also.  Drupal.org [inadvertently for quite a while treated 8.2 as the end-all-and-be-all version of Drupal](https://www.drupal.org/project/apidrupalorg/issues/3085999 ); this has been mitigated and is being fixed but Google is still likely to take you to 8.2 when searching for documentation on a hook.  Just switch to the version you're using, or the latest version of Drupal if 
{{% /note %}}

---


### The Rest of the Internet

* Duck Duck Go (!g for Google results)
  * [ddg.co](https://duckduckgo.com/)
* [agaric.coop/blog](https://agaric.coop/blog)
* Your own notes

{{% note %}}
{{% /note %}}


---


* A module that was generated with Drupal Console https://www.drupal.org/project/ckeditor_youtube/ - simple and used by many, and slowly got more complex


---


* A module bringing in a PHP library and getting crazy (started with simple field formatter, can use this or another module to demo that) - https://drupal.org/project/inotherwords



---

## Why contribute?

* Right thing to do
* I asked you to
* People may contribute to it
* Someone may take it over and upgrade it or improve it greatly

{{% note %}}
{{% /note %}}

---

[![Workflow buttons issue: Add a configure link in Extend page.](images/workflow-buttons-add-configure-link-in-extend-page.png)](https://www.drupal.org/project/workflow_buttons/issues/3120524)

{{% note %}}
Or someone may make a tiny improvement that makes everyone's lives better.
{{% /note %}}

---

## Nice touches

`workflow_buttons.info.yml`

```diff
 type: module
 description: 'Provide workflow buttons for content moderation instead of a select dropdown of states.'
 core_version_requirement: ^8 || ^9
 core: 8.x
+configure: workflow_buttons.settings
 
 dependencies:
   - drupal:system (>=8.4)
```

{{% note %}}
{{% /note %}}

---

### How to develop while sharing the same code you're using

Your module `composer.json`:

```json
{
    "name": "drupal/cyborgtranslate",
    "description": "Trigger Google translate and Drupal interface translation at the same time, allowing sections of your site's localization to be done by humans and the rest by machines.",
    "keywords": ["drupal", "translation", "multilingual", "localization", "google translate"],
    "type": "drupal-module",
    "license": "GPL-2.0+",
    "homepage": "https://www.drupal.org/project/cyborgtranslate",
    "minimum-stability": "dev",
    "support": {
        "issues": "https://www.drupal.org/project/issues/cyborgtranslate",
        "source": "https://gitlab.com/agaric/drupal/cyborgtranslate/tree/8.x-1.x"
    }
}
```

{{% note %}}
Composer makes a lot of stuff seem more complicated, but it makes it easier to contribute.

Trust me... this will only be three screens of gibberish.
{{% /note %}}

---

Your project root `composer.json`:


```json
{
    "require": {
        "drupal/core": "^8.8.0",
        "drupal/cyborgtranslate": "dev-8.x-1.x as 1.x-dev",
        "drush/drush": "^9.0"
    },
    "repositories": {
        "drupal": {
            "type": "composer",
            "url": "https://packages.drupal.org/8"
        },
        "drupal/cyborgtranslate": {
            "type": "git",
            "url": "git@gitlab.com:agaric/drupal/cyborgtranslate.git"
        }
    }
}
```

---


{{% note %}}
{{% /note %}}


---

![Little rabbit in big grass](images/little-rabbit-in-big-grass.jpg)
[MID.CAMP/6337](https://mid.camp/6337) &middot; <a href="mailto:ask@agaric.coop">ASK@AGARIC.COOP</a>

{{% note %}}
It's a big wild world out there.  Bite off a piece you can chew.

Links for all of this and more on the session page.
{{% /note %}}

