5 use Drupal\Core\Cache\CacheBackendInterface;
6 use Drupal\Core\Config\ConfigFactoryInterface;
7 use Drupal\Core\Entity\EntityTypeManagerInterface;
8 use Drupal\Core\Extension\ModuleHandlerInterface;
9 use Drupal\Core\Field\BaseFieldDefinition;
10 use Drupal\Core\Field\FieldDefinitionInterface;
11 use Drupal\Core\Field\FieldStorageDefinitionInterface;
12 use Drupal\Core\Plugin\DefaultPluginManager;
13 use Drupal\field\Entity\FieldStorageConfig;
16 * Plugin type manager for field diff builders.
18 * @ingroup field_diff_builder
20 * Plugin directory Plugin/diff/Field.
22 * @see \Drupal\diff\Annotation\FieldDiffBuilder
23 * @see \Drupal\diff\FieldDiffBuilderInterface
26 class DiffBuilderManager extends DefaultPluginManager {
29 * The entity type manager.
31 * @var \Drupal\Core\Entity\EntityTypeManagerInterface
33 protected $entityTypeManager;
36 * Wrapper object for simple configuration from diff.settings.yml.
38 * @var \Drupal\Core\Config\ImmutableConfig
43 * Wrapper object for simple configuration from diff.plugins.yml.
45 * @var \Drupal\Core\Config\ImmutableConfig
47 protected $pluginsConfig;
50 * Static cache of field definitions per bundle and entity type.
54 protected $pluginDefinitions;
57 * Constructs a DiffBuilderManager object.
59 * @param \Traversable $namespaces
60 * An object that implements \Traversable which contains the root paths
61 * keyed by the corresponding namespace to look for plugin implementations.
62 * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
63 * Cache backend instance to use.
64 * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
66 * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
67 * The entity type manager.
68 * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
71 public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, EntityTypeManagerInterface $entity_type_manager, ConfigFactoryInterface $config_factory) {
72 parent::__construct('Plugin/diff/Field', $namespaces, $module_handler, '\Drupal\diff\FieldDiffBuilderInterface', 'Drupal\diff\Annotation\FieldDiffBuilder');
74 $this->setCacheBackend($cache_backend, 'field_diff_builder_plugins');
75 $this->alterInfo('field_diff_builder_info');
76 $this->entityTypeManager = $entity_type_manager;
77 $this->config = $config_factory->get('diff.settings');
78 $this->pluginsConfig = $config_factory->get('diff.plugins');
83 * Define whether a field should be displayed or not as a diff change.
85 * To define if a field should be displayed in the diff comparison, check if
86 * it is revisionable and is not the bundle or revision field of the entity.
88 * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $field_storage_definition
89 * The field storage definition.
92 * TRUE if the field will be displayed.
94 public function showDiff(FieldStorageDefinitionInterface $field_storage_definition) {
96 // Check if the field is revisionable.
97 if ($field_storage_definition->isRevisionable()) {
99 // Do not display the field, if it is the bundle or revision field of the
101 $entity_type = $this->entityTypeManager->getDefinition($field_storage_definition->getTargetEntityTypeId());
102 // @todo Don't hard code fields after: https://www.drupal.org/node/2248983
103 if (in_array($field_storage_definition->getName(), [
106 $entity_type->getKey('bundle'),
107 $entity_type->getKey('revision'),
116 * Creates a plugin instance for a field definition.
118 * Creates the instance based on the selected plugin for the field.
120 * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
121 * The field definition.
123 * @return \Drupal\diff\FieldDiffBuilderInterface|null
124 * The plugin instance, NULL if none.
126 public function createInstanceForFieldDefinition(FieldDefinitionInterface $field_definition) {
127 $selected_plugin = $this->getSelectedPluginForFieldStorageDefinition($field_definition->getFieldStorageDefinition());
128 if ($selected_plugin['type'] != 'hidden') {
129 return $this->createInstance($selected_plugin['type'], $selected_plugin['settings']);
135 * Selects a default plugin for a field storage definition.
137 * Checks if a plugin has been already selected for the field, otherwise
138 * chooses one between the plugins that can be applied to the field.
140 * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $field_definition
141 * The field storage definition.
144 * An array with the key type (which contains the plugin ID) and settings.
145 * The special type hidden indicates that the field should not be shown.
147 public function getSelectedPluginForFieldStorageDefinition(FieldStorageDefinitionInterface $field_definition) {
148 $plugin_options = $this->getApplicablePluginOptions($field_definition);
149 $field_key = $field_definition->getTargetEntityTypeId() . '.' . $field_definition->getName();
151 // Start with the stored configuration, this returns NULL if there is none.
152 $selected_plugin = $this->pluginsConfig->get('fields.' . $field_key);
154 // If there is configuration and it is a valid type or exlplicitly set to
155 // hidden, then use that, otherwise try to find a suitable default plugin.
156 if ($selected_plugin && (in_array($selected_plugin['type'], array_keys($plugin_options)) || $selected_plugin['type'] == 'hidden')) {
157 return $selected_plugin + ['settings' => []];
159 elseif (!empty($plugin_options) && $this->isFieldStorageDefinitionDisplayed($field_definition)) {
160 return ['type' => key($plugin_options), 'settings' => []];
163 return ['type' => 'hidden', 'settings' => []];
168 * Determines if the field is displayed.
170 * Determines if a field should be displayed when comparing revisions based on
171 * the entity view display if there is no plugin selected for the field.
173 * @param FieldStorageDefinitionInterface $field_storage_definition
177 * Whether the field is displayed.
179 public function isFieldStorageDefinitionDisplayed(FieldStorageDefinitionInterface $field_storage_definition) {
180 if (($field_storage_definition instanceof BaseFieldDefinition && $field_storage_definition->isDisplayConfigurable('view')) || $field_storage_definition instanceof FieldStorageConfig) {
181 $field_key = 'content.' . $field_storage_definition->getName() . '.type';
182 $storage = $this->entityTypeManager->getStorage('entity_view_display');
183 $query = $storage->getQuery()->condition('targetEntityType', $field_storage_definition->getTargetEntityTypeId());
184 if ($field_storage_definition instanceof FieldStorageConfig) {
185 $bundles = $field_storage_definition->getBundles();
186 $query->condition('bundle', (array) $bundles, 'IN');
188 $result = $query->exists($field_key)->range(0, 1)->execute();
189 return !empty($result) ? TRUE : FALSE;
192 $view_options = (bool) $field_storage_definition->getDisplayOptions('view');
193 return $view_options;
198 * Gets the applicable plugin options for a given field.
200 * Loop over the plugins that can be applied to the field and builds an array
201 * of possible plugins based on each plugin weight.
203 * @param FieldStorageDefinitionInterface $field_definition
204 * The field storage definition.
207 * The plugin option for the given field based on plugin weight.
209 public function getApplicablePluginOptions(FieldStorageDefinitionInterface $field_definition) {
210 $plugins = $this->getPluginDefinitions();
211 // Build a list of all diff plugins supporting the field type of the field.
212 $plugin_options = [];
213 if (isset($plugins[$field_definition->getType()])) {
214 // Sort the plugins based on their weight.
215 uasort($plugins[$field_definition->getType()], 'Drupal\Component\Utility\SortArray::sortByWeightElement');
217 foreach ($plugins[$field_definition->getType()] as $id => $weight) {
218 $definition = $this->getDefinition($id, FALSE);
219 // Check if the plugin is applicable.
220 if (isset($definition['class']) && in_array($field_definition->getType(), $definition['field_types'])) {
221 /** @var FieldDiffBuilderInterface $class */
222 $class = $definition['class'];
223 if ($class::isApplicable($field_definition)) {
224 $plugin_options[$id] = $this->getDefinitions()[$id]['label'];
229 return $plugin_options;
233 * Initializes the local pluginDefinitions property.
235 * Loop over the plugin definitions and build an array keyed by the field type
236 * that plugins can be applied to.
239 * The initialized plugins array sort by field type.
241 public function getPluginDefinitions() {
242 if (!isset($this->pluginDefinitions)) {
243 // Get the definition of all the FieldDiffBuilder plugins.
244 foreach ($this->getDefinitions() as $plugin_definition) {
245 if (isset($plugin_definition['field_types'])) {
246 // Iterate through all the field types this plugin supports
247 // and for every such field type add the id of the plugin.
248 if (!isset($plugin_definition['weight'])) {
249 $plugin_definition['weight'] = 0;
252 foreach ($plugin_definition['field_types'] as $id) {
253 $this->pluginDefinitions[$id][$plugin_definition['id']]['weight'] = $plugin_definition['weight'];
258 return $this->pluginDefinitions;
262 * Clear the pluginDefinitions local property array.
264 public function clearCachedDefinitions() {
265 unset($this->pluginDefinitions);