Passed
Branch main (f9aaf7)
by Jonathan
14:43
created

Migration2::upgrade()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 38
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 23
nc 6
nop 0
dl 0
loc 38
rs 8.9297
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * webtrees-lib: MyArtJaub library for webtrees
5
 *
6
 * @package MyArtJaub\Webtrees
7
 * @subpackage GeoDispersion
8
 * @author Jonathan Jaubart <[email protected]>
9
 * @copyright Copyright (c) 2009-2021, Jonathan Jaubart
10
 * @license http://www.gnu.org/licenses/gpl.html GNU General Public License, version 3
11
 */
12
13
declare(strict_types=1);
14
15
namespace MyArtJaub\Webtrees\Module\GeoDispersion\Schema;
16
17
use Fisharebest\Webtrees\FlashMessages;
18
use Fisharebest\Webtrees\I18N;
19
use Fisharebest\Webtrees\Tree;
20
use Fisharebest\Webtrees\Schema\MigrationInterface;
21
use Fisharebest\Webtrees\Services\TreeService;
22
use Illuminate\Database\Capsule\Manager as DB;
23
use MyArtJaub\Webtrees\Common\GeoDispersion\Config\MapColorsConfig;
24
use MyArtJaub\Webtrees\Common\GeoDispersion\Config\MapViewConfig;
25
use MyArtJaub\Webtrees\Module\GeoDispersion\Model\GeoAnalysisMapAdapter;
26
use MyArtJaub\Webtrees\Module\GeoDispersion\PlaceMappers\SimplePlaceMapper;
27
use MyArtJaub\Webtrees\Module\GeoDispersion\Services\GeoAnalysisViewDataService;
28
use MyArtJaub\Webtrees\Module\GeoDispersion\Services\MapAdapterDataService;
29
use MyArtJaub\Webtrees\Module\GeoDispersion\Services\MapDefinitionsService;
30
use MyArtJaub\Webtrees\Module\GeoDispersion\Views\GeoAnalysisMap;
31
use MyArtJaub\Webtrees\Module\GeoDispersion\Views\GeoAnalysisTable;
32
use MyArtJaub\Webtrees\Module\Sosa\GeoAnalyses\SosaByGenerationGeoAnalysis;
33
use Spatie\Color\Hex;
34
use RuntimeException;
35
use stdClass;
36
37
/**
38
 * Upgrade the database schema from version 2 to version 3 (migrated views from webtrees 1.7).
39
 */
40
class Migration2 implements MigrationInterface
41
{
42
    /**
43
     * Mapping from old map definitions to new ones
44
     * @var array<string,mixed> MAPS_XML_MAPPING
45
     */
46
    private const MAPS_XML_MAPPING = [
47
        'aubracmargeridebycommunes.xml' =>  'fr-area-aubrac-lot-margeride-planeze-communes',
48
        'calvadosbycommunes.xml'        =>  'fr-dpt-14-communes',
49
        'cantalbycommunes.xml'          =>  'fr-dpt-15-communes',
50
        'cotesdarmorbycommunes.xml'     =>  'fr-dpt-22-communes',
51
        'essonnebycommunes.xml'         =>  'fr-dpt-91-communes',
52
        'eurebycommunes.xml'            =>  'fr-dpt-27-communes',
53
        'eureetloirbycommunes.xml'      =>  'fr-dpt-28-communes',
54
        'francebydepartements.xml'      =>  'fr-metropole-departements',
55
        'francebyregions1970.xml'       =>  'fr-metropole-regions-1970',
56
        'francebyregions2016.xml'       =>  'fr-metropole-regions-2016',
57
        'hauteloirebycommunes.xml'      =>  'fr-dpt-43-communes',
58
        'illeetvilainebycommunes.xml'   =>  'fr-dpt-35-communes',
59
        'loiretbycommunes.xml'          =>  'fr-dpt-45-communes',
60
        'lozerebycodepostaux.xml'       =>  'fr-dpt-48-codespostaux',
61
        'lozerebycommunes.xml'          =>  'fr-dpt-48-communes',
62
        'mayennebycommunes.xml'         =>  'fr-dpt-53-communes',
63
        'oisebycommunes.xml'            =>  'fr-dpt-60-communes',
64
        'ornebycommunes.xml'            =>  'fr-dpt-61-communes',
65
        'puydedomebycommunes.xml'       =>  'fr-dpt-63-communes',
66
        'sarthebycommunes.xml'          =>  'fr-dpt-72-communes',
67
        'seinemaritimebycommunes.xml'   =>  'fr-dpt-76-communes',
68
        'seinesommeoisebycommunes.xml'  =>  ['fr-dpt-60-communes', 'fr-dpt-76-communes', 'fr-dpt-80-communes'],
69
        'valdoisebycommunes.xml'        =>  'fr-dpt-95-communes',
70
        'yvelinesbycommunes.xml'        =>  'fr-dpt-78-communes'
71
    ];
72
73
    /**
74
     * {@inheritDoc}
75
     * @see \Fisharebest\Webtrees\Schema\MigrationInterface::upgrade()
76
     */
77
    public function upgrade(): void
78
    {
79
        if (!DB::schema()->hasTable('maj_geodispersion')) {
80
            return;
81
        }
82
83
        /** @var TreeService $tree_service */
84
        $tree_service = app(TreeService::class);
85
        /** @var GeoAnalysisViewDataService $geoview_data_service */
86
        $geoview_data_service = app(GeoAnalysisViewDataService::class);
87
88
        $existing_views = DB::table('maj_geodispersion')
89
            ->select()
90
            ->get();
91
92
        foreach ($existing_views as $old_view) {
93
            try {
94
                $tree = $tree_service->find((int) $old_view->majgd_file);
95
            } catch (RuntimeException $ex) {
96
                continue;
97
            }
98
99
            if ($old_view->majgd_map === null) {
100
                $this->migrateGeoAnalysisTable($old_view, $tree, $geoview_data_service);
101
            } else {
102
                DB::connection()->beginTransaction();
103
                if ($this->migrateGeoAnalysisMap($old_view, $tree, $geoview_data_service)) {
104
                    DB::connection()->commit();
105
                } else {
106
                    DB::connection()->rollBack();
107
                }
108
            }
109
        }
110
111
        DB::schema()->drop('maj_geodispersion');
112
113
        FlashMessages::addMessage(I18N::translate(
114
            'The geographical dispersion analyses have been migrated for webtrees 2. Please review their settings.'
115
        ));
116
    }
117
118
    /**
119
     * Create a Table geographical analysis view from a migrated item.
120
     *
121
     * @param stdClass $old_view
122
     * @param Tree $tree
123
     * @param GeoAnalysisViewDataService $geoview_data_service
124
     * @return bool
125
     */
126
    private function migrateGeoAnalysisTable(
127
        stdClass $old_view,
128
        Tree $tree,
129
        GeoAnalysisViewDataService $geoview_data_service
130
    ): bool {
131
        $new_view = new GeoAnalysisTable(
132
            0,
133
            $tree,
134
            $old_view->majgd_status === 'enabled',
135
            $old_view->majgd_descr,
136
            app(SosaByGenerationGeoAnalysis::class),
137
            (int) $old_view->majgd_sublevel,
138
            (int) $old_view->majgd_detailsgen
139
        );
140
141
        return $geoview_data_service->insertGetId($new_view) > 0;
142
    }
143
144
    /**
145
     * Create a Map geographical analysis view from a migrated item.
146
     *
147
     * @param stdClass $old_view
148
     * @param Tree $tree
149
     * @param GeoAnalysisViewDataService $geoview_data_service
150
     * @return bool
151
     */
152
    private function migrateGeoAnalysisMap(
153
        stdClass $old_view,
154
        Tree $tree,
155
        GeoAnalysisViewDataService $geoview_data_service
156
    ): bool {
157
        /** @var MapDefinitionsService $map_definition_service */
158
        $map_definition_service = app(MapDefinitionsService::class);
159
        /** @var MapAdapterDataService $mapadapter_data_service */
160
        $mapadapter_data_service = app(MapAdapterDataService::class);
161
162
        $new_view = new GeoAnalysisMap(
163
            0,
164
            $tree,
165
            $old_view->majgd_status === 'enabled',
166
            $old_view->majgd_descr,
167
            app(SosaByGenerationGeoAnalysis::class),
168
            (int) $old_view->majgd_sublevel,
169
            (int) $old_view->majgd_detailsgen
170
        );
171
172
        $view_id = $geoview_data_service->insertGetId($new_view);
173
        if ($view_id === 0) {
174
            return false;
175
        }
176
        $new_view = $new_view->withId($view_id);
177
178
        $colors = $new_view->colors();
179
        foreach ($this->mapIdsFromOld($old_view->majgd_map) as $new_map_id) {
180
            $map = $map_definition_service->find($new_map_id);
181
            if ($map === null) {
182
                return false;
183
            }
184
            $colors = $this->colorsFromMap($new_map_id);
185
186
            /** @var SimplePlaceMapper $mapper */
187
            $mapper = app(SimplePlaceMapper::class);
188
            $mapview_config = new MapViewConfig($this->mappingPropertyForMap($new_map_id), $mapper->config());
189
            $map_adapter = new GeoAnalysisMapAdapter(0, $view_id, $map, $mapper, $mapview_config);
190
191
            $mapadapter_data_service->insertGetId($map_adapter);
192
        }
193
194
        return $geoview_data_service->update($new_view->withColors($colors)) > 0;
195
    }
196
197
    /**
198
     * Get all new map definitions IDs representing an old map definition
199
     *
200
     * @param string $map_xml
201
     * @return array
202
     */
203
    private function mapIdsFromOld(string $map_xml): array
204
    {
205
        $mapping = self::MAPS_XML_MAPPING[$map_xml] ?? [];
206
        return is_array($mapping) ? $mapping : [ $mapping ];
207
    }
208
209
    /**
210
     * Get the mapping property to be used for the migrated map adapter
211
     *
212
     * @param string $map_id
213
     * @return string
214
     */
215
    private function mappingPropertyForMap(string $map_id): string
216
    {
217
        switch ($map_id) {
218
            case 'fr-metropole-regions-1970':
219
            case 'fr-metropole-regions-2016':
220
                return 'region_insee_libelle';
221
            case 'fr-metropole-departements':
222
                return 'dpt_insee_libelle';
223
            case 'fr-dpt-48-codespostaux':
224
                return 'code_postal';
225
            default:
226
                return 'commune_insee_libelle';
227
        }
228
    }
229
230
    /**
231
     * Get the color configuration to be used for the migrated map view
232
     *
233
     * @param string $map_id
234
     * @return MapColorsConfig
235
     */
236
    private function colorsFromMap(string $map_id): MapColorsConfig
237
    {
238
        $default = Hex::fromString('#f5f5f5');
239
        $stroke = Hex::fromString('#d5d5d5');
240
        $hover = Hex::fromString('#ff6600');
241
242
        switch ($map_id) {
243
            case 'fr-metropole-departements':
244
                return new MapColorsConfig($default, $stroke, Hex::fromString('#0493ab'), $hover);
245
            case 'fr-dpt-48-codespostaux':
246
                return new MapColorsConfig($default, $stroke, Hex::fromString('#44aa00'), $hover);
247
            default:
248
                return new MapColorsConfig($default, $stroke, Hex::fromString('#e2a61d'), $hover);
249
        }
250
    }
251
}
252