Problem/Motivation
Handling of query strings in URL aliases is broken - for example, it is possible to create an alias /?p=123
pointing to /node/123
, but although the alias exists and can be listed/edited/deleted, it will not have an effect. Conversely, it is possible to create an alias /withquery
pointing to /my/custom/module?needs=query
, but the expected path will not be invoked.
Eight years of comment history on this issue indicate that the former case is attempted often, typically to avoid breakage of existing URLs when migrating from a non-Drupal platform. The latter case seems less requested, which stands to reason given the rare use of query strings in Drupal.
Proposed resolution
For the former case (/?p=123 -> /node/123), questions arise because unlike paths, which always are matched by string comparison, query string parameters are unordered. If /path?product=123&sort=asc
should alias /node/4
, shouldn't /path?sort=asc&product=123
? What about /path?product=123
?
The answers to such questions will be site-dependent and could require rules of arbitrary complexity. But, simple support that addresses most users' needs out of the box, as well as a service custom/contrib could implement more advanced rulesets on seems reasonable in core.
In Drupal 8, this alias processing is the purview of the installed InboundPathProcessorInterface
implementations, especially Drupal\Core\PathProcessor\PathProcessorAlias
. Some attempts at quick fixes in PathProcessorAlias
show that simple / seemingly obvious logic like concatenating the query string to the path before sending it to the alias manager do not work reliably, because the path received by PathProcessorAlias
has been subjected to arbitrary futzing-with by the other InboundPathProcessorInterface
implementations. For example, PathProcessorFront
rewrites the path of any URI whose path component is '/' to the site's configured frontpage, which can be anything. PathProcessorAlias
receives this rewritten value that bears no relation to the path '/', so your alias /?p=123
will not work. Contrib is able to add additional unknown futzing before PathProcessorAlias
as well. Unfortunately, the task of reconstructing a path suitable for alias lookups based solely on the Symfony Request is nontrivial, and involves selectively applying only certain InboundPathProcessors to the Request path. See the discussion in
#99 β
for details.
The patch of #99 hardcodes the set of path processors on a site with no contrib modules that yield reliable alias-style matching into PathProcessorAlias
, which works, but has some disadvantages:
- Modules seeking to implement their own
InboundPathProcessorInterface
implementations that support alias-style matching, such as to support more complex query parameter matching rulesets, and likely also modules that want to redirect rather than rewrite the path, should be able to use the same logic to build the match string.
- The set of PathProcessors that need to be applied for aliases to match may depend on the modules in use, for example it already depends on whether or not Language is. If PathProcessors used are hardcoded in
PathProcessorAlias
, contrib modules can't contribute to the process.
Therefore, the proposed solution is to create a list of InboundPathProcessors that are appropriate for building a path suitable for alias-style matching, a means for non-core to supplement this list, a manager class that applies this list in a stacked fashion, and a means for users besides PathProcessorAlias
to call on that manager's services. This is implemented in very little code by extending PathProcessorManager
and creating a new service_collector tag which only those path processors providing useful futzing for reliable alias-style matching can be registered with.
"Resolving" the latter case (/withquery -> /my/custom/module?needs=query) might be best done by just disallowing the creation of such things for now -- even if you arrange for the expected route/controller to run, it would be necessary to provide the custom code expecting the query string with the query parameters in all places it might look for them.
Remaining tasks
New tests
User interface changes
May want to add validation to the alias creation form to prevent the latter case, TBD.
API changes
Provides a new service, alias_processor_manager
. Some modules (redirect?) may benefit from using it.
Original report by geilhufe
I am trying to create a path alias for "civicrm/contribute/transact?reset=1&id=4"
I have tried escaping the characters without result. It seems like the alias works all the way up the the "?" then basically doesn't pass any of the arguments past the "?".