Passed
Branch feature/2.1-geodispersion-dev (38d49e)
by Jonathan
04:17
created

MapAdapterDataService::mapperConfigDecoder()   A

Complexity

Conditions 6
Paths 10

Size

Total Lines 16
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 12
c 1
b 0
f 0
nc 10
nop 1
dl 0
loc 16
rs 9.2222
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\GeoAnalysisMap;
25
use Closure;
26
use stdClass;
27
28
/**
29
 * Service for accessing geographical analysis map adapters.
30
 */
31
class MapAdapterDataService
32
{
33
    private MapDefinitionsService $mapdefinition_service;
34
35
    /**
36
     * Constructor for MapAdapterDataService
37
     *
38
     * @param MapDefinitionsService $mapdefinition_service
39
     */
40
    public function __construct(MapDefinitionsService $mapdefinition_service)
41
    {
42
        $this->mapdefinition_service = $mapdefinition_service;
43
    }
44
45
    /**
46
     * Find a GeoAnalysisMapAdapter by ID
47
     *
48
     * @param int $id
49
     * @return GeoAnalysisMapAdapter|NULL
50
     */
51
    public function find(int $id): ?GeoAnalysisMapAdapter
52
    {
53
        return DB::table('maj_geodisp_mapviews')
54
            ->select('maj_geodisp_mapviews.*')
55
            ->where('majgm_id', '=', $id)
56
            ->get()
57
            ->map($this->mapAdapterMapper())
58
            ->first();
59
    }
60
61
    /**
62
     * Get all GeoAnalysisMapAdapters linked to a Map View.
63
     *
64
     * @param GeoAnalysisMap $map_view
65
     * @return Collection<GeoAnalysisMapAdapter>
66
     */
67
    public function allForView(GeoAnalysisMap $map_view): Collection
68
    {
69
        return DB::table('maj_geodisp_mapviews')
70
            ->select('maj_geodisp_mapviews.*')
71
            ->where('majgm_majgv_id', '=', $map_view->id())
72
            ->get()
73
            ->map($this->mapAdapterMapper())
74
            ->filter();
75
    }
76
77
    /**
78
     * Insert a GeoAnalysisMapAdapter in the database.
79
     *
80
     * @param GeoAnalysisMapAdapter $map_adapter
81
     * @return int
82
     */
83
    public function insertGetId(GeoAnalysisMapAdapter $map_adapter): int
84
    {
85
        return DB::table('maj_geodisp_mapviews')
86
            ->insertGetId([
87
                'majgm_majgv_id' => $map_adapter->geoAnalysisViewId(),
88
                'majgm_map_id' => $map_adapter->map()->id(),
89
                'majgm_mapper' => get_class($map_adapter->placeMapper()),
90
                'majgm_feature_prop' => $map_adapter->viewConfig()->mapMappingProperty(),
91
                'majgm_config' => json_encode($map_adapter->viewConfig()->mapperConfig())
92
            ]);
93
    }
94
95
    /**
96
     * Update a GeoAnalysisMapAdapter in the database.
97
     *
98
     * @param GeoAnalysisMapAdapter $map_adapter
99
     * @return int
100
     */
101
    public function update(GeoAnalysisMapAdapter $map_adapter): int
102
    {
103
        return DB::table('maj_geodisp_mapviews')
104
            ->where('majgm_id', '=', $map_adapter->id())
105
            ->update([
106
                'majgm_map_id' => $map_adapter->map()->id(),
107
                'majgm_mapper' => get_class($map_adapter->placeMapper()),
108
                'majgm_feature_prop' => $map_adapter->viewConfig()->mapMappingProperty(),
109
                'majgm_config' => json_encode($map_adapter->placeMapper()->config())
110
            ]);
111
    }
112
113
    /**
114
     * Delete a GeoAnalysisMapAdapter from the database.
115
     *
116
     * @param GeoAnalysisMapAdapter $map_adapter
117
     * @return int
118
     */
119
    public function delete(GeoAnalysisMapAdapter $map_adapter): int
120
    {
121
        return DB::table('maj_geodisp_mapviews')
122
            ->where('majgm_id', '=', $map_adapter->id())
123
            ->delete();
124
    }
125
126
    /**
127
     * Get the closure to create a GeoAnalysisMapAdapter object from a row in the database.
128
     * It returns null if the classes stored in the DB cannot be loaded through the Laravel container,
129
     * or if the types do not match with the ones expected.
130
     *
131
     * @return Closure(\stdClass $row):?GeoAnalysisMapAdapter
132
     */
133
    private function mapAdapterMapper(): Closure
134
    {
135
        return function (stdClass $row): ?GeoAnalysisMapAdapter {
136
            if (null === $map = $this->mapdefinition_service->find($row->majgm_map_id)) {
137
                return null;
138
            }
139
            try {
140
                $mapper = app($row->majgm_mapper);
141
                if (!($mapper instanceof PlaceMapperInterface)) {
142
                    return null;
143
                }
144
145
                return new GeoAnalysisMapAdapter(
146
                    (int) $row->majgm_id,
147
                    (int) $row->majgm_majgv_id,
148
                    $map,
149
                    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

149
                    /** @scrutinizer ignore-type */ app($row->majgm_mapper),
Loading history...
150
                    new MapViewConfig($row->majgm_feature_prop, $this->mapperConfigDecoder($row->majgm_config))
151
                );
152
            } catch (BindingResolutionException $ex) {
153
                return null;
154
            }
155
        };
156
    }
157
158
    /**
159
     * Create a PlaceMapperConfigInterface object from a JSON column value.
160
     * Returns null if the JSON string is invalid/empty or if the extracted mapper class cannot be loaded
161
     * through the Laravel container or if the type do not match with the one expected.
162
     *
163
     * @param string $json_config
164
     * @return PlaceMapperConfigInterface|NULL
165
     */
166
    private function mapperConfigDecoder(?string $json_config): ?PlaceMapperConfigInterface
167
    {
168
        $config = $json_config === null ? [] : json_decode($json_config, true);
169
        $class = $config['class'] ?? null;
170
        $json_mapper_config = $config['config'] ?? null;
171
        if ($class === null || $json_mapper_config === null) {
172
            return null;
173
        }
174
        try {
175
            $mapper_config = app($class);
176
            if (!$mapper_config instanceof PlaceMapperConfigInterface) {
177
                return null;
178
            }
179
            return $mapper_config->jsonDeserialize($json_mapper_config);
180
        } catch (BindingResolutionException $ex) {
181
            return null;
182
        }
183
    }
184
}
185