diff options
| -rw-r--r-- | app/Contracts/XrefFactoryInterface.php | 35 | ||||
| -rw-r--r-- | app/Factories/XrefFactory.php | 81 | ||||
| -rw-r--r-- | app/Factory.php | 20 | ||||
| -rw-r--r-- | app/Functions/FunctionsImport.php | 2 | ||||
| -rw-r--r-- | app/Http/Controllers/AdminTreesController.php | 2 | ||||
| -rw-r--r-- | app/Http/Middleware/RegisterFactories.php | 2 | ||||
| -rw-r--r-- | app/Tree.php | 43 | ||||
| -rw-r--r-- | tests/TestCase.php | 2 |
8 files changed, 149 insertions, 38 deletions
diff --git a/app/Contracts/XrefFactoryInterface.php b/app/Contracts/XrefFactoryInterface.php new file mode 100644 index 0000000000..d085f77d29 --- /dev/null +++ b/app/Contracts/XrefFactoryInterface.php @@ -0,0 +1,35 @@ +<?php + +/** + * webtrees: online genealogy + * Copyright (C) 2020 webtrees development team + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +declare(strict_types=1); + +namespace Fisharebest\Webtrees\Contracts; + +/** + * Make an XREF. + */ +interface XrefFactoryInterface +{ + /** + * Create a new XREF. + * + * @param string $record_type + * + * @return string + */ + public function make(string $record_type): string; +} diff --git a/app/Factories/XrefFactory.php b/app/Factories/XrefFactory.php new file mode 100644 index 0000000000..807de864ce --- /dev/null +++ b/app/Factories/XrefFactory.php @@ -0,0 +1,81 @@ +<?php + +/** + * webtrees: online genealogy + * Copyright (C) 2020 webtrees development team + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +declare(strict_types=1); + +namespace Fisharebest\Webtrees\Factories; + +use Fisharebest\Webtrees\Contracts\XrefFactoryInterface; +use Fisharebest\Webtrees\Site; +use Illuminate\Database\Capsule\Manager as DB; + +/** + * Make an XREF. + */ +class XrefFactory implements XrefFactoryInterface +{ + /** + * Create a new XREF. + * + * @param string $record_type + * + * @return string + */ + public function make(string $record_type): string + { + return $this->generate('X', ''); + } + + /** + * @param string $prefix + * @param string $suffix + * + * @return string + */ + protected function generate(string $prefix, string $suffix): string + { + // Lock the row, so that only one new XREF may be generated at a time. + DB::table('site_setting') + ->where('setting_name', '=', 'next_xref') + ->lockForUpdate() + ->get(); + + $increment = 1.0; + do { + $num = (int) Site::getPreference('next_xref') + (int) $increment; + + // This exponential increment allows us to scan over large blocks of + // existing data in a reasonable time. + $increment *= 1.01; + + $xref = $prefix . $num . $suffix; + + // Records may already exist with this sequence number. + $already_used = + DB::table('individuals')->where('i_id', '=', $xref)->exists() || + DB::table('families')->where('f_id', '=', $xref)->exists() || + DB::table('sources')->where('s_id', '=', $xref)->exists() || + DB::table('media')->where('m_id', '=', $xref)->exists() || + DB::table('other')->where('o_id', '=', $xref)->exists() || + DB::table('change')->where('xref', '=', $xref)->exists(); + } while ($already_used); + + Site::setPreference('next_xref', (string) $num); + + return $xref; + } +} diff --git a/app/Factory.php b/app/Factory.php index d6269b6ed8..f164c6287c 100644 --- a/app/Factory.php +++ b/app/Factory.php @@ -30,6 +30,7 @@ use Fisharebest\Webtrees\Contracts\RepositoryFactoryInterface; use Fisharebest\Webtrees\Contracts\SourceFactoryInterface; use Fisharebest\Webtrees\Contracts\SubmissionFactoryInterface; use Fisharebest\Webtrees\Contracts\SubmitterFactoryInterface; +use Fisharebest\Webtrees\Contracts\XrefFactoryInterface; /** * A service locator for our various factory objects. @@ -69,6 +70,9 @@ class Factory /** @var SubmitterFactoryInterface */ private static $submitter_factory; + /** @var XrefFactoryInterface */ + private static $xref_factory; + /** * Store or retrieve a factory object. * @@ -244,4 +248,20 @@ class Factory return self::$submitter_factory; } + + /** + * Store or retrieve a factory object. + * + * @param XrefFactoryInterface|null $factory + * + * @return XrefFactoryInterface + */ + public static function xref(XrefFactoryInterface $factory = null): XrefFactoryInterface + { + if ($factory instanceof XrefFactoryInterface) { + self::$xref_factory = $factory; + } + + return self::$xref_factory; + } } diff --git a/app/Functions/FunctionsImport.php b/app/Functions/FunctionsImport.php index b8acad3584..26f5255095 100644 --- a/app/Functions/FunctionsImport.php +++ b/app/Functions/FunctionsImport.php @@ -666,7 +666,7 @@ class FunctionsImport ->value('m_id'); if ($xref === null) { - $xref = $tree->getNewXref(); + $xref = Factory::xref()->make(Media::RECORD_TYPE); // renumber the lines $gedrec = preg_replace_callback('/\n(\d+)/', static function (array $m) use ($level): string { return "\n" . ($m[1] - $level); diff --git a/app/Http/Controllers/AdminTreesController.php b/app/Http/Controllers/AdminTreesController.php index fb71935a4b..0a31631ff3 100644 --- a/app/Http/Controllers/AdminTreesController.php +++ b/app/Http/Controllers/AdminTreesController.php @@ -995,7 +995,7 @@ class AdminTreesController extends AbstractBaseController $xrefs = $this->duplicateXrefs($tree); foreach ($xrefs as $old_xref => $type) { - $new_xref = $tree->getNewXref(); + $new_xref = Factory::xref()->make($type); switch ($type) { case Individual::RECORD_TYPE: DB::table('individuals') diff --git a/app/Http/Middleware/RegisterFactories.php b/app/Http/Middleware/RegisterFactories.php index f30dbfb3a3..128030fb8f 100644 --- a/app/Http/Middleware/RegisterFactories.php +++ b/app/Http/Middleware/RegisterFactories.php @@ -31,6 +31,7 @@ use Fisharebest\Webtrees\Factories\RepositoryFactory; use Fisharebest\Webtrees\Factories\SourceFactory; use Fisharebest\Webtrees\Factories\SubmissionFactory; use Fisharebest\Webtrees\Factories\SubmitterFactory; +use Fisharebest\Webtrees\Factories\XrefFactory; use Fisharebest\Webtrees\Factory; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -67,6 +68,7 @@ class RegisterFactories implements MiddlewareInterface Factory::source(new SourceFactory($cache)); Factory::submission(new SubmissionFactory($cache)); Factory::submitter(new SubmitterFactory($cache)); + Factory::xref(new XrefFactory()); return $handler->handle($request); } diff --git a/app/Tree.php b/app/Tree.php index 8c95393812..34b1b6a90b 100644 --- a/app/Tree.php +++ b/app/Tree.php @@ -446,11 +446,11 @@ class Tree */ public function createRecord(string $gedcom): GedcomRecord { - if (!str_starts_with($gedcom, '0 @@ ')) { + if (!preg_match('/^0 @@ ([_A-Z]+)/', $gedcom, $match)) { throw new InvalidArgumentException('GedcomRecord::createRecord(' . $gedcom . ') does not begin 0 @@'); } - $xref = $this->getNewXref(); + $xref = Factory::xref()->make($match[1]); $gedcom = substr_replace($gedcom, $xref, 3, 0); // Create a change record @@ -483,40 +483,11 @@ class Tree * Generate a new XREF, unique across all family trees * * @return string + * @deprecated - use the factory directly. */ public function getNewXref(): string { - // Lock the row, so that only one new XREF may be generated at a time. - DB::table('site_setting') - ->where('setting_name', '=', 'next_xref') - ->lockForUpdate() - ->get(); - - $prefix = 'X'; - - $increment = 1.0; - do { - $num = (int) Site::getPreference('next_xref') + (int) $increment; - - // This exponential increment allows us to scan over large blocks of - // existing data in a reasonable time. - $increment *= 1.01; - - $xref = $prefix . $num; - - // Records may already exist with this sequence number. - $already_used = - DB::table('individuals')->where('i_id', '=', $xref)->exists() || - DB::table('families')->where('f_id', '=', $xref)->exists() || - DB::table('sources')->where('s_id', '=', $xref)->exists() || - DB::table('media')->where('m_id', '=', $xref)->exists() || - DB::table('other')->where('o_id', '=', $xref)->exists() || - DB::table('change')->where('xref', '=', $xref)->exists(); - } while ($already_used); - - Site::setPreference('next_xref', (string) $num); - - return $xref; + return Factory::xref()->make(GedcomRecord::RECORD_TYPE); } /** @@ -533,7 +504,7 @@ class Tree throw new InvalidArgumentException('GedcomRecord::createFamily(' . $gedcom . ') does not begin 0 @@ FAM'); } - $xref = $this->getNewXref(); + $xref = Factory::xref()->make(Family::RECORD_TYPE); $gedcom = substr_replace($gedcom, $xref, 3, 0); // Create a change record @@ -576,7 +547,7 @@ class Tree throw new InvalidArgumentException('GedcomRecord::createIndividual(' . $gedcom . ') does not begin 0 @@ INDI'); } - $xref = $this->getNewXref(); + $xref = Factory::xref()->make(Individual::RECORD_TYPE); $gedcom = substr_replace($gedcom, $xref, 3, 0); // Create a change record @@ -619,7 +590,7 @@ class Tree throw new InvalidArgumentException('GedcomRecord::createIndividual(' . $gedcom . ') does not begin 0 @@ OBJE'); } - $xref = $this->getNewXref(); + $xref = Factory::xref()->make(Media::RECORD_TYPE); $gedcom = substr_replace($gedcom, $xref, 3, 0); // Create a change record diff --git a/tests/TestCase.php b/tests/TestCase.php index c27ddcb8f6..d18d1ccca6 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -33,6 +33,7 @@ use Fisharebest\Webtrees\Factories\RepositoryFactory; use Fisharebest\Webtrees\Factories\SourceFactory; use Fisharebest\Webtrees\Factories\SubmissionFactory; use Fisharebest\Webtrees\Factories\SubmitterFactory; +use Fisharebest\Webtrees\Factories\XrefFactory; use Fisharebest\Webtrees\Http\Controllers\GedcomFileController; use Fisharebest\Webtrees\Http\Routes\WebRoutes; use Fisharebest\Webtrees\Module\ModuleThemeInterface; @@ -102,6 +103,7 @@ class TestCase extends \PHPUnit\Framework\TestCase Factory::source(new SourceFactory($cache)); Factory::submission(new SubmissionFactory($cache)); Factory::submitter(new SubmitterFactory($cache)); + Factory::xref(new XrefFactory()); app()->bind(ModuleThemeInterface::class, WebtreesTheme::class); |
