a12s-1.0.0-beta7/modules/theme_builder/src/Block/SettingsAlterBase.php
modules/theme_builder/src/Block/SettingsAlterBase.php
<?php
declare(strict_types=1);
namespace Drupal\a12s_theme_builder\Block;
use Drupal\block\BlockInterface;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\SubformState;
use Drupal\Core\Form\SubformStateInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Utility\Error;
/**
* The base class for altering block configuration.
*/
abstract class SettingsAlterBase implements SettingsAlterInterface {
use DependencySerializationTrait;
use StringTranslationTrait;
/**
* List the classes altering block configuration.
*
* @todo Create a plugin manager? For now, it doesn't seems relevant.
*/
const BLOCK_SETTINGS_ALTER_CLASSES = [
GlobalAttributes::class,
MenuBlockAttributes::class,
LanguageSwitcher::class,
];
/**
* {@inheritdoc}
*/
public static function trustedCallbacks(): array {
return ['preRenderBlock'];
}
/**
* {@inheritdoc}
*/
public static function getInstance(): ?static {
try {
/** @noinspection PhpIncompatibleReturnTypeInspection */
return \Drupal::classResolver(static::class);
}
catch (\InvalidArgumentException $e) {
Error::logException(\Drupal::logger('a12s_theme_builder'), $e);
}
return NULL;
}
/**
* {@inheritdoc}
*/
public static function alterForm(BlockInterface $entity, array &$form, FormStateInterface $formState): void {
$instance = self::getInstance();
if ($instance && $instance->applies($entity)) {
$key = $instance->configurationKey();
$settings = $entity->getThirdPartySetting('a12s_theme_builder', $key, []);
$form['a12s_theme_builder']['#tree'] = TRUE;
$form['a12s_theme_builder']['#weight'] = 0;
$form['a12s_theme_builder'][$key] = [
'#parents' => ['a12s_theme_builder', $key],
];
$subformState = SubformState::createForSubform($form['a12s_theme_builder'][$key], $form, $formState);
$instance->buildForm($form['a12s_theme_builder'][$key], $subformState, $settings);
$form['actions']['submit']['#submit'][] = static::class . '::submit';
$form['#submit'][] = static::class . '::submit';
}
}
/**
* {@inheritdoc}
*/
public static function submit(array $form, FormStateInterface $formState): void {
if ($instance = self::getInstance()) {
$key = $instance->configurationKey();
$subformState = SubformState::createForSubform($form['a12s_theme_builder'][$key], $form, $formState);
$instance->submitForm($form['a12s_theme_builder'][$key], $subformState);
}
}
/**
* Merge the given values with the default ones.
*
* @param array $values
*/
protected function mergeWithDefault(array &$values): void {
$values = NestedArray::mergeDeep($this->defaultValues(), $values);
}
/**
* Clean recursively the given values by comparing with default values.
*
* All values identical to default values are removed, all empty values are
* also removed.
*
* @param array $values
*
* @return array
*/
protected function cleanValues(array $values): array {
// Remove default values.
$cleanValues = function(array $values, array $defaults) use (&$cleanValues) {
$values = array_intersect_key($values, $defaults);
foreach ($values as $key => $item) {
if (is_array($item)) {
// This should never happen...
if (!is_array($defaults[$key])) {
\Drupal::logger('a12s_theme_builder')->error('The value %name has an invalid array structure for the key %key.', [
'%name' => $values['name'],
'%key' => $key,
]);
throw new \InvalidArgumentException();
}
else {
$values[$key] = $cleanValues($item, $defaults[$key]);
if (empty($values[$key])) {
unset($values[$key]);
}
}
}
else {
$item = match (gettype($defaults[$key])) {
'integer' => (int) $item,
'boolean' => (bool) $item,
default => (string) $item,
};
if ($item === $defaults[$key]) {
unset($values[$key]);
}
}
}
return $values;
};
try {
return $cleanValues($values, $this->defaultValues());
}
catch (\InvalidArgumentException $e) {
return [];
}
}
/**
* Save the third-party settings.
*
* @param array $values
* The values to store.
* @param \Drupal\Core\Form\FormStateInterface $formState
* The subform state.
*/
protected function saveSettings(array $values, FormStateInterface $formState): void {
$key = $this->configurationKey();
/** @var \Drupal\block\Entity\Block $block */
$block = $formState->getFormObject()->getEntity();
if (empty($values)) {
$block->unsetThirdPartySetting('a12s_theme_builder', $key);
}
else {
$block->setThirdPartySetting('a12s_theme_builder', $key, $values);
}
$block->save();
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, SubformStateInterface $formState): void {
$values = $formState->getValues();
$values = $this->cleanValues($values);
$this->saveSettings($values, $formState);
}
/**
* Whether this instance uses a pre-render callback or not.
*
* @return bool
*/
protected function usePreRenderCallback(): bool {
return FALSE;
}
/**
* {@inheritdoc}
*/
public function preRenderBlock($build): array {
return $build;
}
/**
* {@inheritdoc}
*/
public static function alterView(BlockInterface $block, array &$build): void {
$instance = static::getInstance();
if ($instance && $instance->applies($block)) {
$key = $instance->configurationKey();
$settings = $block->getThirdPartySetting('a12s_theme_builder', $key, []);
if ($settings) {
$build['#a12s_theme_builder'][$key] = $settings;
if ($instance->usePreRenderCallback()) {
$build['#pre_render'][] = [$instance, 'preRenderBlock'];
}
}
}
}
}
