Completed
Pull Request — master (#145)
by ARCANEDEV
04:24
created

Localization::transRoute()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
ccs 2
cts 2
cp 1
crap 1
1
<?php namespace Arcanedev\Localization;
2
3
use Arcanedev\Localization\Contracts\LocalesManager as LocalesManagerContract;
4
use Arcanedev\Localization\Contracts\Localization as LocalizationContract;
5
use Arcanedev\Localization\Contracts\RouteTranslator as RouteTranslatorContract;
6
use Arcanedev\Localization\Exceptions\UnsupportedLocaleException;
7
use Arcanedev\Localization\Utilities\Url;
8
use Illuminate\Contracts\Foundation\Application as ApplicationContract;
9
use Illuminate\Contracts\View\Factory as ViewFactoryContract;
10
use Illuminate\Http\Request;
11
12
/**
13
 * Class     Localization
14
 *
15
 * @package  Arcanedev\Localization
16
 * @author   ARCANEDEV <[email protected]>
17
 */
18
class Localization implements LocalizationContract
19
{
20
    /* -----------------------------------------------------------------
21
     |  Properties
22
     | -----------------------------------------------------------------
23
     */
24
25
    /**
26
     * Base url.
27
     *
28
     * @var string
29
     */
30
    protected $baseUrl;
31
32
    /**
33
     * Laravel application instance.
34
     *
35
     * @var \Illuminate\Contracts\Foundation\Application
36
     */
37
    private $app;
38
39
    /**
40
     * The RouteTranslator instance.
41
     *
42
     * @var \Arcanedev\Localization\Contracts\RouteTranslator
43
     */
44
    protected $routeTranslator;
45
46
    /**
47
     * The LocalesManager instance.
48
     *
49
     * @var \Arcanedev\Localization\Contracts\LocalesManager
50
     */
51
    private $localesManager;
52
53
    /* -----------------------------------------------------------------
54
     |  Constructor
55
     | -----------------------------------------------------------------
56
     */
57
58
    /**
59
     * Localization constructor.
60
     *
61
     * @param  \Illuminate\Contracts\Foundation\Application       $app
62
     * @param  \Arcanedev\Localization\Contracts\RouteTranslator  $routeTranslator
63
     * @param  \Arcanedev\Localization\Contracts\LocalesManager   $localesManager
64
     */
65 268
    public function __construct(
66
        ApplicationContract     $app,
67
        RouteTranslatorContract $routeTranslator,
68
        LocalesManagerContract  $localesManager
69
    ) {
70 268
        $this->app             = $app;
71 268
        $this->routeTranslator = $routeTranslator;
72 268
        $this->localesManager  = $localesManager;
73
74 268
        $this->localesManager->setDefaultLocale(
75 268
            $this->app['config']->get('app.locale')
76
        );
77 268
    }
78
79
    /* -----------------------------------------------------------------
80
     |  Getters & Setters
81
     | -----------------------------------------------------------------
82
     */
83
84
    /**
85
     * Get Request instance.
86
     *
87
     * @return \Illuminate\Http\Request
88
     */
89 106
    private function request()
90
    {
91 106
        return $this->app['request'];
92
    }
93
94
    /**
95
     * Returns default locale.
96
     *
97
     * @return string
98
     */
99 112
    public function getDefaultLocale()
100
    {
101 112
        return $this->localesManager->getDefaultLocale();
102
    }
103
104
    /**
105
     * Return an array of all supported Locales.
106
     *
107
     * @return \Arcanedev\Localization\Entities\LocaleCollection
108
     */
109 108
    public function getSupportedLocales()
110
    {
111 108
        return $this->localesManager->getSupportedLocales();
112
    }
113
114
    /**
115
     * Set the supported locales.
116
     *
117
     * @param  array  $supportedLocales
118
     *
119
     * @return self
120
     */
121 4
    public function setSupportedLocales(array $supportedLocales)
122
    {
123 4
        $this->localesManager->setSupportedLocales($supportedLocales);
124
125 2
        return $this;
126
    }
127
128
    /**
129
     * Get supported locales keys.
130
     *
131
     * @return array
132
     */
133 2
    public function getSupportedLocalesKeys()
134
    {
135 2
        return $this->localesManager->getSupportedLocalesKeys();
136
    }
137
138
    /**
139
     * Returns current language.
140
     *
141
     * @return string
142
     */
143 110
    public function getCurrentLocale()
144
    {
145 110
        return $this->localesManager->getCurrentLocale();
146
    }
147
148
    /**
149
     * Returns current language.
150
     *
151
     * @return \Arcanedev\Localization\Entities\Locale
152
     */
153 10
    public function getCurrentLocaleEntity()
154
    {
155 10
        return $this->localesManager->getCurrentLocaleEntity();
156
    }
157
158
    /**
159
     * Returns current locale name.
160
     *
161
     * @return string
162
     */
163 2
    public function getCurrentLocaleName()
164
    {
165 2
        return $this->getCurrentLocaleEntity()->name();
166
    }
167
168
    /**
169
     * Returns current locale script.
170
     *
171
     * @return string
172
     */
173 2
    public function getCurrentLocaleScript()
174
    {
175 2
        return $this->getCurrentLocaleEntity()->script();
176
    }
177
178
    /**
179
     * Returns current locale direction.
180
     *
181
     * @return string
182
     */
183 2
    public function getCurrentLocaleDirection()
184
    {
185 2
        return $this->getCurrentLocaleEntity()->direction();
186
    }
187
188
    /**
189
     * Returns current locale native name.
190
     *
191
     * @return string
192
     */
193 2
    public function getCurrentLocaleNative()
194
    {
195 2
        return $this->getCurrentLocaleEntity()->native();
196
    }
197
198
    /**
199
     * Returns current locale regional.
200
     *
201
     * @return string
202
     */
203 2
    public function getCurrentLocaleRegional()
204
    {
205 2
        return $this->getCurrentLocaleEntity()->regional();
206
    }
207
208
    /**
209
     * Get all locales.
210
     *
211
     * @return \Arcanedev\Localization\Entities\LocaleCollection
212
     */
213 2
    public function getAllLocales()
214
    {
215 2
        return $this->localesManager->getAllLocales();
216
    }
217
218
    /**
219
     * Set and return current locale.
220
     *
221
     * @param  string|null  $locale
222
     *
223
     * @return string
224
     */
225 268
    public function setLocale($locale = null)
226
    {
227 268
        return $this->localesManager->setLocale($locale);
228
    }
229
230
    /**
231
     * Sets the base url for the site.
232
     *
233
     * @param  string  $url
234
     *
235
     * @return self
236
     */
237 268
    public function setBaseUrl($url)
238
    {
239 268
        if (substr($url, -1) !== '/') $url .= '/';
240
241 268
        $this->baseUrl = $url;
242
243 268
        return $this;
244
    }
245
246
    /* -----------------------------------------------------------------
247
     |  Main Methods
248
     | -----------------------------------------------------------------
249
     */
250
251
    /**
252
     * Translate routes and save them to the translated routes array (used in the localize route filter).
253
     *
254
     * @param  string  $routeName
255
     *
256
     * @return string
257
     */
258 268
    public function transRoute($routeName)
259
    {
260 268
        return $this->routeTranslator->trans($routeName);
261
    }
262
263
    /**
264
     * Returns an URL adapted to $locale or current locale.
265
     *
266
     * @param  string|null  $url
267
     * @param  string|null  $locale
268
     *
269
     * @return string
270
     */
271 4
    public function localizeURL($url = null, $locale = null)
272
    {
273 4
        return $this->getLocalizedURL($locale, $url);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression $this->getLocalizedURL($locale, $url); of type string|false adds false to the return on line 273 which is incompatible with the return type declared by the interface Arcanedev\Localization\C...calization::localizeURL of type string. It seems like you forgot to handle an error condition.
Loading history...
274
    }
275
276
    /**
277
     * It returns an URL without locale (if it has it).
278
     *
279
     * @param  string|null  $url
280
     *
281
     * @return string
282
     */
283 106
    public function getNonLocalizedURL($url = null)
284
    {
285 106
        return $this->getLocalizedURL(false, $url);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Comprehensibility Best Practice introduced by
The expression $this->getLocalizedURL(false, $url); of type string|false adds false to the return on line 285 which is incompatible with the return type declared by the interface Arcanedev\Localization\C...ion::getNonLocalizedURL of type string. It seems like you forgot to handle an error condition.
Loading history...
286
    }
287
288
    /**
289
     * Returns an URL adapted to $locale or current locale.
290
     *
291
     * @todo: Refactor this beast
292
     *
293
     * @param  string|null  $locale
294
     * @param  string|null  $url
295
     * @param  array        $attributes
296
     * @param  bool|false   $showHiddenLocale
297
     *
298
     * @return string|false
299
     */
300 106
    public function getLocalizedURL($locale = null, $url = null, array $attributes = [], $showHiddenLocale = false)
301
    {
302 106
        if (is_null($locale))
303 10
            $locale = $this->getCurrentLocale();
304
305 106
        $this->isLocaleSupportedOrFail($locale);
306
307 106
        if (empty($attributes))
308 106
            $attributes = Url::extractAttributes($url);
309
310 106
        if (empty($url)) {
311 10
            if ($this->routeTranslator->hasCurrentRoute()) {
312 2
                if (empty($attributes))
313 2
                    $attributes = $this->request()->route()->parameters();
314
315 2
                return $this->getUrlFromRouteName(
316 2
                    $locale,
317 2
                    $this->routeTranslator->getCurrentRoute(),
318 1
                    $attributes,
319 1
                    $showHiddenLocale
320
                );
321
            }
322
323 10
            $url = $this->request()->fullUrl();
324
        }
325
326
        if (
327 106
            $locale &&
328 106
            $translatedRoute = $this->findTranslatedRouteByUrl($url, $attributes, $this->getCurrentLocale())
329
        ) {
330 52
            return $this->getUrlFromRouteName($locale, $translatedRoute, $attributes, $showHiddenLocale);
331
        }
332
333 106
        $baseUrl    = $this->request()->getBaseUrl();
334 106
        $parsedUrl  = parse_url($url);
335
336 106
        $translatedRoute = $this->routeTranslator->getTranslatedRoute(
337 106
            $baseUrl, $parsedUrl, $this->getDefaultLocale(), $this->getSupportedLocales()
338
        );
339
340 106
        if ($translatedRoute !== false)
341 104
            return $this->getUrlFromRouteName($locale, $translatedRoute, $attributes, $showHiddenLocale);
342
343 90
        if ( ! empty($locale)) {
344 56
            if ($locale !== $this->getDefaultLocale() || ! $this->isDefaultLocaleHiddenInUrl() || $showHiddenLocale) {
345 52
                $parsedUrl['path'] = $locale.'/'.ltrim($parsedUrl['path'], '/');
346
            }
347
        }
348
349 90
        $parsedUrl['path'] = ltrim(ltrim($baseUrl, '/') . '/' . $parsedUrl['path'], '/');
350 90
        $parsedUrl['path'] = rtrim($parsedUrl['path'], '/');
351
352 90
        $url = Url::unparse($parsedUrl);
353
354 90
        if (filter_var($url, FILTER_VALIDATE_URL)) return $url;
355
356 2
        return $this->createUrlFromUri(
357 2
            empty($url) ? $parsedUrl['path'] : $url
358
        );
359
    }
360
361
    /**
362
     * Create an url from the uri.
363
     *
364
     * @param  string  $uri
365
     *
366
     * @return string
367
     */
368 110
    public function createUrlFromUri($uri)
369
    {
370 110
        $uri = ltrim($uri, '/');
371
372 110
        return empty($this->baseUrl)
373
            ? $this->app[\Illuminate\Contracts\Routing\UrlGenerator::class]->to($uri)
374 110
            : $this->baseUrl.$uri;
375
    }
376
377
    /**
378
     * Get locales navigation bar.
379
     *
380
     * @return string
381
     */
382 2
    public function localesNavbar()
383
    {
384
        /** @var  \Illuminate\Contracts\View\Factory  $view */
385 2
        $view = $this->app[ViewFactoryContract::class];
386
387 2
        return $view->make('localization::navbar', ['supportedLocales' => $this->getSupportedLocales()])
388 2
                    ->render();
389
    }
390
391
    /* -----------------------------------------------------------------
392
     |  Translation Methods
393
     | -----------------------------------------------------------------
394
     */
395
396
    /**
397
     * Returns the translated route for an url and the attributes given and a locale
398
     *
399
     * @param  string  $url
400
     * @param  array   $attributes
401
     * @param  string  $locale
402
     *
403
     * @return string|false
404
     */
405 104
    private function findTranslatedRouteByUrl($url, $attributes, $locale)
406
    {
407 104
        if (empty($url))
408
            return false;
409
410
        // check if this url is a translated url
411 104
        foreach ($this->routeTranslator->getTranslatedRoutes() as $translatedRoute) {
412 104
            $translatedUrl = $this->getUrlFromRouteName($locale, $translatedRoute, $attributes);
413
414 104
            if ($this->getNonLocalizedURL($translatedUrl) === $this->getNonLocalizedURL($url))
0 ignored issues
show
Security Bug introduced by
It seems like $translatedUrl defined by $this->getUrlFromRouteNa...atedRoute, $attributes) on line 412 can also be of type false; however, Arcanedev\Localization\L...n::getNonLocalizedURL() does only seem to accept string|null, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
415 52
                return $translatedRoute;
416
        }
417
418 56
        return false;
419
    }
420
421
    /**
422
     * Returns an URL adapted to the route name and the locale given.
423
     *
424
     * @param  string|bool  $locale
425
     * @param  string       $transKey
426
     * @param  array        $attributes
427
     * @param  bool|false   $showHiddenLocale
428
     *
429
     * @return string|false
430
     */
431 110
    public function getUrlFromRouteName($locale, $transKey, array $attributes = [], $showHiddenLocale = false)
432
    {
433 110
        $this->isLocaleSupportedOrFail($locale);
0 ignored issues
show
Bug introduced by
It seems like $locale defined by parameter $locale on line 431 can also be of type boolean; however, Arcanedev\Localization\L...LocaleSupportedOrFail() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
434
435 108
        $route = $this->routeTranslator->getUrlFromRouteName(
436 108
            $locale,
0 ignored issues
show
Bug introduced by
It seems like $locale defined by parameter $locale on line 431 can also be of type boolean; however, Arcanedev\Localization\C...::getUrlFromRouteName() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
437 108
            $this->getDefaultLocale(),
438 54
            $transKey,
439 54
            $attributes,
440 108
            $this->isDefaultLocaleHiddenInUrl(),
441 54
            $showHiddenLocale
442
        );
443
444
        // This locale does not have any key for this route name
445 108
        if (empty($route)) return false;
446
447 108
        return rtrim($this->createUrlFromUri($route));
448
    }
449
450
    /**
451
     * Set route name from request.
452
     *
453
     * @param  \Illuminate\Http\Request  $request
454
     */
455 2
    public function setRouteNameFromRequest(Request $request)
456
    {
457 2
        $routeName = $this->routeTranslator->getRouteNameFromPath(
458 2
            $request->getUri(), $this->getCurrentLocale()
459
        );
460
461 2
        $this->routeTranslator->setCurrentRoute($routeName);
462 2
    }
463
464
    /* -----------------------------------------------------------------
465
     |  Check Methods
466
     | -----------------------------------------------------------------
467
     */
468
469
    /**
470
     * Hide the default locale in URL ??
471
     *
472
     * @return bool
473
     */
474 108
    public function isDefaultLocaleHiddenInUrl()
475
    {
476 108
        return $this->localesManager->isDefaultLocaleHiddenInUrl();
477
    }
478
479
    /**
480
     * Check if Locale exists on the supported locales collection.
481
     *
482
     * @param  string|bool  $locale
483
     *
484
     * @return bool
485
     */
486 112
    public function isLocaleSupported($locale)
487
    {
488 112
        return ! ($locale !== false && ! $this->localesManager->isSupportedLocale($locale));
0 ignored issues
show
Bug introduced by
It seems like $locale defined by parameter $locale on line 486 can also be of type boolean; however, Arcanedev\Localization\C...er::isSupportedLocale() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
489
    }
490
491
    /**
492
     * Check if the locale is supported or fail if not.
493
     *
494
     * @param  string  $locale
495
     *
496
     * @throws \Arcanedev\Localization\Exceptions\UnsupportedLocaleException
497
     */
498 112
    private function isLocaleSupportedOrFail($locale): void
499
    {
500 112
        if ( ! $this->isLocaleSupported($locale))
501 2
            throw new UnsupportedLocaleException(
502 2
                "Locale '{$locale}' is not in the list of supported locales."
503
            );
504 110
    }
505
}
506