• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

luttje / filament-user-attributes / 6917010354

18 Nov 2023 11:58PM UTC coverage: 76.475% (+13.3%) from 63.165%
6917010354

push

github

luttje
Merge branch 'main' of https://github.com/luttje/filament-user-attributes

1141 of 1492 relevant lines covered (76.47%)

28.91 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

7.37
/src/Commands/WizardStepResources.php
1
<?php
2

3
namespace Luttje\FilamentUserAttributes\Commands;
4

5
use Illuminate\Console\Command;
6
use Illuminate\Support\Collection;
7
use Luttje\FilamentUserAttributes\CodeGeneration\CodeEditor;
8
use Luttje\FilamentUserAttributes\Contracts\ConfiguresUserAttributesContract;
9
use Luttje\FilamentUserAttributes\Contracts\UserAttributesConfigContract;
10
use Luttje\FilamentUserAttributes\Facades\FilamentUserAttributes;
11
use Luttje\FilamentUserAttributes\Traits\UserAttributesResource;
12

13
class WizardStepResources extends Command
14
{
15
    public function __construct()
4✔
16
    {
17
        $this->signature = 'filament-user-attributes:wizard-resources';
4✔
18
        $this->description = 'Wizard to help setup your resources with Filament User Attributes';
4✔
19

20
        parent::__construct();
4✔
21
    }
22

23
    public function handle()
4✔
24
    {
25
        if (!$this->promptForResourcesSetup()) {
4✔
26
            return;
4✔
27
        }
28

29
        $this->finalizeResourcesSetup();
×
30
    }
31

32
    protected function promptForResourcesSetup(): bool
4✔
33
    {
34
        $models = $this->getModelsImplementingConfiguresUserAttributesContract();
4✔
35

36
        if ($models->isEmpty()) {
4✔
37
            $this->info('(Failing) No models found to have been setup to configure user attributes.');
×
38
            return false;
×
39
        }
40

41
        return $this->confirm('Do you want to setup any resources to display and edit user attributes?', true);
4✔
42
    }
43

44
    protected function getModelsImplementingConfiguresUserAttributesContract(): Collection
4✔
45
    {
46
        return collect(FilamentUserAttributes::getConfigurableModels(configuredOnly: false))
4✔
47
            ->filter(fn ($model) => in_array(ConfiguresUserAttributesContract::class, class_implements($model)));
4✔
48
    }
49

50
    protected function getChosenResources(array $resources): array
×
51
    {
52
        return $this->choice(
×
53
            'Which resources should display and edit user attributes?',
×
54
            $resources,
×
55
            null,
×
56
            null,
×
57
            true
×
58
        );
×
59
    }
60

61
    protected function finalizeResourcesSetup()
×
62
    {
63
        $resources = FilamentUserAttributes::getConfigurableResources(configuredOnly: false);
×
64
        $resources = array_keys($resources);
×
65
        $chosenResources = $this->getChosenResources($resources);
×
66

67
        if (empty($chosenResources)) {
×
68
            return;
×
69
        }
70

71
        $this->setupResources($chosenResources);
×
72

73
        return;
×
74
    }
75

76
    protected function setupResources(array $resources)
×
77
    {
78
        foreach ($resources as $resource) {
×
79
            $this->setupResource($resource);
×
80
        }
81
    }
82

83
    protected function setupResource(string $resource)
×
84
    {
85
        $file = FilamentUserAttributes::findResourceFilePath($resource);
×
86

87
        $editor = CodeEditor::make();
×
88
        $editor->editFileWithBackup($file, function ($code) use ($editor, $resource) {
×
89
            $code = $editor->addTrait($code, UserAttributesResource::class);
×
90
            $code = $editor->addInterface($code, UserAttributesConfigContract::class);
×
91
            $code = $editor->addMethod($code, 'getUserAttributesConfig', function () use ($resource) {
×
92
                $method = new \PhpParser\Node\Stmt\ClassMethod('getUserAttributesConfig', [
×
93
                    'flags' => \PhpParser\Node\Stmt\Class_::MODIFIER_PUBLIC | \PhpParser\Node\Stmt\Class_::MODIFIER_STATIC,
×
94
                    'returnType' => new \PhpParser\Node\NullableType(
×
95
                        new \PhpParser\Node\Name\FullyQualified(ConfiguresUserAttributesContract::class)
×
96
                    ),
×
97
                ]);
×
98
                $method->stmts = $this->guessTemplate($resource, $this->getModelsImplementingConfiguresUserAttributesContract());
×
99
                return $method;
×
100
            });
×
101
            $code = self::applyWrapperMethod($editor, $code, 'form', 'schema', 'withUserAttributeFields');
×
102
            $code = self::applyWrapperMethod($editor, $code, 'table', 'columns', 'withUserAttributeColumns');
×
103
            return $code;
×
104
        });
×
105
    }
106

107
    private static function applyWrapperMethod($editor, $contents, $parentMethodName, $methodNameToWrapInside, $methodNameToCall)
×
108
    {
109
        return $editor->modifyMethod(
×
110
            $contents,
×
111
            $parentMethodName,
×
112
            function ($method) use ($editor, $methodNameToWrapInside, $methodNameToCall) {
×
113
                /** @var \PhpParser\Node\Stmt\ClassMethod */
114
                $method = $method;
×
115
                $firstParameter = $method->params[0];
×
116
                $schema = $editor->findCall(
×
117
                    $method->stmts,
×
118
                    $firstParameter->var->name,
×
119
                    $methodNameToWrapInside
×
120
                );
×
121

122
                if (
123
                    $schema->args[0]->value instanceof \PhpParser\Node\Expr\StaticCall
×
124
                    && $schema->args[0]->value->name->name === $methodNameToCall
×
125
                ) {
126
                    return $method;
×
127
                }
128

129
                $schema->args = [
×
130
                    new \PhpParser\Node\Arg(
×
131
                        new \PhpParser\Node\Expr\StaticCall(
×
132
                            new \PhpParser\Node\Name('self'),
×
133
                            $methodNameToCall,
×
134
                            $schema->args
×
135
                        )
×
136
                    ),
×
137
                ];
×
138

139
                return $method;
×
140
            }
×
141
        );
×
142
    }
143

144
    protected function guessTemplate(string $resource, Collection $models)
×
145
    {
146
        $this->warn("\nWe will prepare the getUserAttributesConfig method for you, but you'll have to finish it for $resource.");
×
147

148
        $model = $models->first();
×
149

150
        if (!$model) {
×
151
            return $this->guessTemplateNoModels($resource);
×
152
        }
153

154
        if (is_subclass_of($model, \Illuminate\Foundation\Auth\User::class)) {
×
155
            return $this->guessTemplateAuthUser();
×
156
        }
157

158
        if (strstr($model, 'Tenant') !== false) {
×
159
            return $this->guessTemplateTenant();
×
160
        }
161

162
        return $this->guessTemplateNoKnownModels($resource, $models);
×
163
    }
164

165
    protected function guessTemplateAuthUser()
×
166
    {
167
        $this->line("\nGuessing that your configuration model may be a user model...");
×
168

169
        return [
×
170
            new \PhpParser\Node\Stmt\Expression(
×
171
                new \PhpParser\Node\Expr\Assign(
×
172
                    new \PhpParser\Node\Expr\Variable('user'),
×
173
                    new \PhpParser\Node\Expr\MethodCall(
×
174
                        new \PhpParser\Node\Expr\StaticCall(
×
175
                            new \PhpParser\Node\Name\FullyQualified(\Illuminate\Support\Facades\Auth::class),
×
176
                            'user'
×
177
                        ),
×
178
                        'get'
×
179
                    )
×
180
                ),
×
181
                [
×
182
                    'comments' => [
×
183
                        new \PhpParser\Comment\Doc(
×
184
                            <<<PHPDOC
×
185
/** @var \App\Models\User */
186
PHPDOC
×
187
                        ),
×
188
                    ],
×
189
                ]
×
190
            ),
×
191
            new \PhpParser\Node\Stmt\Return_(
×
192
                new \PhpParser\Node\Expr\Variable('user')
×
193
            ),
×
194
        ];
×
195
    }
196

197
    protected function guessTemplateTenant()
×
198
    {
199
        $this->line("\nGuessing that your configuration model may be a multi-tenancy model...");
×
200

201
        return [
×
202
            new \PhpParser\Node\Stmt\Expression(
×
203
                new \PhpParser\Node\Expr\Assign(
×
204
                    new \PhpParser\Node\Expr\Variable('tenant'),
×
205
                    new \PhpParser\Node\Expr\StaticCall(
×
206
                        new \PhpParser\Node\Name\FullyQualified(\Filament\Facades\Filament::class),
×
207
                        'getTenant'
×
208
                    ),
×
209
                ),
×
210
                [
×
211
                    'comments' => [
×
212
                        new \PhpParser\Comment\Doc(
×
213
                            <<<PHPDOC
×
214
// TODO: Double-check that this is the correct configuration model for your app.
215
PHPDOC
×
216
                        ),
×
217
                    ],
×
218
                ]
×
219
            ),
×
220
            new \PhpParser\Node\Stmt\Return_(
×
221
                new \PhpParser\Node\Expr\Variable('tenant')
×
222
            ),
×
223
        ];
×
224
    }
225

226
    protected function guessTemplateNoModels(string $resource)
×
227
    {
228
        $this->warn("\nWe didn't find any models that implement the correct interface, so you'll have to do this yourself.");
×
229

230
        return [
×
231
            new \PhpParser\Node\Stmt\Throw_(
×
232
                new \PhpParser\Node\Expr\New_(
×
233
                    new \PhpParser\Node\Name\FullyQualified(\Exception::class),
×
234
                    [
×
235
                        new \PhpParser\Node\Arg(
×
236
                            new \PhpParser\Node\Scalar\String_('You have to implement the getUserAttributesConfig method in ' . $resource . '.')
×
237
                        ),
×
238
                    ]
×
239
                ),
×
240
                [
×
241
                    'comments' => [
×
242
                        new \PhpParser\Comment\Doc(
×
243
                            <<<PHPDOC
×
244
// TODO: You should finish this method and return the model that configures the user attributes.
245
// TODO: We didn't find any models that implement the correct interface, so you'll have to do this yourself.
246
PHPDOC
×
247
                        ),
×
248
                    ],
×
249
                ]
×
250
            ),
×
251
        ];
×
252
    }
253

254
    protected function guessTemplateNoKnownModels(string $resource, Collection $models)
×
255
    {
256
        $this->warn("\nWe didn't recognize any of the models that implement the correct interface, so you'll have to finish this implementation yourself.");
×
257

258
        return [
×
259
            new \PhpParser\Node\Stmt\Throw_(
×
260
                new \PhpParser\Node\Expr\New_(
×
261
                    new \PhpParser\Node\Name\FullyQualified(\Exception::class),
×
262
                    [
×
263
                        new \PhpParser\Node\Arg(
×
264
                            new \PhpParser\Node\Scalar\String_('You have to implement the getUserAttributesConfig method in ' . $resource . '.')
×
265
                        ),
×
266
                    ]
×
267
                ),
×
268
                [
×
269
                    'comments' => [
×
270
                        new \PhpParser\Comment\Doc(
×
271
                            '// TODO: You should finish this method and return the model that configures the user attributes.' .
×
272
                            $models->map(fn ($model) => '\\' . $model . '::class')
×
273
                                ->values()
×
274
                                ->reduce(fn ($carry, $model) => $carry . ($carry ? "\n" : '') . "\t\t// $model", "\t\t// These are the models that implement the correct interface:")
×
275
                        ),
×
276
                    ],
×
277
                ]
×
278
            ),
×
279
        ];
×
280
    }
281
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc