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

MapAdapterDataService::mapperConfigDecoder()   A

Complexity

Conditions 6
Paths 10

Size

Total Lines 16
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 12
nc 10
nop 1
dl 0
loc 16
rs 9.2222
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) 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\Services;
16
17
use Illuminate\Contracts\Container\BindingResolutionException;
18
use Illuminate\Database\Capsule\Manager as DB;
19
use Illuminate\Support\Collection;
20
use MyArtJaub\Webtrees\Common\GeoDispersion\Config\MapViewConfig;
21
use MyArtJaub\Webtrees\Contracts\GeoDispersion\PlaceMapperConfigInterface;
22
use MyArtJaub\Webtrees\Contracts\GeoDispersion\PlaceMapperInterface;
23
use MyArtJaub\Webtrees\Module\GeoDispersion\Model\GeoAnalysisMapAdapter;
24
use MyArtJaub\Webtrees\Module\GeoDispersion\Views\AbstractGeoAnalysisView;
25
use MyArtJaub\Webtrees\Module\GeoDispersion\Views\GeoAnalysisMap;
26
use Closure;
27
use stdClass;
28
29
/**
30
 * Service for accessing geographical analysis map adapters.
31
 */
32
class MapAdapterDataService
33
{
34
    private MapDefinitionsService $mapdefinition_service;
35
36
    /**
37
     * Constructor for MapAdapterDataService
38
     *
39
     * @param MapDefinitionsService $mapdefinition_service
40
     */
41
    public function __construct(MapDefinitionsService $mapdefinition_service)
42
    {
43
        $this->mapdefinition_service = $mapdefinition_service;
44
    }
45
46
    /**
47
     * Find a GeoAnalysisMapAdapter by ID
48
     *
49
     * @param int $id
50
     * @return GeoAnalysisMapAdapter|NULL
51
     */
52
    public function find(int $id): ?GeoAnalysisMapAdapter
53
    {
54
        return DB::table('maj_geodisp_mapviews')
55
            ->select('maj_geodisp_mapviews.*')
56
            ->where('majgm_id', '=', $id)
57
            ->get()
58
            ->map($this->mapAdapterMapper())
59
            ->first();
60
    }
61
62
    /**
63
     * Get all GeoAnalysisMapAdapters linked to a Map View.
64
     *
65
     * @param GeoAnalysisMap $map_view
66
     * @param bool $show_invalid
67
     * @return Collection<GeoAnalysisMapAdapter|null>
68
     */
69
    public function allForView(GeoAnalysisMap $map_view, bool $show_invalid = false): Collection
70
    {
71
        $map_adapters = DB::table('maj_geodisp_mapviews')
72
            ->select('maj_geodisp_mapviews.*')
73
            ->where('majgm_majgv_id', '=', $map_view->id())
74
            ->get()
75
            ->map($this->mapAdapterMapper());
76
        return $show_invalid ? $map_adapters : $map_adapters->filter();
77
    }
78
79
    /**
80
     * Insert a GeoAnalysisMapAdapter in the database.
81
     *
82
     * @param GeoAnalysisMapAdapter $map_adapter
83
     * @return int
84
     */
85
    public function insertGetId(GeoAnalysisMapAdapter $map_adapter): int
86
    {
87
        return DB::table('maj_geodisp_mapviews')
88
            ->insertGetId([
89
                'majgm_majgv_id' => $map_adapter->geoAnalysisViewId(),
90
                'majgm_map_id' => $map_adapter->map()->id(),
91
                'majgm_mapper' => get_class($map_adapter->placeMapper()),
92
                'majgm_feature_prop' => $map_adapter->viewConfig()->mapMappingProperty(),
93
                'majgm_config' => json_encode($map_adapter->viewConfig()->mapperConfig())
94
            ]);
95
    }
96
97
    /**
98
     * Update a GeoAnalysisMapAdapter in the database.
99
     *
100
     * @param GeoAnalysisMapAdapter $map_adapter
101
     * @return int
102
     */
103
    public function update(GeoAnalysisMapAdapter $map_adapter): int
104
    {
105
        return DB::table('maj_geodisp_mapviews')
106
            ->where('majgm_id', '=', $map_adapter->id())
107
            ->update([
108
                'majgm_map_id' => $map_adapter->map()->id(),
109
                'majgm_mapper' => get_class($map_adapter->placeMapper()),
110
                'majgm_feature_prop' => $map_adapter->viewConfig()->mapMappingProperty(),
111
                'majgm_config' => json_encode($map_adapter->placeMapper()->config())
112
            ]);
113
    }
114
115
    /**
116
     * Delete a GeoAnalysisMapAdapter from the database.
117
     *
118
     * @param GeoAnalysisMapAdapter $map_adapter
119
     * @return int
120
     */
121
    public function delete(GeoAnalysisMapAdapter $map_adapter): int
122
    {
123
        return DB::table('maj_geodisp_mapviews')
124
            ->where('majgm_id', '=', $map_adapter->id())
125
            ->delete();
126
    }
127
128
    /**
129
     * Delete invalid GeoAnalysisMapAdapters from the database.
130
     *
131
     * @param AbstractGeoAnalysisView $view
132
     * @param Collection $valid_map_adapters
133
     * @return int
134
     */
135
    public function deleteInvalid(AbstractGeoAnalysisView $view, Collection $valid_map_adapters): int
136
    {
137
        return DB::table('maj_geodisp_mapviews')
138
            ->where('majgm_majgv_id', '=', $view->id())
139
            ->whereNotIn('majgm_id', $valid_map_adapters)
140
            ->delete();
141
    }
142
143
    /**
144
     * Get the closure to create a GeoAnalysisMapAdapter object from a row in the database.
145
     * It returns null if the classes stored in the DB cannot be loaded through the Laravel container,
146
     * or if the types do not match with the ones expected.
147
     *
148
     * @return Closure(\stdClass $row):?GeoAnalysisMapAdapter
149
     */
150
    private function mapAdapterMapper(): Closure
151
    {
152
        return function (stdClass $row): ?GeoAnalysisMapAdapter {
153
            if (null === $map = $this->mapdefinition_service->find($row->majgm_map_id)) {
154
                return null;
155
            }
156
            try {
157
                $mapper = app($row->majgm_mapper);
158
                if (!($mapper instanceof PlaceMapperInterface)) {
159
                    return null;
160
                }
161
162
                return new GeoAnalysisMapAdapter(
163
                    (int) $row->majgm_id,
164
                    (int) $row->majgm_majgv_id,
165
                    $map,
166
                    app($row->majgm_mapper),
0 ignored issues
show
Bug introduced by
It seems like app($row->majgm_mapper) can also be of type Illuminate\Container\Container; however, parameter $mapper of MyArtJaub\Webtrees\Modul...pAdapter::__construct() does only seem to accept MyArtJaub\Webtrees\Contr...on\PlaceMapperInterface, maybe add an additional type check? ( Ignorable by Annotation )

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

166
                    /** @scrutinizer ignore-type */ app($row->majgm_mapper),
Loading history...
167
                    new MapViewConfig($row->majgm_feature_prop, $this->mapperConfigDecoder($row->majgm_config))
168
                );
169
            } catch (BindingResolutionException $ex) {
170
                return null;
171
            }
172
        };
173
    }
174
175
    /**
176
     * Create a PlaceMapperConfigInterface object from a JSON column value.
177
     * Returns null if the JSON string is invalid/empty or if the extracted mapper class cannot be loaded
178
     * through the Laravel container or if the type do not match with the one expected.
179
     *
180
     * @param string $json_config
181
     * @return PlaceMapperConfigInterface|NULL
182
     */
183
    private function mapperConfigDecoder(?string $json_config): ?PlaceMapperConfigInterface
184
    {
185
        $config = $json_config === null ? [] : json_decode($json_config, true);
186
        $class = $config['class'] ?? null;
187
        $json_mapper_config = $config['config'] ?? null;
188
        if ($class === null || $json_mapper_config === null) {
189
            return null;
190
        }
191
        try {
192
            $mapper_config = app($class);
193
            if (!$mapper_config instanceof PlaceMapperConfigInterface) {
194
                return null;
195
            }
196
            return $mapper_config->jsonDeserialize($json_mapper_config);
197
        } catch (BindingResolutionException $ex) {
198
            return null;
199
        }
200
    }
201
}
202