added example for developpers module

This commit is contained in:
Bachir Soussi Chiadmi
2018-01-06 11:28:03 +01:00
parent 5017966908
commit 5ec2f4311c
396 changed files with 26299 additions and 0 deletions
@@ -0,0 +1,13 @@
name: Events Example
type: module
description: Provides an example of subscribing to and dispatching events.
package: Example modules
# core: 8.x
dependencies:
- examples:examples
# Information added by Drupal.org packaging script on 2017-12-17
version: '8.x-1.x-dev'
core: '8.x'
project: 'examples'
datestamp: 1513537386
@@ -0,0 +1,4 @@
events_example.description:
title: 'Events Example'
description: 'Example of dispatching and subscribing to events.'
route_name: events_example.description
@@ -0,0 +1,75 @@
<?php
/**
* @file
* Demonstrates how to subscribe to and dispatch events.
*/
/**
* @defgroup events_example Example: Events
* @ingroup examples
* @{
* Demonstrates subscribing to, and dispatching, events.
*
* Events allow for different components of the system to interact and
* communicate with each other. Modules can either dispatch events, subscribe to
* events, or both.
*
* Subscribing to an event allows a module to declare that it would like to be
* notified anytime a specific event happens. A module can subscribe to any
* number of events, and can even subscribe to the same event more than once.
*
* Dispatching an event allows a module, or Drupal core subsystem, to notify any
* registered subscribers that a specific event has just taken place. When an
* event is dispatched the registered code for each subscriber is executed. For
* example, whenever a configuration entity is updated the Configuration API
* dispatches a new event allowing all subscribers to react to the change.
*
* This allows modules to extend other systems without the need to modify the
* original code.
*
* Each event has a unique string name. This string is often referred to as "the
* event", or "the event name". This string is how you identify which event(s)
* you are interested in. A complete list of events dispatched by core is
* available at
* https://api.drupal.org/api/drupal/core%21core.api.php/group/events/
*
* Drupal's event system is an extension of the
* @link http://symfony.com/doc/current/components/event_dispatcher.html Symfony
* EventDispatcher component @endlink, and implements the Mediator pattern.
*
* Subscribing to an event requires:
* - Defining a service in your module, tagged with 'event_subscriber'.
* - Defining a class for your subscriber service that implements
* \Symfony\Component\EventDispatcher\EventSubscriberInterface
* - Using the getSubscribedEvents method to return a list of the events you
* want to subscribe to, and which methods on the class should be called for
* each one.
*
* For an example of subscribing to an event see the events_example.services.yml
* file. And the \Drupal\events_example\EventSubscriber\EventsExampleSubscriber
* class.
*
* Dispatching an event requires:
* - Defining a new static class with constants for unique event names and
* documentation. Example: \Drupal\events_exampl\Event\IncidentEvents
* - Defining an event class that extends
* \Symfony\Component\EventDispatcher\Event
* Example: \Drupal\events_example\Event\IncidentReportEvent
* - Use the 'event_dispatcher' service in your code to dispatch an event and
* provide an event object as an argument. Example:
* \Drupal\events_example\Form\EventsExampleForm::submitForm()
*
* This example code is based off of the article @link
* https://drupalize.me/blog/201502/responding-events-drupal-8 Responding to
* Events in Drupal 8 @endlink.
*
* @see events
* @see \Symfony\Component\EventDispatcher\EventDispatcherInterface
* @see \Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher
* @see service_tag
*/
/**
* @} End of "defgroup events_example".
*/
@@ -0,0 +1,8 @@
# Define a route for our event dispatching example.
events_example.description:
path: 'examples/events-example'
defaults:
_form: '\Drupal\events_example\Form\EventsExampleForm'
_title: 'Events Example'
requirements:
_permission: 'access content'
@@ -0,0 +1,16 @@
# Subscribing to an event requires you to create a new service tagged with the
# 'event_subscriber' tag. This tells the service container, and by proxy the
# event dispatcher service, that the class registered here can be queried to get
# a list of events that it would like to be notified about.
#
# For more on defining and tagging services see
# https://api.drupal.org/api/drupal/core%21core.api.php/group/container/8.2.x
services:
# Give your service a unique name, convention is to prefix service names with
# the name of the module that implements them.
events_example_subscriber:
# Point to the class that will contain your implementation of
# \Symfony\Component\EventDispatcher\EventSubscriberInterface
class: Drupal\events_example\EventSubscriber\EventsExampleSubscriber
tags:
- {name: event_subscriber}
@@ -0,0 +1,58 @@
<?php
namespace Drupal\events_example\Event;
/**
* Defines events for the events_example module.
*
* It is best practice define the unique names for events as constants on a
* class. This provides a place for documentation of the events. As well as
* allowing the event dispatcher to use the constants instead of hard coding a
* string.
*
* In this example we're defining one new event:
* 'events_example.new_incident_report'. This event will be dispatched by the
* form controller \Drupal\events_example\Form\EventsExampleForm whenever a new
* incident is reported. If your application dispatches more than one event
* you can use a single class to document multiple events. Just add a new
* constant for each. Group related events together with a single class, define
* another class for unrelated events.
*
* The docblock for each event constant should contain an "@Event" tag. This is
* used to ensure documentation parsing tools can gather and list all events.
* For example,
* https://api.drupal.org/api/drupal/core%21core.api.php/group/events/
*
* The docblock should also contain a description of when, and
* under what conditions, the event is triggered. A module developer should be
* able to read this description in order to determine whether or not this is
* the event that they want to subscribe to.
*
* This class is declared as final so that it can not be extended. It should
* only ever be used to provide unique event names, and documentation.
*
* In core \Drupal\Core\Config\ConfigCrudEvent is a good example of defining and
* documenting new events.
*
* @see \Drupal\Core\Config\ConfigCrudEvent
*
* @ingroup events_example
*/
final class IncidentEvents {
/**
* Name of the event fired when a new incident is reported.
*
* This event allows modules to perform an action whenever a new incident is
* reported via the incident report form. The event listener method receives a
* \Drupal\events_example\Event\IncidentReportEvent instance.
*
* @Event
*
* @see \Drupal\events_example\Event\IncidentReportEvent
*
* @var string
*/
const NEW_REPORT = 'events_example.new_incident_report';
}
@@ -0,0 +1,69 @@
<?php
namespace Drupal\events_example\Event;
use Symfony\Component\EventDispatcher\Event;
/**
* Wraps a incident report event for event subscribers.
*
* Whenever there is additional contextual data that you want to provide to the
* event subscribers when dispatching an event you should create a new class
* that extends \Symfony\Component\EventDispatcher\Event.
*
* See \Drupal\Core\Config\ConfigCrudEvent for an example of this in core.
*
* @see \Drupal\Core\Config\ConfigCrudEvent
*
* @ingroup events_example
*/
class IncidentReportEvent extends Event {
/**
* Incident type.
*
* @var string
*/
protected $type;
/**
* Detailed incident report.
*
* @var string
*/
protected $report;
/**
* Constructs an incident report event object.
*
* @param string $type
* The incident report type.
* @param string $report
* A detailed description of the incident provided by the reporter.
*/
public function __construct($type, $report) {
$this->type = $type;
$this->report = $report;
}
/**
* Get the incident type.
*
* @return string
* The type of report.
*/
public function getType() {
return $this->type;
}
/**
* Get the detailed incident report.
*
* @return string
* The text of the report.
*/
public function getReport() {
return $this->report;
}
}
@@ -0,0 +1,111 @@
<?php
namespace Drupal\events_example\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\events_example\Event\IncidentEvents;
use Drupal\events_example\Event\IncidentReportEvent;
/**
* Subscribe to IncidentEvents::NEW_REPORT events and react to new reports.
*
* In this example we subscribe to all IncidentEvents::NEW_REPORT events and
* point to two different methods to execute when the event is triggered. In
* each method we have some custom logic that determines if we want to react to
* the event by examining the event object, and the displaying a message to the
* user indicating whether or not that method reacted to the event.
*
* By convention, classes subscribing to an event live in the
* Drupal/{module_name}/EventSubscriber namespace.
*
* @ingroup events_example
*/
class EventsExampleSubscriber implements EventSubscriberInterface {
use StringTranslationTrait;
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
// Return an array of events that you want to subscribe to mapped to the
// method on this class that you would like called whenever the event is
// triggered. A single class can subscribe to any number of events. For
// organization purposes it's a good idea to create a new class for each
// unique task/concept rather than just creating a catch-all class for all
// event subscriptions.
//
// See EventSubscriberInterface::getSubscribedEvents() for an explanation
// of the array's format.
//
// The array key is the name of the event your want to subscribe to. Best
// practice is to use the constant that represents the event as defined by
// the code responsible for dispatching the event. This way, if, for
// example, the string name of an event changes your code will continue to
// work. You can get a list of event constants for all events triggered by
// core here:
// https://api.drupal.org/api/drupal/core%21core.api.php/group/events/8.2.x.
//
// Since any module can define and trigger new events there may be
// additional events available in your application. Look for classes with
// the special @Event docblock indicator to discover other events.
//
// For each event key define an array of arrays composed of the method names
// to call and optional priorities. The method name here refers to a method
// on this class to call whenever the event is triggered.
$events[IncidentEvents::NEW_REPORT][] = ['notifyMario'];
// Subscribers can optionally set a priority. If more than one subscriber is
// listening to an event when it is triggered they will be executed in order
// of priority. If no priority is set the default is 0.
$events[IncidentEvents::NEW_REPORT][] = ['notifyBatman', -100];
return $events;
}
/**
* If this incident is about a missing princess notify Mario.
*
* Per our configuration above, this method is called whenever the
* IncidentEvents::NEW_REPORT event is dispatched. This method is where you
* place any custom logic that you want to perform when the specific event is
* triggered.
*
* These responder methods receive an event object as their argument. The
* event object is usually, but not always, specific to the event being
* triggered and contains data about application state and configuration
* relative to what was happening when the event was triggered.
*
* For example, when responding to an event triggered by saving a
* configuration change you'll get an event object that contains the relevant
* configuration object.
*
* @param \Drupal\events_example\Event\IncidentReportEvent $event
* The event object containing the incident report.
*/
public function notifyMario(IncidentReportEvent $event) {
// You can use the event object to access information about the event passed
// along by the event dispatcher.
if ($event->getType() == 'stolen_princess') {
drupal_set_message($this->t('Mario has been alerted. Thank you. This message was set by an event subscriber. See \Drupal\events_example\EventSubscriber\EventsExampleSubscriber::notifyMario()'), 'status');
}
}
/**
* Let Batman know about any events involving the Joker.
*
* @param \Drupal\events_example\Event\IncidentReportEvent $event
* The event object containing the incident report.
*/
public function notifyBatman(IncidentReportEvent $event) {
if ($event->getType() == 'joker') {
drupal_set_message($this->t('Batman has been alerted. Thank you. This message was set by an event subscriber. See \Drupal\events_example\EventSubscriber\EventsExampleSubscriber::notifyBatman()'), 'status');
// Optionally use the event object to stop propagation.
// If there are other subscribers that have not been called yet this will
// cause them to be skipped.
$event->stopPropagation();
}
}
}
@@ -0,0 +1,130 @@
<?php
namespace Drupal\events_example\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Drupal\events_example\Event\IncidentEvents;
use Drupal\events_example\Event\IncidentReportEvent;
/**
* Implements the SimpleForm form controller.
*
* The submitForm() method of this class demonstrates using the event dispatcher
* service to dispatch an event.
*
* @see \Drupal\events_exampl\Event\IncidentEvents
* @see \Drupal\events_example\Event\IncidentReportEvent
* @see \Symfony\Component\EventDispatcher\EventDispatcherInterface
* @see \Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher
*
* @ingroup events_example
*/
class EventsExampleForm extends FormBase {
/**
* The event dispatcher service.
*
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
*/
protected $eventDispatcher;
/**
* Constructs a new UserLoginForm.
*
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
* The event dispatcher service.
*/
public function __construct(EventDispatcherInterface $event_dispatcher) {
// The event dispatcher service is an implementation of
// \Symfony\Component\EventDispatcher\EventDispatcherInterface. In Drupal
// this is generally and instance of the
// \Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher service.
// This dispatcher improves performance when dispatching events by compiling
// a list of subscribers into the service container so that they do not need
// to be looked up every time.
$this->eventDispatcher = $event_dispatcher;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('event_dispatcher')
);
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form['intro'] = [
'#markup' => '<p>' . $this->t('This form demonstrates subscribing to, and dispatching, events. When the form is submitted an event is dispatched indicating a new report has been submitted. Event subscribers respond to this event with various messages depending on the incident type. Review the code for the events_example module to see how it works.') . '</p>',
];
$form['incident_type'] = [
'#type' => 'radios',
'#required' => TRUE,
'#title' => t('What type of incident do you want to report?'),
'#options' => [
'stolen_princess' => $this->t('Missing princess'),
'cat' => $this->t('Cat stuck in tree'),
'joker' => $this->t('Something involving the Joker'),
],
];
$form['incident'] = [
'#type' => 'textarea',
'#required' => FALSE,
'#title' => t('Incident report'),
'#description' => t('Describe the incident in detail. This information will be passed along to all crime fighters.'),
'#cols' => 60,
'#rows' => 5,
];
$form['actions'] = [
'#type' => 'actions',
];
$form['actions']['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Submit'),
];
return $form;
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'events_example_form';
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$type = $form_state->getValue('incident_type');
$report = $form_state->getValue('incident');
// When dispatching, or triggering, an event start by constructing a new
// event object. Then use the event dispatcher service to notify any event
// subscribers. Event objects are used to transport relevant data to any
// subscribers, as well as keep track of the current state of an event. It
// is best practice to create a unique class wrapping
// \Symfony\Component\EventDispatcher\Event.
$event = new IncidentReportEvent($type, $report);
// Dispatch an event by specifying which event, and providing an event
// object. Rather than hard code the event name you should use a constant
// to represent the event being dispatched. The constant serves as a
// location for documentation of the event, and ensures your code is future
// proofed against event name changes.
$this->eventDispatcher->dispatch(IncidentEvents::NEW_REPORT, $event);
}
}
@@ -0,0 +1,64 @@
<?php
namespace Drupal\Tests\events_example\Functional;
use Drupal\Tests\BrowserTestBase;
/**
* Test the functionality of the Events Example module.
*
* For another example of testing whether or not events are dispatched see
* \Drupal\Tests\migrate\Kernel\MigrateEventsTest.
*
* @ingroup events_example
* @group examples
*/
class EventsExampleTest extends BrowserTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['events_example'];
/**
* {@inheritdoc}
*/
protected $profile = 'minimal';
/**
* Test the output of the example page.
*/
public function testEventsExample() {
// Test that the main page for the example is accessible.
$this->drupalGet('examples/events-example');
$this->assertSession()->statusCodeEquals(200);
// Verify the page contains the required form fields.
$this->assertSession()->fieldExists('incident_type');
$this->assertSession()->fieldExists('incident');
// Submit the form with an incident type of 'stolen_princess'. This does a
// couple of things. Fist of all, it ensures that our code in
// EventsExampleForm::submitForm() that dispatches events works. If it did
// not work, no event would be dispatched, and the message below would never
// get displayed. Secondly, it tests that our
// EventsExampleSubscriber::notifyMario() event subscriber is triggered for
// incidents of the type 'stolen_princess'.
$values = [
'incident_type' => 'stolen_princess',
'incident' => $this->randomString(),
];
$this->drupalPostForm('examples/events-example', $values, 'Submit');
$this->assertSession()->pageTextContains('Mario has been alerted. Thank you.');
// Fill out the form again, this time testing that the
// EventsExampleSubscriber::notifyBatman() subscriber is working.
$values = [
'incident_type' => 'joker',
'incident' => $this->randomString(),
];
$this->drupalPostForm('examples/events-example', $values, 'Submit');
$this->assertSession()->pageTextContains('Batman has been alerted. Thank you.');
}
}
@@ -0,0 +1,29 @@
<?php
namespace Drupal\Tests\events_example\Kernel;
use Drupal\KernelTests\KernelTestBase;
use Drupal\events_example\EventSubscriber\EventsExampleSubscriber;
/**
* Test to ensure 'events_example_subscriber' service is reachable.
*
* @ingroup events_example
* @group examples
*/
class EventsExampleServiceTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['events_example'];
/**
* Test for existence of 'events_example_subscriber' service.
*/
public function testEventsExampleService() {
$subscriber = $this->container->get('events_example_subscriber');
$this->assertInstanceOf(EventsExampleSubscriber::class, $subscriber);
}
}