EventSubscriber example

Last updated on
6 February 2020

IMPORTANT: Please note that these instructions are only applicable for version 8.x-3.x of Simple FB Connect.

Drupal 8 provides a powerful and performance efficient way how modules can interact with each other. Simple FB Connect module provides events for the following purposes:

  • simple_fb_connect.scope: This allows other modules to modify the Facebook Permissions that are requested from the user.
  • simple_fb_connect.user_created: This allows other modules to react on an event when new user is created via Facebook login.
  • simple_fb_connect.user_login: This allows other modules to react on an event when user logs in via Facebook login.

This page contains an example module which demonstrates how to write your own module that subscribes to these events and how to make your own calls to Facebook API. The example module is called mymodule. If you're new to Drupal 8 module development, you might want to check the excellent article series at Sitepoint: http://www.sitepoint.com/series/how-to-build-a-drupal-8-module/

This example module also demonstrates one of the most powerful concepts of object oriented Drupal 8 development called Dependency Injection. In this example we inject class SimpleFbConnectFbFactory to our new custom module. This is explained in detail later in this article.

mymodule/mymodule.info.yml

This file informs Drupal about the existence of our module and defines the dependency with simple_fb_connect module.

name: My Module
description: 'Example module demonstrating interaction with Simple FB Connect.'
package: Custom
type: module
core: 8.x
dependencies:
  - simple_fb_connect

mymodule/mymodule.services.yml

This file informs Drupal that mymodule provides a service called mymodule.subscriber which is defined in class Drupal\mymodule\EventSubscriber\MyModuleSubscriber. This class can be found below in this article.

We also define here that the constructor of Drupal\mymodule\EventSubscriber\MyModuleSubscriber class will need simple_fb_connect.fb_factory service as an argument. This is called Dependency Injection.

Last, we add a tag event_subscriber to our new service. This tells Drupal that our class wants to subscribe to events that other modules (Simple FB Connect in this case) are triggering.

services:
  mymodule.subscriber:
    class: Drupal\mymodule\EventSubscriber\MyModuleSubscriber
    arguments:
      - '@simple_fb_connect.fb_factory'
    tags:
      - {name: event_subscriber}

Pay attention when copy-pasting the content of the services file. The indentation must be exactly like it is here (indent does NOT use tabulators but two space bars). If you get a white screen of death (PHP fatal error), please fix the indents, clear Drupal caches and try again.

mymodule/src/EventSubscriber/MyModuleSubscriber.php

<?php

// Declare the namespace for our own event subscriber.
namespace Drupal\mymodule\EventSubscriber;

// This is the interface that we are implementing.
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

// This is a generic event class that Simple FB Connect will dispatch
use Symfony\Component\EventDispatcher\GenericEvent;

// We need these classes to interact with SimpleFbConnect and FB SDK.
use Drupal\simple_fb_connect\SimpleFbConnectFbFactory;
use Facebook\Exceptions\FacebookSDKException;
use Facebook\Exceptions\FacebookResponseException;

/**
 * Event subscriptions for events dispatched by SimpleFbConnect.
 */
class MyModuleSubscriber implements EventSubscriberInterface {

  protected $facebook;
  protected $persistentDataHandler;

  /**
   * Constructor.
   *
   * We use dependency injection get SimpleFbConnectFbFactory.
   *
   * @param SimpleFbConnectFbFactory $fb_factory
   *   For getting Facebook and SimpleFbConnectPersistentDataHandler services.
   */
  public function __construct(SimpleFbConnectFbFactory $fb_factory) {
    $this->facebook = $fb_factory->getFbService();
    $this->persistentDataHandler = $fb_factory->getPersistentDataHandler();
  }

  /**
   * Returns an array of event names this subscriber wants to listen to.
   *
   * @return array
   *   The event names to listen to
   */
  static function getSubscribedEvents() {
    $events = array();
    $events['simple_fb_connect.scope'][] = array('modifyPermissionScope');
    $events['simple_fb_connect.user_created'][] = array('userCreated');
    $events['simple_fb_connect.user_login'][] = array('userLogin');
    return $events;
  }

  /**
   * Adds Facebook permissions to the scope array.
   *
   * Facebook permissions can be found at
   * https://developers.facebook.com/docs/facebook-login/permissions
   *
   * Note that most permissions require that Facebook will review your app
   * so only add those permissions that you really need. In this example we
   * add 'public_profile' which you don't have to do since this permission
   * is always granted.
   */
  public function modifyPermissionScope(GenericEvent $event) {
    $scope = $event->getArgument('scope');
    // Add the permission here. In this example we add 'public_profile'.
    $scope[] = 'public_profile';
    $event->setArgument('scope', $scope);
  }

  /**
   * Reacts to the event when new user is created via Simple FB Connect.
   *
   */
  public function userCreated(GenericEvent $event) {
    $user = $event->getArgument('account');

    // Enter your own code here. Remember to save the user with $user->save()
    // if you modify the user object.
    drupal_set_message("Debug: user created. This message is from mymodule!");
  }
  /**
   * Reacts to the event when user logs in via Simple FB Connect.
   *
   * This example adds role 'facebook' to the user if the user
   * didn't have that role already. You have to create the role manually.
   *
   * This function also demonstrates how you can get the access token for the
   * current user and how to make your own API calls using Facebook service.
   */
  public function userLogin(GenericEvent $event) {
    $user = $event->getArgument('account');

    // Enter your code here. Remember to save the user with $user->save()
    // if you modify the user object.
    drupal_set_message("Debug: user logged in. This message is from mymodule!");

    // Let's add a role 'facebook' for the user if she didn't have it already.
    // The role itself must obviously be first created manually.
    $user->addRole('facebook');
    $user->save();

    // Let's see how we can get make our own API calls to Facebook. We need
    // user's Facebook access token, which SimpleFbConnect has stored to session
    // for other modules.
    $access_token = $this->persistentDataHandler->get('access_token');

    if ($access_token) {
      try {
        $graph_node = $this->facebook->get('/me?fields=name', $access_token)->getGraphNode();
        $name = $graph_node->getField('name');
        drupal_set_message("You probably knew this: user's name on Facebook is " . $name);
      }
      catch (FacebookRequestException $ex) {
        // Add exception handling here for FacebookRequestExceptions.
      }
      catch (FacebookSDKException $ex) {
        // Add exception handling here for all other exceptions.
      }
    }
    else {
      drupal_set_message("No FB access token found for current user!");
    }
  }

}

Additional notes from the example

Dependency injection

As mentioned above, we defined in mymodule.services.yml that the constructor of MyModuleSubscriber class will take SimpleFbConnectFbFactory as an argument. To be more exact, we defined in mymodule.services.yml that the argument is a service called simple_fb_connect.fb_factory. This service is provided by Simple FB Connect module. Simple FB Connect declares in simple_fb_connect/simple_fb_connect.services.yml that the class for this service is SimpleFbConnectFbFactory.

If you need to add any other services to mymodule, you can add the service to the arguments list in mymodule.services.yml. Drupal core services can be found at https://api.drupal.org/api/drupal/core!core.services.yml/8. Just use the service name prefixed with @ character.

After the dependency injection is defined in mymodule.services.yml, we will do the other end of this in the constructor of our class. SimpleFbConnectFbFactory is a so called factory class that can be used to create instances of other classes. Here we use the factory to get instances of Facebook\Facebook and Drupal\simple_fb_connect\SimpleFbConnectPersistentDataHandler classes.

 public function __construct(SimpleFbConnectFbFactory $fb_factory) {
    $this->facebook = $fb_factory->getFbService();
    $this->persistentDataHandler = $fb_factory->getPersistentDataHandler();
  }

Reacting to the simple_fb_connect.scope event

Our static function getSubscribedEvents declares that

$events['simple_fb_connect.scope'][] = array('modifyPermissionScope');

This means that we are subscribing to event simple_fb_connect.scope with our function modifyPermissionScope. The function is very simple. We first get the current scope of Facebook Permissions, then add whatever permissions are needed and then set the modified scope back.

public function modifyPermissionScope(GenericEvent $event) {
    $scope = $event->getArgument('scope');
    // Add the permission here. In this example we add 'public_profile'.
    $scope[] = 'public_profile';
    $event->setArgument('scope', $scope);
  }

Adding a role to the user

Our response to simple_fb_connect.user_login adds a role facebook for the user as follows:

    $user = $event->getArgument('account');
    $user->addRole('facebook');
    $user->save();

We don't need to bother checking if the user already had the role since Drupal's addRole will do this automatically. It is better to add the role in the simple_fb_connect.user_login response than simple_fb_connect.user_created because Simple FB Connect supports logins for previously created Drupal user accounts as well (as long as the email address matches to the one sent by Facebook).

Making our own API call to Facebook

SimpleFbConnect module is designed so that other modules can easily make their own API calls without having to create their own module configuration forms for storing Facebook App ID and App Secret.

When working with Facebook PHP SDK version 5.x.x, the very first thing that you have to do is to get an instance of Facebook\Facebook service. SimpleFbConnectFbFactory provides a method getFbService which creates the Facebook service for you using the App ID and App Secret defined in Simple FB Connect module settings. This method is called in the constructor of MyModule.

When making API calls to Facebook, you will also need user's access token. Simple FB Connect stores the access token to user session. SimpleFbFactory can also be used to get an instance of SimpleFbConnectPersistentDataHandler service, which we do in the constructor of MyModule. You can read the access token using this service like $access_token = $this->persistentDataHandler->get('access_token').

The actual query to Facebook is provided by Facebook PHP SDK v5.x.x. In this extremely simplified example we ask from Facebook what is the name of the current user. Concrete examples for making fancier calls to Facebook API is not in the scope of this article. The Facebook official documentation contains examples for posting links, uploading photos and videos. See https://developers.facebook.com/docs/php/gettingstarted.

Help improve this page

Page status: No known problems

You can: