Skip to content

Latest commit

 

History

History
206 lines (163 loc) · 9.01 KB

File metadata and controls

206 lines (163 loc) · 9.01 KB

Object-Oriented Programming (OOP)

It is recommended you have at least some basic understanding of OOP concepts prior to attempting the certification exam. There are many tutorials available to learn those. This documentation will only cover some of the essential Drupal-related topics that fall under the scope of the D8 certification exam. See Getting Started - Background & Prerequisites (Drupal 8) for a list of resources for learning OOP.

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():

<?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

If you'd like to do more research into specific instances, as of Drupal 8.3.1, core provides the following factories:

  • core/lib/Drupal/Component/FileCache/FileCacheFactory.php
  • core/lib/Drupal/Component/Plugin/Factory/DefaultFactory.php
  • core/lib/Drupal/Component/Plugin/Factory/ReflectionFactory.php
  • core/lib/Drupal/Core/Access/AccessArgumentsResolverFactory.php
  • core/lib/Drupal/Core/AppRootFactory.php
  • core/lib/Drupal/Core/Cache/ApcuBackendFactory.php
  • core/lib/Drupal/Core/Cache/CacheFactory.php
  • core/lib/Drupal/Core/Cache/ChainedFastBackendFactory.php
  • core/lib/Drupal/Core/Cache/DatabaseBackendFactory.php
  • core/lib/Drupal/Core/Cache/MemoryBackendFactory.php
  • core/lib/Drupal/Core/Cache/NullBackendFactory.php
  • core/lib/Drupal/Core/Cache/PhpBackendFactory.php
  • core/lib/Drupal/Core/Config/BootstrapConfigStorageFactory.php
  • core/lib/Drupal/Core/Config/ConfigFactory.php
  • core/lib/Drupal/Core/Config/Entity/Query/QueryFactory.php
  • core/lib/Drupal/Core/Config/FileStorageFactory.php
  • core/lib/Drupal/Core/Entity/KeyValueStore/Query/QueryFactory.php
  • core/lib/Drupal/Core/Entity/Query/Null/QueryFactory.php
  • core/lib/Drupal/Core/Entity/Query/QueryFactory.php
  • core/lib/Drupal/Core/Entity/Query/Sql/pgsql/QueryFactory.php
  • core/lib/Drupal/Core/Entity/Query/Sql/QueryFactory.php
  • core/lib/Drupal/Core/Http/ClientFactory.php
  • core/lib/Drupal/Core/Http/TrustedHostsRequestFactory.php
  • core/lib/Drupal/Core/Image/ImageFactory.php
  • core/lib/Drupal/Core/KeyValueStore/KeyValueDatabaseExpirableFactory.php
  • core/lib/Drupal/Core/KeyValueStore/KeyValueDatabaseFactory.php
  • core/lib/Drupal/Core/KeyValueStore/KeyValueExpirableFactory.php
  • core/lib/Drupal/Core/KeyValueStore/KeyValueFactory.php
  • core/lib/Drupal/Core/KeyValueStore/KeyValueMemoryFactory.php
  • core/lib/Drupal/Core/KeyValueStore/KeyValueNullExpirableFactory.php
  • core/lib/Drupal/Core/Logger/LoggerChannelFactory.php
  • core/lib/Drupal/Core/PhpStorage/PhpStorageFactory.php
  • core/lib/Drupal/Core/Plugin/Factory/ContainerFactory.php
  • core/lib/Drupal/Core/Plugin/PluginFormFactory.php
  • core/lib/Drupal/Core/Queue/QueueDatabaseFactory.php
  • core/lib/Drupal/Core/Queue/QueueFactory.php
  • core/lib/Drupal/Core/SitePathFactory.php
  • core/lib/Drupal/Core/TypedData/Validation/ExecutionContextFactory.php
  • core/lib/Drupal/Core/Update/UpdateRegistryFactory.php
  • core/lib/Drupal/Core/Validation/ConstraintValidatorFactory.php
  • core/modules/update/src/UpdateRootFactory.php
  • core/modules/user/src/PrivateTempStoreFactory.php
  • core/modules/user/src/SharedTempStoreFactory.php
  • core/modules/views/src/ViewExecutableFactory.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 Drupal coding standards require you to include use statements, 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:

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

Symfony2, 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 8 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.

Additional Resources