Skip to content

Commit d169312

Browse files
committed
LinkGenerator: added support for '#' key in args to set URL fragment
1 parent 1ce1c8d commit d169312

2 files changed

Lines changed: 25 additions & 1 deletion

File tree

src/Application/LinkGenerator.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,19 @@ public function link(
4646
{
4747
$parts = self::parseDestination($destination);
4848
$args = $parts['args'] ?? $args;
49+
$hash = $args['#'] ?? null;
50+
unset($args['#']);
51+
if ($hash !== null && !is_scalar($hash)) {
52+
throw new UI\InvalidLinkException("Value of '#' must be scalar, " . get_debug_type($hash) . ' given.');
53+
}
54+
$fragment = (string) $hash !== ''
55+
? '#' . rawurlencode((string) $hash)
56+
: $parts['fragment'];
4957
$request = $this->createRequest($component, $parts['path'] . ($parts['signal'] ? '!' : ''), $args, $mode ?? 'link');
5058
$relative = $mode === 'link' && !$parts['absolute'] && !$component?->getPresenter()?->absoluteUrls;
5159
return $mode === 'forward' || $mode === 'test'
5260
? null
53-
: $this->requestToUrl($request, $relative) . $parts['fragment'];
61+
: $this->requestToUrl($request, $relative) . $fragment;
5462
}
5563

5664

tests/UI/Presenter.link().phpt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,22 @@ class TestPresenter extends Application\UI\Presenter
9292
Assert::same('/index.php?x=1&y=2&action=product&presenter=Test', $this->link('product?x=1&y=2'));
9393
Assert::same('/index.php?x=1&y=2&action=product&presenter=Test#fragment', $this->link('product?x=1&y=2#fragment'));
9494

95+
// fragment via args
96+
Assert::same('/index.php?action=product&presenter=Test#fragment', $this->link('product', ['#' => 'fragment']));
97+
Assert::same('/index.php?x=1&action=product&presenter=Test#fragment', $this->link('product', ['x' => 1, '#' => 'fragment']));
98+
Assert::same('/index.php?action=product&presenter=Test#b', $this->link('product#a', ['#' => 'b'])); // args win over destination
99+
Assert::same('/index.php?action=product&presenter=Test#a', $this->link('product#a', ['#' => ''])); // empty arg falls back to destination
100+
Assert::same('/index.php?action=product&presenter=Test', $this->link('product', ['#' => ''])); // empty fragment omitted
101+
Assert::same('/index.php?action=product&presenter=Test', $this->link('product', ['#' => null]));
102+
Assert::same(['pint' => null, 'parr' => null, 'pbool' => null, 'action' => 'product'], $this->getLastCreatedRequest()->getParameters()); // # not propagated to request
103+
Assert::same('http://localhost/index.php?x=1&action=product&presenter=Test#fragment', $this->link('//product', ['x' => 1, '#' => 'fragment']));
104+
Assert::same('/index.php?action=product&presenter=Test#hello%20world%20%26%20co', $this->link('product', ['#' => 'hello world & co'])); // URL encoding
105+
Assert::same('/index.php?action=product&presenter=Test#42', $this->link('product', ['#' => 42])); // scalar coercion
106+
Assert::same("#error: Value of '#' must be scalar, array given.", $this->link('product', ['#' => [1, 2]]));
107+
Assert::same("#error: Value of '#' must be scalar, stdClass given.", $this->link('product', ['#' => new stdClass]));
108+
Assert::same('/index.php?mycontrol-x=1&mycontrol-y=2&action=default&do=mycontrol-click&presenter=Test#frag', $this['mycontrol']->link('click', ['x' => 1, 'y' => 2, '#' => 'frag'])); // component signal + fragment
109+
Assert::same('/index.php?action=product&presenter=Test#frag', (string) new Application\UI\Link($this, 'product', ['#' => 'frag'])); // Link class
110+
95111
// absolute
96112
Assert::same('http://localhost/index.php?x=1&y=2&action=product&presenter=Test#fragment', $this->link('//product?x=1&y=2#fragment'));
97113
$this->absoluteUrls = true;

0 commit comments

Comments
 (0)