| Total Complexity | 129 |
| Total Lines | 876 |
| Duplicated Lines | 0 % |
| Changes | 8 | ||
| Bugs | 1 | Features | 0 |
Complex classes like FunctionsImport often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use FunctionsImport, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 72 | class FunctionsImport |
||
| 73 | { |
||
| 74 | /** |
||
| 75 | * Tidy up a gedcom record on import, so that we can access it consistently/efficiently. |
||
| 76 | * |
||
| 77 | * @param string $rec |
||
| 78 | * @param Tree $tree |
||
| 79 | * |
||
| 80 | * @return string |
||
| 81 | */ |
||
| 82 | public static function reformatRecord(string $rec, Tree $tree): string |
||
| 83 | { |
||
| 84 | $gedcom_service = app(GedcomService::class); |
||
| 85 | assert($gedcom_service instanceof GedcomService); |
||
| 86 | |||
| 87 | // Strip out mac/msdos line endings |
||
| 88 | $rec = preg_replace("/[\r\n]+/", "\n", $rec); |
||
| 89 | |||
| 90 | // Extract lines from the record; lines consist of: level + optional xref + tag + optional data |
||
| 91 | $num_matches = preg_match_all('/^[ \t]*(\d+)[ \t]*(@[^@]*@)?[ \t]*(\w+)[ \t]?(.*)$/m', $rec, $matches, PREG_SET_ORDER); |
||
| 92 | |||
| 93 | // Process the record line-by-line |
||
| 94 | $newrec = ''; |
||
| 95 | foreach ($matches as $n => $match) { |
||
| 96 | [, $level, $xref, $tag, $data] = $match; |
||
| 97 | |||
| 98 | $tag = $gedcom_service->canonicalTag($tag); |
||
| 99 | |||
| 100 | switch ($tag) { |
||
| 101 | case 'AFN': |
||
| 102 | // AFN values are upper case |
||
| 103 | $data = strtoupper($data); |
||
| 104 | break; |
||
| 105 | case 'DATE': |
||
| 106 | // Preserve text from INT dates |
||
| 107 | if (str_contains($data, '(')) { |
||
|
|
|||
| 108 | [$date, $text] = explode('(', $data, 2); |
||
| 109 | $text = ' (' . $text; |
||
| 110 | } else { |
||
| 111 | $date = $data; |
||
| 112 | $text = ''; |
||
| 113 | } |
||
| 114 | // Capitals |
||
| 115 | $date = strtoupper($date); |
||
| 116 | // Temporarily add leading/trailing spaces, to allow efficient matching below |
||
| 117 | $date = " {$date} "; |
||
| 118 | // Ensure space digits and letters |
||
| 119 | $date = preg_replace('/([A-Z])(\d)/', '$1 $2', $date); |
||
| 120 | $date = preg_replace('/(\d)([A-Z])/', '$1 $2', $date); |
||
| 121 | // Ensure space before/after calendar escapes |
||
| 122 | $date = preg_replace('/@#[^@]+@/', ' $0 ', $date); |
||
| 123 | // "BET." => "BET" |
||
| 124 | $date = preg_replace('/(\w\w)\./', '$1', $date); |
||
| 125 | // "CIR" => "ABT" |
||
| 126 | $date = str_replace(' CIR ', ' ABT ', $date); |
||
| 127 | $date = str_replace(' APX ', ' ABT ', $date); |
||
| 128 | // B.C. => BC (temporarily, to allow easier handling of ".") |
||
| 129 | $date = str_replace(' B.C. ', ' BC ', $date); |
||
| 130 | // TMG uses "EITHER X OR Y" |
||
| 131 | $date = preg_replace('/^ EITHER (.+) OR (.+)/', ' BET $1 AND $2', $date); |
||
| 132 | // "BET X - Y " => "BET X AND Y" |
||
| 133 | $date = preg_replace('/^(.* BET .+) - (.+)/', '$1 AND $2', $date); |
||
| 134 | $date = preg_replace('/^(.* FROM .+) - (.+)/', '$1 TO $2', $date); |
||
| 135 | // "@#ESC@ FROM X TO Y" => "FROM @#ESC@ X TO @#ESC@ Y" |
||
| 136 | $date = preg_replace('/^ +(@#[^@]+@) +FROM +(.+) +TO +(.+)/', ' FROM $1 $2 TO $1 $3', $date); |
||
| 137 | $date = preg_replace('/^ +(@#[^@]+@) +BET +(.+) +AND +(.+)/', ' BET $1 $2 AND $1 $3', $date); |
||
| 138 | // "@#ESC@ AFT X" => "AFT @#ESC@ X" |
||
| 139 | $date = preg_replace('/^ +(@#[^@]+@) +(FROM|BET|TO|AND|BEF|AFT|CAL|EST|INT|ABT) +(.+)/', ' $2 $1 $3', $date); |
||
| 140 | // Ignore any remaining punctuation, e.g. "14-MAY, 1900" => "14 MAY 1900" |
||
| 141 | // (don't change "/" - it is used in NS/OS dates) |
||
| 142 | $date = preg_replace('/[.,:;-]/', ' ', $date); |
||
| 143 | // BC => B.C. |
||
| 144 | $date = str_replace(' BC ', ' B.C. ', $date); |
||
| 145 | // Append the "INT" text |
||
| 146 | $data = $date . $text; |
||
| 147 | break; |
||
| 148 | case '_FILE': |
||
| 149 | $tag = 'FILE'; |
||
| 150 | break; |
||
| 151 | case 'FORM': |
||
| 152 | // Consistent commas |
||
| 153 | $data = preg_replace('/ *, */', ', ', $data); |
||
| 154 | break; |
||
| 155 | case 'HEAD': |
||
| 156 | // HEAD records don't have an XREF or DATA |
||
| 157 | if ($level === '0') { |
||
| 158 | $xref = ''; |
||
| 159 | $data = ''; |
||
| 160 | } |
||
| 161 | break; |
||
| 162 | case 'NAME': |
||
| 163 | // Tidy up non-printing characters |
||
| 164 | $data = preg_replace('/ +/', ' ', trim($data)); |
||
| 165 | break; |
||
| 166 | case 'PEDI': |
||
| 167 | // PEDI values are lower case |
||
| 168 | $data = strtolower($data); |
||
| 169 | break; |
||
| 170 | case 'PLAC': |
||
| 171 | // Consistent commas |
||
| 172 | $data = preg_replace('/ *[,,،] */u', ', ', $data); |
||
| 173 | // The Master Genealogist stores LAT/LONG data in the PLAC field, e.g. Pennsylvania, USA, 395945N0751013W |
||
| 174 | if (preg_match('/(.*), (\d\d)(\d\d)(\d\d)([NS])(\d\d\d)(\d\d)(\d\d)([EW])$/', $data, $match)) { |
||
| 175 | $data = |
||
| 176 | $match[1] . "\n" . |
||
| 177 | ($level + 1) . " MAP\n" . |
||
| 178 | ($level + 2) . ' LATI ' . ($match[5] . round($match[2] + ($match[3] / 60) + ($match[4] / 3600), 4)) . "\n" . |
||
| 179 | ($level + 2) . ' LONG ' . ($match[9] . round($match[6] + ($match[7] / 60) + ($match[8] / 3600), 4)); |
||
| 180 | } |
||
| 181 | break; |
||
| 182 | case 'RESN': |
||
| 183 | // RESN values are lower case (confidential, privacy, locked, none) |
||
| 184 | $data = strtolower($data); |
||
| 185 | if ($data === 'invisible') { |
||
| 186 | $data = 'confidential'; // From old versions of Legacy. |
||
| 187 | } |
||
| 188 | break; |
||
| 189 | case 'SEX': |
||
| 190 | $data = strtoupper($data); |
||
| 191 | break; |
||
| 192 | case 'STAT': |
||
| 193 | if ($data === 'CANCELLED') { |
||
| 194 | // PhpGedView mis-spells this tag - correct it. |
||
| 195 | $data = 'CANCELED'; |
||
| 196 | } |
||
| 197 | break; |
||
| 198 | case 'TEMP': |
||
| 199 | // Temple codes are upper case |
||
| 200 | $data = strtoupper($data); |
||
| 201 | break; |
||
| 202 | case 'TRLR': |
||
| 203 | // TRLR records don't have an XREF or DATA |
||
| 204 | if ($level === '0') { |
||
| 205 | $xref = ''; |
||
| 206 | $data = ''; |
||
| 207 | } |
||
| 208 | break; |
||
| 209 | } |
||
| 210 | // Suppress "Y", for facts/events with a DATE or PLAC |
||
| 211 | if ($data === 'y') { |
||
| 212 | $data = 'Y'; |
||
| 213 | } |
||
| 214 | if ($level === '1' && $data === 'Y') { |
||
| 215 | for ($i = $n + 1; $i < $num_matches - 1 && $matches[$i][1] !== '1'; ++$i) { |
||
| 216 | if ($matches[$i][3] === 'DATE' || $matches[$i][3] === 'PLAC') { |
||
| 217 | $data = ''; |
||
| 218 | break; |
||
| 219 | } |
||
| 220 | } |
||
| 221 | } |
||
| 222 | // Reassemble components back into a single line |
||
| 223 | switch ($tag) { |
||
| 224 | default: |
||
| 225 | // Remove tabs and multiple/leading/trailing spaces |
||
| 226 | $data = strtr($data, ["\t" => ' ']); |
||
| 227 | $data = trim($data, ' '); |
||
| 228 | while (str_contains($data, ' ')) { |
||
| 229 | $data = strtr($data, [' ' => ' ']); |
||
| 230 | } |
||
| 231 | $newrec .= ($newrec ? "\n" : '') . $level . ' ' . ($level === '0' && $xref ? $xref . ' ' : '') . $tag . ($data === '' && $tag !== 'NOTE' ? '' : ' ' . $data); |
||
| 232 | break; |
||
| 233 | case 'NOTE': |
||
| 234 | case 'TEXT': |
||
| 235 | case 'DATA': |
||
| 236 | case 'CONT': |
||
| 237 | $newrec .= ($newrec ? "\n" : '') . $level . ' ' . ($level === '0' && $xref ? $xref . ' ' : '') . $tag . ($data === '' && $tag !== 'NOTE' ? '' : ' ' . $data); |
||
| 238 | break; |
||
| 239 | case 'FILE': |
||
| 240 | // Strip off the user-defined path prefix |
||
| 241 | $GEDCOM_MEDIA_PATH = $tree->getPreference('GEDCOM_MEDIA_PATH'); |
||
| 242 | if ($GEDCOM_MEDIA_PATH !== '' && str_starts_with($data, $GEDCOM_MEDIA_PATH)) { |
||
| 243 | $data = substr($data, strlen($GEDCOM_MEDIA_PATH)); |
||
| 244 | } |
||
| 245 | // convert backslashes in filenames to forward slashes |
||
| 246 | $data = preg_replace("/\\\\/", '/', $data); |
||
| 247 | |||
| 248 | $newrec .= ($newrec ? "\n" : '') . $level . ' ' . ($level === '0' && $xref ? $xref . ' ' : '') . $tag . ($data === '' && $tag !== 'NOTE' ? '' : ' ' . $data); |
||
| 249 | break; |
||
| 250 | case 'CONC': |
||
| 251 | // Merge CONC lines, to simplify access later on. |
||
| 252 | $newrec .= ($tree->getPreference('WORD_WRAPPED_NOTES') ? ' ' : '') . $data; |
||
| 253 | break; |
||
| 254 | } |
||
| 255 | } |
||
| 256 | |||
| 257 | return $newrec; |
||
| 258 | } |
||
| 259 | |||
| 260 | /** |
||
| 261 | * import record into database |
||
| 262 | * this function will parse the given gedcom record and add it to the database |
||
| 263 | * |
||
| 264 | * @param string $gedrec the raw gedcom record to parse |
||
| 265 | * @param Tree $tree import the record into this tree |
||
| 266 | * @param bool $update whether or not this is an updated record that has been accepted |
||
| 267 | * |
||
| 268 | * @return void |
||
| 269 | * @throws GedcomErrorException |
||
| 270 | */ |
||
| 271 | public static function importRecord(string $gedrec, Tree $tree, bool $update): void |
||
| 272 | { |
||
| 273 | $tree_id = $tree->id(); |
||
| 274 | |||
| 275 | // Escaped @ signs (only if importing from file) |
||
| 276 | if (!$update) { |
||
| 277 | $gedrec = str_replace('@@', '@', $gedrec); |
||
| 278 | } |
||
| 279 | |||
| 280 | // Standardise gedcom format |
||
| 281 | $gedrec = self::reformatRecord($gedrec, $tree); |
||
| 282 | |||
| 283 | // import different types of records |
||
| 284 | if (preg_match('/^0 @(' . Gedcom::REGEX_XREF . ')@ (' . Gedcom::REGEX_TAG . ')/', $gedrec, $match)) { |
||
| 285 | [, $xref, $type] = $match; |
||
| 286 | // check for a _UID, if the record doesn't have one, add one |
||
| 287 | if ($tree->getPreference('GENERATE_UIDS') === '1' && !str_contains($gedrec, "\n1 _UID ")) { |
||
| 288 | $gedrec .= "\n1 _UID " . GedcomTag::createUid(); |
||
| 289 | } |
||
| 290 | } elseif (preg_match('/0 (HEAD|TRLR|_PLAC |_PLAC_DEFN)/', $gedrec, $match)) { |
||
| 291 | $type = $match[1]; |
||
| 292 | $xref = $type; // For records without an XREF, use the type as a pseudo XREF. |
||
| 293 | } else { |
||
| 294 | throw new GedcomErrorException($gedrec); |
||
| 295 | } |
||
| 296 | |||
| 297 | // If the user has downloaded their GEDCOM data (containing media objects) and edited it |
||
| 298 | // using an application which does not support (and deletes) media objects, then add them |
||
| 299 | // back in. |
||
| 300 | if ($tree->getPreference('keep_media')) { |
||
| 301 | $old_linked_media = DB::table('link') |
||
| 302 | ->where('l_from', '=', $xref) |
||
| 303 | ->where('l_file', '=', $tree_id) |
||
| 304 | ->where('l_type', '=', 'OBJE') |
||
| 305 | ->pluck('l_to'); |
||
| 306 | |||
| 307 | // Delete these links - so that we do not insert them again in updateLinks() |
||
| 308 | DB::table('link') |
||
| 309 | ->where('l_from', '=', $xref) |
||
| 310 | ->where('l_file', '=', $tree_id) |
||
| 311 | ->where('l_type', '=', 'OBJE') |
||
| 312 | ->delete(); |
||
| 313 | |||
| 314 | foreach ($old_linked_media as $media_id) { |
||
| 315 | $gedrec .= "\n1 OBJE @" . $media_id . '@'; |
||
| 316 | } |
||
| 317 | } |
||
| 318 | |||
| 319 | // Convert inline media into media objects |
||
| 320 | $gedrec = self::convertInlineMedia($tree, $gedrec); |
||
| 321 | |||
| 322 | switch ($type) { |
||
| 323 | case Individual::RECORD_TYPE: |
||
| 324 | $record = Registry::individualFactory()->new($xref, $gedrec, null, $tree); |
||
| 325 | |||
| 326 | if (preg_match('/\n1 RIN (.+)/', $gedrec, $match)) { |
||
| 327 | $rin = $match[1]; |
||
| 328 | } else { |
||
| 329 | $rin = $xref; |
||
| 330 | } |
||
| 331 | |||
| 332 | DB::table('individuals')->insert([ |
||
| 333 | 'i_id' => $xref, |
||
| 334 | 'i_file' => $tree_id, |
||
| 335 | 'i_rin' => $rin, |
||
| 336 | 'i_sex' => $record->sex(), |
||
| 337 | 'i_gedcom' => $gedrec, |
||
| 338 | ]); |
||
| 339 | |||
| 340 | // Update the cross-reference/index tables. |
||
| 341 | self::updatePlaces($xref, $tree, $gedrec); |
||
| 342 | self::updateDates($xref, $tree_id, $gedrec); |
||
| 343 | self::updateNames($xref, $tree_id, $record); |
||
| 344 | break; |
||
| 345 | |||
| 346 | case Family::RECORD_TYPE: |
||
| 347 | if (preg_match('/\n1 HUSB @(' . Gedcom::REGEX_XREF . ')@/', $gedrec, $match)) { |
||
| 348 | $husb = $match[1]; |
||
| 349 | } else { |
||
| 350 | $husb = ''; |
||
| 351 | } |
||
| 352 | if (preg_match('/\n1 WIFE @(' . Gedcom::REGEX_XREF . ')@/', $gedrec, $match)) { |
||
| 353 | $wife = $match[1]; |
||
| 354 | } else { |
||
| 355 | $wife = ''; |
||
| 356 | } |
||
| 357 | $nchi = preg_match_all('/\n1 CHIL @(' . Gedcom::REGEX_XREF . ')@/', $gedrec, $match); |
||
| 358 | if (preg_match('/\n1 NCHI (\d+)/', $gedrec, $match)) { |
||
| 359 | $nchi = max($nchi, $match[1]); |
||
| 360 | } |
||
| 361 | |||
| 362 | DB::table('families')->insert([ |
||
| 363 | 'f_id' => $xref, |
||
| 364 | 'f_file' => $tree_id, |
||
| 365 | 'f_husb' => $husb, |
||
| 366 | 'f_wife' => $wife, |
||
| 367 | 'f_gedcom' => $gedrec, |
||
| 368 | 'f_numchil' => $nchi, |
||
| 369 | ]); |
||
| 370 | |||
| 371 | // Update the cross-reference/index tables. |
||
| 372 | self::updatePlaces($xref, $tree, $gedrec); |
||
| 373 | self::updateDates($xref, $tree_id, $gedrec); |
||
| 374 | break; |
||
| 375 | |||
| 376 | case Source::RECORD_TYPE: |
||
| 377 | if (preg_match('/\n1 TITL (.+)/', $gedrec, $match)) { |
||
| 378 | $name = $match[1]; |
||
| 379 | } elseif (preg_match('/\n1 ABBR (.+)/', $gedrec, $match)) { |
||
| 380 | $name = $match[1]; |
||
| 381 | } else { |
||
| 382 | $name = $xref; |
||
| 383 | } |
||
| 384 | |||
| 385 | DB::table('sources')->insert([ |
||
| 386 | 's_id' => $xref, |
||
| 387 | 's_file' => $tree_id, |
||
| 388 | 's_name' => mb_substr($name, 0, 255), |
||
| 389 | 's_gedcom' => $gedrec, |
||
| 390 | ]); |
||
| 391 | break; |
||
| 392 | |||
| 393 | case Repository::RECORD_TYPE: |
||
| 394 | case Note::RECORD_TYPE: |
||
| 395 | case Submission::RECORD_TYPE: |
||
| 396 | case Submitter::RECORD_TYPE: |
||
| 397 | case Location::RECORD_TYPE: |
||
| 398 | DB::table('other')->insert([ |
||
| 399 | 'o_id' => $xref, |
||
| 400 | 'o_file' => $tree_id, |
||
| 401 | 'o_type' => $type, |
||
| 402 | 'o_gedcom' => $gedrec, |
||
| 403 | ]); |
||
| 404 | break; |
||
| 405 | |||
| 406 | case Header::RECORD_TYPE: |
||
| 407 | // Force HEAD records to have a creation date. |
||
| 408 | if (!str_contains($gedrec, "\n1 DATE ")) { |
||
| 409 | $today = strtoupper(date('d M Y')); |
||
| 410 | $gedrec .= "\n1 DATE " . $today; |
||
| 411 | } |
||
| 412 | |||
| 413 | DB::table('other')->insert([ |
||
| 414 | 'o_id' => $xref, |
||
| 415 | 'o_file' => $tree_id, |
||
| 416 | 'o_type' => Header::RECORD_TYPE, |
||
| 417 | 'o_gedcom' => $gedrec, |
||
| 418 | ]); |
||
| 419 | break; |
||
| 420 | |||
| 421 | |||
| 422 | case Media::RECORD_TYPE: |
||
| 423 | $record = Registry::mediaFactory()->new($xref, $gedrec, null, $tree); |
||
| 424 | |||
| 425 | DB::table('media')->insert([ |
||
| 426 | 'm_id' => $xref, |
||
| 427 | 'm_file' => $tree_id, |
||
| 428 | 'm_gedcom' => $gedrec, |
||
| 429 | ]); |
||
| 430 | |||
| 431 | foreach ($record->mediaFiles() as $media_file) { |
||
| 432 | DB::table('media_file')->insert([ |
||
| 433 | 'm_id' => $xref, |
||
| 434 | 'm_file' => $tree_id, |
||
| 435 | 'multimedia_file_refn' => mb_substr($media_file->filename(), 0, 248), |
||
| 436 | 'multimedia_format' => mb_substr($media_file->format(), 0, 4), |
||
| 437 | 'source_media_type' => mb_substr($media_file->type(), 0, 15), |
||
| 438 | 'descriptive_title' => mb_substr($media_file->title(), 0, 248), |
||
| 439 | ]); |
||
| 440 | } |
||
| 441 | break; |
||
| 442 | |||
| 443 | case '_PLAC ': |
||
| 444 | self::importTNGPlac($gedrec); |
||
| 445 | return; |
||
| 446 | |||
| 447 | case '_PLAC_DEFN': |
||
| 448 | self::importLegacyPlacDefn($gedrec); |
||
| 449 | return; |
||
| 450 | |||
| 451 | default: // Custom record types. |
||
| 452 | DB::table('other')->insert([ |
||
| 453 | 'o_id' => $xref, |
||
| 454 | 'o_file' => $tree_id, |
||
| 455 | 'o_type' => mb_substr($type, 0, 15), |
||
| 456 | 'o_gedcom' => $gedrec, |
||
| 457 | ]); |
||
| 458 | break; |
||
| 459 | } |
||
| 460 | |||
| 461 | // Update the cross-reference/index tables. |
||
| 462 | self::updateLinks($xref, $tree_id, $gedrec); |
||
| 463 | } |
||
| 464 | |||
| 465 | /** |
||
| 466 | * Legacy Family Tree software generates _PLAC_DEFN records containing LAT/LONG values |
||
| 467 | * |
||
| 468 | * @param string $gedcom |
||
| 469 | */ |
||
| 470 | private static function importLegacyPlacDefn(string $gedcom): void |
||
| 471 | { |
||
| 472 | $gedcom_service = new GedcomService(); |
||
| 473 | |||
| 474 | if (preg_match('/\n1 PLAC (.+)/', $gedcom, $match)) { |
||
| 475 | $place_name = $match[1]; |
||
| 476 | } else { |
||
| 477 | return; |
||
| 478 | } |
||
| 479 | |||
| 480 | if (preg_match('/\n3 LATI ([NS].+)/', $gedcom, $match)) { |
||
| 481 | $latitude = $gedcom_service->readLatitude($match[1]); |
||
| 482 | } else { |
||
| 483 | return; |
||
| 484 | } |
||
| 485 | |||
| 486 | if (preg_match('/\n3 LONG ([EW].+)/', $gedcom, $match)) { |
||
| 487 | $longitude = $gedcom_service->readLongitude($match[1]); |
||
| 488 | } else { |
||
| 489 | return; |
||
| 490 | } |
||
| 491 | |||
| 492 | $location = new PlaceLocation($place_name); |
||
| 493 | |||
| 494 | if ($location->latitude() === 0.0 && $location->longitude() === 0.0) { |
||
| 495 | DB::table('placelocation') |
||
| 496 | ->where('pl_id', '=', $location->id()) |
||
| 497 | ->update([ |
||
| 498 | 'pl_lati' => $latitude, |
||
| 499 | 'pl_long' => $longitude, |
||
| 500 | ]); |
||
| 501 | } |
||
| 502 | } |
||
| 503 | |||
| 504 | /** |
||
| 505 | * Legacy Family Tree software generates _PLAC_DEFN records containing LAT/LONG values |
||
| 506 | * |
||
| 507 | * @param string $gedcom |
||
| 508 | */ |
||
| 509 | private static function importTNGPlac(string $gedcom): void |
||
| 510 | { |
||
| 511 | if (preg_match('/^0 _PLAC (.+)/', $gedcom, $match)) { |
||
| 512 | $place_name = $match[1]; |
||
| 513 | } else { |
||
| 514 | return; |
||
| 515 | } |
||
| 516 | |||
| 517 | if (preg_match('/\n2 LATI (.+)/', $gedcom, $match)) { |
||
| 518 | $latitude = (float) $match[1]; |
||
| 519 | } else { |
||
| 520 | return; |
||
| 521 | } |
||
| 522 | |||
| 523 | if (preg_match('/\n2 LONG (.+)/', $gedcom, $match)) { |
||
| 524 | $longitude = (float) $match[1]; |
||
| 525 | } else { |
||
| 526 | return; |
||
| 527 | } |
||
| 528 | |||
| 529 | $location = new PlaceLocation($place_name); |
||
| 530 | |||
| 531 | if ($location->latitude() === 0.0 && $location->longitude() === 0.0) { |
||
| 532 | DB::table('placelocation') |
||
| 533 | ->where('pl_id', '=', $location->id()) |
||
| 534 | ->update([ |
||
| 535 | 'pl_lati' => $latitude, |
||
| 536 | 'pl_long' => $longitude, |
||
| 537 | ]); |
||
| 538 | } |
||
| 539 | } |
||
| 540 | |||
| 541 | /** |
||
| 542 | * Extract all level 2 places from the given record and insert them into the places table |
||
| 543 | * |
||
| 544 | * @param string $xref |
||
| 545 | * @param Tree $tree |
||
| 546 | * @param string $gedrec |
||
| 547 | * |
||
| 548 | * @return void |
||
| 549 | */ |
||
| 550 | public static function updatePlaces(string $xref, Tree $tree, string $gedrec): void |
||
| 551 | { |
||
| 552 | // Insert all new rows together |
||
| 553 | $rows = []; |
||
| 554 | |||
| 555 | preg_match_all('/\n2 PLAC (.+)/', $gedrec, $matches); |
||
| 556 | |||
| 557 | $places = array_unique($matches[1]); |
||
| 558 | |||
| 559 | foreach ($places as $place_name) { |
||
| 560 | $place = new Place($place_name, $tree); |
||
| 561 | |||
| 562 | // Calling Place::id() will create the entry in the database, if it doesn't already exist. |
||
| 563 | while ($place->id() !== 0) { |
||
| 564 | $rows[] = [ |
||
| 565 | 'pl_p_id' => $place->id(), |
||
| 566 | 'pl_gid' => $xref, |
||
| 567 | 'pl_file' => $tree->id(), |
||
| 568 | ]; |
||
| 569 | |||
| 570 | $place = $place->parent(); |
||
| 571 | } |
||
| 572 | } |
||
| 573 | |||
| 574 | // array_unique doesn't work with arrays of arrays |
||
| 575 | $rows = array_intersect_key($rows, array_unique(array_map('serialize', $rows))); |
||
| 576 | |||
| 577 | // PDO has a limit of 65535 placeholders, and each row requires 3 placeholders. |
||
| 578 | foreach (array_chunk($rows, 20000) as $chunk) { |
||
| 579 | DB::table('placelinks')->insert($chunk); |
||
| 580 | } |
||
| 581 | } |
||
| 582 | |||
| 583 | /** |
||
| 584 | * Extract all the dates from the given record and insert them into the database. |
||
| 585 | * |
||
| 586 | * @param string $xref |
||
| 587 | * @param int $ged_id |
||
| 588 | * @param string $gedrec |
||
| 589 | * |
||
| 590 | * @return void |
||
| 591 | */ |
||
| 592 | public static function updateDates(string $xref, int $ged_id, string $gedrec): void |
||
| 593 | { |
||
| 594 | // Insert all new rows together |
||
| 595 | $rows = []; |
||
| 596 | |||
| 597 | preg_match_all("/\n1 (\w+).*(?:\n[2-9].*)*(?:\n2 DATE (.+))(?:\n[2-9].*)*/", $gedrec, $matches, PREG_SET_ORDER); |
||
| 598 | |||
| 599 | foreach ($matches as $match) { |
||
| 600 | $fact = $match[1]; |
||
| 601 | $date = new Date($match[2]); |
||
| 602 | $rows[] = [ |
||
| 603 | 'd_day' => $date->minimumDate()->day, |
||
| 604 | 'd_month' => $date->minimumDate()->format('%O'), |
||
| 605 | 'd_mon' => $date->minimumDate()->month, |
||
| 606 | 'd_year' => $date->minimumDate()->year, |
||
| 607 | 'd_julianday1' => $date->minimumDate()->minimumJulianDay(), |
||
| 608 | 'd_julianday2' => $date->minimumDate()->maximumJulianDay(), |
||
| 609 | 'd_fact' => $fact, |
||
| 610 | 'd_gid' => $xref, |
||
| 611 | 'd_file' => $ged_id, |
||
| 612 | 'd_type' => $date->minimumDate()->format('%@'), |
||
| 613 | ]; |
||
| 614 | |||
| 615 | $rows[] = [ |
||
| 616 | 'd_day' => $date->maximumDate()->day, |
||
| 617 | 'd_month' => $date->maximumDate()->format('%O'), |
||
| 618 | 'd_mon' => $date->maximumDate()->month, |
||
| 619 | 'd_year' => $date->maximumDate()->year, |
||
| 620 | 'd_julianday1' => $date->maximumDate()->minimumJulianDay(), |
||
| 621 | 'd_julianday2' => $date->maximumDate()->maximumJulianDay(), |
||
| 622 | 'd_fact' => $fact, |
||
| 623 | 'd_gid' => $xref, |
||
| 624 | 'd_file' => $ged_id, |
||
| 625 | 'd_type' => $date->minimumDate()->format('%@'), |
||
| 626 | ]; |
||
| 627 | } |
||
| 628 | |||
| 629 | // array_unique doesn't work with arrays of arrays |
||
| 630 | $rows = array_intersect_key($rows, array_unique(array_map('serialize', $rows))); |
||
| 631 | |||
| 632 | DB::table('dates')->insert($rows); |
||
| 633 | } |
||
| 634 | |||
| 635 | /** |
||
| 636 | * Extract all the links from the given record and insert them into the database |
||
| 637 | * |
||
| 638 | * @param string $xref |
||
| 639 | * @param int $ged_id |
||
| 640 | * @param string $gedrec |
||
| 641 | * |
||
| 642 | * @return void |
||
| 643 | */ |
||
| 644 | public static function updateLinks(string $xref, int $ged_id, string $gedrec): void |
||
| 645 | { |
||
| 646 | // Insert all new rows together |
||
| 647 | $rows = []; |
||
| 648 | |||
| 649 | preg_match_all('/\n\d+ (' . Gedcom::REGEX_TAG . ') @(' . Gedcom::REGEX_XREF . ')@/', $gedrec, $matches, PREG_SET_ORDER); |
||
| 650 | |||
| 651 | foreach ($matches as $match) { |
||
| 652 | // Take care of "duplicates" that differ on case/collation, e.g. "SOUR @S1@" and "SOUR @s1@" |
||
| 653 | $rows[$match[1] . strtoupper($match[2])] = [ |
||
| 654 | 'l_from' => $xref, |
||
| 655 | 'l_to' => $match[2], |
||
| 656 | 'l_type' => $match[1], |
||
| 657 | 'l_file' => $ged_id, |
||
| 658 | ]; |
||
| 659 | } |
||
| 660 | |||
| 661 | DB::table('link')->insert($rows); |
||
| 662 | } |
||
| 663 | |||
| 664 | /** |
||
| 665 | * Extract all the names from the given record and insert them into the database. |
||
| 666 | * |
||
| 667 | * @param string $xref |
||
| 668 | * @param int $ged_id |
||
| 669 | * @param Individual $record |
||
| 670 | * |
||
| 671 | * @return void |
||
| 672 | */ |
||
| 673 | public static function updateNames(string $xref, int $ged_id, Individual $record): void |
||
| 674 | { |
||
| 675 | // Insert all new rows together |
||
| 676 | $rows = []; |
||
| 677 | |||
| 678 | foreach ($record->getAllNames() as $n => $name) { |
||
| 679 | if ($name['givn'] === '@P.N.') { |
||
| 680 | $soundex_givn_std = null; |
||
| 681 | $soundex_givn_dm = null; |
||
| 682 | } else { |
||
| 683 | $soundex_givn_std = Soundex::russell($name['givn']); |
||
| 684 | $soundex_givn_dm = Soundex::daitchMokotoff($name['givn']); |
||
| 685 | } |
||
| 686 | |||
| 687 | if ($name['surn'] === '@N.N.') { |
||
| 688 | $soundex_surn_std = null; |
||
| 689 | $soundex_surn_dm = null; |
||
| 690 | } else { |
||
| 691 | $soundex_surn_std = Soundex::russell($name['surname']); |
||
| 692 | $soundex_surn_dm = Soundex::daitchMokotoff($name['surname']); |
||
| 693 | } |
||
| 694 | |||
| 695 | $rows[] = [ |
||
| 696 | 'n_file' => $ged_id, |
||
| 697 | 'n_id' => $xref, |
||
| 698 | 'n_num' => $n, |
||
| 699 | 'n_type' => $name['type'], |
||
| 700 | 'n_sort' => mb_substr($name['sort'], 0, 255), |
||
| 701 | 'n_full' => mb_substr($name['fullNN'], 0, 255), |
||
| 702 | 'n_surname' => mb_substr($name['surname'], 0, 255), |
||
| 703 | 'n_surn' => mb_substr($name['surn'], 0, 255), |
||
| 704 | 'n_givn' => mb_substr($name['givn'], 0, 255), |
||
| 705 | 'n_soundex_givn_std' => $soundex_givn_std, |
||
| 706 | 'n_soundex_surn_std' => $soundex_surn_std, |
||
| 707 | 'n_soundex_givn_dm' => $soundex_givn_dm, |
||
| 708 | 'n_soundex_surn_dm' => $soundex_surn_dm, |
||
| 709 | ]; |
||
| 710 | } |
||
| 711 | |||
| 712 | DB::table('name')->insert($rows); |
||
| 713 | } |
||
| 714 | |||
| 715 | /** |
||
| 716 | * Extract inline media data, and convert to media objects. |
||
| 717 | * |
||
| 718 | * @param Tree $tree |
||
| 719 | * @param string $gedcom |
||
| 720 | * |
||
| 721 | * @return string |
||
| 722 | */ |
||
| 723 | public static function convertInlineMedia(Tree $tree, string $gedcom): string |
||
| 739 | } |
||
| 740 | |||
| 741 | /** |
||
| 742 | * Create a new media object, from inline media data. |
||
| 743 | * |
||
| 744 | * GEDCOM 5.5.1 specifies: +1 FILE / +2 FORM / +3 MEDI / +1 TITL |
||
| 745 | * GEDCOM 5.5 specifies: +1 FILE / +1 FORM / +1 TITL |
||
| 746 | * GEDCOM 5.5.1 says that GEDCOM 5.5 specifies: +1 FILE / +1 FORM / +2 MEDI |
||
| 747 | * |
||
| 748 | * Legacy generates: +1 FORM / +1 FILE / +1 TITL / +1 _SCBK / +1 _PRIM / +1 _TYPE / +1 NOTE |
||
| 749 | * RootsMagic generates: +1 FILE / +1 FORM / +1 TITL |
||
| 750 | * |
||
| 751 | * @param string $gedcom |
||
| 752 | * @param Tree $tree |
||
| 753 | * |
||
| 754 | * @return string |
||
| 755 | */ |
||
| 756 | public static function createMediaObject(string $gedcom, Tree $tree): string |
||
| 757 | { |
||
| 758 | preg_match('/\n\d FILE (.+)/', $gedcom, $match); |
||
| 759 | $file = $match[1] ?? ''; |
||
| 760 | |||
| 761 | preg_match('/\n\d TITL (.+)/', $gedcom, $match); |
||
| 762 | $title = $match[1] ?? ''; |
||
| 763 | |||
| 764 | preg_match('/\n\d FORM (.+)/', $gedcom, $match); |
||
| 765 | $format = $match[1] ?? ''; |
||
| 766 | |||
| 767 | preg_match('/\n\d MEDI (.+)/', $gedcom, $match); |
||
| 768 | $media = $match[1] ?? ''; |
||
| 769 | |||
| 770 | preg_match('/\n\d _SCBK (.+)/', $gedcom, $match); |
||
| 771 | $scrapbook = $match[1] ?? ''; |
||
| 772 | |||
| 773 | preg_match('/\n\d _PRIM (.+)/', $gedcom, $match); |
||
| 774 | $primary = $match[1] ?? ''; |
||
| 775 | |||
| 776 | preg_match('/\n\d _TYPE (.+)/', $gedcom, $match); |
||
| 777 | if ($media === '') { |
||
| 778 | // Legacy uses _TYPE instead of MEDI |
||
| 779 | $media = $match[1] ?? ''; |
||
| 780 | $type2 = ''; |
||
| 781 | } else { |
||
| 782 | $type2 = $match[1] ?? ''; |
||
| 783 | } |
||
| 784 | |||
| 785 | preg_match('/\n\d NOTE (.+(?:\n\d CONT.+)*)/', $gedcom, $match); |
||
| 786 | $note = $match[1] ?? ''; |
||
| 787 | |||
| 788 | // Have we already created a media object with the same title/filename? |
||
| 789 | $xref = DB::table('media_file') |
||
| 790 | ->where('m_file', '=', $tree->id()) |
||
| 791 | ->where('descriptive_title', '=', mb_substr($title, 0, 248)) |
||
| 792 | ->where('multimedia_file_refn', '=', mb_substr($file, 0, 248)) |
||
| 793 | ->value('m_id'); |
||
| 794 | |||
| 795 | if ($xref === null && $file !== '') { |
||
| 796 | $xref = Registry::xrefFactory()->make(Media::RECORD_TYPE); |
||
| 797 | |||
| 798 | // convert to a media-object |
||
| 799 | $gedcom = '0 @' . $xref . "@ OBJE\n1 FILE " . $file; |
||
| 800 | |||
| 801 | if ($format !== '') { |
||
| 802 | $gedcom .= "\n2 FORM " . $format; |
||
| 803 | |||
| 804 | if ($media !== '') { |
||
| 805 | $gedcom .= "\n3 TYPE " . $media; |
||
| 806 | } |
||
| 807 | } |
||
| 808 | |||
| 809 | if ($title !== '') { |
||
| 810 | $gedcom .= "\n3 TITL " . $title; |
||
| 811 | } |
||
| 812 | |||
| 813 | if ($scrapbook !== '') { |
||
| 814 | $gedcom .= "\n1 _SCBK " . $scrapbook; |
||
| 815 | } |
||
| 816 | |||
| 817 | if ($primary !== '') { |
||
| 818 | $gedcom .= "\n1 _PRIM " . $primary; |
||
| 819 | } |
||
| 820 | |||
| 821 | if ($type2 !== '') { |
||
| 822 | $gedcom .= "\n1 _TYPE " . $type2; |
||
| 823 | } |
||
| 824 | |||
| 825 | if ($note !== '') { |
||
| 826 | $gedcom .= "\n1 NOTE " . strtr($note, ["\n3 CONT" => "\n2 CONT", "\n4 CONT" => "\n3 CONT"]); |
||
| 827 | } |
||
| 828 | |||
| 829 | DB::table('media')->insert([ |
||
| 830 | 'm_id' => $xref, |
||
| 831 | 'm_file' => $tree->id(), |
||
| 832 | 'm_gedcom' => $gedcom, |
||
| 833 | ]); |
||
| 834 | |||
| 835 | DB::table('media_file')->insert([ |
||
| 836 | 'm_id' => $xref, |
||
| 837 | 'm_file' => $tree->id(), |
||
| 838 | 'multimedia_file_refn' => mb_substr($file, 0, 248), |
||
| 839 | 'multimedia_format' => mb_substr($format, 0, 4), |
||
| 840 | 'source_media_type' => mb_substr($media, 0, 15), |
||
| 841 | 'descriptive_title' => mb_substr($title, 0, 248), |
||
| 842 | ]); |
||
| 843 | } |
||
| 844 | |||
| 845 | return $xref; |
||
| 846 | } |
||
| 847 | |||
| 848 | /** |
||
| 849 | * update a record in the database |
||
| 850 | * |
||
| 851 | * @param string $gedrec |
||
| 852 | * @param Tree $tree |
||
| 853 | * @param bool $delete |
||
| 854 | * |
||
| 855 | * @return void |
||
| 856 | * @throws GedcomErrorException |
||
| 857 | */ |
||
| 858 | public static function updateRecord(string $gedrec, Tree $tree, bool $delete): void |
||
| 948 | } |
||
| 949 | } |
||
| 951 |
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.