Object-Oriented Programming (OOP)
Goal: Demonstrate ability to write code using core and object-oriented PHP.
You need a basic understanding of OOP concepts prior to attempting the certification exam. There are many tutorials available to learn those. This documentation covers some of the essential Drupal-related topics that fall under the scope of the Drupal certification exam.
- Getting Started - Background & Prerequisites on Drupal.org - Updated Jun 2022 covers for a list of resources for learning OOP.
- For a general overview of PHP best practices, read through PHP The Right Way
- Brush up on your OOP knowledge by reading the official PHP documentation on Classes and Objects
- Object-oriented programming on Wikipedia
- Object-Oriented PHP for Beginners at envato tuts+ - Feb 2022
- Object Oriented Concepts at tutorialspoint.com
- Object Oriented Programming in PHP at youtube.com - 2014
- Programming Foundations: Object-Oriented Design at LinkedIn Learning (paid course)
- Learn Object-oriented PHP
- Object Oriented Programming Course 1 on SymfonyCasts(paid)
- PHP OOP in Full Effect - 2008
OOP File Structure
All classes, interfaces and traits should live in their own files, where the name of the file matches the name of the class. Additionally, the files should be located at src/{objecttype}/{classname}.php
For example:
<?php
class HelloController extends ControllerBase {
// ...
}
This class should be located at src/Controller/HelloController.php
inside your module.
Factory Pattern
The Factory Pattern is a common OOP design pattern that creates an object of the class you want to use. By using factories instead of directly instantiating objects, you can reduce complexity around the actual creation of objects, especially when multiple steps are required.
Example Factory
Consider this example from PHP The Right Way - Design Patterns:
<?php
class Automobile
{
private $vehicleMake;
private $vehicleModel;
public function __construct($make, $model)
{
$this->vehicleMake = $make;
$this->vehicleModel = $model;
}
public function getMakeAndModel()
{
return $this->vehicleMake . ' ' . $this->vehicleModel;
}
}
<?php
class AutomobileFactory
{
public static function create($make, $model)
{
return new Automobile($make, $model);
}
}
<?php
// have the factory create the Automobile object
$veyron = AutomobileFactory::create('Bugatti', 'Veyron');
print_r($veyron->getMakeAndModel()); // outputs "Bugatti Veyron"
The AutomobileFactory
class will create a new automobile object for you as needed.
Drupal Config Factory
Drupal makes use of factories in several places. One such example is \Drupal::configFactory()
in Drupal.php
:
<?php
public static function configFactory() {
return static::getContainer()->get('config.factory');
}
Then you can just use \Drupal::configFactory()
to retrieve a new config object.
For example, have a look at system_update_8200()
:
<?php
function system_update_8200(&$sandbox) {
$config_factory = \Drupal::configFactory();
if (!array_key_exists('config_names', $sandbox)) {
$sandbox['config_names'] = $config_factory->listAll();
$sandbox['max'] = count($sandbox['config_names']);
}
// Get a list of 50 to work on at a time.
$config_names_to_process = array_slice($sandbox['config_names'], 0, 50);
// Preload in a single query.
$config_factory->loadMultiple($config_names_to_process);
foreach ($config_names_to_process as $config_name) {
$config_factory->getEditable($config_name)->save();
}
// Update the list of names to process.
$sandbox['config_names'] = array_diff($sandbox['config_names'], $config_names_to_process);
$sandbox['#finished'] = empty($sandbox['config_names']) ? 1 : ($sandbox['max'] - count($sandbox['config_names'])) / $sandbox['max'];
}
Other Drupal Factories
To research into specific instances, check out the Drupal core factories in web/core/lib.Drupal/Component
and web/core/lib/Drupal/Core
for many examples of factories in use. e.g. web/core/lib/Drupal/Component/FileCache/FileCacheFactory.php
, web/core/lib/Drupal/Core/Cache/CacheFactory.php
and web/core/lib/Drupal/Core/Image/ImageFactory.php
.
PHP Namespaces
Drupal projects should be properly namespaced to prevent potential overlap with other modules. This is done through PHP namespacing. The recommended namespace for any given module is namespace Drupal\{modulename}
.
For example, the namespace for the block
module is:
namespace Drupal\block;
For example, if two modules had a poorly named ModuleController.php
that contained a ModuleController
class with a description()
method, by using namespaces we can distinguish between those two methods:
\Drupal\module1\Controller\ModuleController::description()
\Drupal\module2\Controller\ModuleController::description()
As mentioned in Drupal coding standards - updated Oct 2023 you need to include use
statements however, you can use class aliasing
to distinguish between these two classes:
use \Drupal\module1\Controller\ModuleController as Module1ModuleController;
use \Drupal\module2\Controller\ModuleController as Module2ModuleController;
Module1ModuleController::description();
Module2ModuleController::description();
Namespace Resolution
From PSR-4 namespaces and autoloading in Drupal 8 - updated Dec 2022:
Component | Base namespace | Base directory | Contains |
---|---|---|---|
Drupal core | Drupal\Component\ | core/lib/Drupal/Component/ | Components that are reusable outside of Drupal. |
-- | Drupal\Core\ | core/lib/Drupal/Core/ | Components that are specific to Drupal. |
-- | Drupal\Tests\ | core/tests/Drupal/Tests/ | PHPUnit tests of core components. |
Modules | Drupal\$modulename\ | modules/$modulename/src/ | Main integration files. |
-- | Drupal\$modulename\Tests\ | modules/$modulename/src/Tests/ | Simpletest tests of the module. |
-- | Drupal\Tests\$modulename\ | modules/$modulename/tests/src/ | PHPUnit tests of the module. |
Views and Controllers
Symfony (version 6), the underlying PHP framework Drupal 8 is built upon, is not officially a MVC
(Model/View/Controller) framework, but does make heavy use of views and controllers. Drupal is an extension of that. Views are handled almost exclusively through Twig and controllers are managed via the Routing System.
By keeping the controller and views separate, it allows for a better separation of concerns. This helps keep a system flexible moving forward.
Resources
- Getting Started - Background & Prerequisites on Drupal.org- updated June 2022
- PSR-4 namespaces and autoloading in Drupal 8 on Drupal.org - Updated Dec 2022
- phptherightway.com - Design Patterns
Questions
What is Object-Oriented Programming (OOP) in Drupal, and how does it help developers manage complex functionality in custom modules?
Object-Oriented Programming (OOP) in Drupal is a programming paradigm that organizes code into objects and classes, which allows for more structured, maintainable, and reusable code. OOP in Drupal enables developers to manage complex functionality in custom modules, and By using OOP principles, developers can create modular and reusable code for custom modules, improving maintainability, scalability, and the ability to extend Drupal’s functionality.
What is OOP file structure, factory pattern and Namespaces
All classes, interfaces and traits should live in their own files, where the name of the file matches the name of the class. Additionally, the files should be located at src/{objecttype}/{classname}.php
. The Factory Pattern is a common OOP design pattern that creates an object of the class you want to use. By using factories instead of directly instantiating objects, you can reduce complexity around the actual creation of objects, especially when multiple steps are required. A namespace is a way to encapsulate and organize code in OOP to prevent naming conflicts between classes, functions, or constants that may have the same name, Drupal projects should be properly namespaced to prevent potential overlap with other modules. This is done through PHP namespacing. The recommended namespace for any given module is namespace Drupal\{modulename}
.