Yet another customer project. But fear not, it's already implemented and works quite nicely. Well, there are a still a few problems with the Neos backend interface, but those should not be too hard to resolve I guess. And I had to write an aspect to feed additional preset data to the ContentDimensionPresetRepository-thingy. Might be more fun without it.
Status quo
Using domains instead of / in addition to configured path segments is currently not possible out of the box.
Why I consider this un-Neos-y
It would be more awesome if it was.
Proposition: Implementation alternatives
Currently, the implementation resides in the project's site package. It could either go to the Neos core (since the CR does not know what a domain is) or to a separate package. That's actually the main question here, I think.
A few facts to provide a better overview:
It almost completely relies on domains. There are a few configuration options necessary (e.g. the dimension name and the default domain), but it’s much less than for path segement based dimensions
The host pattern is used as the identifier
No subdomain support as yet, so there can only be one dimension based on domains (I’m not sure whether subdomain support would make much sense, anyway)
The implementation uses a custom ContentDimensionPresetSourceand provides full compatibility with path segment based dimensions (mandator1.tld/en/wat.html can be resolved to mandator: mandator1.tld, language: en)
Different environments are supported via domain mapping mechanisms. Development and preview servers usually use different domains than the live system does. To make content imported from the live environment work, the domains / dimension values can be mapped either via regex pattern or simple key-value replacement
There are a few issues that I could need help with, though:
Currently to switch dimension values, you have to re-login at another domain. It seems like the dimension context parameters in the backend URLs are not evaluated when the content context is initialized, are they?
The backend dimension selector does not work and would have to be adjusted.
The “preview in target workspace” button does not link to a valid node URL because the dimension values contain dots and the URL is pruned at the wrong one.
Good idea and I wanted to make different preset sources possible anyway.
I have a bit of an uneasy feeling about domain records but I cannot put my finger down yet, have to think about more cases I guess.
I would love to talk you through some of my plans/ideas for CR as you came up with at least two things now about which I was thinking for some time already (in different directions and more abstract).
Definitely a common use case, so makes sense to me.
Seeing as you wouldn’t be able to do this without breaking the UI, that’s a good indicator it has to be part of the core.
One thing is that it must be implemented in a generic way without certain scenarios not working. Quite some effort went into ensuring the path segment stuff is always predictable/deterministic.
Do you have an example of how the configuration could look like?
Sounds like a generic bug?
Adjusted how exactly? It’s probably best to keep the backend as is and only use the domain as the default content dimension selection, which would be overridden when changed in the backend.
We too have had similar requirements. For now, we worked around using rewrites. However a more robust solution closer to the core would be very much appreciated.
this will most probably come up again in one of my next projects. And if so, it may very well make its way into 3.3 LTS. I can’t promise anything yet, though.
The implemented concept and prototype add three (non-breaking) changes:
Configuration
Additional configuration options are available:
Neos:
ContentRepository:
contentDimensions:
market:
detectionMode: topLevelDomain
presets:
DE:
values:
- DE
detectionValue: de
UK:
values:
- UK
- US
detectionValue: co.uk
possible detection modes are uriPathSegment (default), topLevelDomain , domainName and subdomain. Thus de.seller-a.ch/18-49/ can be resolved to language German, seller A, market Switzerland and targetGroup 18 to 49 years.
Detection
A new routing interface is introduced: the ContentDimensionPresetDetectorInterface. It is used in the FrontendRoutePartHandler and is passed the current request’s URI, the request path and some configuration and extracts the dimension values from that information.
I’m not 100% happy with this since it exceeds the capabilities of a regular route part handler since it requires more information than given, but it works nontheless since it is still capable of adjusting the route path as expected.
Linking
Again, the route part handler has to be adjusted to only respect dimensions that use detection mode uriPathSegment.
The most significant change is to the LinkingService, though. It needs to be able to detect whether a new base URI is required and if so, what it must look like. Note that links might have to be created absolutely although the parameter is set to false.
Questions
@core team: Do you agree with the course of action in general? Note that this is a prototype and implementation details are still subject to change
Also, did I miss any points in the code base that have to be adjusted?
Answer 1:
Detectors can use arbitrary additional information from any source they want. By default they get the path segment and Uri, but they can inject additional services like LunarPhaseDetectorInterface any way they wish. It’s just nothing the default implementation does. We might have to adjust caching parameters though.
Edit: I’m also planning to add support for the @language=…;market=… Neos backend parameters that are currently ignored
Answer 2:
I’ve thought about that too. This would move the detection strategy from protected methods to public classes, which has the advantage that they are independent components and the disadvantage that they are independent components . There are use cases where dimensions may depend on each other (e.g. the default language depends on the market) and so would the components. Domain detection also might benefit from TLD and Subdomain being extracted first to make domain name detection easier.
If we can manage to do this with separate components I’m absolutely in favor of that solution, I just haven’t tried yet
Agreed. I think the most suitable solution would be to allow the detection components to return null if nothing was found and let the detector deal with that result. That keeps the components independent and the detector powerful enough to deal with business logic. It’s far simpler to implement if ($detectedMarket === 'DE' && !$detectedLanguage) $detectedLanguage = 'de';
in PHP than configuring complex parameters in YAML.
Plus projects with multiple dimensions should provide the resources for putting a little brainpower into writing their own detectors. Should most probably provide better results anyway.
Having the dimension presets decide about which component to use is fine with me too as it may provide for a good API in the future, where presets will most probably be objects and not arrays. Whether we explicitly declare a class or a mode is a convenience thing I think. Maybe we should just support both, being able to explicitly declare a component class while having sensible short codes for the default ones. And in more extreme cases detectors may inject components directly if needed and completely ignore the configuration anyway.