xref: /webtrees/app/Factories/XrefFactory.php (revision 1270d2767576ed4a83917769b0ee3613e3b010bf)
1<?php
2
3/**
4 * webtrees: online genealogy
5 * Copyright (C) 2023 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 <https://www.gnu.org/licenses/>.
16 */
17
18declare(strict_types=1);
19
20namespace Fisharebest\Webtrees\Factories;
21
22use Fisharebest\Webtrees\Contracts\XrefFactoryInterface;
23use Fisharebest\Webtrees\DB;
24use Fisharebest\Webtrees\Site;
25
26/**
27 * Make an XREF.
28 */
29class XrefFactory implements XrefFactoryInterface
30{
31    /**
32     * Create a new XREF.
33     *
34     * @param string $record_type
35     *
36     * @return string
37     */
38    public function make(string $record_type): string
39    {
40        return $this->generate('X', '');
41    }
42
43    /**
44     * @param string $prefix
45     * @param string $suffix
46     *
47     * @return string
48     */
49    protected function generate(string $prefix, string $suffix): string
50    {
51        // Lock the row, so that only one new XREF may be generated at a time.
52        $num = (int) DB::table('site_setting')
53            ->where('setting_name', '=', 'next_xref')
54            ->lockForUpdate()
55            ->value('setting_value');
56
57        $increment = 1.0;
58
59        do {
60            $num += (int) $increment;
61
62            // This exponential increment allows us to scan over large blocks of
63            // existing data in a reasonable time.
64            $increment *= 1.01;
65
66            $xref = $prefix . $num . $suffix;
67
68            // Records may already exist with this sequence number.
69            $already_used =
70                DB::table('individuals')->where('i_id', '=', $xref)->exists() ||
71                DB::table('families')->where('f_id', '=', $xref)->exists() ||
72                DB::table('sources')->where('s_id', '=', $xref)->exists() ||
73                DB::table('media')->where('m_id', '=', $xref)->exists() ||
74                DB::table('other')->where('o_id', '=', $xref)->exists() ||
75                DB::table('change')->where('xref', '=', $xref)->exists();
76        } while ($already_used);
77
78        Site::setPreference('next_xref', (string) $num);
79
80        return $xref;
81    }
82}
83