From e470e96be69b05e643417359951d7ad8c93af70f Mon Sep 17 00:00:00 2001 From: BertKoor Date: Tue, 25 Nov 2025 16:51:33 +0100 Subject: [PATCH 1/2] fix #4511, #5137: when adding a child to a family, put it between older and younger children --- .../AddChildToFamilyAction.php | 8 ++- .../LinkChildToFamilyAction.php | 13 +++- app/Services/FamilyService.php | 63 +++++++++++++++++++ tests/app/Services/FamilyServiceTest.php | 33 ++++++++++ 4 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 app/Services/FamilyService.php create mode 100644 tests/app/Services/FamilyServiceTest.php diff --git a/app/Http/RequestHandlers/AddChildToFamilyAction.php b/app/Http/RequestHandlers/AddChildToFamilyAction.php index fe0c2d7429d..0d0c2ffb298 100644 --- a/app/Http/RequestHandlers/AddChildToFamilyAction.php +++ b/app/Http/RequestHandlers/AddChildToFamilyAction.php @@ -22,6 +22,7 @@ use Fisharebest\Webtrees\Auth; use Fisharebest\Webtrees\Individual; use Fisharebest\Webtrees\Registry; +use Fisharebest\Webtrees\Services\FamilyService; use Fisharebest\Webtrees\Services\GedcomEditService; use Fisharebest\Webtrees\Validator; use Psr\Http\Message\ResponseInterface; @@ -36,13 +37,16 @@ class AddChildToFamilyAction implements RequestHandlerInterface { private GedcomEditService $gedcom_edit_service; + private FamilyService $family_service; /** * @param GedcomEditService $gedcom_edit_service + * @param FamilyService $family_service */ - public function __construct(GedcomEditService $gedcom_edit_service) + public function __construct(GedcomEditService $gedcom_edit_service, FamilyService $family_service) { $this->gedcom_edit_service = $gedcom_edit_service; + $this->family_service = $family_service; } /** @@ -66,7 +70,7 @@ public function handle(ServerRequestInterface $request): ResponseInterface $child = $tree->createIndividual("0 @@ INDI\n1 FAMC @" . $xref . '@' . $gedcom); // Link the child to the family - $family->createFact('1 CHIL @' . $child->xref() . '@', false); + $this->family_service->addChildToFamily($child, $family); $url = Validator::parsedBody($request)->isLocalUrl()->string('url', $child->url()); diff --git a/app/Http/RequestHandlers/LinkChildToFamilyAction.php b/app/Http/RequestHandlers/LinkChildToFamilyAction.php index 3a3ba2c2e10..7906ed449c6 100644 --- a/app/Http/RequestHandlers/LinkChildToFamilyAction.php +++ b/app/Http/RequestHandlers/LinkChildToFamilyAction.php @@ -22,6 +22,7 @@ use Fisharebest\Webtrees\Auth; use Fisharebest\Webtrees\Elements\PedigreeLinkageType; use Fisharebest\Webtrees\Registry; +use Fisharebest\Webtrees\Services\FamilyService; use Fisharebest\Webtrees\Validator; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -34,6 +35,16 @@ */ class LinkChildToFamilyAction implements RequestHandlerInterface { + private FamilyService $family_service; + + /** + * @param FamilyService $family_service + */ + public function __construct(FamilyService $family_service) + { + $this->family_service = $family_service; + } + /** * @param ServerRequestInterface $request * @@ -90,7 +101,7 @@ public function handle(ServerRequestInterface $request): ResponseInterface } if (!$chil_link_exists) { - $family->createFact('1 CHIL @' . $individual->xref() . '@', true); + $this->family_service->addChildToFamily($individual, $family); } return redirect($individual->url()); diff --git a/app/Services/FamilyService.php b/app/Services/FamilyService.php new file mode 100644 index 00000000000..9b1f19962d0 --- /dev/null +++ b/app/Services/FamilyService.php @@ -0,0 +1,63 @@ +. + */ + +declare(strict_types=1); + +namespace Fisharebest\Webtrees\Services; + +use Fisharebest\Webtrees\Family; +use Fisharebest\Webtrees\Individual; + +use function implode; + +class FamilyService +{ + /** + * Add a child to a family. + * + * @param Individual $child + * @param Family $family + * + * @return void + */ + public function addChildToFamily(Individual $child, Family $family): void + { + $child_birth_day = $child->getBirthDate()->julianDay(); + $child_gedcom = '1 CHIL @' . $child->xref() . '@'; + $family_facts = ['0 @' . $family->xref() . '@ FAM']; + + // Insert new child at the right place + $done = false; + foreach ($family->facts() as $fact) { + if ($fact->tag() === 'FAM:CHIL' && !$done) { + // insert new child when born before this child + if ($child_birth_day < $fact->target()->getBirthDate()->julianDay()) { + $family_facts[] = $child_gedcom; + $done = true; + } + } + $family_facts[] = $fact->gedcom(); + } + if (!$done) { + // Append child at end + $family_facts[] = $child_gedcom; + } + + $gedcom = implode("\n", $family_facts); + $family->updateRecord($gedcom, false); + } +} diff --git a/tests/app/Services/FamilyServiceTest.php b/tests/app/Services/FamilyServiceTest.php new file mode 100644 index 00000000000..8cbed635c87 --- /dev/null +++ b/tests/app/Services/FamilyServiceTest.php @@ -0,0 +1,33 @@ +. + */ + +declare(strict_types=1); + +namespace Fisharebest\Webtrees\Services; + +use Fisharebest\Webtrees\TestCase; +use PHPUnit\Framework\Attributes\CoversClass; + +#[CoversClass(FamilyService::class)] +class FamilyServiceTest extends TestCase +{ + public function testClass(): void + { + self::assertTrue(class_exists(FamilyService::class)); + } + +} \ No newline at end of file From 47c6df8c3059998e17ba849caba600d8e3f347e7 Mon Sep 17 00:00:00 2001 From: BertKoor Date: Thu, 11 Dec 2025 09:25:32 +0100 Subject: [PATCH 2/2] fixed issues from code review --- .../AddChildToFamilyAction.php | 8 +-- .../LinkChildToFamilyAction.php | 12 ++-- app/Services/FamilyService.php | 63 ------------------- app/Services/GedcomEditService.php | 37 +++++++++++ tests/app/Services/FamilyServiceTest.php | 33 ---------- 5 files changed, 45 insertions(+), 108 deletions(-) delete mode 100644 app/Services/FamilyService.php delete mode 100644 tests/app/Services/FamilyServiceTest.php diff --git a/app/Http/RequestHandlers/AddChildToFamilyAction.php b/app/Http/RequestHandlers/AddChildToFamilyAction.php index 0d0c2ffb298..cf8f5a76e3a 100644 --- a/app/Http/RequestHandlers/AddChildToFamilyAction.php +++ b/app/Http/RequestHandlers/AddChildToFamilyAction.php @@ -22,7 +22,6 @@ use Fisharebest\Webtrees\Auth; use Fisharebest\Webtrees\Individual; use Fisharebest\Webtrees\Registry; -use Fisharebest\Webtrees\Services\FamilyService; use Fisharebest\Webtrees\Services\GedcomEditService; use Fisharebest\Webtrees\Validator; use Psr\Http\Message\ResponseInterface; @@ -37,16 +36,13 @@ class AddChildToFamilyAction implements RequestHandlerInterface { private GedcomEditService $gedcom_edit_service; - private FamilyService $family_service; /** * @param GedcomEditService $gedcom_edit_service - * @param FamilyService $family_service */ - public function __construct(GedcomEditService $gedcom_edit_service, FamilyService $family_service) + public function __construct(GedcomEditService $gedcom_edit_service) { $this->gedcom_edit_service = $gedcom_edit_service; - $this->family_service = $family_service; } /** @@ -70,7 +66,7 @@ public function handle(ServerRequestInterface $request): ResponseInterface $child = $tree->createIndividual("0 @@ INDI\n1 FAMC @" . $xref . '@' . $gedcom); // Link the child to the family - $this->family_service->addChildToFamily($child, $family); + $this->gedcom_edit_service->addChildToFamily($child, $family); $url = Validator::parsedBody($request)->isLocalUrl()->string('url', $child->url()); diff --git a/app/Http/RequestHandlers/LinkChildToFamilyAction.php b/app/Http/RequestHandlers/LinkChildToFamilyAction.php index 7906ed449c6..2305810c462 100644 --- a/app/Http/RequestHandlers/LinkChildToFamilyAction.php +++ b/app/Http/RequestHandlers/LinkChildToFamilyAction.php @@ -22,7 +22,7 @@ use Fisharebest\Webtrees\Auth; use Fisharebest\Webtrees\Elements\PedigreeLinkageType; use Fisharebest\Webtrees\Registry; -use Fisharebest\Webtrees\Services\FamilyService; +use Fisharebest\Webtrees\Services\GedcomEditService; use Fisharebest\Webtrees\Validator; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -35,14 +35,14 @@ */ class LinkChildToFamilyAction implements RequestHandlerInterface { - private FamilyService $family_service; + private GedcomEditService $gedcom_edit_service; /** - * @param FamilyService $family_service + * @param GedcomEditService $gedcom_edit_service */ - public function __construct(FamilyService $family_service) + public function __construct(GedcomEditService $gedcom_edit_service) { - $this->family_service = $family_service; + $this->gedcom_edit_service = $gedcom_edit_service; } /** @@ -101,7 +101,7 @@ public function handle(ServerRequestInterface $request): ResponseInterface } if (!$chil_link_exists) { - $this->family_service->addChildToFamily($individual, $family); + $this->gedcom_edit_service->addChildToFamily($individual, $family); } return redirect($individual->url()); diff --git a/app/Services/FamilyService.php b/app/Services/FamilyService.php deleted file mode 100644 index 9b1f19962d0..00000000000 --- a/app/Services/FamilyService.php +++ /dev/null @@ -1,63 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace Fisharebest\Webtrees\Services; - -use Fisharebest\Webtrees\Family; -use Fisharebest\Webtrees\Individual; - -use function implode; - -class FamilyService -{ - /** - * Add a child to a family. - * - * @param Individual $child - * @param Family $family - * - * @return void - */ - public function addChildToFamily(Individual $child, Family $family): void - { - $child_birth_day = $child->getBirthDate()->julianDay(); - $child_gedcom = '1 CHIL @' . $child->xref() . '@'; - $family_facts = ['0 @' . $family->xref() . '@ FAM']; - - // Insert new child at the right place - $done = false; - foreach ($family->facts() as $fact) { - if ($fact->tag() === 'FAM:CHIL' && !$done) { - // insert new child when born before this child - if ($child_birth_day < $fact->target()->getBirthDate()->julianDay()) { - $family_facts[] = $child_gedcom; - $done = true; - } - } - $family_facts[] = $fact->gedcom(); - } - if (!$done) { - // Append child at end - $family_facts[] = $child_gedcom; - } - - $gedcom = implode("\n", $family_facts); - $family->updateRecord($gedcom, false); - } -} diff --git a/app/Services/GedcomEditService.php b/app/Services/GedcomEditService.php index e6b04bca974..c53c0bd0961 100644 --- a/app/Services/GedcomEditService.php +++ b/app/Services/GedcomEditService.php @@ -19,6 +19,7 @@ namespace Fisharebest\Webtrees\Services; +use Fisharebest\Webtrees\Auth; use Fisharebest\Webtrees\Elements\AbstractXrefElement; use Fisharebest\Webtrees\Fact; use Fisharebest\Webtrees\Family; @@ -344,4 +345,40 @@ private function isHiddenTag(string $tag): bool return false; } + + /** + * Add a child to a family. + * + * @param Individual $child + * @param Family $family + * + * @return void + */ + public function addChildToFamily(Individual $child, Family $family): void + { + $child_birth_day = $child->getBirthDate()->julianDay(); + $child_gedcom = '1 CHIL @' . $child->xref() . '@'; + $family_facts = ['0 @' . $family->xref() . '@ FAM']; + + // Insert new child at the right place + $done = false; + foreach ($family->facts([], false, Auth::PRIV_HIDE, true) as $fact) { + if ($fact->tag() === 'FAM:CHIL' && !$done) { + // insert new child when born before this child + if ($child_birth_day < $fact->target()->getBirthDate()->julianDay()) { + $family_facts[] = $child_gedcom; + $done = true; + } + } + $family_facts[] = $fact->gedcom(); + } + if (!$done) { + // Append child at end + $family_facts[] = $child_gedcom; + } + + $gedcom = implode("\n", $family_facts); + $family->updateRecord($gedcom, false); + } + } diff --git a/tests/app/Services/FamilyServiceTest.php b/tests/app/Services/FamilyServiceTest.php deleted file mode 100644 index 8cbed635c87..00000000000 --- a/tests/app/Services/FamilyServiceTest.php +++ /dev/null @@ -1,33 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace Fisharebest\Webtrees\Services; - -use Fisharebest\Webtrees\TestCase; -use PHPUnit\Framework\Attributes\CoversClass; - -#[CoversClass(FamilyService::class)] -class FamilyServiceTest extends TestCase -{ - public function testClass(): void - { - self::assertTrue(class_exists(FamilyService::class)); - } - -} \ No newline at end of file