Developer Guide

Last updated on
26 April 2023

There are two main entry points for extension in the Funding module:

  1. Funding Provider plugin type - The mechanism that controls how each line of the Yaml field is processed.
  2. Drupal's templating system - Between hook_preprocess_hook() (in our case, hook_preprocess_funding_link), and custom template suggestions, the output styles of this module are completely under the control of the developer.

Plugin type: FundingProvider

Each FundingProvider plugin is responsible for a top-level key, or "namespace", in the input Yaml data. When rendering the output, each item in the parsed Yaml is passed to the responsible FundingProvider plugin.

For example, the output for the open_collective Funding Yaml namespace is provided by the OpenCollective funding provider plugin.

How to write your own Funding Provider plugin

Assuming your module name is mymodule, and you would like your own Funding Provider named "Html Card" to be responsible for the namespace html_card.

The first step is to create a new PHP Class that implements the FundingProvider plugin annotation:

mymodule/src/Plugin/Funding/Provider/HtmlCard.php

Here is an example of that simple Funding Provider:

namespace Drupal\mymodule\Plugin\Funding\Provider;

use Drupal\funding\Plugin\Funding\FundingProviderBase;

/**
 * Plugin implementation of the funding_provider.
 *
 * @FundingProvider(
 *   id = "html_card",
 *   label = @Translation("Html Card"),
 *   description = @Translation("Handles processing for the 'html_card' namespace."),
 *   enabledByDefault = TRUE,
 * )
 */
class HtmlCard extends FundingProviderBase {

  /**
   * {@inheritdoc}
   */
  public function build($data): array {
    return [
      '#type' => 'inline_template',
      '#template' => '<div><h2>{{ title }}</h2><div>{{ content }}</div></div>',
      '#context' => [
        'title' => $data['title'],
        'content' => $data['content'],
      ],
    ];
  }

}

Provider Examples

Each Funding Provider is responsible for representing its own examples. The plugin can do this by implementing the examples() method.

The examples() method expects you return an array of strings. Each string should be a working Yaml example for the plugin.

Here is our previous Funding Provider, now with examples of its use provided to the system:

<?php

namespace Drupal\mymodule\Plugin\Funding\Provider;

use Drupal\funding\Plugin\Funding\FundingProviderBase;

/**
 * Plugin implementation of the funding_provider.
 *
 * @FundingProvider(
 *   id = "html_card",
 *   label = @Translation("Html Card"),
 *   description = @Translation("Handles processing for the 'html_card' namespace."),
 *   enabledByDefault = TRUE,
 * )
 */
class HtmlCard extends FundingProviderBase {

  /**
   * {@inheritdoc}
   */
  public function examples(): array {
    return [
      'html_card:
        title: Your title goes here
        content: Your extended content goes here',
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function build($data): array {
    return [
      '#type' => 'inline_template',
      '#template' => '<div><h2>{{ title }}</h2><div>{{ content }}</div></div>',
      '#context' => [
        'title' => $data['title'],
        'content' => $data['content'],
      ],
    ];
  }

}

Examples for each Funding Provider are presented to users on the Funding field's form. Here is a screenshot of what examples look like to the end user.

Screenshot of the examples on the funding field.

Validation

You can further improve your Funding Provider plugin by validating the data that is passed along to it.

When a Funding Yaml field is validated each Funding Provider can pass its own errors up to the system. This means that each of our custom plugins can relay useful information to the end-user of our plugin. When an InvalidFundingProviderData exception is thrown, that message is shown to the user on the content edit form as a field validation error.

In our example so far we have three data requirements that are implied. We can improve the plugin significantly for both ourselves and the end users by providing validation for our requirements.

Requirements:

  1. That html_card's value is an array.
  2. The array contains a key named title with a string value.
  3. The array contains a key named content with a string value.

Luckily, the Funding module comes with a few validation utility methods out of the box. Let's use these to improve our Funding Provider.

<?php

namespace Drupal\mymodule\Plugin\Funding\Provider;

use Drupal\funding\Exception\InvalidFundingProviderData;
use Drupal\funding\Plugin\Funding\FundingProviderBase;

/**
 * Plugin implementation of the funding_provider.
 *
 * @FundingProvider(
 *   id = "html_card",
 *   label = @Translation("Html Card"),
 *   description = @Translation("Handles processing for the 'html_card' namespace."),
 *   enabledByDefault = TRUE,
 * )
 */
class HtmlCard extends FundingProviderBase {

  /**
   * {@inheritdoc}
   */
  public function examples(): array {
    return [
      'html_card:
        title: Your title goes here
        content: Your extended content goes here',
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function validate($data): bool {
    if (!is_array($data)) {
      throw new InvalidFundingProviderData('The html_card value must be an array with "title" and "content" keys.');
    }

    $this->validateRequiredPropertyIsString($data, 'title');
    $this->validateRequiredPropertyIsString($data, 'content');

    return TRUE;
  }

  /**
   * {@inheritdoc}
   */
  public function build($data): array {
    return [
      '#type' => 'inline_template',
      '#template' => '<div><h2>{{ title }}</h2><div>{{ content }}</div></div>',
      '#context' => [
        'title' => $data['title'],
        'content' => $data['content'],
      ],
    ];
  }

}

There we have it. A functional and useful new Funding Provider will be available to your Drupal site as soon as you clear the cache.

Drupal Templates

Each Funding Provider should pass its results to the Drupal templating system for the best developer experience.

This allows each site to modify the output as desired. By using hook_preprocess_funding_link, or overriding the funding-link.html.twig template, any theme or module can extend control over the output.

Funding hook_theme implementations

Out of the box, the Funding module provides only one template that should be important to the site owner.

Developers can manually make use of this template in a render array if desired:

return [
  '#theme' => 'funding_link',
  '#provider' => 'github',
  '#url' => 'https://github.com/sponsors/my-github-name',
  '#content' => 'my-github-name',
];

Assume your module is named mymodule. And that we want change the CustomUrl Provider funding link content to only show the url's host (domain) name.

/**
 * Implements hook_preprocess_HOOK().
 */
function mymodule_preprocess_funding_link(&$variables) {
  if ($variables['provider'] == 'custom') {
    $variables['content'] = parse_url($variables['url'], PHP_URL_HOST);
  }
}

Styling Funding Links

As a developer you may want to change the styles for the funding links. Rather than fighting against the module's default styles, you can disable the Funding module's styles by adding this to your theme's info yaml file.

libraries-override:
  funding/funding-link: false 

Help improve this page

Page status: No known problems

You can: