annotate core/modules/layout_builder/src/Section.php @ 5:12f9dff5fda9 tip

Update to Drupal core 8.7.1
author Chris Cannam
date Thu, 09 May 2019 15:34:47 +0100
parents a9cd425dd02b
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\layout_builder;
Chris@0 4
Chris@5 5 use Drupal\Core\Config\Entity\ThirdPartySettingsInterface;
Chris@5 6
Chris@0 7 /**
Chris@0 8 * Provides a domain object for layout sections.
Chris@0 9 *
Chris@0 10 * A section consists of three parts:
Chris@0 11 * - The layout plugin ID for the layout applied to the section (for example,
Chris@0 12 * 'layout_onecol').
Chris@0 13 * - An array of settings for the layout plugin.
Chris@0 14 * - An array of components that can be rendered in the section.
Chris@0 15 *
Chris@0 16 * @see \Drupal\Core\Layout\LayoutDefinition
Chris@0 17 * @see \Drupal\layout_builder\SectionComponent
Chris@0 18 */
Chris@5 19 class Section implements ThirdPartySettingsInterface {
Chris@0 20
Chris@0 21 /**
Chris@0 22 * The layout plugin ID.
Chris@0 23 *
Chris@0 24 * @var string
Chris@0 25 */
Chris@0 26 protected $layoutId;
Chris@0 27
Chris@0 28 /**
Chris@0 29 * The layout plugin settings.
Chris@0 30 *
Chris@0 31 * @var array
Chris@0 32 */
Chris@0 33 protected $layoutSettings = [];
Chris@0 34
Chris@0 35 /**
Chris@0 36 * An array of components, keyed by UUID.
Chris@0 37 *
Chris@0 38 * @var \Drupal\layout_builder\SectionComponent[]
Chris@0 39 */
Chris@0 40 protected $components = [];
Chris@0 41
Chris@0 42 /**
Chris@5 43 * Third party settings.
Chris@5 44 *
Chris@5 45 * An array of key/value pairs keyed by provider.
Chris@5 46 *
Chris@5 47 * @var array[]
Chris@5 48 */
Chris@5 49 protected $thirdPartySettings = [];
Chris@5 50
Chris@5 51 /**
Chris@0 52 * Constructs a new Section.
Chris@0 53 *
Chris@0 54 * @param string $layout_id
Chris@0 55 * The layout plugin ID.
Chris@0 56 * @param array $layout_settings
Chris@0 57 * (optional) The layout plugin settings.
Chris@0 58 * @param \Drupal\layout_builder\SectionComponent[] $components
Chris@0 59 * (optional) The components.
Chris@5 60 * @param array[] $third_party_settings
Chris@5 61 * (optional) Any third party settings.
Chris@0 62 */
Chris@5 63 public function __construct($layout_id, array $layout_settings = [], array $components = [], array $third_party_settings = []) {
Chris@0 64 $this->layoutId = $layout_id;
Chris@0 65 $this->layoutSettings = $layout_settings;
Chris@0 66 foreach ($components as $component) {
Chris@0 67 $this->setComponent($component);
Chris@0 68 }
Chris@5 69 $this->thirdPartySettings = $third_party_settings;
Chris@0 70 }
Chris@0 71
Chris@0 72 /**
Chris@0 73 * Returns the renderable array for this section.
Chris@0 74 *
Chris@0 75 * @param \Drupal\Core\Plugin\Context\ContextInterface[] $contexts
Chris@0 76 * An array of available contexts.
Chris@0 77 * @param bool $in_preview
Chris@0 78 * TRUE if the section is being previewed, FALSE otherwise.
Chris@0 79 *
Chris@0 80 * @return array
Chris@0 81 * A renderable array representing the content of the section.
Chris@0 82 */
Chris@0 83 public function toRenderArray(array $contexts = [], $in_preview = FALSE) {
Chris@0 84 $regions = [];
Chris@0 85 foreach ($this->getComponents() as $component) {
Chris@0 86 if ($output = $component->toRenderArray($contexts, $in_preview)) {
Chris@0 87 $regions[$component->getRegion()][$component->getUuid()] = $output;
Chris@0 88 }
Chris@0 89 }
Chris@0 90
Chris@0 91 return $this->getLayout()->build($regions);
Chris@0 92 }
Chris@0 93
Chris@0 94 /**
Chris@0 95 * Gets the layout plugin for this section.
Chris@0 96 *
Chris@0 97 * @return \Drupal\Core\Layout\LayoutInterface
Chris@0 98 * The layout plugin.
Chris@0 99 */
Chris@0 100 public function getLayout() {
Chris@0 101 return $this->layoutPluginManager()->createInstance($this->getLayoutId(), $this->getLayoutSettings());
Chris@0 102 }
Chris@0 103
Chris@0 104 /**
Chris@0 105 * Gets the layout plugin ID for this section.
Chris@0 106 *
Chris@0 107 * @return string
Chris@0 108 * The layout plugin ID.
Chris@0 109 *
Chris@0 110 * @internal
Chris@0 111 * This method should only be used by code responsible for storing the data.
Chris@0 112 */
Chris@0 113 public function getLayoutId() {
Chris@0 114 return $this->layoutId;
Chris@0 115 }
Chris@0 116
Chris@0 117 /**
Chris@0 118 * Gets the layout plugin settings for this section.
Chris@0 119 *
Chris@0 120 * @return mixed[]
Chris@0 121 * The layout plugin settings.
Chris@0 122 *
Chris@0 123 * @internal
Chris@0 124 * This method should only be used by code responsible for storing the data.
Chris@0 125 */
Chris@0 126 public function getLayoutSettings() {
Chris@0 127 return $this->layoutSettings;
Chris@0 128 }
Chris@0 129
Chris@0 130 /**
Chris@0 131 * Sets the layout plugin settings for this section.
Chris@0 132 *
Chris@0 133 * @param mixed[] $layout_settings
Chris@0 134 * The layout plugin settings.
Chris@0 135 *
Chris@0 136 * @return $this
Chris@0 137 */
Chris@0 138 public function setLayoutSettings(array $layout_settings) {
Chris@0 139 $this->layoutSettings = $layout_settings;
Chris@0 140 return $this;
Chris@0 141 }
Chris@0 142
Chris@0 143 /**
Chris@0 144 * Gets the default region.
Chris@0 145 *
Chris@0 146 * @return string
Chris@0 147 * The machine-readable name of the default region.
Chris@0 148 */
Chris@0 149 public function getDefaultRegion() {
Chris@0 150 return $this->layoutPluginManager()->getDefinition($this->getLayoutId())->getDefaultRegion();
Chris@0 151 }
Chris@0 152
Chris@0 153 /**
Chris@0 154 * Returns the components of the section.
Chris@0 155 *
Chris@0 156 * @return \Drupal\layout_builder\SectionComponent[]
Chris@0 157 * The components.
Chris@0 158 */
Chris@0 159 public function getComponents() {
Chris@0 160 return $this->components;
Chris@0 161 }
Chris@0 162
Chris@0 163 /**
Chris@0 164 * Gets the component for a given UUID.
Chris@0 165 *
Chris@0 166 * @param string $uuid
Chris@0 167 * The UUID of the component to retrieve.
Chris@0 168 *
Chris@0 169 * @return \Drupal\layout_builder\SectionComponent
Chris@0 170 * The component.
Chris@0 171 *
Chris@0 172 * @throws \InvalidArgumentException
Chris@0 173 * Thrown when the expected UUID does not exist.
Chris@0 174 */
Chris@0 175 public function getComponent($uuid) {
Chris@0 176 if (!isset($this->components[$uuid])) {
Chris@0 177 throw new \InvalidArgumentException(sprintf('Invalid UUID "%s"', $uuid));
Chris@0 178 }
Chris@0 179
Chris@0 180 return $this->components[$uuid];
Chris@0 181 }
Chris@0 182
Chris@0 183 /**
Chris@0 184 * Helper method to set a component.
Chris@0 185 *
Chris@0 186 * @param \Drupal\layout_builder\SectionComponent $component
Chris@0 187 * The component.
Chris@0 188 *
Chris@0 189 * @return $this
Chris@0 190 */
Chris@0 191 protected function setComponent(SectionComponent $component) {
Chris@0 192 $this->components[$component->getUuid()] = $component;
Chris@0 193 return $this;
Chris@0 194 }
Chris@0 195
Chris@0 196 /**
Chris@0 197 * Removes a given component from a region.
Chris@0 198 *
Chris@0 199 * @param string $uuid
Chris@0 200 * The UUID of the component to remove.
Chris@0 201 *
Chris@0 202 * @return $this
Chris@0 203 */
Chris@0 204 public function removeComponent($uuid) {
Chris@0 205 unset($this->components[$uuid]);
Chris@0 206 return $this;
Chris@0 207 }
Chris@0 208
Chris@0 209 /**
Chris@0 210 * Appends a component to the end of a region.
Chris@0 211 *
Chris@0 212 * @param \Drupal\layout_builder\SectionComponent $component
Chris@0 213 * The component being appended.
Chris@0 214 *
Chris@0 215 * @return $this
Chris@0 216 */
Chris@0 217 public function appendComponent(SectionComponent $component) {
Chris@0 218 $component->setWeight($this->getNextHighestWeight($component->getRegion()));
Chris@0 219 $this->setComponent($component);
Chris@0 220 return $this;
Chris@0 221 }
Chris@0 222
Chris@0 223 /**
Chris@0 224 * Returns the next highest weight of the component in a region.
Chris@0 225 *
Chris@0 226 * @param string $region
Chris@0 227 * The region name.
Chris@0 228 *
Chris@0 229 * @return int
Chris@0 230 * A number higher than the highest weight of the component in the region.
Chris@0 231 */
Chris@0 232 protected function getNextHighestWeight($region) {
Chris@0 233 $components = $this->getComponentsByRegion($region);
Chris@0 234 $weights = array_map(function (SectionComponent $component) {
Chris@0 235 return $component->getWeight();
Chris@0 236 }, $components);
Chris@0 237 return $weights ? max($weights) + 1 : 0;
Chris@0 238 }
Chris@0 239
Chris@0 240 /**
Chris@0 241 * Gets the components for a specific region.
Chris@0 242 *
Chris@0 243 * @param string $region
Chris@0 244 * The region name.
Chris@0 245 *
Chris@0 246 * @return \Drupal\layout_builder\SectionComponent[]
Chris@0 247 * An array of components in the specified region, sorted by weight.
Chris@0 248 */
Chris@5 249 public function getComponentsByRegion($region) {
Chris@0 250 $components = array_filter($this->getComponents(), function (SectionComponent $component) use ($region) {
Chris@0 251 return $component->getRegion() === $region;
Chris@0 252 });
Chris@0 253 uasort($components, function (SectionComponent $a, SectionComponent $b) {
Chris@0 254 return $a->getWeight() > $b->getWeight() ? 1 : -1;
Chris@0 255 });
Chris@0 256 return $components;
Chris@0 257 }
Chris@0 258
Chris@0 259 /**
Chris@0 260 * Inserts a component after a specified existing component.
Chris@0 261 *
Chris@0 262 * @param string $preceding_uuid
Chris@0 263 * The UUID of the existing component to insert after.
Chris@0 264 * @param \Drupal\layout_builder\SectionComponent $component
Chris@0 265 * The component being inserted.
Chris@0 266 *
Chris@0 267 * @return $this
Chris@0 268 *
Chris@0 269 * @throws \InvalidArgumentException
Chris@0 270 * Thrown when the expected UUID does not exist.
Chris@0 271 */
Chris@0 272 public function insertAfterComponent($preceding_uuid, SectionComponent $component) {
Chris@0 273 // Find the delta of the specified UUID.
Chris@0 274 $uuids = array_keys($this->getComponentsByRegion($component->getRegion()));
Chris@0 275 $delta = array_search($preceding_uuid, $uuids, TRUE);
Chris@0 276 if ($delta === FALSE) {
Chris@0 277 throw new \InvalidArgumentException(sprintf('Invalid preceding UUID "%s"', $preceding_uuid));
Chris@0 278 }
Chris@0 279 return $this->insertComponent($delta + 1, $component);
Chris@0 280 }
Chris@0 281
Chris@0 282 /**
Chris@0 283 * Inserts a component at a specified delta.
Chris@0 284 *
Chris@0 285 * @param int $delta
Chris@0 286 * The zero-based delta in which to insert the component.
Chris@0 287 * @param \Drupal\layout_builder\SectionComponent $new_component
Chris@0 288 * The component being inserted.
Chris@0 289 *
Chris@0 290 * @return $this
Chris@0 291 *
Chris@0 292 * @throws \OutOfBoundsException
Chris@0 293 * Thrown when the specified delta is invalid.
Chris@0 294 */
Chris@0 295 public function insertComponent($delta, SectionComponent $new_component) {
Chris@0 296 $components = $this->getComponentsByRegion($new_component->getRegion());
Chris@0 297 $count = count($components);
Chris@0 298 if ($delta > $count) {
Chris@0 299 throw new \OutOfBoundsException(sprintf('Invalid delta "%s" for the "%s" component', $delta, $new_component->getUuid()));
Chris@0 300 }
Chris@0 301
Chris@0 302 // If the delta is the end of the list, append the component instead.
Chris@0 303 if ($delta === $count) {
Chris@0 304 return $this->appendComponent($new_component);
Chris@0 305 }
Chris@0 306
Chris@0 307 // Find the weight of the component that exists at the specified delta.
Chris@0 308 $weight = array_values($components)[$delta]->getWeight();
Chris@0 309 $this->setComponent($new_component->setWeight($weight++));
Chris@0 310
Chris@0 311 // Increase the weight of every subsequent component.
Chris@0 312 foreach (array_slice($components, $delta) as $component) {
Chris@0 313 $component->setWeight($weight++);
Chris@0 314 }
Chris@0 315 return $this;
Chris@0 316 }
Chris@0 317
Chris@0 318 /**
Chris@0 319 * Wraps the layout plugin manager.
Chris@0 320 *
Chris@0 321 * @return \Drupal\Core\Layout\LayoutPluginManagerInterface
Chris@0 322 * The layout plugin manager.
Chris@0 323 */
Chris@0 324 protected function layoutPluginManager() {
Chris@0 325 return \Drupal::service('plugin.manager.core.layout');
Chris@0 326 }
Chris@0 327
Chris@0 328 /**
Chris@0 329 * Returns an array representation of the section.
Chris@0 330 *
Chris@0 331 * Only use this method if you are implementing custom storage for sections.
Chris@0 332 *
Chris@0 333 * @return array
Chris@0 334 * An array representation of the section component.
Chris@0 335 */
Chris@0 336 public function toArray() {
Chris@0 337 return [
Chris@0 338 'layout_id' => $this->getLayoutId(),
Chris@0 339 'layout_settings' => $this->getLayoutSettings(),
Chris@0 340 'components' => array_map(function (SectionComponent $component) {
Chris@0 341 return $component->toArray();
Chris@0 342 }, $this->getComponents()),
Chris@5 343 'third_party_settings' => $this->thirdPartySettings,
Chris@0 344 ];
Chris@0 345 }
Chris@0 346
Chris@0 347 /**
Chris@0 348 * Creates an object from an array representation of the section.
Chris@0 349 *
Chris@0 350 * Only use this method if you are implementing custom storage for sections.
Chris@0 351 *
Chris@0 352 * @param array $section
Chris@0 353 * An array of section data in the format returned by ::toArray().
Chris@0 354 *
Chris@0 355 * @return static
Chris@0 356 * The section object.
Chris@0 357 */
Chris@0 358 public static function fromArray(array $section) {
Chris@5 359 // Ensure expected array keys are present.
Chris@5 360 $section += [
Chris@5 361 'layout_id' => '',
Chris@5 362 'layout_settings' => [],
Chris@5 363 'components' => [],
Chris@5 364 'third_party_settings' => [],
Chris@5 365 ];
Chris@0 366 return new static(
Chris@0 367 $section['layout_id'],
Chris@0 368 $section['layout_settings'],
Chris@5 369 array_map([SectionComponent::class, 'fromArray'], $section['components']),
Chris@5 370 $section['third_party_settings']
Chris@0 371 );
Chris@0 372 }
Chris@0 373
Chris@4 374 /**
Chris@4 375 * Magic method: Implements a deep clone.
Chris@4 376 */
Chris@4 377 public function __clone() {
Chris@4 378 foreach ($this->components as $uuid => $component) {
Chris@4 379 $this->components[$uuid] = clone $component;
Chris@4 380 }
Chris@4 381 }
Chris@4 382
Chris@5 383 /**
Chris@5 384 * {@inheritdoc}
Chris@5 385 */
Chris@5 386 public function getThirdPartySetting($provider, $key, $default = NULL) {
Chris@5 387 return isset($this->thirdPartySettings[$provider][$key]) ? $this->thirdPartySettings[$provider][$key] : $default;
Chris@5 388 }
Chris@5 389
Chris@5 390 /**
Chris@5 391 * {@inheritdoc}
Chris@5 392 */
Chris@5 393 public function getThirdPartySettings($provider) {
Chris@5 394 return isset($this->thirdPartySettings[$provider]) ? $this->thirdPartySettings[$provider] : [];
Chris@5 395 }
Chris@5 396
Chris@5 397 /**
Chris@5 398 * {@inheritdoc}
Chris@5 399 */
Chris@5 400 public function setThirdPartySetting($provider, $key, $value) {
Chris@5 401 $this->thirdPartySettings[$provider][$key] = $value;
Chris@5 402 return $this;
Chris@5 403 }
Chris@5 404
Chris@5 405 /**
Chris@5 406 * {@inheritdoc}
Chris@5 407 */
Chris@5 408 public function unsetThirdPartySetting($provider, $key) {
Chris@5 409 unset($this->thirdPartySettings[$provider][$key]);
Chris@5 410 // If the third party is no longer storing any information, completely
Chris@5 411 // remove the array holding the settings for this provider.
Chris@5 412 if (empty($this->thirdPartySettings[$provider])) {
Chris@5 413 unset($this->thirdPartySettings[$provider]);
Chris@5 414 }
Chris@5 415 return $this;
Chris@5 416 }
Chris@5 417
Chris@5 418 /**
Chris@5 419 * {@inheritdoc}
Chris@5 420 */
Chris@5 421 public function getThirdPartyProviders() {
Chris@5 422 return array_keys($this->thirdPartySettings);
Chris@5 423 }
Chris@5 424
Chris@0 425 }