Completed
Push — master ( b4e56c...1b6c56 )
by Avtandil
04:39
created

MultiLang   B

Complexity

Total Complexity 47

Size/Duplication

Total Lines 452
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 76.16%

Importance

Changes 20
Bugs 8 Features 8
Metric Value
c 20
b 8
f 8
dl 0
loc 452
ccs 115
cts 151
cp 0.7616
rs 8.439
wmc 47
lcom 1
cbo 6

24 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 1
A setConfig() 0 5 1
A setRepository() 0 5 1
A getRepository() 0 4 1
A setScope() 0 5 1
A getScope() 0 4 1
A get() 0 20 4
A replaceMarkers() 0 8 2
A sortReplacements() 0 6 1
A detectLocale() 0 11 3
A getTexts() 0 5 1
A autoSaveIsAllowed() 0 7 3
A getLocale() 0 4 1
A getLocales() 0 4 1
A saveTexts() 0 9 2
A queueToSave() 0 4 1
A setLocale() 0 13 3
A loadTexts() 0 16 4
A makeReplacements() 0 14 2
B getRedirectUrl() 0 33 6
A routeGroup() 0 13 2
A manageTextsRoutes() 0 18 1
A setTexts() 0 11 2
A getUrl() 0 8 2

How to fix   Complexity   

Complex Class

Complex classes like MultiLang 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 MultiLang, and based on these observations, apply Extract Interface, too.

1
<?php
2
/*
3
 * This file is part of the Laravel MultiLang package.
4
 *
5
 * (c) Avtandil Kikabidze aka LONGMAN <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Longman\LaravelMultiLang;
12
13
use Closure;
14
use Illuminate\Cache\CacheManager as Cache;
15
use Illuminate\Database\DatabaseManager as Database;
16
use Illuminate\Http\Request;
17
use InvalidArgumentException;
18
use Illuminate\Support\Str;
19
use Illuminate\Support\Collection;
20
21
class MultiLang
22
{
23
    /**
24
     * Language/Locale.
25
     *
26
     * @var string
27
     */
28
    protected $lang;
29
30
    /**
31
     * System environment
32
     *
33
     * @var string
34
     */
35
    protected $environment;
36
37
    /**
38
     * Config.
39
     *
40
     * @var \Longman\LaravelMultiLang\Config
41
     */
42
    protected $config;
43
44
    /**
45
     * Repository
46
     *
47
     * @var \Longman\LaravelMultiLang\Repository
48
     */
49
    protected $repository;
50
51
    /**
52
     * Texts.
53
     *
54
     * @var array
55
     */
56
    protected $texts;
57
58
    /**
59
     * Missing texts.
60
     *
61
     * @var array
62
     */
63
    protected $new_texts;
64
65
    /**
66
     * Application scope.
67
     *
68
     * @var string
69
     */
70
    protected $scope;
71
72
    /**
73
     * Create a new MultiLang instance.
74
     *
75
     * @param string                               $environment
76
     * @param array                                $config
77
     * @param \Illuminate\Cache\CacheManager       $cache
78
     * @param \Illuminate\Database\DatabaseManager $db
79
     */
80 24
    public function __construct($environment, array $config, Cache $cache, Database $db)
81
    {
82 24
        $this->environment = $environment;
83 24
        $this->cache       = $cache;
0 ignored issues
show
Bug introduced by
The property cache does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
84 24
        $this->db          = $db;
0 ignored issues
show
Bug introduced by
The property db does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
85
86 24
        $this->setConfig($config);
87
88 24
        $this->setRepository(new Repository($this->config, $cache, $db));
89 24
    }
90
91
    /**
92
     * Set multilang config
93
     *
94
     * @param array $config
95
     * @return $this
96
     */
97 24
    public function setConfig(array $config)
98
    {
99 24
        $this->config = new Config($config);
100 24
        return $this;
101
    }
102
103
    /**
104
     * Set repository object
105
     *
106
     * @param \Longman\LaravelMultiLang\Repository $repository
107
     * @return $this
108
     */
109 24
    public function setRepository(Repository $repository)
110
    {
111 24
        $this->repository = $repository;
112 24
        return $this;
113
    }
114
115
    /**
116
     * Get repository object
117
     *
118
     * @return \Longman\LaravelMultiLang\Repository
119
     */
120 2
    public function getRepository()
121
    {
122 2
        return $this->repository;
123
    }
124
125
    /**
126
     * Set application scope
127
     *
128
     * @param $scope
129
     * @return $this
130
     */
131
    public function setScope($scope)
132
    {
133
        $this->scope = $scope;
134
        return $this;
135
    }
136
137
    /**
138
     * Get application scope
139
     *
140
     * @return string
141
     */
142
    public function getScope()
143
    {
144
        return $this->scope;
145
    }
146
147
    /**
148
     * Set locale and load texts
149
     *
150
     * @param  string $lang
151
     * @param  array  $texts
152
     * @return void
153
     */
154 22
    public function setLocale($lang, array $texts = null)
155
    {
156 22
        if (!$lang) {
157 1
            throw new InvalidArgumentException('Locale is empty');
158
        }
159 21
        $this->lang = $lang;
160
161 21
        if (!is_array($texts)) {
162 18
            $texts = $this->loadTexts($this->getLocale(), $this->scope);
163 18
        }
164
165 21
        $this->texts = $texts;
166 21
    }
167
168
    /**
169
     * Load texts
170
     *
171
     * @param  string $lang
172
     * @param  string $scope
173
     * @return array
174
     */
175 18
    public function loadTexts($lang, $scope = null)
176
    {
177 18
        if ($this->environment != 'production' || $this->config->get('cache.enabled', true) === false) {
178 16
            $texts = $this->repository->loadFromDatabase($lang, $scope);
179 16
            return $texts;
180
        }
181
182 3
        if ($this->repository->existsInCache($lang)) {
183
            $texts = $this->repository->loadFromCache($lang, $scope);
184
        } else {
185 3
            $texts = $this->repository->loadFromDatabase($lang, $scope);
186 3
            $this->repository->storeInCache($lang, $texts, $scope);
187
        }
188
189 3
        return $texts;
190
    }
191
192
    /**
193
     * Get translated text
194
     *
195
     * @param  string $key
196
     * @param  array  $replace
197
     * @return string
198
     */
199 9
    public function get($key, array $replace = [])
200
    {
201
202 9
        if (empty($key)) {
203 1
            throw new InvalidArgumentException('String key not provided');
204
        }
205
206 8
        if (!$this->lang) {
207 1
            return $key;
208
        }
209
210 7
        if (!isset($this->texts[$key])) {
211 4
            $this->queueToSave($key);
212 4
            return $this->replaceMarkers($key, $replace);
213
        }
214
215 3
        $text = $this->texts[$key];
216
217 3
        return $this->replaceMarkers($text, $replace);
218
    }
219
220
    /**
221
     * Replace markers in text
222
     *
223
     * @param  string $text
224
     * @param  array  $replace
225
     * @return string
226
     */
227 7
    protected function replaceMarkers($text, array $replace = [])
228
    {
229 7
        if (empty($replace)) {
230 6
            return $text;
231
        }
232
233 1
        return $this->makeReplacements($text, $replace);
234
    }
235
236
    /**
237
     * Make the place-holder replacements on a line.
238
     *
239
     * @param  string $text
240
     * @param  array  $replace
241
     * @return string
242
     */
243 1
    protected function makeReplacements($text, array $replace)
244
    {
245 1
        $replace = $this->sortReplacements($replace);
246
247 1
        foreach ($replace as $key => $value) {
248 1
            $text = str_replace(
249 1
                [':' . $key, ':' . Str::upper($key), ':' . Str::ucfirst($key)],
250 1
                [$value, Str::upper($value), Str::ucfirst($value)],
251
                $text
252 1
            );
253 1
        }
254
255 1
        return $text;
256
    }
257
258
    /**
259
     * Sort the replacements array.
260
     *
261
     * @param  array $replace
262
     * @return \Illuminate\Support\Collection
263
     */
264
    protected function sortReplacements(array $replace)
265
    {
266 1
        return (new Collection($replace))->sortBy(function ($value, $key) {
267 1
            return mb_strlen($key) * -1;
268 1
        });
269
    }
270
271
    /**
272
     * Get redirect url in middleware
273
     *
274
     * @param \Illuminate\Http\Request $request
275
     * @return null|string
276
     */
277 4
    public function getRedirectUrl(Request $request)
278
    {
279 4
        $locale           = $request->segment(1);
280 4
        $fallback_locale  = $this->config->get('default_locale', 'en');
281 4
        $exclude_segments = $this->config->get('exclude_segments', []);
282 4
        if (in_array($locale, $exclude_segments)) {
283
            return null;
284
        }
285
286 4
        if (strlen($locale) == 2) {
287 3
            $locales = $this->config->get('locales', []);
288
289 3
            if (!isset($locales[$locale])) {
290 2
                $segments    = $request->segments();
291 2
                $segments[0] = $fallback_locale;
292 2
                $url         = implode('/', $segments);
293 2
                if ($query_string = $request->server->get('QUERY_STRING')) {
294 1
                    $url .= '?' . $query_string;
295 1
                }
296
297 2
                return $url;
298
            }
299 1
        } else {
300 1
            $segments = $request->segments();
301 1
            $url      = $fallback_locale . '/' . implode('/', $segments);
302 1
            if ($query_string = $request->server->get('QUERY_STRING')) {
303
                $url .= '?' . $query_string;
304
            }
305 1
            return $url;
306
        }
307
308 1
        return null;
309
    }
310
311
    /**
312
     * Detect locale based on url segment
313
     *
314
     * @param \Illuminate\Http\Request $request
315
     * @return string
316
     */
317 1
    public function detectLocale(Request $request)
318
    {
319 1
        $locale  = $request->segment(1);
320 1
        $locales = $this->config->get('locales');
321
322 1
        if (isset($locales[$locale])) {
323 1
            return isset($locales[$locale]['locale']) ? $locales[$locale]['locale'] : $locale;
324
        }
325
326
        return $this->config->get('default_locale', 'en');
327
    }
328
329
    /**
330
     * Wrap routes to available languages group
331
     *
332
     * @param \Closure $callback
333
     */
334
    public function routeGroup(Closure $callback)
335
    {
336
        $router = app('router');
337
338
        $locales = $this->config->get('locales', []);
339
340
        foreach ($locales as $locale => $val) {
341
            $router->group([
342
                               'prefix' => $locale,
343
                               'as'     => $locale . '.',
344
                           ], $callback);
345
        }
346
    }
347
348
    /**
349
     *  Manage texts
350
     */
351
    public function manageTextsRoutes()
352
    {
353
        $router     = app('router');
354
        $route      = $this->config->get('text-route.route', 'texts');
355
        $controller = $this->config->get(
356
            'text-route.controller',
357
            '\Longman\LaravelMultiLang\Controllers\TextsController'
358
        );
359
360
        $router->get(
361
            $route,
362
            ['uses' => $controller . '@index']
363
        );
364
        $router->post(
365
            $route,
366
            ['uses' => $controller . '@save']
367
        );
368
    }
369
370
    /**
371
     * Get texts
372
     *
373
     * @return array
374
     */
375 4
    public function getTexts()
376
    {
377
378 4
        return $this->texts;
379
    }
380
381
    /**
382
     * Set texts manually
383
     *
384
     * @param  array $texts_array
385
     * @return \Longman\LaravelMultiLang\MultiLang
386
     */
387 4
    public function setTexts(array $texts_array)
388
    {
389 4
        $texts = [];
390 4
        foreach ($texts_array as $key => $value) {
391 4
            $texts[$key] = $value;
392 4
        }
393
394 4
        $this->texts = $texts;
395
396 4
        return $this;
397
    }
398
399
    /**
400
     * Queue missing texts
401
     *
402
     * @param  string $key
403
     * @return void
404
     */
405 4
    protected function queueToSave($key)
406
    {
407 4
        $this->new_texts[$key] = $key;
408 4
    }
409
410
    /**
411
     * Get language prefixed url
412
     *
413
     * @param $path
414
     * @return string
415
     */
416 2
    public function getUrl($path)
417
    {
418 2
        $locale = $this->getLocale();
419 2
        if ($locale) {
420 2
            $path = $locale . '/' . $path;
421 2
        }
422 2
        return $path;
423
    }
424
425
    /**
426
     * Check if autosave allowed
427
     *
428
     * @return bool
429
     */
430 4
    public function autoSaveIsAllowed()
431
    {
432 4
        if ($this->environment == 'local' && $this->config->get('db.autosave', true)) {
433 1
            return true;
434
        }
435 4
        return false;
436
    }
437
438
    /**
439
     * Get locale
440
     *
441
     * @return string
442
     */
443 19
    public function getLocale()
444
    {
445 19
        return $this->lang;
446
    }
447
448
    /**
449
     * Get available locales
450
     *
451
     * @return array
452
     */
453 1
    public function getLocales()
454
    {
455 1
        return $this->config->get('locales');
456
    }
457
458
    /**
459
     * Save missing texts
460
     *
461
     * @return bool
462
     */
463 3
    public function saveTexts()
464
    {
465 3
        if (empty($this->new_texts)) {
466 3
            return false;
467
        }
468
469 3
        $this->repository->save($this->new_texts, $this->scope);
470 3
        return true;
471
    }
472
}
473