Skip to content

Commit

Permalink
Breadcrumb query (#27)
Browse files Browse the repository at this point in the history
* start off

* Breadcrumb

* cs fixes

* fix: add subrequest

* fix: cleanup

* test: add breadcrumb test

* fix: enable breadcrumbs

Co-authored-by: Christian Fritsch <chr.fritsch@gmx.net>
  • Loading branch information
dbosen and chrfritsch committed May 4, 2021
1 parent abccc22 commit e2c4b3e
Show file tree
Hide file tree
Showing 12 changed files with 270 additions and 0 deletions.
1 change: 1 addition & 0 deletions config/install/graphql.graphql_servers.thunder_graphql.yml
Expand Up @@ -7,6 +7,7 @@ schema: composable
schema_configuration:
composable:
extensions:
thunder_breadcrumb: thunder_breadcrumb
thunder_paragraphs: thunder_paragraphs
thunder_metatags: thunder_metatags
thunder_media: thunder_media
Expand Down
Empty file.
3 changes: 3 additions & 0 deletions graphql/thunder_breadcrumb.extension.graphqls
@@ -0,0 +1,3 @@
extend type Query {
breadcrumb(path: String!): [Link]
}
5 changes: 5 additions & 0 deletions graphql/thunder_pages.base.graphqls
Expand Up @@ -20,6 +20,11 @@ type Teaser {
text: String
}

type Link {
url: String!
title: String!
}

type EntityLinks {
canonical: String
deleteForm: String
Expand Down
126 changes: 126 additions & 0 deletions src/Plugin/GraphQL/DataProducer/ThunderBreadcrumb.php
@@ -0,0 +1,126 @@
<?php

namespace Drupal\thunder_gqls\Plugin\GraphQL\DataProducer;

use Drupal\Component\Serialization\Json;
use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\graphql\Plugin\GraphQL\DataProducer\DataProducerPluginBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\HttpKernelInterface;

/**
* Resolves breadcrumbs for an entity.
*
* @DataProducer(
* id = "thunder_breadcrumb",
* name = @Translation("Breadcrumb"),
* description = @Translation("Resolves the breadcrumb for an entity."),
* produces = @ContextDefinition("string",
* label = @Translation("The url")
* ),
* consumes = {
* "entity" = @ContextDefinition("entity",
* label = @Translation("Root value")
* )
* }
* )
*/
class ThunderBreadcrumb extends DataProducerPluginBase implements ContainerFactoryPluginInterface {

/**
* The HTTP kernel service.
*
* @var \Symfony\Component\HttpKernel\HttpKernelInterface
*/
protected $httpKernel;

/**
* The request stack service.
*
* @var \Symfony\Component\HttpFoundation\RequestStack
*/
protected $requestStack;

/**
* {@inheritdoc}
*
* @codeCoverageIgnore
*/
public static function create(ContainerInterface $container, array $configuration, $pluginId, $pluginDefinition) {
return new static(
$configuration,
$pluginId,
$pluginDefinition,
$container->get('http_kernel'),
$container->get('request_stack')
);
}

/**
* Breadcrumb constructor.
*
* @param array $configuration
* The plugin configuration array.
* @param string $pluginId
* The plugin id.
* @param mixed $pluginDefinition
* The plugin definition.
* @param \Symfony\Component\HttpKernel\HttpKernelInterface $httpKernel
* The HTTP kernel service.
* @param \Symfony\Component\HttpFoundation\RequestStack $requestStack
* The request stack service.
*/
public function __construct(
array $configuration,
string $pluginId,
$pluginDefinition,
HttpKernelInterface $httpKernel,
RequestStack $requestStack
) {
parent::__construct($configuration, $pluginId, $pluginDefinition);
$this->httpKernel = $httpKernel;
$this->requestStack = $requestStack;
}

/**
* Resolve the breadcrumb.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity.
* @param \Drupal\Core\Cache\RefinableCacheableDependencyInterface $metadata
* The cacheable dependency interface.
*
* @throws \Drupal\Core\Entity\EntityMalformedException
*
* @return mixed
* The breadcrumb.
*/
public function resolve(EntityInterface $entity, RefinableCacheableDependencyInterface $metadata) {
$currentRequest = $this->requestStack->getCurrentRequest();
$request = Request::create(
$entity->toUrl()->getInternalPath(),
'GET',
[MainContentViewSubscriber::WRAPPER_FORMAT => 'thunder_gqls'],
$currentRequest->cookies->all(),
$currentRequest->files->all(),
$currentRequest->server->all()
);

if ($session = $currentRequest->getSession()) {
$request->setSession($session);
}

/** @var \Symfony\Component\HttpFoundation\JsonResponse $response */
$response = $this->httpKernel->handle($request, HttpKernelInterface::SUB_REQUEST);

$content = (string) $response->getContent();

return Json::decode($content)['breadcrumb'];
}

}
@@ -0,0 +1,36 @@
<?php

namespace Drupal\thunder_gqls\Plugin\GraphQL\SchemaExtension;

use Drupal\graphql\GraphQL\ResolverRegistryInterface;

/**
* The menu schema extension.
*
* @SchemaExtension(
* id = "thunder_breadcrumb",
* name = "Breadcrumb",
* description = "Adds the breadcrumb.",
* schema = "thunder"
* )
*/
class ThunderBreadcrumbSchemaExtension extends ThunderSchemaExtensionPluginBase {

/**
* {@inheritdoc}
*/
public function registerResolvers(ResolverRegistryInterface $registry) {
parent::registerResolvers($registry);

$this->addFieldResolverIfNotExists('Query', 'breadcrumb', $this->builder->compose(
$this->builder->produce('route_load')
->map('path', $this->builder->fromArgument('path')),
$this->builder->produce('route_entity')
->map('url', $this->builder->fromParent()),
$this->builder->produce('thunder_breadcrumb')
->map('entity', $this->builder->fromParent())
));

}

}
67 changes: 67 additions & 0 deletions src/Render/MainContent/ThunderGqlsRenderer.php
@@ -0,0 +1,67 @@
<?php

namespace Drupal\thunder_gqls\Render\MainContent;

use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
use Drupal\Core\Cache\CacheableJsonResponse;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Render\MainContent\MainContentRendererInterface;
use Drupal\Core\Routing\CurrentRouteMatch;
use Drupal\Core\Routing\RouteMatchInterface;
use Symfony\Component\HttpFoundation\Request;

/**
* Thunder GraphQL Schema content renderer.
*
* @internal
*/
class ThunderGqlsRenderer implements MainContentRendererInterface {

/**
* The breadcrumb manager.
*
* @var \Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface
*/
protected $breadcrumbManager;

/**
* The route match service.
*
* @var \Drupal\Core\Routing\CurrentRouteMatch
*/
protected $currentRouteMatch;

/**
* Constructs a new JsonRenderer.
*
* @param \Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface $breadcrumbManager
* The breadcrumb manager.
* @param \Drupal\Core\Routing\CurrentRouteMatch $currentRouteMatch
* The route match service.
*/
public function __construct(BreadcrumbBuilderInterface $breadcrumbManager, CurrentRouteMatch $currentRouteMatch) {
$this->breadcrumbManager = $breadcrumbManager;
$this->currentRouteMatch = $currentRouteMatch;
}

/**
* {@inheritdoc}
*/
public function renderResponse(array $main_content, Request $request, RouteMatchInterface $route_match) {
$json = [];

$breadCrumb = [];
foreach ($this->breadcrumbManager->build($this->currentRouteMatch->getCurrentRouteMatch())->getLinks() as $link) {
$breadCrumb[] = [
'url' => $link->getUrl()->toString(),
'title' => $link->getText(),
];
}
$json['breadcrumb'] = $breadCrumb;

$response = new CacheableJsonResponse($json, 200);
$response->addCacheableDependency(CacheableMetadata::createFromRenderArray($main_content));
return $response;
}

}
6 changes: 6 additions & 0 deletions tests/examples/breadcrumb.query.graphql
@@ -0,0 +1,6 @@
query ($path: String!) {
breadcrumb(path: $path) {
url
title
}
}
14 changes: 14 additions & 0 deletions tests/examples/breadcrumb.response.json
@@ -0,0 +1,14 @@
{
"data": {
"breadcrumb": [
{
"url": "/",
"title": "Home"
},
{
"url": "/events",
"title": "Events"
}
]
}
}
3 changes: 3 additions & 0 deletions tests/examples/breadcrumb.variables.json
@@ -0,0 +1,3 @@
{
"path": "/come-drupalcon-new-orleans"
}
3 changes: 3 additions & 0 deletions tests/src/Functional/SchemaTest.php
Expand Up @@ -40,6 +40,9 @@ public function schemas(): array {
[
'menu',
],
[
'breadcrumb',
],
];
}

Expand Down
6 changes: 6 additions & 0 deletions thunder_gqls.services.yml
@@ -0,0 +1,6 @@
services:
main_content_renderer.thunder_gqls:
class: Drupal\thunder_gqls\Render\MainContent\ThunderGqlsRenderer
arguments: ['@breadcrumb', '@current_route_match']
tags:
- { name: render.main_content_renderer, format: thunder_gqls }

0 comments on commit e2c4b3e

Please sign in to comment.