Skip to content

Commit d675fc0

Browse files
committed
Cleanup and readme.
1 parent 42bc390 commit d675fc0

File tree

5 files changed

+111
-24
lines changed

5 files changed

+111
-24
lines changed

README.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Contextual aliases
2+
3+
This module allows to split Drupal's url aliases into different contexts that allow to use the *same* alias for
4+
*different* source paths. The most prominent use case is a setup with different domains, where the same path is
5+
supposed to point to a different node, depending on the current domain.
6+
7+
```
8+
http://a.com/imprint -> /node/1
9+
http://b.com/imprint -> /node/2
10+
http://c.com/imprint -> /node/3
11+
```
12+
13+
**Disclaimer:** This is an advanced module that requires coding and knowledge about cache configuration.
14+
15+
## How it works
16+
17+
When saving a new alias, the module will search for services tagged with `alias_context_resolver` that implement the
18+
`AliasContextResolverInterface`, and use them to determine the context for a given path.
19+
20+
```
21+
/node/1 -> a
22+
/node/2 -> b
23+
/node/3 -> NULL
24+
```
25+
26+
It will store this path and use it for resolving paths from aliases and vice versa. There it will consult the context
27+
resolver again to obtain the *current* context (e.g. the domain the request was sent to). If the result matches the
28+
stored context for this alias, it will behave normal. If not, aliases will prefixed (both ways) with their context.
29+
Non-contextual aliases remain functional, but will be picked with a lower priority.
30+
31+
### Current context: none
32+
```
33+
/node/1 -> /a/imprint
34+
/node/2 -> /b/imprint
35+
/node/3 -> /imprint
36+
37+
/imprint -> /node/3
38+
```
39+
40+
### Current context: 'a'
41+
```
42+
/node/1 -> /imprint
43+
/node/2 -> /b/imprint
44+
/node/3 -> /imprint
45+
46+
/imprint -> /node/1
47+
```
48+
49+
### Current context: 'b'
50+
```
51+
current context: b
52+
53+
/node/1 -> /a/imprint
54+
/node/2 -> /imprint
55+
/node/3 -> /imprint
56+
57+
/imprint -> /node/2
58+
```
59+
60+
## How to use it
61+
The module requires you to implement your own context resolvers and add them as services tagged with
62+
`alias_context_resolver` and (more importantly) configure caching in a way it also respects the same contextual
63+
information. Otherwise you will have a very bad time watching your page cache serving random pages.
64+

contextual_aliases.install

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
use Drupal\Core\Database\Database;
44

5+
/**
6+
* Implements hook_install().
7+
*
8+
* Alter the existing schema for `url_alias`. Add a `context` column and
9+
* indices that include it.
10+
*/
511
function contextual_aliases_install() {
612
$spec = array(
713
'description' => "The context for this alias..",
@@ -27,6 +33,11 @@ function contextual_aliases_install() {
2733
], $table);
2834
}
2935

36+
/**
37+
* Implements hook_uninstall().
38+
*
39+
* Revert changes from `contextual_aliases_install`.
40+
*/
3041
function contextual_aliases_uninstall() {
3142
$schema = Database::getConnection()->schema();
3243

contextual_aliases.module

Lines changed: 0 additions & 5 deletions
This file was deleted.

src/ContextualAliasStorage.php

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,6 @@
1313
*/
1414
class ContextualAliasStorage extends AliasStorage {
1515

16-
protected $currentContexts = NULL;
17-
18-
protected $cachedContexts = [];
19-
2016
/**
2117
* The list of alias context resolvers.
2218
*
@@ -32,18 +28,18 @@ public function addContextResolver(AliasContextResolverInterface $resolver) {
3228
}
3329

3430
/**
35-
* Retrieve the list of current contexts.
31+
* Retrieve the current context.
3632
*
37-
* @return array
38-
* The list of current contexts.
33+
* @return string
34+
* The identifier for the current context.
3935
*/
4036
protected function getCurrentContext() {
41-
if (is_null($this->currentContexts)) {
42-
$this->currentContexts = array_filter(array_map(function (AliasContextResolverInterface $resolver) {
43-
return $resolver->getCurrentContext();
44-
}, $this->contextResolvers));
37+
foreach ($this->contextResolvers as $resolver) {
38+
if ($context = $resolver->getCurrentContext()) {
39+
return $context;
40+
}
4541
}
46-
return $this->currentContexts ? $this->currentContexts[0] : NULL;
42+
return NULL;
4743
}
4844

4945
/**
@@ -53,15 +49,12 @@ protected function getCurrentContext() {
5349
* The list of source contexts.
5450
*/
5551
protected function getSourceContext($source) {
56-
if (!array_key_exists($source, $this->cachedContexts)) {
57-
$this->cachedContexts[$source] = NULL;
58-
foreach ($this->contextResolvers as $resolver) {
59-
if ($context = $resolver->resolveContext($source)) {
60-
$this->cachedContexts[$source] = $context;
61-
}
52+
foreach ($this->contextResolvers as $resolver) {
53+
if ($context = $resolver->resolveContext($source)) {
54+
return $context;
6255
}
6356
}
64-
return $this->cachedContexts[$source];
57+
return NULL;
6558
}
6659

6760
protected function _contextCondition($select, $context, $prefix = FALSE) {

tests/src/Kernel/ContextualAliasesTest.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,50 +111,74 @@ public function testServiceInjection() {
111111
$this->assertInstanceOf(ContextualAliasStorage::class, $storage);
112112
}
113113

114+
/**
115+
* Test for vanilla aliases without contexts.
116+
*/
114117
public function testNoContextSimpleAlias() {
115118
$this->resolver->getCurrentContext()->willReturn(NULL);
116119
$this->assertEquals('/c', $this->manager->getPathByAlias('/C'));
117120
$this->assertEquals('/C', $this->manager->getAliasByPath('/c'));
118121
}
119122

123+
/**
124+
* Test contextual aliases outside of global context.
125+
*/
120126
public function testNoContextContextualAlias() {
121127
$this->resolver->getCurrentContext()->willReturn(NULL);
122128
$this->assertEquals('/A', $this->manager->getPathByAlias('/A'));
123129
$this->assertEquals('/a', $this->manager->getPathByAlias('/one/A'));
124130
$this->assertEquals('/one/A', $this->manager->getAliasByPath('/a'));
125131
}
126132

133+
/**
134+
* Test contextual aliases within a matching global context.
135+
*/
127136
public function testContextMatchingAlias() {
128137
$this->resolver->getCurrentContext()->willReturn('one');
129138
$this->assertEquals('/a', $this->manager->getPathByAlias('/A'));
130139
$this->assertEquals('/a', $this->manager->getPathByAlias('/one/A'));
131140
$this->assertEquals('/A', $this->manager->getAliasByPath('/a'));
132141
}
133142

143+
/**
144+
* Test contextual aliases within a different global context.
145+
*/
134146
public function testContextNotMatchingAlias() {
135147
$this->resolver->getCurrentContext()->willReturn('two');
136148
$this->assertEquals('/A', $this->manager->getPathByAlias('/A'));
137149
$this->assertEquals('/a', $this->manager->getPathByAlias('/one/A'));
138150
$this->assertEquals('/one/A', $this->manager->getAliasByPath('/a'));
139151
}
140152

153+
/**
154+
* Test aliases that contain another context's prefix.
155+
*/
141156
public function testNonContextualConflictingAlias() {
142157
$this->resolver->getCurrentContext()->willReturn(NULL);
143158
$this->assertEquals('/d', $this->manager->getPathByAlias('/one/D'));
144159
$this->assertEquals('/one/D', $this->manager->getAliasByPath('/d'));
160+
145161
$this->resolver->getCurrentContext()->willReturn('one');
146162
$this->assertEquals('/d', $this->manager->getPathByAlias('/one/D'));
147163
$this->assertEquals('/one/D', $this->manager->getAliasByPath('/d'));
148164
}
149165

166+
/**
167+
* Test contextual aliases that contain another context's prefix.
168+
*/
150169
public function testContextualConflictingAlias() {
151170
$this->resolver->getCurrentContext()->willReturn(NULL);
152171
$this->assertEquals('/e', $this->manager->getPathByAlias('/two/one/E'));
153172
$this->assertEquals('/two/one/E', $this->manager->getAliasByPath('/e'));
173+
154174
$this->resolver->getCurrentContext()->willReturn('one');
155175
$this->assertEquals('/two/E', $this->manager->getPathByAlias('/two/E'));
156176
$this->assertEquals('/e', $this->manager->getPathByAlias('/two/one/E'));
157177
$this->assertEquals('/two/one/E', $this->manager->getAliasByPath('/e'));
178+
179+
$this->resolver->getCurrentContext()->willReturn('two');
180+
$this->assertEquals('/e', $this->manager->getPathByAlias('/one/E'));
181+
$this->assertEquals('/one/E', $this->manager->getAliasByPath('/e'));
158182
}
159183

160184
}

0 commit comments

Comments
 (0)