RouteGroupLocalizer::routeGroupLocalizer()   B
last analyzed

Complexity

Conditions 6
Paths 18

Size

Total Lines 40

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
nc 18
nop 2
dl 0
loc 40
rs 8.6577
c 0
b 0
f 0
1
<?php
2
3
namespace VSamovarov\LaravelLocalizer;
4
5
use Illuminate\Http\Request;
6
use Illuminate\Routing\Router;
7
use Illuminate\Contracts\Translation\Translator as TranslatorContract;
8
use Illuminate\Routing\RouteCollection;
9
use VSamovarov\LaravelLocalizer\Exceptions\NestedLocalizerRouteGroup;
10
11
class RouteGroupLocalizer
12
{
13
    private $localizer;
14
    private $translator;
15
    private $route;
16
    private $request;
17
18
19
    public function __construct(Localizer $localizer, TranslatorContract $translator, Router $route, Request $request)
0 ignored issues
show
Bug introduced by
You have injected the Request via parameter $request. This is generally not recommended as there might be multiple instances during a request cycle (f.e. when using sub-requests). Instead, it is recommended to inject the RequestStack and retrieve the current request each time you need it via getCurrentRequest().
Loading history...
20
    {
21
        $this->localizer = $localizer;
22
        $this->translator = $translator;
23
        $this->route = $route;
24
        $this->request = $request;
25
    }
26
27
    /**
28
     * Cоздает группу роутеров
29
     * для каждого языка с соответствующими префиксами и именами
30
     * Должен быть в самом верху и оборачивать все локализируемые роутеры.
31
     *
32
     * @param \Closure|string|array $attributes
33
     * @param \Closure|string $routes
34
     * @return void
35
     */
36
    public function routeGroupLocalizer($attributes, $routes)
37
    {
38
      /**
39
       * Роутеры с языком по умолчанию, должны быть в самом низу
40
       * иначе не будет работать, когда локаль по умолчанию скрывается
41
       * Сортируем соответствующим образом
42
       */
43
      $locales = [];
44
      foreach($this->localizer->getSupportedLocales() as $lang) {
45
            if($lang['slug'] === $this->localizer->getSupportedLocales() ) {
46
                array_push ($locales,$lang);
47
            } else {
48
                array_unshift($locales,$lang);
49
            }
50
        }
51
52
        foreach ($locales as $lang) {
53
            $this->route->group([
54
                'prefix' => $lang['prefix'],
55
                'as' =>  $this->localizer->getNamePrefix() . "{$lang['slug']}."
56
            ], function () use ($attributes, $routes) {
57
                $this->route->group($attributes, $routes);
58
            });
59
        }
60
61
        $routes = $this->route->getRoutes();
62
63
        /**
64
         * Локализующие группы, запрещено вкладывать друг в друга
65
         */
66
        if ($this->checkGroupNested($routes)) {
67
            throw new NestedLocalizerRouteGroup();
68
        }
69
70
        if (!app('localizer')->isHideDefaultLocaleInURL()) {
71
            $this->addMainRoute($routes);
72
        }
73
74
        $this->translateRoutes($routes);
75
    }
76
77
    /**
78
     * Проверяем, есть ли вложения локализующих групп роутеров.
79
     * Просто анализируем имя роутера.
80
     * Если в нем несколько раз встречается префикс локализации,
81
     * значит группа вложенная
82
     *
83
     * @param RouteCollection $routes
84
     * @return boolean
85
     */
86
    private function checkGroupNested($routes): bool
87
    {
88
        $locales = $this->localizer->getSlagsSupportedLocales();
89
        $matchPattern =  '{' . $this->localizer->getNamePrefix() . '(' . implode('|', $locales) . ').*[.]' . $this->localizer->getNamePrefix() . '(' . implode('|', $locales) . ')[.]' . '}';
90
        foreach (array_keys($routes->getRoutesByName()) as $name) {
91
            if (preg_match($matchPattern, $name)) return true;
92
        }
93
        return false;
94
    }
95
96
    /**
97
     * Создаем дополнительный роутер, для главной страницы,
98
     * без указания языка - site.name/
99
     * Надо для SEO и правильной обработки редиректа
100
     * В миделваре примем решения, оставить дубль страницы
101
     * или делать редирект на страницу с указанием локали по умолчанию  - site.name/uk
102
     *
103
     * @param RouteCollection $routes
104
     * @return void
105
     */
106
    public function addMainRoute($routes): void
107
    {
108
        try {
109
            /**
110
             * Ищем в коллекции роутер с URL '/'
111
             * Если не находит, конструкция выбрасывает исключение
112
             * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
113
             * тогда пытаемся сделать новый роутер
114
             */
115
            $routes->match($this->request->create('/', 'GET'));
116
        } catch (\Exception $e) {
117
            try {
118
                $action = $routes->match($this->request->create('/' . $this->localizer->getDefaultLocale(), 'GET'))
119
                    ->getAction();
120
                $action = array_merge($action, ['namespace' => '\\', 'prefix' => '', 'as' => '']);
121
                $this->route->get('/', $action);
122
            } catch (\Exception $e) {
123
                //
124
            }
125
        }
126
    }
127
128
    /**
129
     * Локализирует роутеры
130
     *
131
     * Ищет роутеры с префиксом локалайзера и устанавливает новые (переведенные) урлы
132
     *
133
     * @param RouteCollection $routes
134
     * @return void
135
     */
136
    public function translateRoutes($routes): void
137
    {
138
        $prefix = $this->localizer->getNamePrefix();
139
        foreach ($routes as $route) {
140
            $name = $route->getName();
141
142
            if (strpos($name, $prefix) !== false) {
143
                // определяем локаль из имени роутера
144
                $locale = substr(
145
                    substr($name, 0, strpos($name, '.')),
146
                    strlen($this->localizer->getNamePrefix())
147
                );
148
                if ($locale) {
149
                    $route->setUri(
150
                        $this->translateUri($route->uri(), $locale, $this->localizer->getTranslationFileName(), $this->translator)
151
                    );
152
                }
153
            }
154
        }
155
    }
156
157
    /**
158
     * Локализирует УРЛы
159
     *
160
     * Переводится каждый сегмент отдельно.
161
     * Если перевода сегмента нет, то остается прежний
162
     *
163
     * @param string $uri
164
     * @param string $locale
165
     * @param string $group
166
     * @param TranslatorContract $translator
167
     * @return string
168
     */
169
    public function translateUri(string $uri, string $locale, string $group, TranslatorContract $translator): string
170
    {
171
        $parts = array_map(function ($part) use ($locale, $group, $translator) {
172
            $newPart = $translator->get("{$group}.{$part}", [], $locale, false);
0 ignored issues
show
Unused Code introduced by
The call to Translator::get() has too many arguments starting with false.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
173
            return $newPart == "{$group}.{$part}" ? $part : $newPart;
174
        }, explode('/', $uri));
175
        return implode('/', $parts);
176
    }
177
}
178