toolshed-8.x-1.x-dev/src/Strategy/ThemeAwareStrategyManager.php
src/Strategy/ThemeAwareStrategyManager.php
<?php
namespace Drupal\toolshed\Strategy;
/**
* The base strategy manager for theme aware strategy types.
*
* The theme aware manager can change the strategy definitions based on the
* requested theme context. Normally this will be the "default" theme or the
* currently active theme, can be any theme passed in the $context parameter
* of the this::getDefinitions(), this::getDefinition() or this::hasDefinition()
* methods.
*
* The theme is set by the "theme" key of the $context parameter, as follows:
* @code
* $context = ['theme' => <theme_name>];
* @endcode
*
* Module definitions are always available, but theme provided definitions are
* only available for the theme context that defines the strategy. Theme
* strategies can override module defined strategies of the same name when that
* theme context is active.
*/
abstract class ThemeAwareStrategyManager extends StrategyManager {
use ThemeStrategyDiscoveryTrait;
/**
* Currently discovered theme strategy definitions by theme name.
*
* Definitions are grouped by the providing theme and has the following
* structure:
*
* $this->themeDefinitions = [
* "<theme_name>" => [
* "<strategy_id>" => StrategyDefinition,
* "<strategy_id>" => StrategyDefinition,
* ...
* ],
* ];
*
* This is different from how module definitions are stored because the same
* strategy identifier can be used by different themes or other modules. The
* theme definitions also need to be grouped by theme, so they can be easily
* found and applied for the active, or requested theme contexts.
*
* @var \Drupal\toolshed\Strategy\StrategyDefinitionInterface[][]|null
*/
protected ?array $themeDefinitions;
/**
* Definitions grouped by theme name.
*
* These are definitions after the theme overrides have been applied, and can
* be used for the theme context they are grouped for.
*
* @var \Drupal\toolshed\Strategy\StrategyDefinitionInterface[][]
*/
protected array $definitionsByTheme = [];
/**
* {@inheritdoc}
*/
public function clearCachedDefinitions(): void {
parent::clearCachedDefinitions();
if ($this->cacheBackend) {
$cid = $this->getCacheId() . ':theme';
$this->cacheBackend->delete($cid);
}
$this->definitionsByTheme = [];
}
/**
* {@inheritdoc}
*/
public function getDefinitions(array $context = []): array {
$themeId = $context['theme'] ?? $this->getDefaultTheme();
// Have we generated the definitions available for this theme yet?
if (!isset($this->definitionsByTheme[$themeId])) {
$themeDefinitions = $this->getThemeDefinitions();
// Theme definitions also need to include definitions provided by their
// base themes as well. We apply the definitions by current theme first
// and earilest ancestor last.
$enabledThemes = $this->themeHandler->listInfo();
$themes = array_keys($this->themeHandler->getBaseThemes($enabledThemes, $themeId));
$themes[] = $themeId;
$this->definitionsByTheme[$themeId] = [];
foreach (array_reverse($themes) as $themeName) {
if (!empty($themeDefinitions[$themeName])) {
$this->definitionsByTheme[$themeId] += $themeDefinitions[$themeName];
}
}
// Apply the module defined definitions last. This allows the theme
// defined strategies to override the module definitions.
$this->definitionsByTheme[$themeId] += parent::getDefinitions($context);
}
return $this->definitionsByTheme[$themeId];
}
/**
* {@inheritdoc}
*/
public function getInstance(string $id, array $context = []): StrategyInterface {
$definition = $this->getDefinition($id, $context);
$key = $id;
if ($definition->getProviderType() === 'theme') {
// It is possible due to theme switching that the a strategy with the
// same ID is available for a module or a different theme, so we can't
// rely on the ID alone for theme strategies.
$key = $definition->getProvider() . ':' . $key;
}
if (!isset($this->instances[$key])) {
$instance = $this->getFactory()->create($id, $definition);
$this->initInstance($id, $instance);
$this->instances[$key] = $instance;
}
return $this->instances[$key];
}
/**
* Get definitions provided by themes, grouped by their providers.
*
* The definitions are returned in the same format as described by the
* ThemeStrategyDiscoveryTrait::findThemeDefinitions().
*
* @return \Drupal\toolshed\Strategy\StrategyDefinitionInterface[][]
* Get theme provided strategy definitions grouped by the providing theme.
*
* @see \Drupal\toolshed\Strategy\ThemeStrategyDiscoveryTrait::findThemeDefinitions()
*/
protected function getThemeDefinitions(): array {
if (!isset($this->themeDefinitions)) {
$cid = $this->getCacheId() . ':theme';
if ($cached = $this->cacheGet($cid)) {
$this->themeDefinitions = $cached->data;
}
else {
$this->themeDefinitions = $this->findThemeDefinitions();
$this->cacheSet($cid, $this->themeDefinitions);
}
}
return $this->themeDefinitions;
}
}
