From dec724aa19a07e9f37913c25d2db8d978279c7b7 Mon Sep 17 00:00:00 2001 From: chrissawyerfan4 <126926647+chrissawyerfan4@users.noreply.github.com> Date: Sun, 7 Apr 2024 06:31:56 +0200 Subject: [PATCH] Match tag names case-insensitively --- app/Models/Tag.php | 24 +++++++++++++++++++++ tests/Models/LinkCreateTest.php | 37 +++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/app/Models/Tag.php b/app/Models/Tag.php index 6daa7e70..b23b2798 100644 --- a/app/Models/Tag.php +++ b/app/Models/Tag.php @@ -58,6 +58,30 @@ public static function nameHasChanged($tagId, string $newName): bool return $oldName !== $newName; } + /** + * Override inherited function such that it finds existing tags case-insensitively, but creates them with the right casing + * + * @param array $attributes + * @param array $values + * @return Builder + */ + public static function firstOrCreate(array $attributes = [], array $values = []): self + { + if (isset($attributes['name'])) { + // SQL lower() function is checked to be supported by MariaDB, SQLite, PostgreSQL, and MSSQL + $existing = self::whereRaw('lower(name) = lower(?)', [$attributes['name']])->first(); + if (!is_null($existing)) { + return $existing; + } + } + + if (!is_null($instance = self::where($attributes)->first())) { + return $instance; + } + + return self::createOrFirst($attributes, $values); + } + /* | ======================================================================== | SCOPES diff --git a/tests/Models/LinkCreateTest.php b/tests/Models/LinkCreateTest.php index 645cffc4..1b1ef105 100644 --- a/tests/Models/LinkCreateTest.php +++ b/tests/Models/LinkCreateTest.php @@ -65,4 +65,41 @@ public function testValidLinkCreation(): void $this->assertDatabaseHas('links', $assertedData); } + + public function testCaseInsensitiveTagAssignation(): void + { + $this->be($this->user); + + $tag = 'CERN'; + $tagVariant = ucfirst(strtolower($tag)); + + $this->assertNotEquals($tag, $tagVariant); + + $link1Data = [ + 'title' => 'Link1Name', + 'url' => 'https://cern.int/science/physics/antimatter', + 'tags' => $tag, + ]; + $link2Data = [ + 'title' => 'Link2Name', + 'url' => 'https://cern.int/science/accelerators/large-hadron-collider/safety-lhc', + 'tags' => $tagVariant, + ]; + + $link1 = LinkRepository::create($link1Data); + $link2 = LinkRepository::create($link2Data); + + // Ensure that the casing in $tag is preserved + $this->assertEquals($link1->tags->first()->name, $tag); + + // Ensure that $tag was identified as a duplicate of $tagVariant and used instead of the variant + $this->assertEquals($link2->tags->first()->name, $tag); + $this->assertEquals($link1->tags->first()->id, $link2->tags->first()->id); + + // Ensure that only the correct variant exists + $negativeTagData = [ + 'name' => $tagVariant, + ]; + $this->assertDatabaseMissing('tags', $negativeTagData); + } }