annotate core/modules/layout_builder/src/Section.php @ 19:fa3358dc1485 tip

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