rdf_sync-1.x-dev/src/Form/Alter/BundleEntityFormAlter.php
src/Form/Alter/BundleEntityFormAlter.php
<?php
declare(strict_types=1);
namespace Drupal\rdf_sync\Form\Alter;
use Drupal\Component\Plugin\PluginManagerInterface;
use Drupal\Core\Config\Entity\ConfigEntityBundleBase;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldTypePluginManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\text\Plugin\Field\FieldType\TextItemBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Bundle entity form alter.
*/
class BundleEntityFormAlter extends AbstractFormAlter implements ContainerInjectionInterface {
use DependencySerializationTrait;
/**
* Constructs BundleEntityFormAlter instance.
*
* @param \Drupal\Component\Plugin\PluginManagerInterface $uriGeneratorPluginManager
* Plugin manager.
* @param \Drupal\Core\Entity\EntityFieldManagerInterface $entityFieldManager
* Entity field manager.
* @param \Drupal\Core\Field\FieldTypePluginManagerInterface $fieldTypePluginManager
* Field type manager.
*/
public function __construct(
protected PluginManagerInterface $uriGeneratorPluginManager,
protected EntityFieldManagerInterface $entityFieldManager,
protected FieldTypePluginManagerInterface $fieldTypePluginManager,
) {
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container): self {
return new static(
$container->get('plugin.manager.rdf_sync.uri_generator'),
$container->get('entity_field.manager'),
$container->get('plugin.manager.field.field_type'),
);
}
/**
* {@inheritdoc}
*/
public function alter(array &$form, FormStateInterface $formState): void {
/** @var \Drupal\Core\Entity\BundleEntityFormBase $bundleEntity */
$bundleEntity = $formState->getFormObject();
/** @var \Drupal\Core\Config\Entity\ConfigEntityBundleBase $configEntity */
$configEntity = $bundleEntity->getEntity();
$settings = $configEntity->getThirdPartySettings('rdf_sync');
$form['rdf_sync'] = [
'#type' => 'details',
'#group' => 'additional_settings',
'#title' => $this->t('RDF sync'),
'#open' => TRUE,
'#weight' => 10,
'#tree' => TRUE,
];
$hideStates = [
'visible' => [':input[name="rdf_sync[enabled]"]' => ['checked' => TRUE]],
];
$form['rdf_sync']['enabled'] = [
'#type' => 'checkbox',
'#default_value' => !empty($settings['type']),
'#title' => $this->t('Enable RDF sync'),
];
$form['rdf_sync']['type'] = [
'#type' => 'url',
'#title' => $this->t('RDF type mapping'),
'#default_value' => $settings['type'] ?? '',
'#states' => $hideStates +
['required' => $hideStates['visible']],
];
$form['rdf_sync']['uri_field_name'] = [
'#type' => 'textfield',
'#title' => $this->t('URI field name'),
'#description' => $this->t('If omitted, <code>uri</code> will be used.'),
'#default_value' => $settings['uri_field_name'] ?? '',
'#states' => $hideStates,
];
$form['rdf_sync']['uri_plugin'] = [
'#type' => 'select',
'#title' => $this->t('URI generator plugin'),
'#default_value' => $settings['uri_plugin'] ?? '',
'#empty_value' => '',
'#options' => $this->getListOfPluginOptions(),
'#states' => $hideStates,
];
$form['rdf_sync']['base_fields_mapping'] = [
'#type' => 'details',
'#title' => $this->t('Field mapping'),
'#description' => $this->t('Please map the bundle base fields to their corresponding RDF properties.'),
'#states' => $hideStates,
];
$baseFields = $this->entityFieldManager->getBaseFieldDefinitions($configEntity->getEntityType()
->getBundleOf());
foreach ($baseFields as $baseField) {
$fieldColumns = $this->getColumnFields($baseField);
if (empty($fieldColumns)) {
continue;
}
$form['rdf_sync']['base_fields_mapping'][$baseField->getName()] = [
'#type' => 'details',
'#title' => $baseField->getLabel(),
'#description' => $baseField->getDescription(),
];
foreach ($fieldColumns as $column) {
$fieldSettings = $settings['fields'][$baseField->getName()][$column] ?? [];
$form['rdf_sync']['base_fields_mapping'][$baseField->getName()][$column] = [
'#type' => 'details',
'#title' => $column,
'#open' => TRUE,
];
$form['rdf_sync']['base_fields_mapping'][$baseField->getName()][$column]['predicate'] = $this->getPredicateElement($fieldSettings);
$form['rdf_sync']['base_fields_mapping'][$baseField->getName()][$column]['type'] = $this->getTypeElement($fieldSettings);
}
}
$form['rdf_sync']['namespaces'] = [
'#type' => 'textarea',
'#title' => $this->t('Namespaces'),
'#description' => [
[
'#markup' => $this->t("Add one namespace per line with the prefix and its URI separated by a pipe (|). For instance:"),
],
[
'#markup' => "my-ns|http://example.com/my-namespace#\nother|http://example.com/other/",
'#prefix' => '<pre><code>',
'#suffix' => '</code></pre>',
],
],
'#default_value' => $this->packNamespaces($settings['namespaces'] ?? []),
'#states' => $hideStates,
];
// Since this entityFormEntityBuild and also the helper methods it uses are
// heavily building on services, it seems it isn't a very bad idea to add
// it as an non-static callable. But we use DependencySerializationTrait
// because the form *must* stay serializable.
$form['#entity_builders'][] = [$this, 'entityFormEntityBuild'];
}
/**
* Gets list of plugin options.
*
* @return array
* List of plugins.
*/
protected function getListOfPluginOptions(): array {
return array_reduce(
$this->uriGeneratorPluginManager->getDefinitions(),
function ($result, $item) {
if ($item['id'] === 'default') {
return $result;
}
$result[$item['id']] = $item['label'];
return $result;
},
[],
);
}
/**
* Gets field columns.
*
* @param \Drupal\Core\Field\FieldDefinitionInterface $fieldDefinition
* Field definition.
*
* @return array
* Columns.
*/
protected function getColumnFields(FieldDefinitionInterface $fieldDefinition): array {
if (empty($fieldDefinition->getColumns())) {
return [];
}
$columns = array_keys($fieldDefinition->getColumns());
if (is_subclass_of($this->fieldTypePluginManager->getPluginClass($fieldDefinition->getType()), TextItemBase::class)) {
$columns[] = 'processed';
}
return $columns;
}
/**
* Entity builder method.
*
* @param string $entityType
* The type of the entity.
* @param \Drupal\Core\Config\Entity\ConfigEntityBundleBase $fieldConfig
* The field config.
* @param array $form
* A nested array form elements comprising the form.
* @param \Drupal\Core\Form\FormStateInterface $formState
* The current state of the form.
*/
public function entityFormEntityBuild(string $entityType, ConfigEntityBundleBase $fieldConfig, array $form, FormStateInterface $formState): void {
$enabled = $formState->getValue(['rdf_sync', 'enabled']);
// Remove.
$fieldConfig->unsetThirdPartySetting('rdf_sync', 'type');
$fieldConfig->unsetThirdPartySetting('rdf_sync', 'uri_field_name');
$fieldConfig->unsetThirdPartySetting('rdf_sync', 'uri_plugin');
$fieldConfig->unsetThirdPartySetting('rdf_sync', 'fields');
$fieldConfig->unsetThirdPartySetting('rdf_sync', 'namespaces');
// Disabled so nothing to set.
if (!$enabled) {
return;
}
$mappings = ['type', 'uri_field_name'];
foreach ($mappings as $mapping) {
$mappingValue = $formState->getValue(['rdf_sync', $mapping]);
if ($mappingValue) {
$fieldConfig->setThirdPartySetting('rdf_sync', $mapping, $mappingValue);
}
}
$uri_plugin = $formState->getValue(['rdf_sync', 'uri_plugin']) ?: NULL;
$fieldConfig->setThirdPartySetting('rdf_sync', 'uri_plugin', $uri_plugin);
$fields = [];
$baseFields = $this->entityFieldManager->getBaseFieldDefinitions($fieldConfig->getEntityType()
->getBundleOf());
foreach ($baseFields as $baseField) {
foreach ($this->getColumnFields($baseField) as $column) {
$data = $formState->getValue([
'rdf_sync',
'base_fields_mapping',
$baseField->getName(),
$column,
]);
if (!empty($data['predicate'])) {
$data['type'] = $data['type'] ?: NULL;
$fields[$baseField->getName()][$column] = $data;
}
}
}
$fieldConfig->setThirdPartySetting('rdf_sync', 'fields', $fields);
$namespaces = $this->unpackNamespaces($formState->getValue(['rdf_sync', 'namespaces']));
if ($namespaces) {
$fieldConfig->setThirdPartySetting('rdf_sync', 'namespaces', $namespaces);
}
}
/**
* Packs the namespaces array as a blob.
*
* @param array<string, string> $namespaces
* Associative array of namespace URIs, keyed by namespace prefix.
*
* @return string
* Namespaces packed as blob.
*/
protected function packNamespaces(array $namespaces): string {
return implode("\n", array_map(
function ($prefix) use ($namespaces): string {
return "$prefix|$namespaces[$prefix]";
},
array_keys($namespaces)
));
}
/**
* Unpacks the namespaces blob as array.
*
* @param string $namespaces
* Namespaces packed as blob.
*
* @return array<string, string>
* Associative array of namespace URIs, keyed by namespace prefix.
*/
protected function unpackNamespaces(string $namespaces): array {
// Replace Windows or legacy Apple line endings.
$namespaces = str_replace("\r", "\n", $namespaces);
// Explode and remove white spaces.
$namespaces = array_map('trim', explode("\n", $namespaces));
// Remove empty lines.
$namespaces = array_filter($namespaces);
$ns = [];
foreach ($namespaces as $namespace) {
if (!str_contains($namespace, '|')) {
// Ignore malformed.
continue;
}
[$prefix, $uri] = explode('|', $namespace, 2);
$ns[$prefix] = $uri;
}
return $ns;
}
}
