Generator::addCities()   A
last analyzed

Complexity

Conditions 4
Paths 5

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 6
nc 5
nop 1
dl 0
loc 12
rs 9.2
c 0
b 0
f 0
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(...$cities): Generator
141
    {
142
        foreach ($cities as $city) {
143
            if (!is_array($city)) {
144
                $city = [$city];
145
            }
146
            foreach ($city as $item) {
147
                $this->addCity($item);
148
            }
149
        }
150
151
        return $this;
152
    }
153
154
    /**
155
     * Установить список городов, для которого будем генерировать адреса
156
     *
157
     * @param array ...$cities
158
     * @return Generator
159
     */
160
    public function setCities(...$cities): Generator
161
    {
162
        $this->clearCities();
163
164
        if (sizeof($cities) === 1 && is_array($cities[0])) {
165
            $cities = $cities[0];
166
        }
167
168
        $this->addCities($cities);
169
170
        return $this;
171
    }
172
173
    /**
174
     * Очистить список гордов, для которых можно генерировать адреса
175
     *
176
     * @return Generator
177
     */
178
    public function clearCities(): Generator
179
    {
180
        $this->cities = new Collection();
181
182
        $this->clearAddresses();
183
184
        return $this;
185
    }
186
187
    /**
188
     * Очистить список сгенерированных адресов
189
     *
190
     * @return Generator
191
     */
192
    public function clearAddresses(): Generator
193
    {
194
        $this->availableAddresses = new Collection();
195
196
        $this->usedCities = new Collection();
197
198
        return $this;
199
    }
200
201
    /**
202
     * Получить коллекцию городов, для которых можно получать адреса.
203
     *
204
     * Если города заданы, то их. Если нет, то все доступные города.
205
     *
206
     * @return Collection
207
     */
208
    public function getCities(): Collection
209
    {
210
        return $this->cities->isEmpty() ? $this->availableCities : $this->cities;
211
    }
212
213
    /**
214
     * Получить имена городов, установленные для которых можно получать адреса
215
     *
216
     * @return Collection
217
     */
218
    public function getCityNames(): Collection
219
    {
220
        return $this->cities->values();
221
    }
222
223
    /**
224
     * Подключение городов пользователя
225
     *
226
     * @param int $cityId ID города
227
     * @param string|array $cityName Наименование города
228
     * @param string $dataFile Путь до файла данных корня диска
229
     *
230
     * @return Generator
231
     */
232
    public function loadCustomData(int $cityId, $cityName, string $dataFile): Generator
233
    {
234
        $cityNames = is_array($cityName) ? $cityName : [$cityName];
235
236
        $data = [];
237
238
        $data[$cityId] = $cityNames;
239
240
        $this->makeCities($data);
241
242
        $this->customDataFiles->put($cityId, $dataFile);
243
244
        $this->addCity($cityNames[0]);
245
246
        return $this;
247
    }
248
249
    /**
250
     * Получить ID города по его имени
251
     *
252
     * @param string $name
253
     * @return int
254
     * @throws CityNotFound
255
     */
256
    protected function getCityIdByName(string $name): int
257
    {
258
        $key = $this->searchableCityNames->get($this->prepare($name));
259
260
        if (empty($key)) {
261
            throw new CityNotFound();
262
        }
263
264
        return $key;
265
    }
266
267
    /**
268
     * Генерируем коллекцию всех доступных адресов для указанного города
269
     *
270
     * @param int $cityId
271
     */
272
    protected function makeAddresses(int $cityId)
273
    {
274
        $city = $this->availableCities->get($cityId);
275
276
        $addresses = new Collection();
277
278
        foreach ($this->loadData($cityId) as $street => $buildings) {
279
            foreach ($buildings as $building) {
280
                $address = new Address($city, $street, $building);
281
                $addresses->push($address);
282
            }
283
        }
284
285
        $this->usedCities->push($cityId);
286
287
        $this->availableAddresses->put($cityId, $addresses);
288
    }
289
290
    /**
291
     * Загрузка сырых данных по городу
292
     *
293
     * @param int $cityId
294
     * @return array
295
     * @throws CityDataFileNotFound
296
     */
297
    protected function loadData(int $cityId): array
298
    {
299
        $file = $this->customDataFiles->has($cityId)
300
            ? $this->customDataFiles->get($cityId)
301
            :__DIR__ . '/data/ru/' . $cityId . '.php';
302
303
        if (!file_exists($file)) {
304
            throw new CityDataFileNotFound();
305
        }
306
307
        $rawData = require($file);
308
309
        return $rawData;
310
    }
311
312
    /**
313
     * Загрузка списка городов с алиасами
314
     *
315
     * @return array
316
     * @throws CityListFileNotFound
317
     */
318
    protected function loadCities(): array
319
    {
320
        $file = __DIR__ . '/data/cities.php';
321
322
        if (!file_exists($file)) {
323
            throw new CityListFileNotFound();
324
        }
325
326
        $rawData = require($file);
327
328
        return $rawData;
329
    }
330
331
    /**
332
     * Генерирум коллекцию всех доступных городов
333
     *
334
     * @param array $data
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
}