Concord   A
last analyzed

Complexity

Total Complexity 32

Size/Duplication

Total Lines 286
Duplicated Lines 0 %

Importance

Changes 3
Bugs 0 Features 1
Metric Value
wmc 32
eloc 79
c 3
b 0
f 1
dl 0
loc 286
rs 9.84

22 Methods

Rating   Name   Duplication   Size   Complexity  
A registerAlias() 0 3 1
A model() 0 3 1
A registerRequest() 0 11 2
A getModules() 0 10 2
A __construct() 0 6 1
A getEnumBindings() 0 3 1
A resetProxy() 0 3 1
A short() 0 3 1
A hookInto() 0 3 1
A getConvention() 0 3 1
A helper() 0 3 1
A registerHelper() 0 5 1
A registerEnum() 0 12 2
A getModelBindings() 0 3 1
A registerModel() 0 24 4
A getVersion() 0 3 1
A registerModule() 0 14 3
A module() 0 3 1
A getRequestBindings() 0 3 1
A registerShort() 0 5 1
A enum() 0 3 1
A mergeModuleConfig() 0 11 3
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Contains the Concord class.
7
 *
8
 * @copyright   Copyright (c) 2016 Attila Fulop
9
 * @author      Attila Fulop
10
 * @license     MIT
11
 * @since       2016-08-14
12
 *
13
 */
14
15
namespace Konekt\Concord;
16
17
use Illuminate\Contracts\Foundation\Application;
18
use Illuminate\Foundation\AliasLoader;
19
use Illuminate\Support\Arr;
20
use Illuminate\Support\Collection;
21
use Illuminate\Support\Facades\Route;
22
use Illuminate\Support\Str;
23
use InvalidArgumentException;
24
use Konekt\Concord\Contracts\Concord as ConcordContract;
25
use Konekt\Concord\Contracts\Convention;
26
use Konekt\Concord\Events\EnumWasRegistered;
27
use Konekt\Concord\Events\HelperWasRegistered;
28
use Konekt\Concord\Events\ModelWasRegistered;
29
use Konekt\Concord\Events\RequestWasRegistered;
30
use Konekt\Concord\Hooks\HookEvent;
31
use Konekt\Concord\Hooks\Hooks;
32
33
final class Concord implements ConcordContract
34
{
35
    public const VERSION = '2.0-dev';
36
37
    /** @var Collection  */
38
    protected $modules;
39
40
    /** @var array */
41
    protected $models = [];
42
43
    /** @var array */
44
    protected $enums = [];
45
46
    /** @var array */
47
    protected $requests = [];
48
49
    /** @var  array */
50
    protected $implicitModules = [];
51
52
    /** @var  Application */
53
    protected $app;
54
55
    /** @var  array */
56
    protected $shorts = [];
57
58
    /** @var Convention */
59
    private $convention;
60
61
    private Hooks $hooks;
62
63
    /**
64
     * Concord class constructor
65
     *
66
     * @param Application $app
67
     * @param Convention  $convention
68
     */
69
    public function __construct(Application $app, Convention $convention)
70
    {
71
        $this->modules = Collection::make();
72
        $this->app = $app;
73
        $this->convention = $convention;
74
        $this->hooks = new Hooks();
75
    }
76
77
    /**
78
     * @inheritdoc
79
     */
80
    public function registerModule($moduleClass, $config = [])
81
    {
82
        if (is_not_a_concord_module_class($moduleClass)) {
83
            throw new InvalidArgumentException("$moduleClass is not a Concord Module class.");
84
        }
85
        $this->mergeModuleConfig($moduleClass::getId(), $config);
86
87
        $module = $this->app->register($moduleClass);
88
89
        $this->modules->put($module->getId(), $module);
0 ignored issues
show
Bug introduced by
The method getId() does not exist on Illuminate\Support\ServiceProvider. It seems like you code against a sub-type of Illuminate\Support\ServiceProvider such as Konekt\Concord\BaseModuleServiceProvider or Illuminate\Foundation\Su...rs\RouteServiceProvider or Konekt\Concord\BaseServiceProvider. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

89
        $this->modules->put($module->/** @scrutinizer ignore-call */ getId(), $module);
Loading history...
90
        $implicit = $config['implicit'] ?? false;
91
92
        if ($implicit) {
93
            $this->implicitModules[get_class($module)] = true;
94
        }
95
    }
96
97
    /**
98
     * @inheritdoc
99
     */
100
    public function registerHelper($name, $class)
101
    {
102
        $this->app->singleton('concord.helpers.' . $name, $class);
103
104
        event(new HelperWasRegistered($name, $class));
105
    }
106
107
    /**
108
     * @inheritdoc
109
     */
110
    public function getModules($includeImplicits = false): Collection
111
    {
112
        if ($includeImplicits) {
113
            return $this->modules;
114
        }
115
116
        $implicitModules = $this->implicitModules;
117
118
        return $this->modules->reject(function ($module) use ($implicitModules) {
119
            return array_key_exists(get_class($module), $implicitModules);
120
        });
121
    }
122
123
    /**
124
     * @inheritdoc
125
     */
126
    public function registerAlias($alias, $concrete)
127
    {
128
        AliasLoader::getInstance()->alias($alias, $concrete);
129
    }
130
131
    /**
132
     * @inheritDoc
133
     */
134
    public function registerModel(string $abstract, string $concrete, $registerRouteModel = true)
135
    {
136
        if (!is_subclass_of($concrete, $abstract, true)) {
137
            throw new InvalidArgumentException("Class {$concrete} must extend or implement {$abstract}. ");
138
        }
139
140
        $this->models[$abstract] = $concrete;
141
        $this->app->alias($concrete, $abstract);
142
        $this->registerShort($abstract, 'model');
143
144
        // Route models can't resolve models by interface
145
        // so we're registering them explicitly
146
        if ($registerRouteModel) {
147
            $short = shorten($abstract);
148
            Route::model($short, $concrete);
149
            // Register both `payment_method` and `paymentMethod`:
150
            $camel = Str::camel(class_basename($abstract));
151
            if ($short !== $camel) {
152
                Route::model($camel, $concrete);
153
            }
154
        }
155
156
        $this->resetProxy($this->getConvention()->proxyForModelContract($abstract));
157
        event(new ModelWasRegistered($abstract, $concrete, $registerRouteModel));
158
    }
159
160
    /**
161
     * @inheritDoc
162
     */
163
    public function model(string $abstract)
164
    {
165
        return Arr::get($this->models, $abstract);
166
    }
167
168
    /**
169
     * @inheritDoc
170
     */
171
    public function module(string $id)
172
    {
173
        return $this->modules->get($id);
174
    }
175
176
    /**
177
     * @inheritdoc
178
     */
179
    public function getModelBindings(): Collection
180
    {
181
        return collect($this->models);
0 ignored issues
show
Bug introduced by
$this->models of type array is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $value of collect(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

181
        return collect(/** @scrutinizer ignore-type */ $this->models);
Loading history...
182
    }
183
184
    /**
185
     * @inheritdoc
186
     */
187
    public function getConvention(): Convention
188
    {
189
        return $this->convention;
190
    }
191
192
    /**
193
     * @inheritDoc
194
     */
195
    public function registerEnum(string $abstract, string $concrete)
196
    {
197
        if (!is_subclass_of($concrete, $abstract, true)) {
198
            throw new InvalidArgumentException("Class {$concrete} must extend or implement {$abstract}. ");
199
        }
200
201
        $this->enums[$abstract] = $concrete;
202
        $this->app->alias($concrete, $abstract);
203
        $this->registerShort($abstract, 'enum');
204
205
        $this->resetProxy($this->getConvention()->proxyForEnumContract($abstract));
206
        event(new EnumWasRegistered($abstract, $concrete));
207
    }
208
209
    /**
210
     * @inheritDoc
211
     */
212
    public function registerRequest(string $abstract, string $concrete)
213
    {
214
        if (!is_subclass_of($concrete, $abstract, true)) {
215
            throw new InvalidArgumentException("Class {$concrete} must extend or implement {$abstract}. ");
216
        }
217
218
        $this->requests[$abstract] = $concrete;
219
        $this->app->alias($concrete, $abstract);
220
        $this->registerShort($abstract, 'request');
221
222
        event(new RequestWasRegistered($abstract, $concrete));
223
    }
224
225
    /**
226
     * @inheritDoc
227
     */
228
    public function enum(string $abstract)
229
    {
230
        return Arr::get($this->enums, $abstract);
231
    }
232
233
    /**
234
     * @inheritDoc
235
     */
236
    public function getEnumBindings(): Collection
237
    {
238
        return collect($this->enums);
0 ignored issues
show
Bug introduced by
$this->enums of type array is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $value of collect(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

238
        return collect(/** @scrutinizer ignore-type */ $this->enums);
Loading history...
239
    }
240
241
    /**
242
     * @inheritdoc
243
     */
244
    public function getRequestBindings(): Collection
245
    {
246
        return collect($this->requests);
0 ignored issues
show
Bug introduced by
$this->requests of type array is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $value of collect(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

246
        return collect(/** @scrutinizer ignore-type */ $this->requests);
Loading history...
247
    }
248
249
    /**
250
     * @inheritdoc
251
     */
252
    public function helper($name, $arguments = [])
253
    {
254
        return $this->app->make('concord.helpers.' . $name, $arguments);
255
    }
256
257
    /**
258
     * @inheritdoc
259
     */
260
    public function getVersion(): string
261
    {
262
        return self::VERSION;
263
    }
264
265
    /**
266
     * @inheritdoc
267
     */
268
    public function short($name)
269
    {
270
        return Arr::get($this->shorts, "$name.class");
271
    }
272
273
    public function hookInto(HookEvent $event, callable $callback, array|string $filter = null): void
274
    {
275
        $this->hooks->register($event, $callback, $filter);
276
    }
277
278
    /**
279
     * Resets the proxy class to ensure no stale instance gets stuck
280
     *
281
     * @param string $proxyClass
282
     */
283
    protected function resetProxy($proxyClass)
284
    {
285
        $proxyClass::__reset();
286
    }
287
288
    /**
289
     * Register a model/enum/request shorthand
290
     *
291
     * @param string    $abstract
292
     * @param string    $type
293
     */
294
    protected function registerShort($abstract, $type)
295
    {
296
        $this->shorts[shorten($abstract)] = [
297
            'type' => $type,
298
            'class' => $abstract
299
        ];
300
    }
301
302
    /**
303
     * Merge the module's config with the existing config
304
     *
305
     * @param string $moduleId
306
     * @param array  $config
307
     */
308
    protected function mergeModuleConfig(string $moduleId, array $config)
309
    {
310
        $current = $this->app['config']->get($moduleId);
311
        $final = $this->hooks->happening(
312
            HookEvent::LOADING_CONFIGURATION(),
313
            $moduleId,
314
            is_array($current) ? array_merge($current, $config) : $config,
315
        );
316
317
        if (!empty($config)) {
318
            $this->app['config']->set($moduleId, $final);
319
        }
320
    }
321
}
322