Passed
Push — master ( 746da6...035caa )
by Denis
03:20
created

Generator   B

Complexity

Total Complexity 38

Size/Duplication

Total Lines 351
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 351
rs 8.3999
c 0
b 0
f 0
wmc 38

18 Methods

Rating   Name   Duplication   Size   Complexity  
A getCityNames() 0 3 1
A clearCities() 0 7 1
A __construct() 0 15 1
A getAllAddresses() 0 3 1
A makeCities() 0 10 4
A getCityIdByName() 0 9 2
A prepare() 0 5 1
A makeAddresses() 0 16 3
A clearAddresses() 0 7 1
A loadCustomData() 0 13 2
A loadData() 0 13 3
A addCities() 0 14 4
A getCities() 0 3 2
A getRandomAddresses() 0 9 2
A getRandomAddress() 0 15 3
A setCities() 0 13 3
A loadCities() 0 11 2
A addCity() 0 10 2
1
<?php
2
3
namespace Ngtfkx\Laradeck\AddressGenerator;
4
5
6
use Illuminate\Support\Collection;
7
use Ngtfkx\Laradeck\AddressGenerator\Exceptions\CityDataFileNotFound;
8
use Ngtfkx\Laradeck\AddressGenerator\Exceptions\CityListFileNotFound;
9
use Ngtfkx\Laradeck\AddressGenerator\Exceptions\CityNotFound;
10
11
class Generator
12
{
13
    /**
14
     * @var Collection Список городов для которых будем генерировать адреса
15
     */
16
    protected $cities;
17
18
    /**
19
     * @var Collection Список доступных для поиска имен городов городов
20
     */
21
    protected $searchableCityNames;
22
23
    /**
24
     * @var Collection Список достпупных гордов
25
     */
26
    protected $availableCities;
27
28
    /**
29
     * @var Collection Список достпупных адресов
30
     */
31
    protected $availableAddresses;
32
33
    /**
34
     * @var Collection Список городов, для которых сгенерированы адреса
35
     */
36
    protected $usedCities;
37
38
    /**
39
     * @var Collection Коллекция кастомных файлов данныъ
40
     */
41
    protected $customDataFiles;
42
43
    /**
44
     * Generator constructor.
45
     */
46
    public function __construct()
47
    {
48
        $this->cities = new Collection();
49
50
        $this->usedCities = new Collection();
51
52
        $this->availableAddresses = new Collection();
53
54
        $this->availableCities = new Collection();
55
56
        $this->searchableCityNames = new Collection();
57
58
        $this->customDataFiles = new Collection();
59
60
        $this->makeCities($this->loadCities());
61
    }
62
63
    /**
64
     * Получить случайный адрес
65
     *
66
     * @param string|null $forCity Имя города, для которого генерировать адрес. По умолчанию null - из любого установленного
67
     * @return Address
68
     * @throws CityNotFound Город не найден в списке доступных
69
     */
70
    public function getRandomAddress(string $forCity = null): Address
71
    {
72
        $forCity = $forCity ?: $this->getCities()->random();
73
74
        $forCity = $this->prepare($forCity);
75
76
        $cityId = $this->getCityIdByName($forCity);
77
78
        if ($this->usedCities->contains($cityId) === false) {
79
            $this->makeAddresses($cityId);
80
        }
81
82
        $address = $this->availableAddresses->get($cityId)->random();
83
84
        return $address;
85
    }
86
87
    /**
88
     * Получить все сгенированные адреса
89
     *
90
     * @return Collection
91
     */
92
    public function getAllAddresses(): Collection
93
    {
94
        return $this->availableAddresses;
95
    }
96
97
    /**
98
     * Получить несколько случайных адресов
99
     *
100
     * @param int $count Кол-во требуемых адресов
101
     * @param null|string $forCity Имя города, для которого генерировать адрес. По умолчанию null - из любого установленного
102
     * @return Collection
103
     */
104
    public function getRandomAddresses(int $count, string $forCity = null): Collection
105
    {
106
        $addresses = new Collection();
107
108
        for ($i = 1; $i <= $count; $i++) {
109
            $addresses->push($this->getRandomAddress($forCity));
110
        }
111
112
        return $addresses;
113
    }
114
115
    /**
116
     * Добавить город в список, для которого будем генерировать адреса
117
     *
118
     * @param string $city
119
     * @return Generator
120
     * @throws CityNotFound Город не найден в списке доступных
121
     */
122
    public function addCity(string $city): Generator
123
    {
124
        $key = $this->getCityIdByName($city);
125
126
        if (!$this->cities->contains($city)) {
127
            $this->cities->put($key, $city);
128
            $this->makeAddresses($key);
129
        }
130
131
        return $this;
132
    }
133
134
    /**
135
     * Добавить несколько городо в список, для которого будем генерировать адреса
136
     *
137
     * @param array ...$cities
138
     * @return Generator
139
     */
140
    public function addCities(): Generator
141
    {
142
        $cities = func_get_args();
143
144
        foreach ($cities as $city) {
145
            if (!is_array($city)) {
146
                $city = [$city];
147
            }
148
            foreach ($city as $item) {
149
                $this->addCity($item);
150
            }
151
        }
152
153
        return $this;
154
    }
155
156
    /**
157
     * Установить список городов, для которого будем генерировать адреса
158
     *
159
     * @param array ...$cities
160
     * @return Generator
161
     */
162
    public function setCities(): Generator
163
    {
164
        $cities = func_get_args();
165
166
        $this->clearCities();
167
168
        if (sizeof($cities) === 1 && is_array($cities[0])) {
169
            $cities = $cities[0];
170
        }
171
172
        $this->addCities($cities);
173
174
        return $this;
175
    }
176
177
    /**
178
     * Очистить список гордов, для которых можно генерировать адреса
179
     *
180
     * @return Generator
181
     */
182
    public function clearCities(): Generator
183
    {
184
        $this->cities = new Collection();
185
186
        $this->clearAddresses();
187
188
        return $this;
189
    }
190
191
    /**
192
     * Очистить список сгенерированных адресов
193
     *
194
     * @return Generator
195
     */
196
    public function clearAddresses(): Generator
197
    {
198
        $this->availableAddresses = new Collection();
199
200
        $this->usedCities = new Collection();
201
202
        return $this;
203
    }
204
205
    /**
206
     * Получить коллекцию городов, для которых можно получать адреса.
207
     *
208
     * Если города заданы, то их. Если нет, то все доступные города.
209
     *
210
     * @return Collection
211
     */
212
    public function getCities(): Collection
213
    {
214
        return $this->cities->isEmpty() ? $this->availableCities : $this->cities;
215
    }
216
217
    /**
218
     * Получить имена городов, установленные для которых можно получать адреса
219
     *
220
     * @return Collection
221
     */
222
    public function getCityNames(): Collection
223
    {
224
        return $this->cities->values();
225
    }
226
227
    /**
228
     * Подключение городов пользователя
229
     *
230
     * @param int $cityId ID города
231
     * @param string|array $cityName Наименование города
232
     * @param string $dataFile Путь до файла данных относительно папки storage
233
     *
234
     * @return Generator
235
     */
236
    public function loadCustomData(int $cityId, $cityName, string $dataFile): Generator
237
    {
238
        $cityNames = is_array($cityName) ? $cityName : [$cityName];
239
240
        $data[$cityId] = $cityNames;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$data was never initialized. Although not strictly required by PHP, it is generally a good practice to add $data = array(); before regardless.
Loading history...
241
242
        $this->makeCities($data);
243
244
        $this->customDataFiles->put($cityId, $dataFile);
245
246
        $this->addCity($cityNames[0]);
247
248
        return $this;
249
    }
250
251
    /**
252
     * Получить ID города по его имени
253
     *
254
     * @param string $name
255
     * @return int
256
     * @throws CityNotFound
257
     */
258
    protected function getCityIdByName(string $name): int
259
    {
260
        $key = $this->searchableCityNames->get($this->prepare($name));
261
262
        if (empty($key)) {
263
            throw new CityNotFound();
264
        }
265
266
        return $key;
267
    }
268
269
    /**
270
     * Генерируем коллекцию всех доступных адресов для указанного города
271
     *
272
     * @param int $cityId
273
     */
274
    protected function makeAddresses(int $cityId)
275
    {
276
        $city = $this->availableCities->get($cityId);
277
278
        $addresses = new Collection();
279
280
        foreach ($this->loadData($cityId) as $street => $buildings) {
281
            foreach ($buildings as $building) {
282
                $address = new Address($city, $street, $building);
283
                $addresses->push($address);
284
            }
285
        }
286
287
        $this->usedCities->push($cityId);
288
289
        $this->availableAddresses->put($cityId, $addresses);
290
    }
291
292
    /**
293
     * Загрузка сырых данных по городу
294
     *
295
     * @param int $cityId
296
     * @return array
297
     * @throws CityDataFileNotFound
298
     */
299
    protected function loadData(int $cityId): array
300
    {
301
        $file = $this->customDataFiles->has($cityId)
302
            ? storage_path($this->customDataFiles->get($cityId) )
1 ignored issue
show
Bug introduced by
The function storage_path was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

302
            ? /** @scrutinizer ignore-call */ storage_path($this->customDataFiles->get($cityId) )
Loading history...
303
            :__DIR__ . '/data/ru/' . $cityId . '.php';
304
305
        if (!file_exists($file)) {
306
            throw new CityDataFileNotFound();
307
        }
308
309
        $rawData = require($file);
310
311
        return $rawData;
312
    }
313
314
    /**
315
     * Загрузка списка городов с алиасами
316
     *
317
     * @return array
318
     * @throws CityListFileNotFound
319
     */
320
    protected function loadCities(): array
321
    {
322
        $file = __DIR__ . '/data/cities.php';
323
324
        if (!file_exists($file)) {
325
            throw new CityListFileNotFound();
326
        }
327
328
        $rawData = require($file);
329
330
        return $rawData;
331
    }
332
333
    /**
334
     * Генерирум коллекцию всех доступных городов
335
     */
336
    protected function makeCities($data)
337
    {
338
        foreach ($data as $key => $items) {
339
            $this->availableCities->put($key, $items[0]);
340
341
            foreach ($items as $item) {
342
                $item = $this->prepare($item);
343
344
                if (!$this->searchableCityNames->contains($item)) {
345
                    $this->searchableCityNames->put($item, $key);
346
                }
347
            }
348
        }
349
    }
350
351
    /**
352
     * Приведение разных вариантов написания наименования города к общему виду
353
     *
354
     * @param $string
355
     * @return string
356
     */
357
    protected function prepare($string): string
358
    {
359
        $string = str_replace([' ', '-', '_'], '', mb_strtolower($string));
360
361
        return $string;
362
    }
363
}