Passed
Push — master ( fa2345...1dfec5 )
by Greg
05:49
created

GedcomRecordFactory::make()   C

Complexity

Conditions 15
Paths 12

Size

Total Lines 53
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 15
eloc 41
c 2
b 0
f 0
nc 12
nop 3
dl 0
loc 53
rs 5.9166

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * webtrees: online genealogy
5
 * Copyright (C) 2020 webtrees development team
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
 * GNU General Public License for more details.
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16
 */
17
18
declare(strict_types=1);
19
20
namespace Fisharebest\Webtrees\Factories;
21
22
use Closure;
23
use Fisharebest\Webtrees\Contracts\GedcomRecordFactoryInterface;
24
use Fisharebest\Webtrees\Factory;
25
use Fisharebest\Webtrees\Family;
26
use Fisharebest\Webtrees\Gedcom;
27
use Fisharebest\Webtrees\GedcomRecord;
28
use Fisharebest\Webtrees\Header;
29
use Fisharebest\Webtrees\Individual;
30
use Fisharebest\Webtrees\Location;
31
use Fisharebest\Webtrees\Media;
32
use Fisharebest\Webtrees\Note;
33
use Fisharebest\Webtrees\Repository;
34
use Fisharebest\Webtrees\Source;
35
use Fisharebest\Webtrees\Submission;
36
use Fisharebest\Webtrees\Submitter;
37
use Fisharebest\Webtrees\Tree;
38
use Illuminate\Database\Capsule\Manager as DB;
39
use InvalidArgumentException;
40
use stdClass;
41
42
use function assert;
43
44
/**
45
 * Make a GedcomRecord object.
46
 */
47
class GedcomRecordFactory extends AbstractGedcomRecordFactory implements GedcomRecordFactoryInterface
48
{
49
    /**
50
     * Create a GedcomRecord object.
51
     *
52
     * @param string      $xref
53
     * @param Tree        $tree
54
     * @param string|null $gedcom
55
     *
56
     * @return GedcomRecord|null
57
     */
58
    public function make(string $xref, Tree $tree, string $gedcom = null): ?GedcomRecord
59
    {
60
        // We know the type of the record.  Return it directly.
61
        if ($gedcom !== null && preg_match('/^0(?: @[^@]+@)? ([A-Z_]+)/', $gedcom, $match)) {
62
            switch ($match[1]) {
63
                case Family::RECORD_TYPE:
64
                    return Factory::family()->make($xref, $tree, $gedcom);
65
                case Header::RECORD_TYPE:
66
                    return Factory::header()->make($xref, $tree, $gedcom);
67
                case Individual::RECORD_TYPE:
68
                    return Factory::individual()->make($xref, $tree, $gedcom);
69
                case Location::RECORD_TYPE:
70
                    return Factory::location()->make($xref, $tree, $gedcom);
71
                case Media::RECORD_TYPE:
72
                    return Factory::media()->make($xref, $tree, $gedcom);
73
                case Note::RECORD_TYPE:
74
                    return Factory::note()->make($xref, $tree, $gedcom);
75
                case Repository::RECORD_TYPE:
76
                    return Factory::repository()->make($xref, $tree, $gedcom);
77
                case Source::RECORD_TYPE:
78
                    return Factory::source()->make($xref, $tree, $gedcom);
79
                case Submitter::RECORD_TYPE:
80
                    return Factory::submitter()->make($xref, $tree, $gedcom);
81
                case Submission::RECORD_TYPE:
82
                    return Factory::submission()->make($xref, $tree, $gedcom);
83
            }
84
        }
85
86
        // We do not know the type of the record.  Try them all in turn.
87
        return
88
            Factory::family()->make($xref, $tree, $gedcom) ??
89
            Factory::individual()->make($xref, $tree, $gedcom) ??
90
            Factory::media()->make($xref, $tree, $gedcom) ??
91
            Factory::note()->make($xref, $tree, $gedcom) ??
92
            Factory::repository()->make($xref, $tree, $gedcom) ??
93
            Factory::source()->make($xref, $tree, $gedcom) ??
94
            Factory::submitter()->make($xref, $tree, $gedcom) ??
95
            Factory::submission()->make($xref, $tree, $gedcom) ??
96
            Factory::location()->make($xref, $tree, $gedcom) ??
97
            Factory::header()->make($xref, $tree, $gedcom) ??
98
            $this->cache->remember(__CLASS__ . $xref . '@' . $tree->id(), function () use ($xref, $tree, $gedcom) {
99
                $gedcom = $gedcom ?? $this->gedcom($xref, $tree);
100
101
                $pending = $this->pendingChanges($tree)->get($xref);
102
103
                if ($gedcom === null && $pending === null) {
104
                    return null;
105
                }
106
107
                $xref = $this->extractXref($gedcom ?? $pending, $xref);
108
                $type = $this->extractType($gedcom ?? $pending);
109
110
                return $this->newGedcomRecord($type, $xref, $gedcom ?? '', $pending, $tree);
111
            });
112
    }
113
114
    /**
115
     * Create a GedcomRecord object from raw GEDCOM data.
116
     *
117
     * @param string      $xref
118
     * @param string      $gedcom  an empty string for new/pending records
119
     * @param string|null $pending null for a record with no pending edits,
120
     *                             empty string for records with pending deletions
121
     * @param Tree        $tree
122
     *
123
     * @return GedcomRecord
124
     */
125
    public function new(string $xref, string $gedcom, ?string $pending, Tree $tree): GedcomRecord
126
    {
127
        return new GedcomRecord($xref, $gedcom, $pending, $tree);
128
    }
129
130
    /**
131
     * Create a GedcomRecord object from a row in the database.
132
     *
133
     * @param Tree $tree
134
     *
135
     * @return Closure
136
     */
137
    public function mapper(Tree $tree): Closure
138
    {
139
        return function (stdClass $row) use ($tree): GedcomRecord {
140
            $record = $this->make($row->o_id, $tree, $row->o_gedcom);
141
            assert($record instanceof GedcomRecord);
142
143
            return $record;
144
        };
145
    }
146
147
    /**
148
     * @param string      $type
149
     * @param string      $xref
150
     * @param string      $gedcom
151
     * @param string|null $pending
152
     * @param Tree        $tree
153
     *
154
     * @return GedcomRecord
155
     */
156
    private function newGedcomRecord(string $type, string $xref, string $gedcom, ?string $pending, Tree $tree): GedcomRecord
157
    {
158
        switch ($type) {
159
            case Family::RECORD_TYPE:
160
                return Factory::family()->new($xref, $gedcom, $pending, $tree);
161
162
            case Header::RECORD_TYPE:
163
                return Factory::header()->new($xref, $gedcom, $pending, $tree);
164
165
            case Individual::RECORD_TYPE:
166
                return Factory::individual()->new($xref, $gedcom, $pending, $tree);
167
168
            case Media::RECORD_TYPE:
169
                return Factory::media()->new($xref, $gedcom, $pending, $tree);
170
171
            case Note::RECORD_TYPE:
172
                return Factory::note()->new($xref, $gedcom, $pending, $tree);
173
174
            case Repository::RECORD_TYPE:
175
                return Factory::repository()->new($xref, $gedcom, $pending, $tree);
176
177
            case Source::RECORD_TYPE:
178
                return Factory::source()->new($xref, $gedcom, $pending, $tree);
179
180
            case Submission::RECORD_TYPE:
181
                return Factory::submission()->new($xref, $gedcom, $pending, $tree);
182
183
            case Submitter::RECORD_TYPE:
184
                return Factory::submitter()->new($xref, $gedcom, $pending, $tree);
185
186
            default:
187
                return $this->new($xref, $gedcom, $pending, $tree);
188
        }
189
    }
190
191
    /**
192
     * Extract the type of a GEDCOM record
193
     *
194
     * @param string $gedcom
195
     *
196
     * @return string
197
     * @throws InvalidArgumentException
198
     */
199
    private function extractType(string $gedcom): string
200
    {
201
        if (preg_match('/^0(?: @' . Gedcom::REGEX_XREF . '@)? ([_A-Z0-9]+)/', $gedcom, $match)) {
202
            return $match[1];
203
        }
204
205
        throw new InvalidArgumentException('Invalid GEDCOM record: ' . $gedcom);
206
    }
207
208
    /**
209
     * Fetch GEDCOM data from the database.
210
     *
211
     * @param string $xref
212
     * @param Tree   $tree
213
     *
214
     * @return string|null
215
     */
216
    private function gedcom(string $xref, Tree $tree): ?string
217
    {
218
        return DB::table('other')
219
            ->where('o_id', '=', $xref)
220
            ->where('o_file', '=', $tree->id())
221
            ->whereNotIn('o_type', [
222
                Header::RECORD_TYPE,
223
                Note::RECORD_TYPE,
224
                Repository::RECORD_TYPE,
225
                Submission::RECORD_TYPE,
226
                Submitter::RECORD_TYPE,
227
            ])
228
            ->value('o_gedcom');
229
    }
230
}
231