Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
project
drupal
Commits
3cd6aa89
Commit
3cd6aa89
authored
Mar 03, 2014
by
Alex Pott
Browse files
Issue
#2058845
by dawehner, msonnabaum: Pre-load non-admin routes.
parent
428d0f0a
Changes
3
Hide whitespace changes
Inline
Side-by-side
core/core.services.yml
View file @
3cd6aa89
...
...
@@ -278,6 +278,11 @@ services:
arguments
:
[
'
@database'
,
'
@router.builder'
]
tags
:
-
{
name
:
event_subscriber
}
router.route_preloader
:
class
:
Drupal\Core\Routing\RoutePreloader
arguments
:
[
'
@router.route_provider'
,
'
@state'
,
'
@content_negotiation'
]
tags
:
-
{
name
:
'
event_subscriber'
}
router.matcher.final_matcher
:
class
:
Drupal\Core\Routing\UrlMatcher
router.matcher
:
...
...
core/lib/Drupal/Core/Routing/RoutePreloader.php
0 → 100644
View file @
3cd6aa89
<?php
/**
* @file
* Contains \Drupal\Core\Routing\RoutePreloader.
*/
namespace
Drupal\Core\Routing
;
use
Drupal\Core\ContentNegotiation
;
use
Drupal\Core\KeyValueStore\StateInterface
;
use
Symfony\Component\EventDispatcher\Event
;
use
Symfony\Component\EventDispatcher\EventSubscriberInterface
;
use
Symfony\Component\HttpKernel\Event\KernelEvent
;
use
Symfony\Component\HttpKernel\KernelEvents
;
/**
* Defines a class which preloads non-admin routes.
*
* On an actual site we want to avoid too many database queries so we build a
* list of all routes which most likely appear on the actual site, which are all
* HTML routes not starting with "/admin".
*/
class
RoutePreloader
implements
EventSubscriberInterface
{
/**
* The route provider.
*
* @var \Drupal\Core\Routing\RouteProviderInterface
*/
protected
$routeProvider
;
/**
* The state key value store.
*
* @var \Drupal\Core\KeyValueStore\StateInterface
*/
protected
$state
;
/**
* The content negotiation.
*
* @var \Drupal\Core\ContentNegotiation
*/
protected
$negotiation
;
/**
* Contains the non-admin routes while rebuilding the routes.
*
* @var array
*/
protected
$nonAdminRoutesOnRebuild
=
array
();
/**
* Constructs a new RoutePreloader.
*
* @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
* The route provider.
* @param \Drupal\Core\KeyValueStore\StateInterface $state
* The state key value store.
* @param \Drupal\Core\ContentNegotiation $negotiation
* The content negotiation.
*/
public
function
__construct
(
RouteProviderInterface
$route_provider
,
StateInterface
$state
,
ContentNegotiation
$negotiation
)
{
$this
->
routeProvider
=
$route_provider
;
$this
->
state
=
$state
;
$this
->
negotiation
=
$negotiation
;
}
/**
* Loads all non-admin routes right before the actual page is rendered.
*
* @param \Symfony\Component\HttpKernel\Event\KernelEvent $event
* The event to process.
*/
public
function
onRequest
(
KernelEvent
$event
)
{
// Just preload on normal HTML pages, as they will display menu links.
if
(
$this
->
negotiation
->
getContentType
(
$event
->
getRequest
())
==
'html'
)
{
$this
->
loadNonAdminRoutes
();
}
}
/**
* Load all the non-admin routes at once.
*/
protected
function
loadNonAdminRoutes
()
{
if
(
$routes
=
$this
->
state
->
get
(
'routing.non_admin_routes'
,
array
()))
{
$this
->
routeProvider
->
getRoutesByNames
(
$routes
);
}
}
/**
* Alters existing routes for a specific collection.
*
* @param \Drupal\Core\Routing\RouteBuildEvent $event
* The route build event.
*/
public
function
onAlterRoutes
(
RouteBuildEvent
$event
)
{
$collection
=
$event
->
getRouteCollection
();
foreach
(
$collection
->
all
()
as
$name
=>
$route
)
{
if
(
strpos
(
$route
->
getPath
(),
'/admin/'
)
!==
0
&&
$route
->
getPath
()
!=
'/admin'
)
{
$this
->
nonAdminRoutesOnRebuild
[]
=
$name
;
}
}
$this
->
nonAdminRoutesOnRebuild
=
array_unique
(
$this
->
nonAdminRoutesOnRebuild
);
}
/**
* Store the non admin routes in state when the route building is finished.
*
* @param \Symfony\Component\EventDispatcher\Event $event
* The route finish event.
*/
public
function
onFinishedRoutes
(
Event
$event
)
{
$this
->
state
->
set
(
'routing.non_admin_routes'
,
$this
->
nonAdminRoutesOnRebuild
);
$this
->
nonAdminRoutesOnRebuild
=
array
();
}
/**
* {@inheritdoc}
*/
public
static
function
getSubscribedEvents
()
{
// Set a really low priority to catch as many as possible routes.
$events
[
RoutingEvents
::
ALTER
]
=
array
(
'onAlterRoutes'
,
-
1024
);
$events
[
RoutingEvents
::
FINISHED
]
=
array
(
'onFinishedRoutes'
);
// Load the routes before the controller is executed (which happens after
// the kernel request event).
$events
[
KernelEvents
::
REQUEST
][]
=
array
(
'onRequest'
);
return
$events
;
}
}
core/tests/Drupal/Tests/Core/Routing/RoutePreloaderTest.php
0 → 100644
View file @
3cd6aa89
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Routing\RoutePreloaderTest.
*/
namespace
Drupal\Tests\Core\Routing
;
use
Drupal\Core\Routing\RoutePreloader
;
use
Drupal\Tests\UnitTestCase
;
use
Symfony\Component\EventDispatcher\Event
;
use
Symfony\Component\HttpFoundation\Request
;
use
Symfony\Component\Routing\Route
;
use
Symfony\Component\Routing\RouteCollection
;
/**
* Tests the non admin routes preloader.
*
* @see \Drupal\Core\Routing\RoutePreloader
*/
class
RoutePreloaderTest
extends
UnitTestCase
{
/**
* The mocked route provider.
*
* @var \Drupal\Core\Routing\RouteProviderInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected
$routeProvider
;
/**
* The mocked state.
*
* @var \Drupal\Core\KeyValueStore\StateInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected
$state
;
/**
* The mocked content negotiator.
*
* @var \Drupal\Core\ContentNegotiation|\PHPUnit_Framework_MockObject_MockObject
*/
protected
$negotiation
;
/**
* The tested preloader.
*
* @var \Drupal\Core\Routing\RoutePreloader
*/
protected
$preloader
;
/**
* {@inheritdoc}
*/
public
static
function
getInfo
()
{
return
array
(
'name'
=>
'Route preloader'
,
'description'
=>
'Tests the non admin routes preloader.'
,
'group'
=>
'Routing'
,
);
}
/**
* {@inheritdoc}
*/
protected
function
setUp
()
{
$this
->
routeProvider
=
$this
->
getMock
(
'Drupal\Core\Routing\RouteProviderInterface'
);
$this
->
state
=
$this
->
getMock
(
'\Drupal\Core\KeyValueStore\StateInterface'
);
$this
->
negotiation
=
$this
->
getMockBuilder
(
'\Drupal\Core\ContentNegotiation'
)
->
disableOriginalConstructor
()
->
getMock
();
$this
->
preloader
=
new
RoutePreloader
(
$this
->
routeProvider
,
$this
->
state
,
$this
->
negotiation
);
}
/**
* Tests onAlterRoutes with just admin routes.
*/
public
function
testOnAlterRoutesWithAdminRoutes
()
{
$event
=
$this
->
getMockBuilder
(
'Drupal\Core\Routing\RouteBuildEvent'
)
->
disableOriginalConstructor
()
->
getMock
();
$route_collection
=
new
RouteCollection
();
$route_collection
->
add
(
'test'
,
new
Route
(
'/admin/foo'
,
array
(
'_content'
=>
'Drupal\ExampleController'
)));
$route_collection
->
add
(
'test2'
,
new
Route
(
'/admin/bar'
,
array
(
'_content'
=>
'Drupal\ExampleController'
)));
$event
->
expects
(
$this
->
once
())
->
method
(
'getRouteCollection'
)
->
will
(
$this
->
returnValue
(
$route_collection
));
$this
->
state
->
expects
(
$this
->
once
())
->
method
(
'set'
)
->
with
(
'routing.non_admin_routes'
,
array
());
$this
->
preloader
->
onAlterRoutes
(
$event
);
$this
->
preloader
->
onFinishedRoutes
(
new
Event
());
}
/**
* Tests onAlterRoutes with "admin" appearing in the path.
*/
public
function
testOnAlterRoutesWithAdminPathNoAdminRoute
()
{
$event
=
$this
->
getMockBuilder
(
'Drupal\Core\Routing\RouteBuildEvent'
)
->
disableOriginalConstructor
()
->
getMock
();
$route_collection
=
new
RouteCollection
();
$route_collection
->
add
(
'test'
,
new
Route
(
'/foo/admin/foo'
,
array
(
'_content'
=>
'Drupal\ExampleController'
)));
$route_collection
->
add
(
'test2'
,
new
Route
(
'/bar/admin/bar'
,
array
(
'_content'
=>
'Drupal\ExampleController'
)));
$route_collection
->
add
(
'test3'
,
new
Route
(
'/administrator/a'
,
array
(
'_content'
=>
'Drupal\ExampleController'
)));
$route_collection
->
add
(
'test4'
,
new
Route
(
'/admin'
,
array
(
'_content'
=>
'Drupal\ExampleController'
)));
$event
->
expects
(
$this
->
once
())
->
method
(
'getRouteCollection'
)
->
will
(
$this
->
returnValue
(
$route_collection
));
$this
->
state
->
expects
(
$this
->
once
())
->
method
(
'set'
)
->
with
(
'routing.non_admin_routes'
,
array
(
'test'
,
'test2'
,
'test3'
));
$this
->
preloader
->
onAlterRoutes
(
$event
);
$this
->
preloader
->
onFinishedRoutes
(
new
Event
());
}
/**
* Tests onAlterRoutes with admin routes and non admin routes.
*/
public
function
testOnAlterRoutesWithNonAdminRoutes
()
{
$event
=
$this
->
getMockBuilder
(
'Drupal\Core\Routing\RouteBuildEvent'
)
->
disableOriginalConstructor
()
->
getMock
();
$route_collection
=
new
RouteCollection
();
$route_collection
->
add
(
'test'
,
new
Route
(
'/admin/foo'
,
array
(
'_content'
=>
'Drupal\ExampleController'
)));
$route_collection
->
add
(
'test2'
,
new
Route
(
'/bar'
,
array
(
'_content'
=>
'Drupal\ExampleController'
)));
// Non content routes, like ajax callbacks should be ignored.
$route_collection
->
add
(
'test3'
,
new
Route
(
'/bar'
,
array
(
'_controller'
=>
'Drupal\ExampleController'
)));
$event
->
expects
(
$this
->
once
())
->
method
(
'getRouteCollection'
)
->
will
(
$this
->
returnValue
(
$route_collection
));
$this
->
state
->
expects
(
$this
->
once
())
->
method
(
'set'
)
->
with
(
'routing.non_admin_routes'
,
array
(
'test2'
,
'test3'
));
$this
->
preloader
->
onAlterRoutes
(
$event
);
$this
->
preloader
->
onFinishedRoutes
(
new
Event
());
}
/**
* Tests onRequest on a non html request.
*/
public
function
testOnRequestNonHtml
()
{
$event
=
$this
->
getMockBuilder
(
'\Symfony\Component\HttpKernel\Event\KernelEvent'
)
->
disableOriginalConstructor
()
->
getMock
();
$request
=
new
Request
();
$event
->
expects
(
$this
->
any
())
->
method
(
'getRequest'
)
->
will
(
$this
->
returnValue
(
$request
));
$this
->
negotiation
->
expects
(
$this
->
once
())
->
method
(
'getContentType'
)
->
will
(
$this
->
returnValue
(
'non-html'
));
$this
->
routeProvider
->
expects
(
$this
->
never
())
->
method
(
'getRoutesByNames'
);
$this
->
state
->
expects
(
$this
->
never
())
->
method
(
'get'
);
$this
->
preloader
->
onRequest
(
$event
);
}
/**
* Tests onRequest on a html request.
*/
public
function
testOnRequestOnHtml
()
{
$event
=
$this
->
getMockBuilder
(
'\Symfony\Component\HttpKernel\Event\KernelEvent'
)
->
disableOriginalConstructor
()
->
getMock
();
$request
=
new
Request
();
$event
->
expects
(
$this
->
any
())
->
method
(
'getRequest'
)
->
will
(
$this
->
returnValue
(
$request
));
$this
->
negotiation
->
expects
(
$this
->
once
())
->
method
(
'getContentType'
)
->
will
(
$this
->
returnValue
(
'html'
));
$this
->
routeProvider
->
expects
(
$this
->
once
())
->
method
(
'getRoutesByNames'
)
->
with
(
array
(
'test2'
));
$this
->
state
->
expects
(
$this
->
once
())
->
method
(
'get'
)
->
with
(
'routing.non_admin_routes'
)
->
will
(
$this
->
returnValue
(
array
(
'test2'
)));
$this
->
preloader
->
onRequest
(
$event
);
}
}
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment