xref: /webtrees/app/Factories/XrefFactory.php (revision 2ebcf907ed34213f816592af04e6c160335d6311)
1<?php
2
3/**
4 * webtrees: online genealogy
5 * Copyright (C) 2021 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\Site;
24use Illuminate\Database\Capsule\Manager as DB;
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        DB::table('site_setting')
53            ->where('setting_name', '=', 'next_xref')
54            ->lockForUpdate()
55            ->get();
56
57        $increment = 1.0;
58        do {
59            $num = (int) Site::getPreference('next_xref') + (int) $increment;
60
61            // This exponential increment allows us to scan over large blocks of
62            // existing data in a reasonable time.
63            $increment *= 1.01;
64
65            $xref = $prefix . $num . $suffix;
66
67            // Records may already exist with this sequence number.
68            $already_used =
69                DB::table('individuals')->where('i_id', '=', $xref)->exists() ||
70                DB::table('families')->where('f_id', '=', $xref)->exists() ||
71                DB::table('sources')->where('s_id', '=', $xref)->exists() ||
72                DB::table('media')->where('m_id', '=', $xref)->exists() ||
73                DB::table('other')->where('o_id', '=', $xref)->exists() ||
74                DB::table('change')->where('xref', '=', $xref)->exists();
75        } while ($already_used);
76
77        Site::setPreference('next_xref', (string) $num);
78
79        return $xref;
80    }
81}
82