Passed
Pull Request — 2.x (#597)
by Antonio Carlos
07:12
created

BlockMaker::generateRepeaters()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 45
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 4.002

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 27
c 1
b 0
f 0
nc 4
nop 3
dl 0
loc 45
ccs 19
cts 20
cp 0.95
crap 4.002
rs 9.488
1
<?php
2
3
namespace A17\Twill\Services\Blocks;
4
5
use SplFileInfo;
6
use Mockery\Exception;
7
use Illuminate\Support\Str;
8
use Illuminate\Console\Command;
9
use Illuminate\Filesystem\Filesystem;
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 68
    public function __construct(
43
        Filesystem $files,
44
        BlockCollection $blockCollection
45
    ) {
46 68
        $this->files = $files;
47
48 68
        $this->blockCollection = $blockCollection;
49 68
    }
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 3
            "/@a17-title\('(.*)'\)/",
219 3
            "@a17-title('{$title}')",
220
            $stub
221
        );
222
223 3
        $stub = preg_replace(
224 3
            "/@a17-group\('twill'\)/",
225 3
            "@a17-group('app')",
226
            $stub
227
        );
228
229 3
        $stub = preg_replace(
230 3
            "/@a17-icon\('(.*)'\)/",
231 3
            "@a17-icon('{$iconName}')",
232
            $stub
233
        );
234
235 3
        return $stub;
236
    }
237
238
    /**
239
     * @param $blockName
240
     * @return string
241
     * @throws \Exception
242
     */
243 3
    protected function makeBlockIdentifier($blockName)
244
    {
245 3
        return (new Block(
246 3
            $this->blockBase->file,
247 3
            $this->blockBase->type,
248 3
            $this->blockBase->source
249 3
        ))->makeName($blockName);
250
    }
251
252
    /**
253
     * @param string $blockIdentifier
254
     * @param string $type
255
     * @return string
256
     */
257 3
    protected function makeBlockPath(string $blockIdentifier, $type = 'block')
258
    {
259 3
        $destination = config(
260 3
            "twill.block_editor.directories.destination.{$type}s"
261
        );
262
263 3
        if (!$this->files->exists($destination)) {
264
            if (
265
                !config('twill.block_editor.directories.destination.make_dir')
266
            ) {
267
                throw new Exception(
268
                    "Destination directory does not exists: {$destination}"
269
                );
270
            }
271
272
            $this->files->makeDirectory($destination, 0755, true);
273
        }
274
275 3
        return "{$destination}/{$blockIdentifier}.blade.php";
276
    }
277
278
    /**
279
     * @param $string
280
     * @return string
281
     */
282 3
    public function makeBlockTitle($string)
283
    {
284 3
        $string = Str::kebab($string);
285
286 3
        $string = str_replace(['-', '_'], ' ', $string);
287
288 3
        return Str::title($string);
289
    }
290
291
    /**
292
     * @param $block
293
     * @param array $sources
294
     * @return mixed
295
     * @throws \Exception
296
     */
297 5
    public function getBlockByName($block, $sources = [])
298
    {
299 5
        return $this->blockCollection->findByName($block, $sources);
300
    }
301
302
    /**
303
     * @param $icon
304
     * @return mixed
305
     */
306 4
    public function getIconFile($icon)
307
    {
308 4
        $icon .= '.svg';
309
310 4
        return collect(
311 4
            $this->files->files(__DIR__ . '/../../../frontend/icons')
312
        )->reduce(function ($keep, SplFileInfo $file) use ($icon) {
313 4
            if ($keep) {
314 3
                return $keep;
315
            }
316
317 4
            return $file->getFilename() === $icon ? $file->getPathName() : null;
318 4
        }, null);
319
    }
320
321
    /**
322
     * @param $baseName
323
     * @param $blockName
324
     * @param $blockBase
325
     * @return \Illuminate\Support\Collection
326
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
327
     */
328 3
    public function generateRepeaters($baseName, $blockName, &$blockBase)
329
    {
330 3
        preg_match_all(
331 3
            '/@formField(.*\'repeater\'.*\[.*=>.*\'(.*)\'].*)/',
332
            $blockBase,
333
            $matches
334
        );
335
336 3
        $repeaters = collect();
337
338 3
        if (count($matches) === 0) {
339
            return null;
340
        }
341
342 3
        foreach ($matches[2] as $index => $repeaterName) {
343 1
            if (Str::startsWith($repeaterName, $baseName)) {
344 1
                $newRepeater = $this->createRepeater(
345 1
                    $repeaterName,
346
                    $baseName,
347
                    $blockName,
348
                    $blockBase,
349 1
                    $matches[0][$index]
350
                );
351
352
                // Get the update version of the block stub, to be used on next repeaters
353 1
                $blockBase = $newRepeater['newBlockStub'];
354
355 1
                $oldRepeaterTag = $matches[0][0];
356 1
                $newRepeaterTag = str_replace(
357 1
                    "'{$repeaterName}'",
358 1
                    "'{$newRepeater['newRepeaterName']}'",
359
                    $oldRepeaterTag
360
                );
361
362 1
                $blockBase = str_replace(
363 1
                    $oldRepeaterTag,
364
                    $newRepeaterTag,
365
                    $blockBase
366
                );
367
368 1
                $repeaters->push($newRepeater);
369
            }
370
        }
371
372 3
        return $repeaters;
373
    }
374
375
    /**
376
     * @param $repeaterName
377
     * @param $baseName
378
     * @param $blockName
379
     * @param $blockBase
380
     * @param $blockString
381
     * @return array
382
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
383
     * @throws \Exception
384
     */
385 1
    public function createRepeater(
386
        $repeaterName,
387
        $baseName,
388
        $blockName,
389
        $blockBase,
390
        $blockString
391
    ) {
392 1
        $baseRepeater = $this->blockCollection->findByName($repeaterName);
393
394
        return [
395 1
            'baseRepeater' => $baseRepeater,
396
397
            'newRepeaterName' => ($newRepeaterName =
398 1
                $blockName . Str::after($repeaterName, $baseName)),
399
400 1
            'newRepeaterStub' => $this->makeBlock(
401 1
                $newRepeaterName,
402 1
                null,
403 1
                null,
404 1
                $baseRepeater->contents
405
            ),
406
407 1
            'newRepeaterTitle' => $this->makeBlockTitle($newRepeaterName),
408
409 1
            'newRepeaterPath' => $this->makeBlockPath(
410 1
                $newRepeaterName,
411 1
                Block::TYPE_REPEATER
412
            ),
413
414 1
            'newBlockString' => ($newBlockString = str_replace(
415 1
                "'{$repeaterName}'",
416 1
                "'{$newRepeaterName}'",
417
                $blockString
418
            )),
419
420 1
            'newBlockStub' => str_replace(
421 1
                $blockString,
422
                $newBlockString,
423
                $blockBase
424
            ),
425
        ];
426
    }
427
428 3
    public function put($filePath, $contents)
429
    {
430 3
        $directory = dirname($filePath);
431
432 3
        if (!$this->files->exists($directory)) {
433
            $this->files->makeDirectory($directory, 0755, true);
434
        }
435
436 3
        $this->files->put($filePath, $contents);
437 3
    }
438
439
    /**
440
     * @param $blockName
441
     * @param string $blockFile
442
     * @param \Illuminate\Support\Collection $repeaters
443
     * @param string $blockIdentifier
444
     * @return bool
445
     */
446 3
    protected function saveAllFiles(
447
        $blockName,
448
        string $blockFile,
449
        $repeaters,
450
        string $blockIdentifier
451
    ) {
452 3
        $this->put($blockFile, $this->blockBase);
453
454 3
        $this->info("Block {$blockName} was created.");
455
456 3
        foreach ($repeaters as $repeater) {
457 1
            $this->put(
458 1
                $repeater['newRepeaterPath'],
459 1
                $repeater['newRepeaterStub']
460
            );
461
        }
462
463 3
        $this->info("Block is ready to use with the name '{$blockIdentifier}'");
464
465 3
        return true;
466
    }
467
468
    /**
469
     * @param \Illuminate\Console\Command $command
470
     * @return BlockMaker
471
     */
472 5
    public function setCommand(Command $command)
473
    {
474 5
        $this->command = $command;
475
476 5
        return $this;
477
    }
478
479
    /**
480
     * @param $message
481
     */
482 5
    public function info($message)
483
    {
484 5
        if ($this->command) {
485 5
            $this->command->displayInfo($message);
486
        }
487 5
    }
488
489
    /**
490
     * @param $message
491
     */
492 3
    public function error($message)
493
    {
494 3
        if ($this->command) {
495 3
            $this->command->displayError($message);
496
        }
497 3
    }
498
}
499