Passed
Push — master ( 1438ee...6641ad )
by Quentin
06:42 queued 12s
created

BlockCollection::readBlocks()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.2559

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 2
eloc 6
c 2
b 0
f 0
nc 2
nop 3
dl 0
loc 11
ccs 3
cts 5
cp 0.6
crap 2.2559
rs 10
1
<?php
2
3
namespace A17\Twill\Services\Blocks;
4
5
use Exception;
6
use Illuminate\Filesystem\Filesystem;
7
use Illuminate\Support\Collection;
8
use Illuminate\Support\Facades\Log;
9
10
class BlockCollection extends Collection
11
{
12
    /**
13
     * @var \Illuminate\Support\Collection
14
     */
15
    protected $paths;
16
17
    /**
18
     * @var \Illuminate\Filesystem\Filesystem
19
     */
20
    protected $fileSystem;
21
22
    /**
23
     * @var \Illuminate\Support\Collection
24
     */
25
    private $missingDirectories;
26
27
    /**
28 69
     * @param mixed $items
29
     */
30 69
    public function __construct($items = [])
31
    {
32 69
        parent::__construct($items);
33
34 69
        $this->fileSystem = app(Filesystem::class);
35
36 69
        $this->missingDirectories = collect();
37 69
38
        $this->load();
39
    }
40
41
    private function addMissingDirectory($directory)
42
    {
43
        $this->missingDirectories->push($directory);
44
    }
45
46
    /**
47
     * @param $search
48
     * @param array $sources
49
     * @return mixed
50 5
     * @throws \Exception
51
     */
52 5
    public function findByName($search, $sources = [])
53
    {
54 5
        return $this->collect()
55 4
            ->filter(function ($block) use ($search, $sources) {
56 5
                return $block->name == $search &&
57 5
                    (blank($sources) ||
58
                    collect($sources)->contains($block->source));
59 4
            })
60 5
            ->sortBy(function ($block) {
61 5
                return $block->source === 'app' ? 0 : 1;
62
            })
63
            ->first();
64
    }
65
66
    /**
67 6
     * @return \Illuminate\Support\Collection
68
     */
69 6
    public function getBlocks()
70
    {
71 6
        return $this->collect()
72 6
            ->filter(function ($block) {
73 6
                return $block->type === Block::TYPE_BLOCK;
74
            })
75
            ->values();
76
    }
77
78
    /**
79 6
     * @return \Illuminate\Support\Collection
80
     */
81
    public function getBlockList()
82 6
    {
83 6
        return $this->getBlocks()->map(function (Block $block) {
84
            return $block->toList();
85
        });
86
    }
87
88
    /**
89 3
     * @return \Illuminate\Support\Collection
90
     */
91 3
    public function getMissingDirectories()
92
    {
93
        return $this->missingDirectories;
94
    }
95
96
    /**
97
     * @param $directory
98
     * @param $source
99
     * @param null $type
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $type is correct as it would always require null to be passed?
Loading history...
100 69
     * @return \Illuminate\Support\Collection
101
     */
102 69
    public function readBlocks($directory, $source, $type = null)
103
    {
104
        if (!$this->fileSystem->exists($directory)) {
105
            $this->addMissingDirectory($directory);
106
107
            return collect();
108
        }
109
110
        return collect($this->fileSystem->files($directory))
111 69
            ->map(function ($file) use ($source, $type) {
112 69
                return new Block($file, $type, $source);
113
            });
114
    }
115
116
    /**
117
     * @return $this
118 69
     */
119
    public function generatePaths()
120 69
    {
121 69
        $this->paths = collect(
122
            config('twill.block_editor.directories.source.blocks')
123
        )
124 69
            ->map(function ($path) {
125
                $path['type'] = Block::TYPE_BLOCK;
126 69
127 69
                return $path;
128 69
            })
129 69
            ->merge(
130 69
                collect(
131
                    config('twill.block_editor.directories.source.repeaters')
132 69
                )->map(function ($path) {
133
                    $path['type'] = Block::TYPE_REPEATER;
134 69
135 69
                    return $path;
136
                })
137
            );
138 69
139
        return $this;
140
    }
141
142
    /**
143
     * @param Block $block
144
     * @return string
145 69
     */
146
    public function detectCustomSources(Block $block)
147 69
    {
148
        if ($block->source === Block::SOURCE_APP) {
149 69
            if (
150 69
                $this->collect()
151 69
                ->where('fileName', $block->getFileName())
152 69
                ->where('source', Block::SOURCE_TWILL)
153
                ->isNotEmpty()
154 69
            ) {
155
                return Block::SOURCE_CUSTOM;
156
            }
157
        }
158 69
159
        return $block->source;
160
    }
161
162
    /**
163
     * @return $this
164 69
     */
165
    public function load()
166 69
    {
167
        $this->generatePaths();
168 69
169
        $this->items = collect($this->paths)
170 69
            ->reduce(function (Collection $keep, $path) {
171 69
                $this->readBlocks(
172 69
                    $path['path'],
173 69
                    $path['source'],
174
                    $path['type']
175 69
                )->each(function ($block) use ($keep) {
176
                    $keep->push($block);
177 69
178 69
                    return $keep;
179
                });
180 69
181 69
                return $keep;
182 69
            }, collect())
183
            ->toArray();
184 69
185
        $this->items = $this->collect()
186 69
            ->each(function (Block $block) {
187 69
                $block->setSource($this->detectCustomSources($block));
188 69
            })
189
            ->toArray();
190 69
191
        // remove duplicate Twill blocks
192
        $appBlocks = $this->collect()->whereIn('source', [Block::SOURCE_APP, Block::SOURCE_CUSTOM]);
193
        $this->items = $this->collect()->filter(function ($item) use ($appBlocks) {
194
            return !$appBlocks->contains(function ($block) use ($item) {
195
                return $item->source === Block::SOURCE_TWILL && $item->name === $block->name;
196
            });
197
        })->values()->toArray();
198
199
        $this
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->addBlocksFromConf...s\Block::TYPE_REPEATER) targeting A17\Twill\Services\Block...::addBlocksFromConfig() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
200
            ->addBlocksFromConfig(collect(config('twill.block_editor.repeaters')), Block::TYPE_REPEATER)
201
            ->addBlocksFromConfig(collect(config('twill.block_editor.blocks')), Block::TYPE_BLOCK);
202
203
        return $this;
204 5
    }
205
206
    /**
207 5
     * This function will add blocks and repeaters that are only defined in the config
208 5
     *
209
     * For compatibility with 2.0.2 and lower
210
     *
211
     * @param Collection $items
212
     * @param string $type
213
     * @return void
214 69
     */
215
    public function addBlocksFromConfig(Collection $items, $type)
216 69
    {
217
        $items->reject(function ($value, $blockName) use ($type) {
218
            return $this->contains(function ($block) use ($blockName, $type) {
219
                return $block->name === $blockName && $block->type === $type;
220
            }) ? [$blockName, $value] : false;
221
        })
222 5
            ->each(function ($block, $blockName) use ($type) {
223
                if ($block['compiled'] ?? false) {
224 5
                    $file = null;
225
                } else {
226 5
                    $file = $this->findFileByComponentName($block['component']);
227 5
                }
228 5
229
                $this->push($this->blockFromComponentName(
230
                    $file,
231
                    $blockName,
232
                    $type,
233
                    Block::SOURCE_APP
234 5
                ));
235
            });
236
237 5
        return $this;
238 5
    }
239
240
    /**
241
     * @param string $componentName
242
     * @param string $blockName
243
     * @param string $type
244
     * @param string $source
245
     * @return Block
246
     */
247
    public function blockFromComponentName($file, $blockName, $type, $source)
248
    {
249
        $this->logDeprecatedBlockConfig($blockName, $type);
250
251
        $block = new Block($file, $type, $source, $blockName);
252
253
        return $block;
254
    }
255
256
    /**
257
     * @param string $type
258
     * @param string $blockName
259
     * @return void
260
     */
261
    public function logDeprecatedBlockConfig($blockName, $type)
262
    {
263
        $path = $this->paths->filter(function ($path) use ($type) {
264
            return $path['source'] === Block::SOURCE_APP && $path['type'] === $type;
265
        })->pluck('path')->join(', ', ' or ');
266
267
        Log::notice(
268
            "The {$type} '{$blockName}' appears to be defined in the config " .
269
            "'twill.block_editor.blocks' or 'twill.block_editor.repeaters' only. " .
270
            "This will be deprecated in a future release. A {$type} should be " .
271
            "defined in its unique view in [{$path}]."
272
        );
273
    }
274
275
    /**
276
     * This function will try to find a view from the a component name
277
     * (minus the 'a17-block-' namespace).
278
     *
279
     * @param string $componentName
280
     * @return \Symfony\Component\Finder\SplFileInfo
281
     */
282
    public function findFileByComponentName($componentName)
283
    {
284
        $filename = str_replace('a17-block-', '', $componentName) . '.blade.php';
285
        $paths = $this->paths->pluck('path')->filter(function ($path) {
286
            return $this->fileSystem->exists($path);
287
        })->toArray();
288
289
        $files = iterator_to_array(\Symfony\Component\Finder\Finder::create()->name($filename)->in($paths), false);
290
291
        if (empty($files)) {
292
            throw new Exception("Could not find a view for the block or repeater '{$componentName}'.");
293
        }
294
295
        return $files[0];
296
    }
297
298
    /**
299
     * @return array
300
     */
301
    public function toArray()
302
    {
303
        return $this->list()->toArray();
304
    }
305
306
    /**
307
     * @return \Illuminate\Support\Collection
308
     */
309
    public function list()
310
    {
311
        return $this->collect()->map(function (Block $block) {
312
            return $block->toList();
313
        });
314
    }
315
316
    /**
317
     * @return \Illuminate\Support\Collection
318
     */
319
    public function collect()
320
    {
321
        return collect($this);
322
    }
323
324
    /**
325
     * @return \Illuminate\Support\Collection
326
     */
327
    public function getRepeaters()
328
    {
329
        return $this->collect()
330
            ->filter(function ($block) {
331
                return $block->type === Block::TYPE_REPEATER;
332
            })
333
            ->values();
334
    }
335
336
    /**
337
     * @return \Illuminate\Support\Collection
338
     */
339
    public function getRepeaterList()
340
    {
341
        return $this->getRepeaters()->map(function (Block $block) {
342
            return $block->toList();
343
        });
344
    }
345
}
346