1
<?php
2

3
namespace Nuwave\Lighthouse\Console;
4

5
use Illuminate\Support\Collection;
6
use Illuminate\Support\Str;
7
use Nuwave\Lighthouse\Support\Contracts\ArgBuilderDirective;
8
use Nuwave\Lighthouse\Support\Contracts\ArgDirective;
9
use Nuwave\Lighthouse\Support\Contracts\ArgDirectiveForArray;
10
use Nuwave\Lighthouse\Support\Contracts\ArgManipulator;
11
use Nuwave\Lighthouse\Support\Contracts\ArgResolver;
12
use Nuwave\Lighthouse\Support\Contracts\ArgTransformerDirective;
13
use Nuwave\Lighthouse\Support\Contracts\FieldManipulator;
14
use Nuwave\Lighthouse\Support\Contracts\FieldMiddleware;
15
use Nuwave\Lighthouse\Support\Contracts\FieldResolver;
16
use Nuwave\Lighthouse\Support\Contracts\TypeExtensionManipulator;
17
use Nuwave\Lighthouse\Support\Contracts\TypeManipulator;
18
use Nuwave\Lighthouse\Support\Contracts\TypeMiddleware;
19
use Nuwave\Lighthouse\Support\Contracts\TypeResolver;
20
use Symfony\Component\Console\Input\InputOption;
21

22
class DirectiveCommand extends LighthouseGeneratorCommand
23
{
24
    const ARGUMENT_INTERFACES = [
25
        ArgTransformerDirective::class,
26
        ArgBuilderDirective::class,
27
        ArgResolver::class,
28
        ArgManipulator::class,
29
    ];
30

31
    const FIELD_INTERFACES = [
32
        FieldResolver::class,
33
        FieldMiddleware::class,
34
        FieldManipulator::class,
35
    ];
36

37
    const TYPE_INTERFACES = [
38
        TypeManipulator::class,
39
        TypeMiddleware::class,
40
        TypeResolver::class,
41
        TypeExtensionManipulator::class,
42
    ];
43

44
    protected $name = 'lighthouse:directive';
45

46
    protected $description = 'Create a class for a custom schema directive.';
47

48
    /**
49
     * The type of class being generated.
50
     *
51
     * @var string
52
     */
53
    protected $type = 'Directive';
54

55
    /**
56
     * The required imports.
57
     *
58
     * @var \Illuminate\Support\Collection<string>
59
     */
60
    protected $imports;
61

62
    /**
63
     * The implemented interfaces.
64
     *
65
     * @var \Illuminate\Support\Collection<string>
66
     */
67
    protected $implements;
68

69
    /**
70
     * The method stubs.
71
     *
72
     * @var \Illuminate\Support\Collection<string>
73
     */
74
    protected $methods;
75

76 0
    protected function getNameInput(): string
77
    {
78 0
        return parent::getNameInput().'Directive';
79
    }
80

81 0
    protected function namespaceConfigKey(): string
82
    {
83 0
        return 'directives';
84
    }
85

86
    /**
87
     * @param  string  $name
88
     *
89
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
90
     */
91 0
    protected function buildClass($name): string
92
    {
93 0
        $this->imports = new Collection();
94 0
        $this->implements = new Collection();
95 0
        $this->methods = new Collection();
96

97 0
        $stub = parent::buildClass($name);
98

99 0
        $forType = $this->option('type');
100 0
        $forField = $this->option('field');
101 0
        $forArgument = $this->option('argument');
102

103 0
        if (! $forType && ! $forField && ! $forArgument) {
104 0
            throw new \Exception('Must specify at least one of: --type, --field or --argument');
105
        }
106

107 0
        if ($forType) {
108 0
            $this->askForInterfaces(self::TYPE_INTERFACES);
109
        }
110

111 0
        if ($forField) {
112 0
            $this->askForInterfaces(self::FIELD_INTERFACES);
113
        }
114

115 0
        if ($forArgument) {
116
            // Arg directives always either implement ArgDirective or ArgDirectiveForArray.
117 0
            if ($this->confirm('Will your argument directive apply to a list of items?')) {
118 0
                $this->implementInterface(ArgDirectiveForArray::class);
119
            } else {
120 0
                $this->implementInterface(ArgDirective::class);
121
            }
122

123 0
            $this->askForInterfaces(self::ARGUMENT_INTERFACES);
124
        }
125

126 0
        $stub = str_replace(
127 0
            '{{ imports }}',
128 0
            $this->imports
129 0
                ->filter()
130 0
                ->unique()
131 0
                ->implode("\n"),
132
            $stub
133
        );
134

135 0
        $stub = str_replace(
136 0
            '{{ methods }}',
137 0
            $this->methods->implode("\n"),
138
            $stub
139
        );
140

141 0
        return str_replace(
142 0
            '{{ implements }}',
143 0
            $this->implements->implode(', '),
144
            $stub
145
        );
146
    }
147

148
    /**
149
     * Ask the user if the directive should implement any of the given interfaces.
150
     *
151
     * @param  array<class-string> $interfaces
152
     */
153 0
    protected function askForInterfaces(array $interfaces): void
154
    {
155 0
        foreach ($interfaces as $interface) {
156 0
            if ($this->confirm("Should the directive implement the {$this->shortName($interface)} middleware?")) {
157 0
                $this->implementInterface($interface);
158
            }
159
        }
160
    }
161

162
    /**
163
     * @param  class-string  $interface
164
     */
165 0
    protected function shortName(string $interface): string
166
    {
167 0
        return Str::afterLast($interface, '\\');
168
    }
169

170
    /**
171
     * @param  class-string  $interface
172
     */
173 0
    protected function implementInterface(string $interface): void
174
    {
175 0
        $shortName = $this->shortName($interface);
176 0
        $this->implements->push($shortName);
177

178 0
        $this->imports->push("use {$interface};");
179 0
        if ($imports = $this->interfaceImports($shortName)) {
180 0
            $imports = explode("\n", $imports);
181 0
            $this->imports->push(...$imports);
182
        }
183

184 0
        if ($methods = $this->interfaceMethods($shortName)) {
185 0
            $this->methods->push($methods);
186
        }
187
    }
188

189 0
    protected function getStub(): string
190
    {
191 0
        return __DIR__.'/stubs/directive.stub';
192
    }
193

194 0
    protected function interfaceMethods(string $interface): ?string
195
    {
196 0
        return $this->getFileIfExists(
197 0
            __DIR__.'/stubs/directives/'.Str::snake($interface).'_methods.stub'
198
        );
199
    }
200

201 0
    protected function interfaceImports(string $interface): ?string
202
    {
203 0
        return $this->getFileIfExists(
204 0
            __DIR__.'/stubs/directives/'.Str::snake($interface).'_imports.stub'
205
        );
206
    }
207

208 0
    protected function getFileIfExists(string $path): ?string
209
    {
210 0
        if (! $this->files->exists($path)) {
211 0
            return null;
212
        }
213

214 0
        return $this->files->get($path);
215
    }
216

217
    /**
218
     * @return array<array<mixed>>
219
     */
220 1
    protected function getOptions(): array
221
    {
222
        return [
223 1
            ['type', null, InputOption::VALUE_NONE, 'Create a directive that can be applied to types.'],
224
            ['field', null, InputOption::VALUE_NONE, 'Create a directive that can be applied to fields.'],
225
            ['argument', null, InputOption::VALUE_NONE, 'Create a directive that can be applied to arguments.'],
226
        ];
227
    }
228
}

Read our documentation on viewing source code .

Loading