55 * GraphQL Twig hook implementations.
66 */
77
8- use Drupal\graphql_twig\GraphQLNodeVisitor;
8+ use Youshido\GraphQL\Parser\Parser;
9+ use Youshido\GraphQL\Parser\Ast\ArgumentValue\Variable;
10+ use Drupal\Core\Entity\EntityInterface;
11+ use Drupal\graphql_twig\GraphQLTwigEnvironment;
12+
13+ /**
14+ * Implements hook_theme_registry_alter().
15+ *
16+ * Search for GraphQL enhanced templates in the theme registry and append
17+ * the GraphQL preprocessor.
18+ */
19+ function graphql_twig_theme_registry_alter(&$theme_registry) {
20+ /** @var \Drupal\Core\Template\TwigEnvironment $twig */
21+ $twig = Drupal::service('twig');
22+ foreach ($theme_registry as $hook => $info) {
23+ $file = $info['path'] . '/' . $info['template'] . '.html.twig';
24+ if ($query = graphql_twig_get_query($file)) {
25+ $theme_registry[$hook]['graphql_enabled'] = TRUE;
26+ $theme_registry[$hook]['graphql_variables'] = graphql_twig_get_query_variables($query);
27+ $theme_registry[$hook]['preprocess functions'][] = 'graphql_twig_process_query';
28+ }
29+ }
30+ }
31+
32+ /**
33+ * Get the query string for a given template file.
34+ *
35+ * Checks for an annotation, a `*.gql` sibling file or returns NULL if nothing
36+ * is found.
37+ *
38+ * @param string $file
39+ * The template file path.
40+ *
41+ * @return string|null
42+ * The GraphQL query string or NULL.
43+ */
44+ function graphql_twig_get_query($file) {
45+ if (file_exists($file)) {
46+
47+ $graphqlFile = str_replace('.html.twig', '.gql', $file);
48+ if (file_exists($graphqlFile)) {
49+ return file_get_contents($graphqlFile);
50+ }
51+
52+ preg_match(GraphQLTwigEnvironment::$GRAPHQL_ANNOTATION_REGEX, file_get_contents($file), $matches);
53+ if (array_key_exists('query', $matches)) {
54+ $source = (new Parser())->parse($matches['query']);
55+ if ($source['queries']) {
56+ return $matches['query'];
57+ }
58+ }
59+ }
60+
61+ return NULL;
62+ }
63+
64+ /**
65+ * Get the declared variables in a GraphQL query string.
66+ *
67+ * @param $query
68+ * The query string.
69+ *
70+ * @return array
71+ * A list of variable names.
72+ */
73+ function graphql_twig_get_query_variables($query) {
74+ $source = (new Parser())->parse($query);
75+ return array_map(function (Variable $var) {
76+ return $var->getName();
77+ }, $source['variables']);
78+ }
79+
80+ /**
81+ * Implements hook_preprocess().
82+ *
83+ * Execute the GraphQL query if the theme hook is GraphQL enhanced and add
84+ * the query result as well as cache metadata.
85+ */
86+ function graphql_twig_process_query(&$variables, $hook, $info) {
87+ if (isset($info['graphql_enabled']) && $info['graphql_enabled']) {
88+ /** @var \Drupal\graphql\QueryProcessor $processor */
89+ $processor = Drupal::service('graphql.query_processor');
90+
91+ /** @var \Drupal\Core\Template\TwigEnvironment $twig */
92+ $twig = Drupal::service('twig');
93+
94+ $file = $info['path'] . '/' . $info['template'] . '.html.twig';
95+
96+ $query = $twig->loadTemplate($file)->getGraphQLQuery();
97+
98+ $arguments = [];
99+ foreach ($info['graphql_variables'] as $var) {
100+ if (isset($variables[$var])) {
101+ $arguments[$var] = $variables[$var] instanceof EntityInterface ? $variables[$var]->id() : $variables[$var];
102+ }
103+ }
104+
105+ $queryResult = $processor->processQuery($query, $arguments);
106+
107+ $variables['graphql_result'] = $queryResult->getData();
108+
109+ $variables['#cache']['contexts'] = $queryResult->getCacheContexts();
110+ $variables['#cache']['tags'] = $queryResult->getCacheTags();
111+ $variables['#cache']['max-age'] = $queryResult->getCacheMaxAge();
112+ }
113+ }
9114
10115/**
11116 * Implements hook_theme().
@@ -39,21 +144,15 @@ function graphql_twig_theme($existing, $type, $theme, $path) {
39144 continue;
40145 }
41146
42- $content = file_get_contents($file->uri);
43-
44- preg_match(GraphQLNodeVisitor::$GRAPHQL_TWIG_REGEX, $content, $matches);
45- if (array_key_exists('query', $matches)) {
46- $source = (new \Youshido\GraphQL\Parser\Parser())->parse($matches['query']);
147+ if ($query = graphql_twig_get_query($file->uri)) {
47148 $themeRegistry[$hook] = [
48149 'template' => $template,
49150 'path' => dirname($file->uri),
50151 'theme path' => $path,
51152 'variables' => [],
52153 ];
53-
54- foreach ($source['variables'] as $variable) {
55- /** @var \Youshido\GraphQL\Parser\Ast\ArgumentValue\Variable $variable */
56- $themeRegistry[$hook]['variables'][$variable->getName()] = null;
154+ foreach (graphql_twig_get_query_variables($query) as $var) {
155+ $themeRegistry[$hook]['variables'][$var] = NULL;
57156 }
58157 }
59158 }
@@ -64,8 +163,12 @@ function graphql_twig_theme($existing, $type, $theme, $path) {
64163
65164/**
66165 * Implements hook_module_implements_alter().
166+ *
167+ * Make sure graphql_twig_theme runs last to not accidentially override existing
168+ * theme hooks.
67169 */
68170function graphql_twig_module_implements_alter(&$implementations, $hook) {
171+
69172 if ($hook == 'theme') {
70173 unset($implementations['graphql_twig']);
71174 $implementations['graphql_twig'] = FALSE;
0 commit comments