Mercurial > hg > isophonics-drupal-site
comparison core/modules/layout_builder/src/InlineBlockEntityOperations.php @ 17:129ea1e6d783
Update, including to Drupal core 8.6.10
author | Chris Cannam |
---|---|
date | Thu, 28 Feb 2019 13:21:36 +0000 |
parents | |
children | af1871eacc83 |
comparison
equal
deleted
inserted
replaced
16:c2387f117808 | 17:129ea1e6d783 |
---|---|
1 <?php | |
2 | |
3 namespace Drupal\layout_builder; | |
4 | |
5 use Drupal\Core\Database\Connection; | |
6 use Drupal\Core\DependencyInjection\ContainerInjectionInterface; | |
7 use Drupal\Core\Entity\EntityInterface; | |
8 use Drupal\Core\Entity\EntityTypeManagerInterface; | |
9 use Drupal\Core\Entity\RevisionableInterface; | |
10 use Drupal\layout_builder\Plugin\Block\InlineBlock; | |
11 use Symfony\Component\DependencyInjection\ContainerInterface; | |
12 | |
13 /** | |
14 * Defines a class for reacting to entity events related to Inline Blocks. | |
15 * | |
16 * @internal | |
17 */ | |
18 class InlineBlockEntityOperations implements ContainerInjectionInterface { | |
19 | |
20 use LayoutEntityHelperTrait; | |
21 | |
22 /** | |
23 * Inline block usage tracking service. | |
24 * | |
25 * @var \Drupal\layout_builder\InlineBlockUsage | |
26 */ | |
27 protected $usage; | |
28 | |
29 /** | |
30 * The block content storage. | |
31 * | |
32 * @var \Drupal\Core\Entity\EntityStorageInterface | |
33 */ | |
34 protected $blockContentStorage; | |
35 | |
36 /** | |
37 * The entity type manager. | |
38 * | |
39 * @var \Drupal\Core\Entity\EntityTypeManagerInterface | |
40 */ | |
41 protected $entityTypeManager; | |
42 | |
43 /** | |
44 * Constructs a new EntityOperations object. | |
45 * | |
46 * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager | |
47 * The entity type manager service. | |
48 * @param \Drupal\layout_builder\InlineBlockUsage $usage | |
49 * Inline block usage tracking service. | |
50 * @param \Drupal\Core\Database\Connection $database | |
51 * The database connection. | |
52 */ | |
53 public function __construct(EntityTypeManagerInterface $entityTypeManager, InlineBlockUsage $usage, Connection $database) { | |
54 $this->entityTypeManager = $entityTypeManager; | |
55 $this->blockContentStorage = $entityTypeManager->getStorage('block_content'); | |
56 $this->usage = $usage; | |
57 } | |
58 | |
59 /** | |
60 * {@inheritdoc} | |
61 */ | |
62 public static function create(ContainerInterface $container) { | |
63 return new static( | |
64 $container->get('entity_type.manager'), | |
65 $container->get('inline_block.usage'), | |
66 $container->get('database') | |
67 ); | |
68 } | |
69 | |
70 /** | |
71 * Remove all unused inline blocks on save. | |
72 * | |
73 * Entities that were used in prevision revisions will be removed if not | |
74 * saving a new revision. | |
75 * | |
76 * @param \Drupal\Core\Entity\EntityInterface $entity | |
77 * The parent entity. | |
78 */ | |
79 protected function removeUnusedForEntityOnSave(EntityInterface $entity) { | |
80 // If the entity is new or '$entity->original' is not set then there will | |
81 // not be any unused inline blocks to remove. | |
82 // If this is a revisionable entity then do not remove inline blocks. They | |
83 // could be referenced in previous revisions even if this is not a new | |
84 // revision. | |
85 if ($entity->isNew() || !isset($entity->original) || $entity instanceof RevisionableInterface) { | |
86 return; | |
87 } | |
88 $sections = $this->getEntitySections($entity); | |
89 // If this is a layout override and there are no sections then it is a new | |
90 // override. | |
91 if ($this->isEntityUsingFieldOverride($entity) && empty($sections)) { | |
92 return; | |
93 } | |
94 | |
95 // Delete and remove the usage for inline blocks that were removed. | |
96 if ($removed_block_ids = $this->getRemovedBlockIds($entity)) { | |
97 $this->deleteBlocksAndUsage($removed_block_ids); | |
98 } | |
99 } | |
100 | |
101 /** | |
102 * Gets the IDs of the inline blocks that were removed. | |
103 * | |
104 * @param \Drupal\Core\Entity\EntityInterface $entity | |
105 * The layout entity. | |
106 * | |
107 * @return int[] | |
108 * The block content IDs that were removed. | |
109 */ | |
110 protected function getRemovedBlockIds(EntityInterface $entity) { | |
111 $original_sections = $this->getEntitySections($entity->original); | |
112 $current_sections = $this->getEntitySections($entity); | |
113 // Avoid un-needed conversion from revision IDs to block content IDs by | |
114 // first determining if there are any revisions in the original that are not | |
115 // also in the current sections. | |
116 $current_block_content_revision_ids = $this->getInlineBlockRevisionIdsInSections($current_sections); | |
117 $original_block_content_revision_ids = $this->getInlineBlockRevisionIdsInSections($original_sections); | |
118 if ($unused_original_revision_ids = array_diff($original_block_content_revision_ids, $current_block_content_revision_ids)) { | |
119 // If there are any revisions in the original that aren't in the current | |
120 // there may some blocks that need to be removed. | |
121 $current_block_content_ids = $this->getBlockIdsForRevisionIds($current_block_content_revision_ids); | |
122 $unused_original_block_content_ids = $this->getBlockIdsForRevisionIds($unused_original_revision_ids); | |
123 return array_diff($unused_original_block_content_ids, $current_block_content_ids); | |
124 } | |
125 return []; | |
126 } | |
127 | |
128 /** | |
129 * Handles entity tracking on deleting a parent entity. | |
130 * | |
131 * @param \Drupal\Core\Entity\EntityInterface $entity | |
132 * The parent entity. | |
133 */ | |
134 public function handleEntityDelete(EntityInterface $entity) { | |
135 if ($this->isLayoutCompatibleEntity($entity)) { | |
136 $this->usage->removeByLayoutEntity($entity); | |
137 } | |
138 } | |
139 | |
140 /** | |
141 * Handles saving a parent entity. | |
142 * | |
143 * @param \Drupal\Core\Entity\EntityInterface $entity | |
144 * The parent entity. | |
145 */ | |
146 public function handlePreSave(EntityInterface $entity) { | |
147 if (!$this->isLayoutCompatibleEntity($entity)) { | |
148 return; | |
149 } | |
150 $duplicate_blocks = FALSE; | |
151 | |
152 if ($sections = $this->getEntitySections($entity)) { | |
153 if ($this->isEntityUsingFieldOverride($entity)) { | |
154 if (!$entity->isNew() && isset($entity->original)) { | |
155 if (empty($this->getEntitySections($entity->original))) { | |
156 // If there were no sections in the original entity then this is a | |
157 // new override from a default and the blocks need to be duplicated. | |
158 $duplicate_blocks = TRUE; | |
159 } | |
160 } | |
161 } | |
162 $new_revision = FALSE; | |
163 if ($entity instanceof RevisionableInterface) { | |
164 // If the parent entity will have a new revision create a new revision | |
165 // of the block. | |
166 // @todo Currently revisions are never created for the parent entity. | |
167 // This will be fixed in https://www.drupal.org/node/2937199. | |
168 // To work around this always make a revision when the parent entity | |
169 // is an instance of RevisionableInterface. After the issue is fixed | |
170 // only create a new revision if '$entity->isNewRevision()'. | |
171 $new_revision = TRUE; | |
172 } | |
173 | |
174 foreach ($this->getInlineBlockComponents($sections) as $component) { | |
175 $this->saveInlineBlockComponent($entity, $component, $new_revision, $duplicate_blocks); | |
176 } | |
177 } | |
178 $this->removeUnusedForEntityOnSave($entity); | |
179 } | |
180 | |
181 /** | |
182 * Gets a block ID for an inline block plugin. | |
183 * | |
184 * @param \Drupal\layout_builder\Plugin\Block\InlineBlock $block_plugin | |
185 * The inline block plugin. | |
186 * | |
187 * @return int | |
188 * The block content ID or null none available. | |
189 */ | |
190 protected function getPluginBlockId(InlineBlock $block_plugin) { | |
191 $configuration = $block_plugin->getConfiguration(); | |
192 if (!empty($configuration['block_revision_id'])) { | |
193 $revision_ids = $this->getBlockIdsForRevisionIds([$configuration['block_revision_id']]); | |
194 return array_pop($revision_ids); | |
195 } | |
196 return NULL; | |
197 } | |
198 | |
199 /** | |
200 * Delete the inline blocks and the usage records. | |
201 * | |
202 * @param int[] $block_content_ids | |
203 * The block content entity IDs. | |
204 */ | |
205 protected function deleteBlocksAndUsage(array $block_content_ids) { | |
206 foreach ($block_content_ids as $block_content_id) { | |
207 if ($block = $this->blockContentStorage->load($block_content_id)) { | |
208 $block->delete(); | |
209 } | |
210 } | |
211 $this->usage->deleteUsage($block_content_ids); | |
212 } | |
213 | |
214 /** | |
215 * Removes unused inline blocks. | |
216 * | |
217 * @param int $limit | |
218 * The maximum number of inline blocks to remove. | |
219 */ | |
220 public function removeUnused($limit = 100) { | |
221 $this->deleteBlocksAndUsage($this->usage->getUnused($limit)); | |
222 } | |
223 | |
224 /** | |
225 * Gets blocks IDs for an array of revision IDs. | |
226 * | |
227 * @param int[] $revision_ids | |
228 * The revision IDs. | |
229 * | |
230 * @return int[] | |
231 * The block IDs. | |
232 */ | |
233 protected function getBlockIdsForRevisionIds(array $revision_ids) { | |
234 if ($revision_ids) { | |
235 $query = $this->blockContentStorage->getQuery(); | |
236 $query->condition('revision_id', $revision_ids, 'IN'); | |
237 $block_ids = $query->execute(); | |
238 return $block_ids; | |
239 } | |
240 return []; | |
241 } | |
242 | |
243 /** | |
244 * Saves an inline block component. | |
245 * | |
246 * @param \Drupal\Core\Entity\EntityInterface $entity | |
247 * The entity with the layout. | |
248 * @param \Drupal\layout_builder\SectionComponent $component | |
249 * The section component with an inline block. | |
250 * @param bool $new_revision | |
251 * Whether a new revision of the block should be created. | |
252 * @param bool $duplicate_blocks | |
253 * Whether the blocks should be duplicated. | |
254 */ | |
255 protected function saveInlineBlockComponent(EntityInterface $entity, SectionComponent $component, $new_revision, $duplicate_blocks) { | |
256 /** @var \Drupal\layout_builder\Plugin\Block\InlineBlock $plugin */ | |
257 $plugin = $component->getPlugin(); | |
258 $pre_save_configuration = $plugin->getConfiguration(); | |
259 $plugin->saveBlockContent($new_revision, $duplicate_blocks); | |
260 $post_save_configuration = $plugin->getConfiguration(); | |
261 if ($duplicate_blocks || (empty($pre_save_configuration['block_revision_id']) && !empty($post_save_configuration['block_revision_id']))) { | |
262 $this->usage->addUsage($this->getPluginBlockId($plugin), $entity); | |
263 } | |
264 $component->setConfiguration($post_save_configuration); | |
265 } | |
266 | |
267 } |