Passed
Push — 2.x ( e8dd26...ba8025 )
by Quentin
07:25
created

BlockMaker::getIconFile()   A

Complexity

Conditions 6
Paths 2

Size

Total Lines 26
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 6

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 6
eloc 15
nc 2
nop 2
dl 0
loc 26
ccs 14
cts 14
cp 1
crap 6
rs 9.2222
c 2
b 0
f 0
1
<?php
2
3
namespace A17\Twill\Services\Blocks;
4
5
use Illuminate\Console\Command;
6
use Illuminate\Filesystem\Filesystem;
7
use Illuminate\Support\Str;
8
use Mockery\Exception;
9
use SplFileInfo;
10
11
class BlockMaker
12
{
13
    /**
14
     * @var Filesystem
15
     */
16
    protected $files;
17
18
    /**
19
     * @var \A17\Twill\Services\Blocks\BlockCollection
20
     */
21
    protected $blockCollection;
22
23
    /**
24
     * @var \Illuminate\Console\Command
25
     */
26
    protected $command;
27
28
    /**
29
     * @var \A17\Twill\Services\Blocks\Block
30
     */
31
    protected $blockBase;
32
33
    /**
34
     * @var string`
0 ignored issues
show
Documentation Bug introduced by
The doc comment string` at position 0 could not be parsed: Unknown type name 'string`' at position 0 in string`.
Loading history...
35
     */
36
    protected $icon;
37
38
    /**
39
     * @param Filesystem $files
40
     * @param \A17\Twill\Services\Blocks\BlockCollection $blockCollection
41
     */
42 69
    public function __construct(
43
        Filesystem $files,
44
        BlockCollection $blockCollection
45
    ) {
46 69
        $this->files = $files;
47
48 69
        $this->blockCollection = $blockCollection;
49 69
    }
50
51
    /**
52
     * @return \A17\Twill\Services\Blocks\BlockCollection
53
     */
54 5
    public function getBlockCollection()
55
    {
56 5
        return $this->blockCollection;
57
    }
58
59
    /**
60
     * Make a new block.
61
     *
62
     * @param $blockName
63
     * @param $baseName
64
     * @param $iconName
65
     * @return mixed
66
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
67
     * @throws \Exception
68
     */
69 5
    public function make($blockName, $baseName, $iconName)
70
    {
71 5
        $this->info('Creating block...');
72
73
        if (
74 5
            !$this->checkBlockStub($baseName) ||
75 4
            !$this->checkIconFile($iconName) ||
76 3
            !$this->checkBlockBaseFormat(
77 5
                $stubFileName = $this->blockBase->file->getPathName()
78
            )
79
        ) {
80 2
            return false;
81
        }
82
83
        if (
84 3
            !$this->checkBlockFile(
85 3
                $blockFile = $this->makeBlockPath(
86 3
                    $blockIdentifier = $this->makeBlockIdentifier($blockName)
87
                )
88
            )
89
        ) {
90 1
            return false;
91
        }
92
93 3
        $this->blockBase = $this->makeBlock(
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->makeBlock($blockN...conName, $stubFileName) of type string is incompatible with the declared type A17\Twill\Services\Blocks\Block of property $blockBase.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
94 3
            $blockName,
95
            $iconName,
96
            $stubFileName
97
        );
98
99
        if (
100 3
            !$this->checkRepeaters(
101 3
                $repeaters = $this->generateRepeaters(
102 3
                    $baseName,
103
                    $blockIdentifier,
104 3
                    $this->blockBase
105
                )
106
            )
107
        ) {
108
            return false;
109
        }
110
111 3
        return $this->saveAllFiles(
112 3
            $blockName,
113
            $blockFile,
114
            $repeaters,
115
            $blockIdentifier
116
        );
117
    }
118
119
    /**
120
     * @param $baseName
121
     * @return bool
122
     * @throws \Exception
123
     */
124 5
    protected function checkBlockStub($baseName)
125
    {
126 5
        if (blank($this->blockBase = $this->getBlockByName($baseName))) {
127 1
            $this->error("Block '{$baseName}' doesn't exists.");
128
129 1
            return false;
130
        }
131
132 4
        return true;
133
    }
134
135
    /**
136
     * @param $iconName
137
     * @return bool
138
     */
139 4
    protected function checkIconFile($iconName)
140
    {
141 4
        if (blank($this->icon = $this->getIconFile($iconName))) {
142 1
            $this->error("Icon '{$iconName}' doesn't exists.");
143
144 1
            return false;
145
        }
146
147 3
        return true;
148
    }
149
150
    /**
151
     * @param $blockFile
152
     * @return bool
153
     */
154 3
    protected function checkBlockFile($blockFile)
155
    {
156 3
        $this->info("File: {$blockFile}");
157
158 3
        if ($this->files->exists($blockFile)) {
159 1
            $this->error('A file with the same name already exists.');
160
161 1
            return false;
162
        }
163
164 3
        return true;
165
    }
166
167 3
    protected function checkBlockBaseFormat($stubFileName)
168
    {
169 3
        if (!$this->blockBase->isNewFormat) {
170
            $this->error(
171
                "The block file '{$stubFileName}' format is the old one."
172
            );
173
174
            $this->error('Please upgrade it before using as template.');
175
176
            return false;
177
        }
178
179 3
        return true;
180
    }
181
182 3
    protected function checkRepeaters($repeaters)
183
    {
184 3
        foreach ($repeaters as $repeater) {
185 1
            $this->info("Repeater file: {$repeater['newRepeaterPath']}");
186
187 1
            if ($this->files->exists($repeater['newRepeaterPath'])) {
188
                $this->error(
189
                    'A repeater file with the same name already exists.'
190
                );
191
192
                return false;
193
            }
194
        }
195
196 3
        return true;
197
    }
198
199
    /**
200
     * @param $blockName
201
     * @param $iconName
202
     * @param $stubFileName
203
     * @param null|string $stub
204
     * @return string|string[]|null
205
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
206
     */
207 3
    public function makeBlock(
208
        $blockName,
209
        $iconName,
210
        $stubFileName = null,
211
        $stub = null
212
    ) {
213 3
        $stub = $stub ?? $this->files->get($stubFileName);
214
215 3
        $title = $this->makeBlockTitle($blockName);
216
217 3
        $stub = preg_replace(
218
            [
219 3
                "/@twillPropTitle\('(.*)'\)/",
220
                "/@twillBlockTitle\('(.*)'\)/",
221
                "/@twillRepeaterTitle\('(.*)'\)/",
222
            ],
223
            [
224 3
                "@twillPropTitle('{$title}')",
225 3
                "@twillBlockTitle('{$title}')",
226 3
                "@twillRepeaterTitle('{$title}')",
227
            ],
228
            $stub
229
        );
230
231 3
        $stub = preg_replace(
232
            [
233 3
                "/@twillPropGroup\('twill'\)/",
234
                "/@twillBlockGroup\('twill'\)/",
235
                "/@twillRepeaterGroup\('twill'\)/",
236
            ],
237
            [
238 3
                "@twillPropGroup('app')",
239
                "@twillBlockGroup('app')",
240
                "@twillRepeaterGroup('app')",
241
            ],
242
            $stub
243
        );
244
245 3
        $stub = preg_replace(
246
            [
247 3
                "/@twillPropIcon\('(.*)'\)/",
248
                "/@twillBlockIcon\('(.*)'\)/",
249
            ],
250
            [
251 3
                "@twillPropIcon('{$iconName}')",
252 3
                "@twillBlockIcon('{$iconName}')",
253
            ],
254
            $stub
255
        );
256
257 3
        $stub = preg_replace(
258
            [
259 3
                "/@twillPropCompiled\('(.*)'\)\n/",
260
                "/@twillBlockCompiled\('(.*)'\)\n/",
261
                "/@twillRepeaterCompiled\('(.*)'\)\n/",
262
            ],
263 3
            "",
264
            $stub
265
        );
266
267 3
        $stub = preg_replace(
268
            [
269 3
                "/@twillPropComponent\('(.*)'\)\n/",
270
                "/@twillBlockComponent\('(.*)'\)\n/",
271
                "/@twillRepeaterComponent\('(.*)'\)\n/",
272
            ],
273 3
            "",
274
            $stub
275
        );
276
277 3
        return $stub;
278
    }
279
280
    /**
281
     * @param $blockName
282
     * @return string
283
     * @throws \Exception
284
     */
285 3
    protected function makeBlockIdentifier($blockName)
286
    {
287 3
        return (new Block(
288 3
            $this->blockBase->file,
289 3
            $this->blockBase->type,
290 3
            $this->blockBase->source
291 3
        ))->makeName($blockName);
292
    }
293
294
    /**
295
     * @param string $blockIdentifier
296
     * @param string $type
297
     * @return string
298
     */
299 3
    protected function makeBlockPath(string $blockIdentifier, $type = 'block')
300
    {
301 3
        $destination = config(
302 3
            "twill.block_editor.directories.destination.{$type}s"
303
        );
304
305 3
        if (!$this->files->exists($destination)) {
306
            if (
307
                !config('twill.block_editor.directories.destination.make_dir')
308
            ) {
309
                throw new Exception(
310
                    "Destination directory does not exists: {$destination}"
311
                );
312
            }
313
314
            $this->files->makeDirectory($destination, 0755, true);
315
        }
316
317 3
        return "{$destination}/{$blockIdentifier}.blade.php";
318
    }
319
320
    /**
321
     * @param $string
322
     * @return string
323
     */
324 3
    public function makeBlockTitle($string)
325
    {
326 3
        $string = Str::kebab($string);
327
328 3
        $string = str_replace(['-', '_'], ' ', $string);
329
330 3
        return Str::title($string);
331
    }
332
333
    /**
334
     * @param $block
335
     * @param array $sources
336
     * @return mixed
337
     * @throws \Exception
338
     */
339 5
    public function getBlockByName($block, $sources = [])
340
    {
341 5
        return $this->blockCollection->findByName($block, $sources);
342
    }
343
344
    /**
345
     * @param $icon
346
     * @return mixed
347
     */
348 4
    public function getIconFile($icon, $addExtension = true)
349
    {
350 4
        if ($addExtension) {
351 4
            $icon .= '.svg';
352
        }
353
354 4
        return collect(
355 4
            config('twill.block_editor.directories.source.icons')
356
        )->reduce(function ($keep, $path) use ($icon) {
357 4
            if ($keep) {
358 3
                return $keep;
359
            }
360
361 4
            if (!$this->files->exists($path)) {
362 1
                return null;
363
            }
364
365
            return collect($this->files->files($path))->reduce(function ($keep, SplFileInfo $file) use ($icon) {
366 4
                if ($keep) {
367 3
                    return $keep;
368
                }
369
370 4
                return $file->getFilename() === $icon ? $file->getPathName() : null;
371 4
            }, null);
372
373 4
        }, null);
374
    }
375
376
    /**
377
     * @param $baseName
378
     * @param $blockName
379
     * @param $blockBase
380
     * @return \Illuminate\Support\Collection
381
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
382
     */
383 3
    public function generateRepeaters($baseName, $blockName, &$blockBase)
384
    {
385 3
        preg_match_all(
386 3
            '/@formField(.*\'repeater\'.*\[.*=>.*\'(.*)\'].*)/',
387
            $blockBase,
388
            $matches
389
        );
390
391 3
        $repeaters = collect();
392
393 3
        if (count($matches) === 0) {
394
            return null;
395
        }
396
397 3
        foreach ($matches[2] as $index => $repeaterName) {
398 1
            if (Str::startsWith($repeaterName, $baseName)) {
399 1
                $newRepeater = $this->createRepeater(
400 1
                    $repeaterName,
401
                    $baseName,
402
                    $blockName,
403
                    $blockBase,
404 1
                    $matches[0][$index]
405
                );
406
407
                // Get the update version of the block stub, to be used on next repeaters
408 1
                $blockBase = $newRepeater['newBlockStub'];
409
410 1
                $oldRepeaterTag = $matches[0][0];
411 1
                $newRepeaterTag = str_replace(
412 1
                    "'{$repeaterName}'",
413 1
                    "'{$newRepeater['newRepeaterName']}'",
414
                    $oldRepeaterTag
415
                );
416
417 1
                $blockBase = str_replace(
418 1
                    $oldRepeaterTag,
419
                    $newRepeaterTag,
420
                    $blockBase
421
                );
422
423 1
                $repeaters->push($newRepeater);
424
            }
425
        }
426
427 3
        return $repeaters;
428
    }
429
430
    /**
431
     * @param $repeaterName
432
     * @param $baseName
433
     * @param $blockName
434
     * @param $blockBase
435
     * @param $blockString
436
     * @return array
437
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
438
     * @throws \Exception
439
     */
440 1
    public function createRepeater(
441
        $repeaterName,
442
        $baseName,
443
        $blockName,
444
        $blockBase,
445
        $blockString
446
    ) {
447 1
        $baseRepeater = $this->blockCollection->findByName($repeaterName);
448
449
        return [
450 1
            'baseRepeater' => $baseRepeater,
451
452
            'newRepeaterName' => ($newRepeaterName =
453 1
                $blockName . Str::after($repeaterName, $baseName)),
454
455 1
            'newRepeaterStub' => $this->makeBlock(
456 1
                $newRepeaterName,
457 1
                null,
458 1
                null,
459 1
                $baseRepeater->contents
460
            ),
461
462 1
            'newRepeaterTitle' => $this->makeBlockTitle($newRepeaterName),
463
464 1
            'newRepeaterPath' => $this->makeBlockPath(
465 1
                $newRepeaterName,
466 1
                Block::TYPE_REPEATER
467
            ),
468
469 1
            'newBlockString' => ($newBlockString = str_replace(
470 1
                "'{$repeaterName}'",
471 1
                "'{$newRepeaterName}'",
472
                $blockString
473
            )),
474
475 1
            'newBlockStub' => str_replace(
476 1
                $blockString,
477
                $newBlockString,
478
                $blockBase
479
            ),
480
        ];
481
    }
482
483 3
    public function put($filePath, $contents)
484
    {
485 3
        $directory = dirname($filePath);
486
487 3
        if (!$this->files->exists($directory)) {
488
            $this->files->makeDirectory($directory, 0755, true);
489
        }
490
491 3
        $this->files->put($filePath, $contents);
492 3
    }
493
494
    /**
495
     * @param $blockName
496
     * @param string $blockFile
497
     * @param \Illuminate\Support\Collection $repeaters
498
     * @param string $blockIdentifier
499
     * @return bool
500
     */
501 3
    protected function saveAllFiles(
502
        $blockName,
503
        string $blockFile,
504
        $repeaters,
505
        string $blockIdentifier
506
    ) {
507 3
        $this->put($blockFile, $this->blockBase);
508
509 3
        $this->info("Block {$blockName} was created.");
510
511 3
        foreach ($repeaters as $repeater) {
512 1
            $this->put(
513 1
                $repeater['newRepeaterPath'],
514 1
                $repeater['newRepeaterStub']
515
            );
516
        }
517
518 3
        $this->info("Block is ready to use with the name '{$blockIdentifier}'");
519
520 3
        return true;
521
    }
522
523
    /**
524
     * @param \Illuminate\Console\Command $command
525
     * @return BlockMaker
526
     */
527 5
    public function setCommand(Command $command)
528
    {
529 5
        $this->command = $command;
530
531 5
        return $this;
532
    }
533
534
    /**
535
     * @param $message
536
     */
537 5
    public function info($message)
538
    {
539 5
        if ($this->command) {
540 5
            $this->command->displayInfo($message);
541
        }
542 5
    }
543
544
    /**
545
     * @param $message
546
     */
547 3
    public function error($message)
548
    {
549 3
        if ($this->command) {
550 3
            $this->command->displayError($message);
551
        }
552 3
    }
553
}
554