Completed
Pull Request — 8.x-3.x (#448)
by Sebastian
01:59
created

QueryRouteEnhancer::enhanceSingle()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 11
nc 5
nop 3
dl 0
loc 17
rs 9.2
c 0
b 0
f 0
1
<?php
2
3
namespace Drupal\graphql\Routing;
4
5
use Drupal\Core\Routing\Enhancer\RouteEnhancerInterface;
6
use Drupal\graphql\QueryProvider\QueryProviderInterface;
7
use Symfony\Component\HttpFoundation\Request;
8
use Symfony\Component\Routing\Route;
9
10
class QueryRouteEnhancer implements RouteEnhancerInterface {
11
12
  const SINGLE = 'single';
13
  const BATCH = 'batch';
14
15
  /**
16
   * The query provider service.
17
   *
18
   * @var \Drupal\graphql\QueryProvider\QueryProviderInterface
19
   */
20
  protected $queryProvider;
21
22
  /**
23
   * QueryRouteEnhancer constructor.
24
   *
25
   * @param \Drupal\graphql\QueryProvider\QueryProviderInterface $queryProvider
26
   *   The query provider service.
27
   */
28
  public function __construct(QueryProviderInterface $queryProvider) {
29
    $this->queryProvider = $queryProvider;
30
  }
31
32
33
  /**
34
   * {@inheritdoc}
35
   */
36
  public function applies(Route $route) {
37
    return $route->hasDefault('_graphql');
38
  }
39
40
  /**
41
   * {@inheritdoc}
42
   */
43
  public function enhance(array $defaults, Request $request) {
44
    if (!empty($defaults['_controller'])) {
45
      return $defaults;
46
    }
47
48
    $params = $this->extractParams($request);
49
    if ($enhanced = $this->enhanceSingle($defaults, $params, $request)) {
50
      return $enhanced;
51
    }
52
53
    if ($enhanced = $this->enhanceBatch($defaults, $params, $request)) {
54
      return $enhanced;
55
    }
56
57
    // By default we assume a 'single' request. This is going to fail in the
58
    // graphql processor due to a missing query string but at least provides
59
    // the right format for the client to act upon.
60
    return $defaults + [
61
      '_controller' => $defaults['_graphql']['single'],
62
    ];
63
  }
64
65
  /**
66
   * Attempts to enhance the request as a batch query.
67
   *
68
   * @param array $defaults
69
   *   The controller defaults.
70
   * @param array $params
71
   *   The query parameters.
72
   * @param \Symfony\Component\HttpFoundation\Request $request
73
   *   The request object.
74
   *
75
   * @return array|bool
76
   *   The enhanced controller defaults.
77
   */
78
  protected function enhanceBatch(array $defaults, array $params, Request $request) {
79
    // PHP 5.5.x does not yet support the ARRAY_FILTER_USE_KEYS constant.
80
    $keys = array_filter(array_keys($params), function($index) {
81
      return is_numeric($index);
82
    });
83
84
    $queries = array_intersect_key($params, array_flip($keys));
85
    if (!isset($queries[0])) {
86
      return FALSE;
87
    }
88
89
    if (array_keys($queries) !== range(0, count($queries) - 1)) {
90
      // If this is not a continuously numeric array, don't do anything.
91
      return FALSE;
92
    }
93
94
    return $defaults + [
95
      '_controller' => $defaults['_graphql']['multiple'],
96
      'queries' => $queries,
97
      'type' => static::BATCH,
98
    ];
99
  }
100
101
  /**
102
   * Attempts to enhance the request as a single query.
103
   *
104
   * @param array $defaults
105
   *   The controller defaults.
106
   * @param array $params
107
   *   The query parameters.
108
   * @param \Symfony\Component\HttpFoundation\Request $request
109
   *   The request object.
110
   *
111
   * @return array|boolean
112
   *   The enhanced controller defaults.
113
   */
114
  protected function enhanceSingle(array $defaults, array $params, Request $request) {
115
    $values = $params + [
116
      'query' => $this->getQuery($params),
117
      'variables' => [],
118
    ];
119
120
    if (empty($values['query'])) {
121
      return FALSE;
122
    }
123
124
    return $defaults + [
125
      '_controller' => $defaults['_graphql']['single'],
126
      'query' => $values['query'] ?: '',
127
      'variables' => $values['variables'] ?: [],
128
      'type' => static::SINGLE,
129
    ];
130
  }
131
132
  /**
133
   * Extract an associative array of query parameters from the request.
134
   *
135
   * If the given request does not have any POST body content it uses the GET
136
   * query parameters instead.
137
   *
138
   * @param \Symfony\Component\HttpFoundation\Request $request
139
   *   The request object.
140
   *
141
   * @return array
142
   *   An associative array of query parameters.
143
   */
144
  protected function extractParams(Request $request) {
145
    $values = ($content = $request->getContent()) ? json_decode($content, TRUE) : $request->query->all();
146
147
    return array_map(function($value) {
148
      if (!is_string($value)) {
149
        return $value;
150
      }
151
152
      $decoded = json_decode($value, TRUE);
153
      return ($decoded != $value) && $decoded ? $decoded : $value;
154
    }, $values ?: []);
155
  }
156
157
  /**
158
   * Resolves a query string.
159
   *
160
   * @param array $params
161
   *   The query parameters.
162
   *
163
   * @return string|null
164
   *   The resolved query string.
165
   */
166
  protected function getQuery(array $params) {
167
    if (!empty($params['query'])) {
168
      return $params['query'];
169
    }
170
171
    return $this->queryProvider->getQuery($params);
172
  }
173
174
}
175