Cross posting an issue I posted from Drupal.org in the hope that it gets a bit more visibility here.
I'm currently loading facet blocks via AJAX into an off canvas dialog provided by the core Dialog API. To achieve this, I have a controller set up that accepts a facet ID as a parameter, and then loads the block (I've attached the code at the end of this question as an example).
This works perfectly on the face of it (all of my facets are listed, the dialog works and shows the facet widgets for each facet). However, what I've noticed is that if I have a facet enabled, this isn't taken into account when generating facet links. For example; if I have a facet applied for language, and then look at the link generated for my collection facets, the facets link to my search page with the collection facet applied, but without the existing language facet.
The search view itself is not an AJAX view, it's just the loading of the facet blocks. Unfortunately, this is a requirement as we have a large amount of facets that kill the performance if loaded as part of the main request.
Here are some things I've tried:
- Adding the main page parameters to my dialog links
- Using these query parameters to configure, load, and execute my search view during the AJAX request before the facet block is generated (see following example)
These facet blocks are only ever rendered on the same page as the facet source View, however as it's not the same request it may as well be on a different page.
I guess my question is one of multiple parts:
- Am I missing something obvious in my approach
- Is it possible to do what I'm trying to achieve
- If it is and I'm going the wrong way about it, what's the right way to go about it
As an aside, I'm rendering the blocks isn't a requirement if it's easier to render the facets themselves - it just felt like it would be easier.
As promised, here's my controller code. As mentioned, the facet blocks display, but already-applied facets aren't taken into account during link generation.
MY_MODULE.routing.yml
my_facets.ajax_facet_block:
path: '/ajax/my-facets/{facet_id}'
defaults:
_title: 'Filter by'
_controller: '\Drupal\my_facets\Controller\AjaxFacetBlockController::renderFacetBlockAction'
requirements:
_permission: 'access content'
Controller
<?php
namespace Drupal\my_facets\Controller;
use Drupal\Core\Block\BlockManagerInterface;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Plugin\PluginBase;
use Drupal\Core\Render\RendererInterface;
use Drupal\views\Views;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Returns responses for Facets routes.
*/
class AjaxFacetBlockController extends ControllerBase {
public function __construct(
protected BlockManagerInterface $blockManager,
protected RendererInterface $renderer,
protected Request $request
) {
}
public static function create(ContainerInterface $container): static {
return new static(
$container->get('plugin.manager.block'),
$container->get('renderer'),
$container->get('request_stack')?->getCurrentRequest()
);
}
/**
* Builds the response.
*/
public function renderFacetBlockAction(string $facet_id) {
// We need to execute the search view to correctly generate links.
// @ToDo: Once this is working inject the entity type manager rather than calling `Views::getView`
$search_view = Views::getView('search');
if (!$search_view) {
throw new NotFoundHttpException();
}
$search_view->setDisplay('search_page');
$search_view->setExposedInput(array_filter(
$this->request->query->all(),
static fn ($k) => !str_starts_with('_', $k),
ARRAY_FILTER_USE_KEY
));
$non_facet_params = array_filter(
$this->request->query->all(),
static fn ($key) => $key !== 'f' && !str_starts_with($key, '_'),
ARRAY_FILTER_USE_KEY
);
$search_view->exposed_raw_input = array_merge(
$search_view->exposed_raw_input,
$non_facet_params
);
$search_view->exposed_data = array_merge(
$search_view->exposed_data,
$non_facet_params
);
$search_view->preExecute();
$search_view->execute();
try {
/** @var \Drupal\facets\Plugin\Block\FacetBlock $block */
$block = $this->blockManager->createInstance(
'facet_block' . PluginBase::DERIVATIVE_SEPARATOR . $facet_id, []
);
}
catch (\Exception) {
throw new NotFoundHttpException();
}
if (!$block || !$block->access($this->currentUser())) {
throw new NotFoundHttpException();
}
return $block->build();
}
}