Completed
Push — master ( 99dcbb...40d53b )
by Tomáš
05:19
created

SchemaCacheProvider   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 129
Duplicated Lines 0 %

Test Coverage

Coverage 12.73%

Importance

Changes 0
Metric Value
eloc 53
dl 0
loc 129
ccs 7
cts 55
cp 0.1273
rs 10
c 0
b 0
f 0
wmc 17

8 Methods

Rating   Name   Duplication   Size   Complexity  
B getTypeConfigDecorator() 0 46 9
A save() 0 12 1
A isCached() 0 3 1
A getTypesCacheFile() 0 3 1
A getCacheKey() 0 3 1
A getSchema() 0 15 2
A __construct() 0 8 1
A getSchemaCacheFile() 0 3 1
1
<?php
2
declare(strict_types=1);
3
4
namespace Portiny\GraphQL\GraphQL\Schema;
5
6
use Closure;
7
use GraphQL\Language\AST\DocumentNode;
8
use GraphQL\Language\Parser;
9
use GraphQL\Type\Definition\ObjectType;
10
use GraphQL\Type\Definition\ResolveInfo;
11
use GraphQL\Type\Schema;
12
use GraphQL\Utils\AST;
13
use GraphQL\Utils\BuildSchema;
14
use GraphQL\Utils\SchemaPrinter;
15
use Nette\Utils\FileSystem;
16
use Portiny\GraphQL\Contract\Provider\MutationFieldsProviderInterface;
17
use Portiny\GraphQL\Contract\Provider\QueryFieldsProviderInterface;
18
use Portiny\GraphQL\GraphQL\Type\Types;
19
20
final class SchemaCacheProvider
21
{
22
	/**
23
	 * @var string
24
	 */
25
	private $cacheDir;
26
27
	/**
28
	 * @var QueryFieldsProviderInterface
29
	 */
30
	private $queryFieldsProvider;
31
32
	/**
33
	 * @var MutationFieldsProviderInterface
34
	 */
35
	private $mutationFieldsProvider;
36
37
	/**
38
	 * @var Schema
39
	 */
40
	private $schema;
41
42 2
	public function __construct(
43
		string $cacheDir,
44
		QueryFieldsProviderInterface $queryFieldsProvider,
45
		MutationFieldsProviderInterface $mutationFieldsProvider
46
	) {
47 2
		$this->cacheDir = $cacheDir;
48 2
		$this->queryFieldsProvider = $queryFieldsProvider;
49 2
		$this->mutationFieldsProvider = $mutationFieldsProvider;
50 2
	}
51
52
	public function getSchema(string $cacheKey): Schema
53
	{
54
		if ($this->schema !== null) {
55
			return $this->schema;
56
		}
57
58
		// load types from cache
59
		Types::loadTypesFromClasses(unserialize(FileSystem::read($this->getTypesCacheFile($cacheKey))));
60
61
		// load schema from cache
62
		/** @var DocumentNode $document */
63
		$document = AST::fromArray(require $this->getSchemaCacheFile($cacheKey));
64
		$this->schema = BuildSchema::build($document, $this->getTypeConfigDecorator());
65
66
		return $this->schema;
67
	}
68
69
	public function isCached(string $cacheKey): bool
70
	{
71
		return file_exists($this->getSchemaCacheFile($cacheKey));
72
	}
73
74
	public function save(string $cacheKey, Schema $schema): void
75
	{
76
		// schema cache
77
		$sdl = SchemaPrinter::doPrint($schema);
78
		$documentNode = Parser::parse($sdl);
79
		FileSystem::write(
80
			$this->getSchemaCacheFile($cacheKey),
81
			"<?php\nreturn " . var_export(AST::toArray($documentNode), true) . ';'
82
		);
83
84
		// types cache
85
		FileSystem::write($this->getTypesCacheFile($cacheKey), serialize(Types::getTypeClasses()));
86
	}
87
88 1
	public function getCacheKey(?array $allowedQueries = null, ?array $allowedMutations = null): string
89
	{
90 1
		return md5(serialize($allowedQueries) . serialize($allowedMutations));
91
	}
92
93
	private function getTypesCacheFile(string $cacheKey): string
94
	{
95
		return $this->cacheDir . '/types-' . $cacheKey . '.php';
96
	}
97
98
	private function getSchemaCacheFile(string $cacheKey): string
99
	{
100
		return $this->cacheDir . '/schema-' . $cacheKey . '.php';
101
	}
102
103
	private function getTypeConfigDecorator(): Closure
104
	{
105
		return function (array $typeConfig) {
106
			$typeConfig['resolveField'] = function ($value, $args, $context, ResolveInfo $info) use ($typeConfig) {
107
				$fieldName = (string) $info->fieldName;
108
109
				switch ($typeConfig['name']) {
110
					case 'Query':
111
						$queryField = $this->queryFieldsProvider->getField($fieldName);
112
						if ($queryField) {
113
							return $queryField->resolve($value, $args, $context);
114
						}
115
						break;
116
117
					case 'Mutation':
118
						$mutationField = $this->mutationFieldsProvider->getField($fieldName);
119
						if ($mutationField) {
120
							return $mutationField->resolve($value, $args, $context);
121
						}
122
						break;
123
124
					default:
125
						$type = Types::findByName($typeConfig['name']);
126
						if ($type instanceof ObjectType) {
127
							$fieldNameForResolving = $fieldName;
128
							if ($type->hasField($fieldNameForResolving)) {
129
								$typeField = $type->getField($fieldNameForResolving);
130
								$resolver = $typeField->resolveFn;
131
								if (is_callable($resolver)) {
132
									return $resolver($value, $args, $context, $info);
133
								}
134
							}
135
136
							$resolver = $type->resolveFieldFn;
137
							if (is_callable($resolver)) {
138
								return $resolver($value, $args, $context, $info);
139
							}
140
141
							return null;
142
						}
143
				}
144
145
				return null;
146
			};
147
148
			return $typeConfig;
149
		};
150
	}
151
}
152