Note: this change record does not apply to a sub-theme that has not explicitly created its own @BootstrapAlter("theme_suggestions") implementation. If you do not see this in your sub-theme, then you can easily ignore this, unless you're curious about the pure awesomeness below!
Prior to 8.x-3.2, all theme hook suggestions were added directly in ThemeSuggestions::alter via a very ugly switch block.
This caused a tremendous amount of pain when just a few theme hook suggestion needed to be added or removed in a sub-theme. Because the entire alter method had to be overridden and logic from the base theme had to be maintained, it was nearly impossible to tell what was "custom". See the following before and after examples:
Before
namespace Drupal\THEMENAME\Plugin\Alter;
use Drupal\bootstrap\Annotation\BootstrapAlter;
use Drupal\bootstrap\Plugin\Alter\ThemeSuggestions as BootstrapThemeSuggestions;
use Drupal\bootstrap\Utility\Unicode;
use Drupal\bootstrap\Utility\Variables;
/**
* Implements hook_theme_suggestions_alter().
*
* @BootstrapAlter("theme_suggestions")
*/
class ThemeSuggestions extends BootstrapThemeSuggestions {
/**
* {@inheritdoc}
*/
public function alter(&$suggestions, &$context1 = NULL, &$hook = NULL) {
$variables = Variables::create($context1);
switch ($hook) {
case 'links':
if (Unicode::strpos($variables['theme_hook_original'], 'links__dropbutton') !== FALSE) {
// Handle dropbutton "subtypes".
// @see \Drupal\bootstrap\Plugin\Prerender\Dropbutton::preRenderElement()
if ($suggestion = Unicode::substr($variables['theme_hook_original'], 17)) {
$suggestions[] = 'bootstrap_dropdown' . $suggestion;
}
$suggestions[] = 'bootstrap_dropdown';
}
break;
case 'fieldset':
case 'details':
case 'my_custom_element':
if ($variables->element && $variables->element->getProperty('bootstrap_panel', TRUE)) {
$suggestions[] = 'bootstrap_panel';
}
break;
case 'input':
if ($variables->element && $variables->element->isButton()) {
if ($variables->element->getProperty('dropbutton')) {
$suggestions[] = 'input__button__dropdown';
}
else {
$suggestions[] = $variables->element->getProperty('split') ? 'input__button__split' : 'input__button';
}
}
elseif ($variables->element && !$variables->element->isType(['checkbox', 'hidden', 'radio', 'my_custom_element'])) {
$suggestions[] = 'input__form_control';
}
$suggestions[] = 'my_suggestion';
if (Unicode::strpos($variables['theme_hook_original'], '__search') !== FALSE) {
$suggestions[] = 'my_suggestion__search';
$suggestions[] = 'something_else';
}
break;
// Add the "user" entity theme hook suggestions.
// @see https://www.drupal.org/node/2828634
// @see https://www.drupal.org/node/2808481
// @todo Remove/refactor once core issue is resolved.
case 'user':
$this->addEntitySuggestions($suggestions, $variables, 'user');
break;
}
}
}
After
There is now dynamic method detection logic in place so individual theme hooks can be separated into their own (overridable) methods (named like alterThemeHookSuggestion, where ThemeHookSuggestion is the camel cased equivalent).
There is also two new protected overridable properties:
protected $bootstrapPanelTypes = ['details', 'fieldset']- Indicates which elements should receivebootstrap_panelsuggestions.protected $ignoreFormControlTypes = ['checkbox', 'hidden', 'radio']- Indicates which input elements should not receive theinput__form_controlsuggestion.
namespace Drupal\THEMENAME\Plugin\Alter;
use Drupal\bootstrap\Annotation\BootstrapAlter;
use Drupal\bootstrap\Plugin\Alter\ThemeSuggestions as BootstrapThemeSuggestions;
/**
* Implements hook_theme_suggestions_alter().
*
* @BootstrapAlter("theme_suggestions")
*/
class ThemeSuggestions extends BootstrapThemeSuggestions {
/**
* {@inheritdoc}
*/
protected $bootstrapPanelTypes = ['details', 'fieldset', 'my_custom_element'];
/**
* {@inheritdoc}
*/
protected $ignoreFormControlTypes = ['checkbox', 'hidden', 'radio', 'my_custom_element'];
/**
* Dynamic alter method for "input".
*/
protected function alterInput() {
parent::alterInput();
// Automatically adds my_suggestion__search.
$this->addSuggestion('my_suggestion');
}
/**
* Dynamic alter method for "input__search".
*/
protected function alterInputSearch() {
$this->addSuggestion('something_else');
}
}
Deprecated methods
The following two methods have been deprecated (essentially renamed and unnecessary arguments removed). However, for BC they have remained in case any code relies on them. They should be updated to their new counterparts ASAP as they will eventually be removed.
ThemeSuggestions::addEntitySuggestions- UseThemeSuggestions::addSuggestionsForEntityinstead.ThemeSuggestions::getEntity- UseThemeSuggestions::getEntityObjectinstead.