diff --git a/src/Fieldtypes/Concerns/ResolvesStatamicUrls.php b/src/Fieldtypes/Concerns/ResolvesStatamicUrls.php
index 90eb25ae91b..79714ba3d38 100644
--- a/src/Fieldtypes/Concerns/ResolvesStatamicUrls.php
+++ b/src/Fieldtypes/Concerns/ResolvesStatamicUrls.php
@@ -13,11 +13,11 @@ trait ResolvesStatamicUrls
*/
protected function resolveStatamicUrls(string $content)
{
- return preg_replace_callback('/([("])statamic:\/\/([^()"]*)([)"])/im', function ($matches) {
+ return preg_replace_callback('/([("])statamic:\/\/([^()"?#]*)([^()"]*)([)"])/im', function ($matches) {
$data = Data::find($matches[2]);
- $url = $data ? $data->url() : '';
+ $url = $data ? $data->url().$matches[3] : '';
- return $matches[1].$url.$matches[3];
+ return $matches[1].$url.$matches[4];
}, $content);
}
}
diff --git a/tests/Fieldtypes/Concerns/ResolvesStatamicUrlsTest.php b/tests/Fieldtypes/Concerns/ResolvesStatamicUrlsTest.php
new file mode 100644
index 00000000000..5fbab8345e2
--- /dev/null
+++ b/tests/Fieldtypes/Concerns/ResolvesStatamicUrlsTest.php
@@ -0,0 +1,136 @@
+testClass = new class
+ {
+ use ResolvesStatamicUrls;
+
+ public function resolve(string $content)
+ {
+ return $this->resolveStatamicUrls($content);
+ }
+ };
+ }
+
+ #[Test]
+ public function it_calls_data_find_with_correct_id()
+ {
+ $data = Mockery::mock();
+ $data->shouldReceive('url')->andReturn('/some/url');
+
+ DataFacade::shouldReceive('find')
+ ->once()
+ ->with('foo::bar/baz.ext')
+ ->andReturn($data);
+
+ $content = '[link](statamic://foo::bar/baz.ext)';
+ $result = $this->testClass->resolve($content);
+
+ $this->assertEquals('[link](/some/url)', $result);
+ }
+
+ #[Test]
+ public function it_handles_non_existent_data()
+ {
+ DataFacade::shouldReceive('find')
+ ->once()
+ ->with('non-existent')
+ ->andReturn(null);
+
+ $content = '[link](statamic://non-existent)';
+ $result = $this->testClass->resolve($content);
+
+ $this->assertEquals('[link]()', $result);
+ }
+
+ #[Test]
+ public function it_handles_multiple_urls()
+ {
+ $data1 = Mockery::mock();
+ $data1->shouldReceive('url')->andReturn('/url-1');
+
+ $data2 = Mockery::mock();
+ $data2->shouldReceive('url')->andReturn('/url-2');
+
+ DataFacade::shouldReceive('find')
+ ->once()
+ ->with('id-1')
+ ->andReturn($data1);
+
+ DataFacade::shouldReceive('find')
+ ->once()
+ ->with('id-2')
+ ->andReturn($data2);
+
+ $content = '[link1](statamic://id-1) and
';
+ $result = $this->testClass->resolve($content);
+
+ $this->assertEquals('[link1](/url-1) and
', $result);
+ }
+
+ #[Test]
+ public function it_maintains_hash_fragments()
+ {
+ $data = Mockery::mock();
+ $data->shouldReceive('url')->andReturn('/some/page');
+
+ DataFacade::shouldReceive('find')
+ ->once()
+ ->with('entry::123')
+ ->andReturn($data);
+
+ $content = '[link](statamic://entry::123#section)';
+ $result = $this->testClass->resolve($content);
+
+ $this->assertEquals('[link](/some/page#section)', $result);
+ }
+
+ #[Test]
+ public function it_maintains_query_strings()
+ {
+ $data = Mockery::mock();
+ $data->shouldReceive('url')->andReturn('/some/page');
+
+ DataFacade::shouldReceive('find')
+ ->once()
+ ->with('entry::123')
+ ->andReturn($data);
+
+ $content = '[link](statamic://entry::123?foo=bar)';
+ $result = $this->testClass->resolve($content);
+
+ $this->assertEquals('[link](/some/page?foo=bar)', $result);
+ }
+
+ #[Test]
+ public function it_maintains_query_strings_and_hash_fragments()
+ {
+ $data = Mockery::mock();
+ $data->shouldReceive('url')->andReturn('/some/page');
+
+ DataFacade::shouldReceive('find')
+ ->once()
+ ->with('entry::123')
+ ->andReturn($data);
+
+ $content = '[link](statamic://entry::123?foo=bar#section)';
+ $result = $this->testClass->resolve($content);
+
+ $this->assertEquals('[link](/some/page?foo=bar#section)', $result);
+ }
+}