I try to load categories in the admin area with store specific values.
$this->categoryCollectionFactory->create()
->setStoreId($storeId)
->addAttributeToSelect(['name', 'url_key']);
This works so far, but I don't get the right base URL with $category->getUrl()
if the given store has a different base URL than the admin store.
I checked the Category::getUrl()
method and found out that the store ID is used to determine the correct URL rewrite, but when it's found,
$this->setData('url', $this->getUrlInstance()->getDirectUrl($rewrite->getRequestPath()));
The second argument $params
of getDirectUrl()
should receive the scope somehow but it doesn't. So when the base URL is determined, there is no informtion about the current store.
This looks like a bug to me, but is there any way to work around this?
bugfix attempt 1
I traced down the location where the base URL is determined and it's in
Magento\Framework\Url::getRouteUrl
:
return $this->getBaseUrl() . $routeParams['_direct'];
getBaseUrl()
takes a $params
argument where the store id can be passed with the _scope
key, but here the argument is empty, so adding '_scope' => $storeId
to the $params
argument in the line from the category model above, would not help either.
bugfix attempt 2
I tried to pass the parameters along to getBaseUrl()
in the line above:
return $this->getBaseUrl($routeParams) . $routeParams['_direct'];
And now getBaseUrl() sets its scope to the given store id:
$this->setScope($params['_scope']);
This loads the correct store model and assigns it to $this->data['scope']
But to my surprise, a few lines later in $this->_getScope()->getBaseUrl()
, the scope was the admin store again. Why? _getScope()
does not access $this->data['scope']
but $this->_scope
... this is special behavior of Magento\Backend\Model\Url
because there is only one scope for backend URLs.
So here's the next problem: the category should not have been associated with a Backend URL model in the first place. But this apparently happens for any object that receives a \Magento\Framework\UrlInterface
as constructor argument when created in admin area context.
bugfix attempt 3
I tried Magento\Framework\App\State::emulateAreaCode()
around the category loading, but still the backend model is used. Found a similar question, Magento 2 get frontend store url for a path in admin, but the recommended solution is to inject a concrete frontend URL instance via preferences or constructor parameter. I don't know if this is a good idea for the core category model.
I tried it anyways, with the following in my module's etc/adminhtml/di.xml
:
<type name="Magento\Catalog\Model\Category">
<arguments>
<argument name="url" xsi:type="object">Magento\Framework\Url</argument>
</arguments>
</type>
And together with the two core hacks bugfixes from before, I'm getting the correct URLs.
So far, so good. Now I need a real solution :)
$rewrite = $this->urlFinder->findOneByData([ UrlRewrite::ENTITY_ID => $this->getId(), UrlRewrite::ENTITY_TYPE => CategoryUrlRewriteGenerator::ENTITY_TYPE, UrlRewrite::STORE_ID => $this->getStoreId(), ]); if ($rewrite) { $this->setData('url', $this->getUrlInstance()->getDirectUrl($rewrite->getRequestPath())); Profiler::stop('REWRITE: ' . __METHOD__); return $this->getData('url'); }
Store Wise URL. RIght? So