Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/Illuminate/Routing/PendingResourceRegistration.php
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,22 @@ public function where($wheres)
return $this;
}

/**
* Add metadata to the registered resource routes.
*
* @param array $metadata
* @return $this
*/
public function metadata(array $metadata)
{
$this->options['metadata'] = RouteGroup::mergeMetadata(
$this->options['metadata'] ?? [],
$metadata
);

return $this;
}

/**
* Indicate that the resource routes should have "shallow" nesting.
*
Expand Down
16 changes: 16 additions & 0 deletions src/Illuminate/Routing/PendingSingletonResourceRegistration.php
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,22 @@ public function where($wheres)
return $this;
}

/**
* Add metadata to the registered singleton resource routes.
*
* @param array $metadata
* @return $this
*/
public function metadata(array $metadata)
{
$this->options['metadata'] = RouteGroup::mergeMetadata(
$this->options['metadata'] ?? [],
$metadata
);

return $this;
}

/**
* Register the singleton resource route.
*
Expand Down
7 changes: 7 additions & 0 deletions src/Illuminate/Routing/ResourceRegistrar.php
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,13 @@ protected function getResourceAction($resource, $controller, $method, $options)
$action['missing'] = $options['missing'];
}

if (isset($options['metadata'])) {
$action['metadata'] = RouteGroup::mergeMetadata(
$action['metadata'] ?? [],
$options['metadata']
);
}

return $action;
}

Expand Down
43 changes: 43 additions & 0 deletions src/Illuminate/Routing/Route.php
Original file line number Diff line number Diff line change
Expand Up @@ -999,6 +999,49 @@ public function getAction($key = null)
return Arr::get($this->action, $key);
}

/**
* Add metadata to the route.
*
* @param array $metadata
* @return $this
*/
public function metadata(array $metadata)
{
$this->action['metadata'] = RouteGroup::mergeMetadata(
$this->action['metadata'] ?? [],
$metadata
);

return $this;
}

/**
* Set the metadata for the route, replacing any existing metadata.
*
* @param array $metadata
* @return $this
*/
public function setMetadata(array $metadata)
{
$this->action['metadata'] = $metadata;

return $this;
}

/**
* Get metadata for the route.
*
* @param string|null $key
* @param mixed $default
* @return mixed
*/
public function getMetadata($key = null, $default = null)
{
$metadata = $this->action['metadata'] ?? [];

return is_null($key) ? $metadata : Arr::get($metadata, $key, $default);
}

/**
* Set the action array for the route.
*
Expand Down
61 changes: 60 additions & 1 deletion src/Illuminate/Routing/RouteGroup.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,76 @@ public static function merge($new, $old, $prependExistingPrefix = true)
unset($old['controller']);
}

$metadata = static::formatMetadata($new, $old);

unset($new['metadata']);

$new = array_merge(static::formatAs($new, $old), [
'namespace' => static::formatNamespace($new, $old),
'prefix' => static::formatPrefix($new, $old, $prependExistingPrefix),
'where' => static::formatWhere($new, $old),
]);

if ($metadata !== []) {
$new['metadata'] = $metadata;
}

return array_merge_recursive(Arr::except(
$old, ['namespace', 'prefix', 'where', 'as']
$old, ['metadata', 'namespace', 'prefix', 'where', 'as']
), $new);
}

/**
* Associative array values are merged recursively, while all other
* values, including lists, replace the existing value entirely.
*
* @param array $old
* @param array $new
* @return array
*/
public static function mergeMetadata(array $old, array $new)
{
foreach ($new as $key => $value) {
if (isset($old[$key]) && static::mergesMetadata($old[$key], $value)) {
$value = static::mergeMetadata($old[$key], $value);
}

$old[$key] = $value;
}

return $old;
}

/**
* Determine if the given metadata values should be merged.
*
* @param mixed $old
* @param mixed $new
* @return bool
*/
protected static function mergesMetadata($old, $new)
{
return is_array($old) &&
is_array($new) &&
Arr::isAssoc($old) &&
Arr::isAssoc($new);
}

/**
* Format the metadata for the new group attributes.
*
* @param array $new
* @param array $old
* @return array
*/
protected static function formatMetadata($new, $old)
{
return static::mergeMetadata(
$old['metadata'] ?? [],
$new['metadata'] ?? []
);
}

/**
* Format the namespace for the new group attributes.
*
Expand Down
36 changes: 35 additions & 1 deletion src/Illuminate/Routing/RouteRegistrar.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
* @method \Illuminate\Routing\RouteRegistrar can(\UnitEnum|string $ability, array|string $models = [])
* @method \Illuminate\Routing\RouteRegistrar controller(string $controller)
* @method \Illuminate\Routing\RouteRegistrar domain(\BackedEnum|string $value)
* @method \Illuminate\Routing\RouteRegistrar metadata(array $metadata)
* @method \Illuminate\Routing\RouteRegistrar middleware(array|string|null $middleware)
* @method \Illuminate\Routing\RouteRegistrar missing(\Closure $missing)
* @method \Illuminate\Routing\RouteRegistrar name(\BackedEnum|string $value)
Expand Down Expand Up @@ -72,6 +73,7 @@ class RouteRegistrar
'can',
'controller',
'domain',
'metadata',
'middleware',
'missing',
'name',
Expand Down Expand Up @@ -128,6 +130,16 @@ public function attribute($key, $value)
}
}

if ($key === 'metadata') {
if (! is_array($value)) {
throw new InvalidArgumentException('Attribute [metadata] expects an array.');
}

$value = RouteGroup::mergeMetadata(
$this->attributes['metadata'] ?? [], $value
);
}

$attributeKey = Arr::get($this->aliases, $key, $key);

if ($key === 'withoutMiddleware') {
Expand All @@ -149,6 +161,17 @@ public function attribute($key, $value)
return $this;
}

/**
* Add metadata to routes registered by the registrar.
*
* @param array $metadata
* @return $this
*/
public function metadata(array $metadata)
{
return $this->attribute('metadata', $metadata);
}

/**
* Route a resource to a controller.
*
Expand Down Expand Up @@ -272,7 +295,18 @@ protected function compileAction($action)
];
}

return array_merge($this->attributes, $action);
$metadata = RouteGroup::mergeMetadata(
$this->attributes['metadata'] ?? [],
$action['metadata'] ?? []
);

$action = array_merge($this->attributes, $action);

if ($metadata !== []) {
$action['metadata'] = $metadata;
}

return $action;
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/Illuminate/Support/Facades/Route.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
* @method static mixed macroCall(string $method, array $parameters)
* @method static \Illuminate\Support\HigherOrderTapProxy|\Illuminate\Routing\Router tap(callable|null $callback = null)
* @method static \Illuminate\Routing\RouteRegistrar attribute(string $key, mixed $value)
* @method static \Illuminate\Routing\RouteRegistrar metadata(array $metadata)
* @method static \Illuminate\Routing\RouteRegistrar whereAlpha(array|string $parameters)
* @method static \Illuminate\Routing\RouteRegistrar whereAlphaNumeric(array|string $parameters)
* @method static \Illuminate\Routing\RouteRegistrar whereNumber(array|string $parameters)
Expand Down
20 changes: 20 additions & 0 deletions tests/Routing/RouteCollectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
namespace Illuminate\Tests\Routing;

use ArrayIterator;
use Illuminate\Container\Container;
use Illuminate\Events\Dispatcher;
use Illuminate\Http\Request;
use Illuminate\Routing\Route;
use Illuminate\Routing\RouteCollection;
use Illuminate\Routing\Router;
use LogicException;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
Expand Down Expand Up @@ -284,6 +287,23 @@ public function testCannotCacheDuplicateRouteNames()
$this->routeCollection->compile();
}

public function testCompiledRouteCollectionPreservesRouteMetadata()
{
$this->routeCollection->add(
new Route('GET', 'users', [
'uses' => 'UsersController@index',
'as' => 'users',
'metadata' => ['head' => ['title' => 'Users']],
])
);

$route = $this->routeCollection
->toCompiledRouteCollection(new Router(new Dispatcher, new Container), new Container)
->getByName('users');

$this->assertSame(['title' => 'Users'], $route->getMetadata('head'));
}

public function testRouteCollectionDontMatchNonMatchingDoubleSlashes()
{
$this->expectException(NotFoundHttpException::class);
Expand Down
Loading