Newer
Older
<?php
namespace Drupal\layout_builder;
/**
* Provides a domain object for layout sections.
*
* A section consists of three parts:
* - The layout plugin ID for the layout applied to the section (for example,
* 'layout_onecol').
* - An array of settings for the layout plugin.
* - An array of components that can be rendered in the section.
*
* @internal
* Layout Builder is currently experimental and should only be leveraged by
* experimental modules and development releases of contributed modules.
* See https://www.drupal.org/core/experimental for more information.
*
* @see \Drupal\Core\Layout\LayoutDefinition
* @see \Drupal\layout_builder\SectionComponent
*
* @todo Determine whether an interface will be provided for this in
* https://www.drupal.org/project/drupal/issues/2930334.
*/
class Section {
/**
* The layout plugin ID.
*
* @var string
*/
protected $layoutId;
/**
* The layout plugin settings.
*
* @var array
*/
protected $layoutSettings = [];
/**
* An array of components, keyed by UUID.
*
* @var \Drupal\layout_builder\SectionComponent[]
*/
protected $components = [];
/**
* Constructs a new Section.
*
* @param string $layout_id
* The layout plugin ID.
* @param array $layout_settings
* (optional) The layout plugin settings.
* @param \Drupal\layout_builder\SectionComponent[] $components
* (optional) The components.
*/
public function __construct($layout_id, array $layout_settings = [], array $components = []) {
$this->layoutId = $layout_id;
$this->layoutSettings = $layout_settings;
foreach ($components as $component) {
$this->setComponent($component);
}
}
/**
* Returns the renderable array for this section.
Angie Byron
committed
* @param \Drupal\Core\Plugin\Context\ContextInterface[] $contexts
* An array of available contexts.
*
* @return array
* A renderable array representing the content of the section.
*/
Angie Byron
committed
public function toRenderArray(array $contexts = []) {
$regions = [];
foreach ($this->getComponents() as $component) {
Angie Byron
committed
if ($output = $component->toRenderArray($contexts)) {
$regions[$component->getRegion()][$component->getUuid()] = $output;
}
}
return $this->getLayout()->build($regions);
}
/**
* Gets the layout plugin for this section.
* @return \Drupal\Core\Layout\LayoutInterface
* The layout plugin.
*/
public function getLayout() {
return $this->layoutPluginManager()->createInstance($this->getLayoutId(), $this->getLayoutSettings());
}
/**
* Gets the layout plugin ID for this section.
* @return string
* The layout plugin ID.
*
* @internal
* This method should only be used by code responsible for storing the data.
*/
public function getLayoutId() {
return $this->layoutId;
}
/**
* Gets the layout plugin settings for this section.
*
* @return mixed[]
* The layout plugin settings.
*
* @internal
* This method should only be used by code responsible for storing the data.
*/
public function getLayoutSettings() {
return $this->layoutSettings;
}
/**
* Sets the layout plugin settings for this section.
*
* @param mixed[] $layout_settings
* The layout plugin settings.
*
* @return $this
*/
public function setLayoutSettings(array $layout_settings) {
$this->layoutSettings = $layout_settings;
return $this;
}
/**
* Returns the components of the section.
*
* @return \Drupal\layout_builder\SectionComponent[]
* The components.
*/
public function getComponents() {
return $this->components;
}
/**
* Gets the component for a given UUID.
*
* @param string $uuid
* The UUID of the component to retrieve.
*
* @return \Drupal\layout_builder\SectionComponent
* The component.
*
* @throws \InvalidArgumentException
* Thrown when the expected UUID does not exist.
*/
public function getComponent($uuid) {
if (!isset($this->components[$uuid])) {
throw new \InvalidArgumentException(sprintf('Invalid UUID "%s"', $uuid));
}
return $this->components[$uuid];
}
/**
* Helper method to set a component.
*
* @param \Drupal\layout_builder\SectionComponent $component
* The component.
*
* @return $this
*/
protected function setComponent(SectionComponent $component) {
$this->components[$component->getUuid()] = $component;
return $this;
}
/**
* Removes a given component from a region.
*
* @param string $uuid
* The UUID of the component to remove.
*
* @return $this
*/
public function removeComponent($uuid) {
unset($this->components[$uuid]);
return $this;
}
/**
* Appends a component to the end of a region.
*
* @param \Drupal\layout_builder\SectionComponent $component
* The component being appended.
*
* @return $this
*/
public function appendComponent(SectionComponent $component) {
$component->setWeight($this->getNextHighestWeight($component->getRegion()));
$this->setComponent($component);
return $this;
}
/**
* Returns the next highest weight of the component in a region.
*
* @param string $region
* The region name.
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
*
* @return int
* A number higher than the highest weight of the component in the region.
*/
protected function getNextHighestWeight($region) {
$components = $this->getComponentsByRegion($region);
$weights = array_map(function (SectionComponent $component) {
return $component->getWeight();
}, $components);
return $weights ? max($weights) + 1 : 0;
}
/**
* Gets the components for a specific region.
*
* @param string $region
* The region name.
*
* @return \Drupal\layout_builder\SectionComponent[]
* An array of components in the specified region, sorted by weight.
*/
protected function getComponentsByRegion($region) {
$components = array_filter($this->getComponents(), function (SectionComponent $component) use ($region) {
return $component->getRegion() === $region;
});
uasort($components, function (SectionComponent $a, SectionComponent $b) {
return $a->getWeight() > $b->getWeight() ? 1 : -1;
});
return $components;
}
/**
* Inserts a component after a specified existing component.
*
* @param string $preceding_uuid
* The UUID of the existing component to insert after.
* @param \Drupal\layout_builder\SectionComponent $component
* The component being inserted.
*
* @return $this
*
* @throws \InvalidArgumentException
* Thrown when the expected UUID does not exist.
*/
public function insertAfterComponent($preceding_uuid, SectionComponent $component) {
// Find the delta of the specified UUID.
$uuids = array_keys($this->getComponentsByRegion($component->getRegion()));
$delta = array_search($preceding_uuid, $uuids, TRUE);
if ($delta === FALSE) {
throw new \InvalidArgumentException(sprintf('Invalid preceding UUID "%s"', $preceding_uuid));
}
return $this->insertComponent($delta + 1, $component);
}
/**
* Inserts a component at a specified delta.
*
* @param int $delta
* The zero-based delta in which to insert the component.
* @param \Drupal\layout_builder\SectionComponent $new_component
* The component being inserted.
*
* @return $this
*
* @throws \OutOfBoundsException
* Thrown when the specified delta is invalid.
*/
public function insertComponent($delta, SectionComponent $new_component) {
$components = $this->getComponentsByRegion($new_component->getRegion());
$count = count($components);
if ($delta > $count) {
throw new \OutOfBoundsException(sprintf('Invalid delta "%s" for the "%s" component', $delta, $new_component->getUuid()));
}
// If the delta is the end of the list, append the component instead.
if ($delta === $count) {
return $this->appendComponent($new_component);
}
// Find the weight of the component that exists at the specified delta.
$weight = array_values($components)[$delta]->getWeight();
$this->setComponent($new_component->setWeight($weight++));
// Increase the weight of every subsequent component.
foreach (array_slice($components, $delta) as $component) {
$component->setWeight($weight++);
}
return $this;
}
/**
* Wraps the layout plugin manager.
*
* @return \Drupal\Core\Layout\LayoutPluginManagerInterface
* The layout plugin manager.
*/
protected function layoutPluginManager() {
return \Drupal::service('plugin.manager.core.layout');
}