We had a similar requirement on a previous project with Layout Builder: We used a block_content
entity. We are using the Block field module to create a field_view
containing a reference to the views block.
Every other field on the custom block content entity basically was a faked Views UI: Every filter that we wanted to expose to the editors, was created as field on the block_content (mostly taxonomy term references), and then mapped to views exposed filters in a special field formatter widget for the block field.
It is a bit of hack, but our editors were quite happy with it. The field formatter below is tailored to our specfic needs, but might help to get some ideas.
<?php
namespace Drupal\wt_dgm\Plugin\Field\FieldFormatter;
use Drupal\Component\Plugin\Exception\ContextException;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\block_field\Plugin\Field\FieldFormatter\BlockFieldFormatter;
use Drupal\Core\Plugin\ContextAwarePluginInterface;
use Drupal\views\Element\View;
use Drupal\views\Plugin\Block\ViewsBlock;
/**
* Plugin implementation of the 'block_field' formatter.
*
* @FieldFormatter(
* id = "autolist_blockfield",
* label = @Translation("Block field with filters from Auto-List block"),
* field_types = {
* "block_field"
* }
* )
*/
class AutoListBlockField extends BlockFieldFormatter {
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items, $langcode) {
/** @var $parentEntity \Drupal\block_content\Entity\BlockContent */
$parentEntity = $items->getEntity();
if ($parentEntity->getEntityTypeId() != 'block_content' || $items->getFieldDefinition()->getName() != 'field_view') {
return parent::viewElements($items, $langcode);
}
$elements = [];
foreach ($items as $delta => $item) {
/** @var \Drupal\block_field\BlockFieldItemInterface $item */
$block = $item->getBlock();
if (!($block instanceof ViewsBlock)) {
continue;
}
// copy & paste parent::viewElements()
if ($block instanceof ContextAwarePluginInterface) {
try {
$contexts = \Drupal::service('context.repository')->getRuntimeContexts($block->getContextMapping());
\Drupal::service('context.handler')->applyContextMapping($block, $contexts);
}
catch (ContextException $e) {
continue;
}
}
if (!$block || !$block->access(\Drupal::currentUser())) {
continue;
}
// apply my custom filters and sort options to the views block
$view = $block->getViewExecutable();
$display = $view->current_display;
$args = [$parentEntity->id()];
$view->setArguments([$parentEntity->id()]);
$filters = [];
/* ### alter views filter ### */
if ($parentEntity->hasField('field_contentypes')) {
$filters['type'] = array_column($parentEntity->get('field_contentypes')->getValue(), 'value');
}
foreach ($parentEntity->getFields() as $fieldName => $field) {
if ($field->getFieldDefinition()->getType() == 'entity_reference' && $field->getFieldDefinition()->getSetting('target_type') == 'taxonomy_term') {
if (!$field->isEmpty()) {
$filters[$fieldName . '_target_id'] = array_column($field->getValue(), 'target_id');
}
}
}
/* ### alter views sort ### */
$sort = $parentEntity->get('field_sort')->value;
$filters['sort_by'] = $sort;
$view->setExposedInput($filters);
$view->setItemsPerPage($parentEntity->get('field_quantity')->value);
$view->display_handler->setOption('title', $parentEntity->get('field_title')->value);
//@see \Drupal\views\Plugin\Block\ViewsBlock::build()
$output = $view->buildRenderable($display, $args, FALSE);
$output = View::preRenderViewElement($output);
$elements[$delta] = $output;
CacheableMetadata::createFromRenderArray($elements[$delta])
->addCacheableDependency($parentEntity)
->applyTo($elements[$delta]);
}
return $elements;
}
}
PS: Ajax has to be enabled on the views block, otherwise setting $filter
did not work correctly for us.