Completed
Push — master ( b02d8b...54b42c )
by Nicolas
10:22
created

Repository   C

Complexity

Total Complexity 67

Size/Duplication

Total Lines 627
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 85.06%

Importance

Changes 0
Metric Value
dl 0
loc 627
ccs 131
cts 154
cp 0.8506
rs 5.5387
c 0
b 0
f 0
wmc 67
lcom 1
cbo 8

45 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A addLocation() 0 6 1
A addPath() 0 4 1
A getPaths() 0 4 1
A getScanPaths() 0 15 2
scan() 0 1 ?
A all() 0 8 2
formatCached() 0 1 ?
A getCached() 0 6 1
A toCollection() 0 4 1
A getByStatus() 0 12 3
A has() 0 4 1
A enabled() 0 4 1
A disabled() 0 4 1
A count() 0 4 1
B getOrdered() 0 18 5
A getPath() 0 4 2
A register() 0 6 2
A boot() 0 6 2
A find() 0 10 3
A findByAlias() 0 10 3
A findRequirements() 0 12 2
A get() 0 4 1
A findOrFail() 0 10 2
A collections() 0 4 1
A getModulePath() 0 8 2
A assetPath() 0 4 1
A config() 0 4 1
A getUsedStoragePath() 0 14 3
A setUsed() 0 6 1
A forgetUsed() 0 6 2
A getUsedNow() 0 4 1
A getUsed() 0 4 1
A getFiles() 0 4 1
A getAssetsPath() 0 4 1
A asset() 0 13 2
A active() 0 4 1
A notActive() 0 4 1
A enable() 0 4 1
A disable() 0 4 1
A delete() 0 4 1
A update() 0 4 1
A install() 0 6 1
A getStubPath() 0 12 3
A setStubPath() 0 6 1

How to fix   Complexity   

Complex Class

Complex classes like Repository often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Repository, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Nwidart\Modules;
4
5
use Countable;
6
use Illuminate\Container\Container;
7
use Illuminate\Support\Str;
8
use Illuminate\Support\Traits\Macroable;
9
use Nwidart\Modules\Contracts\RepositoryInterface;
10
use Nwidart\Modules\Exceptions\InvalidAssetPath;
11
use Nwidart\Modules\Exceptions\ModuleNotFoundException;
12
use Nwidart\Modules\Process\Installer;
13
use Nwidart\Modules\Process\Updater;
14
15
abstract class Repository implements RepositoryInterface, Countable
16
{
17
    use Macroable;
18
19
    /**
20
     * Application instance.
21
     *
22
     * @var Illuminate\Contracts\Foundation\Application|Laravel\Lumen\Application
23
     */
24
    protected $app;
25
26
    /**
27
     * The module path.
28
     *
29
     * @var string|null
30
     */
31
    protected $path;
32
33
    /**
34
     * The scanned paths.
35
     *
36
     * @var array
37
     */
38
    protected $paths = [];
39
40
    /**
41
     * @var string
42
     */
43
    protected $stubPath;
44
45
    /**
46
     * The constructor.
47
     *
48
     * @param Container $app
49
     * @param string|null $path
50
     */
51 161
    public function __construct(Container $app, $path = null)
52
    {
53 161
        $this->app = $app;
0 ignored issues
show
Documentation Bug introduced by
It seems like $app of type object<Illuminate\Container\Container> is incompatible with the declared type object<Nwidart\Modules\I...avel\Lumen\Application> of property $app.

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...
54 161
        $this->path = $path;
55 161
    }
56
57
    /**
58
     * Add other module location.
59
     *
60
     * @param string $path
61
     *
62
     * @return $this
63
     */
64 15
    public function addLocation($path)
65
    {
66 15
        $this->paths[] = $path;
67
68 15
        return $this;
69
    }
70
71
    /**
72
     * Alternative method for "addPath".
73
     *
74
     * @param string $path
75
     *
76
     * @return $this
77
     * @deprecated
78
     */
79 1
    public function addPath($path)
80
    {
81 1
        return $this->addLocation($path);
82
    }
83
84
    /**
85
     * Get all additional paths.
86
     *
87
     * @return array
88
     */
89 1
    public function getPaths() : array
90
    {
91 1
        return $this->paths;
92
    }
93
94
    /**
95
     * Get scanned modules paths.
96
     *
97
     * @return array
98
     */
99 161
    public function getScanPaths() : array
100
    {
101 161
        $paths = $this->paths;
102
103 161
        $paths[] = $this->getPath();
104
105 161
        if ($this->config('scan.enabled')) {
106
            $paths = array_merge($paths, $this->config('scan.paths'));
107
        }
108 161
        $paths = array_map(function($path) {
109 161
            return str_finish($path, '/*');
110 161
        }, $paths);
111
112 161
        return $paths;
113
    }
114
115
    /**
116
     * Get & scan all modules.
117
     *
118
     * @return array
119
     */
120
    abstract public function scan();
121
122
    /**
123
     * Get all modules.
124
     *
125
     * @return array
126
     */
127 161
    public function all() : array
128
    {
129 161
        if (!$this->config('cache.enabled')) {
130 161
            return $this->scan();
131
        }
132
133
        return $this->formatCached($this->getCached());
134
    }
135
136
    /**
137
     * Format the cached data as array of modules.
138
     *
139
     * @param array $cached
140
     *
141
     * @return array
142
     */
143
    abstract protected function formatCached($cached);
144
145
    /**
146
     * Get cached modules.
147
     *
148
     * @return array
149
     */
150
    public function getCached()
151
    {
152
        return $this->app['cache']->remember($this->config('cache.key'), $this->config('cache.lifetime'), function () {
153
            return $this->toCollection()->toArray();
154
        });
155
    }
156
157
    /**
158
     * Get all modules as collection instance.
159
     *
160
     * @return Collection
161
     */
162 1
    public function toCollection() : Collection
163
    {
164 1
        return new Collection($this->scan());
165
    }
166
167
    /**
168
     * Get modules by status.
169
     *
170
     * @param $status
171
     *
172
     * @return array
173
     */
174 161
    public function getByStatus($status) : array
175
    {
176 161
        $modules = [];
177
178 161
        foreach ($this->all() as $name => $module) {
179 3
            if ($module->isStatus($status)) {
180 3
                $modules[$name] = $module;
181
            }
182
        }
183
184 161
        return $modules;
185
    }
186
187
    /**
188
     * Determine whether the given module exist.
189
     *
190
     * @param $name
191
     *
192
     * @return bool
193
     */
194 75
    public function has($name) : bool
195
    {
196 75
        return array_key_exists($name, $this->all());
197
    }
198
199
    /**
200
     * Get list of enabled modules.
201
     *
202
     * @return array
203
     */
204 161
    public function enabled() : array
205
    {
206 161
        return $this->getByStatus(1);
207
    }
208
209
    /**
210
     * Get list of disabled modules.
211
     *
212
     * @return array
213
     */
214 1
    public function disabled() : array
215
    {
216 1
        return $this->getByStatus(0);
217
    }
218
219
    /**
220
     * Get count from all modules.
221
     *
222
     * @return int
223
     */
224 1
    public function count() : int
225
    {
226 1
        return count($this->all());
227
    }
228
229
    /**
230
     * Get all ordered modules.
231
     *
232
     * @param string $direction
233
     *
234
     * @return array
235
     */
236 161
    public function getOrdered($direction = 'asc') : array
237
    {
238 161
        $modules = $this->enabled();
239
240 161
        uasort($modules, function (Module $a, Module $b) use ($direction) {
241
            if ($a->order == $b->order) {
0 ignored issues
show
Documentation introduced by
The property order does not exist on object<Nwidart\Modules\Module>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
242
                return 0;
243
            }
244
245
            if ($direction == 'desc') {
246
                return $a->order < $b->order ? 1 : -1;
0 ignored issues
show
Documentation introduced by
The property order does not exist on object<Nwidart\Modules\Module>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
247
            }
248
249
            return $a->order > $b->order ? 1 : -1;
0 ignored issues
show
Documentation introduced by
The property order does not exist on object<Nwidart\Modules\Module>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
250 161
        });
251
252 161
        return $modules;
253
    }
254
255
    /**
256
     * Get a module path.
257
     *
258
     * @return string
259
     */
260 161
    public function getPath() : string
261
    {
262 161
        return $this->path ?: $this->config('paths.modules', base_path('Modules'));
263
    }
264
265
    /**
266
     * Register the modules.
267
     */
268 161
    public function register()
269
    {
270 161
        foreach ($this->getOrdered() as $module) {
271
            $module->register();
272
        }
273 161
    }
274
275
    /**
276
     * Boot the modules.
277
     */
278 161
    public function boot()
279
    {
280 161
        foreach ($this->getOrdered() as $module) {
281
            $module->boot();
282
        }
283 161
    }
284
285
    /**
286
     * Find a specific module.
287
     * @param $name
288
     * @return mixed|void
289
     */
290 84
    public function find($name)
291
    {
292 84
        foreach ($this->all() as $module) {
293 83
            if ($module->getLowerName() === strtolower($name)) {
294 83
                return $module;
295
            }
296
        }
297
298 76
        return;
299
    }
300
301
    /**
302
     * Find a specific module by its alias.
303
     * @param $alias
304
     * @return mixed|void
305
     */
306 2
    public function findByAlias($alias)
307
    {
308 2
        foreach ($this->all() as $module) {
309 2
            if ($module->getAlias() === $alias) {
310 2
                return $module;
311
            }
312
        }
313
314
        return;
315
    }
316
317
    /**
318
     * Find all modules that are required by a module. If the module cannot be found, throw an exception.
319
     *
320
     * @param $name
321
     * @return array
322
     * @throws ModuleNotFoundException
323
     */
324 1
    public function findRequirements($name)
325
    {
326 1
        $requirements = [];
327
328 1
        $module = $this->findOrFail($name);
329
330 1
        foreach ($module->getRequires() as $requirementName) {
331 1
            $requirements[] = $this->findByAlias($requirementName);
332
        }
333
334 1
        return $requirements;
335
    }
336
337
    /**
338
     * Alternative for "find" method.
339
     * @param $name
340
     * @return mixed|void
341
     * @deprecated
342
     */
343 1
    public function get($name)
344
    {
345 1
        return $this->find($name);
346
    }
347
348
    /**
349
     * Find a specific module, if there return that, otherwise throw exception.
350
     *
351
     * @param $name
352
     *
353
     * @return Module
354
     *
355
     * @throws ModuleNotFoundException
356
     */
357 82
    public function findOrFail($name)
358
    {
359 82
        $module = $this->find($name);
360
361 82
        if ($module !== null) {
362 81
            return $module;
363
        }
364
365 76
        throw new ModuleNotFoundException("Module [{$name}] does not exist!");
366
    }
367
368
    /**
369
     * Get all modules as laravel collection instance.
370
     *
371
     * @param $status
372
     *
373
     * @return Collection
374
     */
375 1
    public function collections($status = 1) : Collection
376
    {
377 1
        return new Collection($this->getByStatus($status));
378
    }
379
380
    /**
381
     * Get module path for a specific module.
382
     *
383
     * @param $module
384
     *
385
     * @return string
386
     */
387 75
    public function getModulePath($module)
388
    {
389
        try {
390 75
            return $this->findOrFail($module)->getPath() . '/';
391 75
        } catch (ModuleNotFoundException $e) {
392 75
            return $this->getPath() . '/' . Str::studly($module) . '/';
393
        }
394
    }
395
396
    /**
397
     * Get asset path for a specific module.
398
     *
399
     * @param $module
400
     *
401
     * @return string
402
     */
403 2
    public function assetPath($module) : string
404
    {
405 2
        return $this->config('paths.assets') . '/' . $module;
406
    }
407
408
    /**
409
     * Get a specific config data from a configuration file.
410
     *
411
     * @param $key
412
     *
413
     * @param null $default
414
     * @return mixed
415
     */
416 161
    public function config($key, $default = null)
417
    {
418 161
        return $this->app['config']->get('modules.' . $key, $default);
419
    }
420
421
    /**
422
     * Get storage path for module used.
423
     *
424
     * @return string
425
     */
426 2
    public function getUsedStoragePath() : string
427
    {
428 2
        $directory = storage_path('app/modules');
429 2
        if ($this->app['files']->exists($directory) === false) {
430 1
            $this->app['files']->makeDirectory($directory, 0777, true);
431
        }
432
433 2
        $path = storage_path('app/modules/modules.used');
434 2
        if (!$this->app['files']->exists($path)) {
435 1
            $this->app['files']->put($path, '');
436
        }
437
438 2
        return $path;
439
    }
440
441
    /**
442
     * Set module used for cli session.
443
     *
444
     * @param $name
445
     *
446
     * @throws ModuleNotFoundException
447
     */
448 1
    public function setUsed($name)
449
    {
450 1
        $module = $this->findOrFail($name);
451
452 1
        $this->app['files']->put($this->getUsedStoragePath(), $module);
453 1
    }
454
455
    /**
456
     * Forget the module used for cli session.
457
     */
458
    public function forgetUsed()
459
    {
460
        if ($this->app['files']->exists($this->getUsedStoragePath())) {
461
            $this->app['files']->delete($this->getUsedStoragePath());
462
        }
463
    }
464
465
    /**
466
     * Get module used for cli session.
467
     * @return string
468
     * @throws \Nwidart\Modules\Exceptions\ModuleNotFoundException
469
     */
470 1
    public function getUsedNow() : string
471
    {
472 1
        return $this->findOrFail($this->app['files']->get($this->getUsedStoragePath()));
473
    }
474
475
    /**
476
     * Get used now.
477
     *
478
     * @return string
479
     * @deprecated
480
     */
481 1
    public function getUsed()
482
    {
483 1
        return $this->getUsedNow();
484
    }
485
486
    /**
487
     * Get laravel filesystem instance.
488
     *
489
     * @return \Illuminate\Filesystem\Filesystem
490
     */
491 4
    public function getFiles()
492
    {
493 4
        return $this->app['files'];
494
    }
495
496
    /**
497
     * Get module assets path.
498
     *
499
     * @return string
500
     */
501 2
    public function getAssetsPath() : string
502
    {
503 2
        return $this->config('paths.assets');
504
    }
505
506
    /**
507
     * Get asset url from a specific module.
508
     * @param string $asset
509
     * @return string
510
     * @throws InvalidAssetPath
511
     */
512 2
    public function asset($asset) : string
513
    {
514 2
        if (str_contains($asset, ':') === false) {
515 1
            throw InvalidAssetPath::missingModuleName($asset);
516
        }
517 1
        list($name, $url) = explode(':', $asset);
518
519 1
        $baseUrl = str_replace(public_path() . DIRECTORY_SEPARATOR, '', $this->getAssetsPath());
520
521 1
        $url = $this->app['url']->asset($baseUrl . "/{$name}/" . $url);
522
523 1
        return str_replace(['http://', 'https://'], '//', $url);
524
    }
525
526
    /**
527
     * Determine whether the given module is activated.
528
     *
529
     * @param string $name
530
     *
531
     * @return bool
532
     */
533 4
    public function active($name) : bool
534
    {
535 4
        return $this->findOrFail($name)->active();
0 ignored issues
show
Deprecated Code introduced by
The method Nwidart\Modules\Module::active() has been deprecated.

This method has been deprecated.

Loading history...
536
    }
537
538
    /**
539
     * Determine whether the given module is not activated.
540
     *
541
     * @param string $name
542
     *
543
     * @return bool
544
     */
545 2
    public function notActive($name) : bool
546
    {
547 2
        return !$this->active($name);
548
    }
549
550
    /**
551
     * Enabling a specific module.
552
     * @param string $name
553
     * @return void
554
     * @throws \Nwidart\Modules\Exceptions\ModuleNotFoundException
555
     */
556 1
    public function enable($name)
557
    {
558 1
        $this->findOrFail($name)->enable();
559 1
    }
560
561
    /**
562
     * Disabling a specific module.
563
     * @param string $name
564
     * @return void
565
     * @throws \Nwidart\Modules\Exceptions\ModuleNotFoundException
566
     */
567 1
    public function disable($name)
568
    {
569 1
        $this->findOrFail($name)->disable();
570 1
    }
571
572
    /**
573
     * Delete a specific module.
574
     * @param string $name
575
     * @return bool
576
     * @throws \Nwidart\Modules\Exceptions\ModuleNotFoundException
577
     */
578 2
    public function delete($name) : bool
579
    {
580 2
        return $this->findOrFail($name)->delete();
581
    }
582
583
    /**
584
     * Update dependencies for the specified module.
585
     *
586
     * @param string $module
587
     */
588
    public function update($module)
589
    {
590
        with(new Updater($this))->update($module);
591
    }
592
593
    /**
594
     * Install the specified module.
595
     *
596
     * @param string $name
597
     * @param string $version
598
     * @param string $type
599
     * @param bool   $subtree
600
     *
601
     * @return \Symfony\Component\Process\Process
602
     */
603
    public function install($name, $version = 'dev-master', $type = 'composer', $subtree = false)
604
    {
605
        $installer = new Installer($name, $version, $type, $subtree);
606
607
        return $installer->run();
608
    }
609
610
    /**
611
     * Get stub path.
612
     *
613
     * @return string|null
614
     */
615 3
    public function getStubPath()
616
    {
617 3
        if ($this->stubPath !== null) {
618 1
            return $this->stubPath;
619
        }
620
621 2
        if ($this->config('stubs.enabled') === true) {
622 1
            return $this->config('stubs.path');
623
        }
624
625 1
        return $this->stubPath;
626
    }
627
628
    /**
629
     * Set stub path.
630
     *
631
     * @param string $stubPath
632
     *
633
     * @return $this
634
     */
635 1
    public function setStubPath($stubPath)
636
    {
637 1
        $this->stubPath = $stubPath;
638
639 1
        return $this;
640
    }
641
}
642