Blogs | Srijan

Revisiting PHP Design Pattern Concepts in Drupal 8

Written by Panshul khurana | Apr 12, 2020 7:00:00 AM

While there is a difference between knowing something and knowing the name of something; the former method gives importance to knowing the name of something and the latter one ensures that a person actually knows and understands something. 

This Richard Feynman technique states that the ultimate test of your knowledge is when you can convey it to another person.

Considering this, there are many concepts of Drupal 8 based on object-oriented PHP, which we might not have nailed it earlier, so let’s get a good refresher on the same to see its implementation as well as the use cases. This blog will focus on the following elements-

  • Singleton Design Pattern
  • Factory Design Pattern
  • Mediator Design Pattern
  • PHP Introspection and Reflection 
  • Services and Dependency Injection

A quick note before we start-

Pragmatic programming is all about teaching yourself, implementing what you've learned, and sharing the knowledge. Sharing allows you to get more feedback and useful insights. So, learn more, and share more!

Design Patterns and Their Importance

Design patterns describe the communicating objects and classes that are customized to solve a problem in a particular context.

In simple words, they are patterns or templates that can be implemented to solve a problem in different particular solutions.

Why are they required? (Design Patterns’ Superpowers)

  • Design patterns speed up the development process by providing re-usable templates for developing features and describing problems that occur time and again.
  • Only, implementation is required. The templates are well tested by developers.
  • Encapsulates big ideas in a simpler way
  • Design Patterns enforces SOLID principles - SOLID is a set of object-oriented design principles aimed at making code more maintainable and flexible. 

The categorization of GoF (Gang of Four Design Patterns)

 

  • Creational - Patterns that are used for constructing objects in a way that they can be decoupled from their implementations.
  • Structural - Patterns used to form large structures among several different objects.
  • Behavioral - As the name suggests, the behavior of the objects, relationships, and responsibilities between different objects.

They are called GoF Design Patterns because the patterns were originally written and designed by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides,

Getting the Basics Right

Let's start with :

  1. Singleton design pattern

It is the creational design pattern that doesn’t allow the system to create more than one instance of the class.


Benefits -

    1. Boosts performance by restricting the creation of multiple objects to avoid consumption of more memory  
    2. Since the class controls the process of object creation, the class has the flexibility to change how the objects are created and instantiated
    3. Ease of implementation

Let's take a look at the example which explains it's working. This example considers that you are familiar with creating a custom module and creating routing and controllers in Drupal 8-

 

    1. Over here, I have created a custom route with path /get-connection which makes a call to the method makeConnection of the Controller Class MakeConnection :
    2. Let's take a look at the method makeConnection of the Controller MakeConnection :
      Over here, you can see that the method creates the instance (object) of the class GetConnection by calling its method get_instance(). Using the object, I am calling the method getConnection() 2 times and printing the output of each variable using the debugger function provided by the Kint module.
    3. In this class, you can see that inside the get_instance() method, I am creating the object of the class (GetConnection) and return the object if it is already created/instantiated.


      Also in my method getConnection(), I am returning the object hashcode using the built-in php function spl_object_hash(). Let's take a look at the output of this code. The result shows the same hash-codes for both method calls.
      This is how it saves the object memory consumption. Also, notice that the process of object creation is controlled by the object creating class itself.

2.  Factory design pattern (the creational pattern)

The factory method is used for creating objects instead of making direct constructor calls. The factory pattern just separates the process of "object creation" from the "business logic" by creating a "factory class" whose sole responsibility is to create "objects".

Extended classes can override this method to change the class of objects that will be created.

 

This is a pretty simple example of understanding factory patterns, where multiple objects creation is involved based upon some business logic.

Let's look at the example to understand this in a better way.

    1. Look at the routing YAML which initiates the object creation based upon some logic :

      Over here, I have created a route with path /get-content/{role} that accepts a user role as the argument and calls the method getContent() of controller class GetContent.

    2. In the GetContent class, I have created an object of class ContentFactory using the constructor method. The class contains a method getContent() with a default value of role as null.

      Next, I have called the method of the class ContentFactory at line no. 22 and have passed the role that was being added to the URL as the argument.
      In the end, I have called the method getUserNameWithRole() for that specific role and returned the result in the same method.

    3. Factory class :



      Over here, the factory class contains the process of object creation based upon the role passed in the URL. This creates the user object of the class UserRoleAuthenticated and UserRoleOther, using which I am calling the method ( getUserNameWithRole() ) of the class UserRoleAuthenticated and UserRoleOther .
    4. Let's take a look at the business logic in the method getUserNameWithRole().


This method contains the business logic for user role creation and returning the user-created with its role and user name.

This is how the separation of logic and object creation works in factory patterns.

Note: The ideal factory pattern will include the dynamic object creation logic. To keep it simple here, I have hardcoded the user role in if condition to create an object of a specific class.

3.  Mediator design pattern

The mediator is a behavioral design pattern that is used to reduce the dependency between the components and act as a mediator between them by handling the communication between them.


 

One of the best examples in Drupal 8 for the Mediator design pattern is events. It allows several components to interact with each other using a mediator object.

As the class diagram shows, the Mediator has the responsibility to carry out the communication between the classes and the objects.

Using the example mentioned in https://www.drupal.org/docs/8/creating-custom-modules/subscribe-to-and-dispatch-events, I created a simple implementation in Drupal which subscribes to a specific event and fires an action based upon the event triggered.

Based upon the event-triggered ConfigEvents::Save, I am displaying the message with the name of the configuration saved and action performed on it as follows-

You can refer to the example in the link above to learn how to create Events in Drupal and create one for your own.

4.  Introspection and Reflection

Introspection in a programming language can be defined as a process through which developers can manipulate objects and classes. 

It is useful when the execution of classes or methods is unknown at design time.

In PHP, it provides the ability to examine classes, interfaces, properties, and methods. PHP offers a large number of functions that you can use to accomplish the task.

PHP Introspection Functions:

    1. get_class(): Method used to get the class name of the object
    2. class_exists(): Checks whether a class has been defined or not
    3. is_subclass_of(): Method used to check if an object is a subclass of the parent class passed as a parameter
    4. get_parent_class() : Method that returns the class name of an object’s parent class


PHP supports reflection through its Reflection API class. It provides a large number of classes and methods that can be used to achieve reflection tasks.


    1. The perfect example of PHP Introspection and Reflection is the Devel Module in Drupal.
    2. The purpose of the Devel module is to break down and display the element information that includes its class name, where it is coming from, and methods information.
    3. Similarly, the Kint module that comes with Devel installation displays what methods are included in a specific class along with their documentation and method / variable visibility.
    4. Take a look at the example snippet below which uses the Reflection Class and methods to display the object information :

 You can learn more on the same from here-

 

 

5. Services and Dependency injection

Drupal 8 introduces the concept of services and dependency injection that can be used to write reusable functions and keep them at a single place in a way that these are pluggable and replaceable as well.

Services and dependency injections are the best way to make reusable and pluggable components
They are design patterns in itself which can be categorized under the Creational Pattern, and where the object creation has a specific pattern based upon some business logic.


They are ready to use code blocks that pull in the required code functionality and can be used to avoid writing the same piece of code multiple times.

You can learn more about services and dependency injections here.

Wrapping up-

This brings us to the end of our discussion about "Revisiting the PHP programming concepts in Drupal 8". However, there are so many other important PHP concepts as well which should be revisited time and again to ensure easy implementation, whenever required. 

Never stop learning!