3

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 :)

3
  • We are already getting $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
    – Jackson
    Commented Sep 27, 2016 at 9:18
  • No. That line only returns the store specific request path without the base URL. Commented Sep 27, 2016 at 9:22
  • Any solution on this? Commented Feb 11, 2019 at 15:37

1 Answer 1

0

<argument name="url" xsi:type="object">Magento\Framework\Url</argument>

As per SOLID, please use Interface, even Magento does it mostly.

Also, this seems to cover it. You can set the $url->setScope($scope) as well, and then no need to pass query data, $scope being store code

With this theres no need to extend core classes to edit them (NEVER edit them directly, except while debugging for convenience)

2
  • "please use Interface" - but the interface was the issue, because I did not need the implementation that Magento chooses by default (backend), but a different one (frontend). However, thanks for your link, I'll look into it. The question is three years old, so chances are that it works better now. Commented Oct 28, 2019 at 17:48
  • @FabianSchmengler yes, but specifying input argument class on the class you dont own, especially like that one so low in the app level, is really bad practice. In that case, either create your own class that implements that interface and make preference (which is a lot of work, but the cleanest solution), or inject Url class dependency in the class you own, so youre sure you wont break smth else or make unnecessary changes for specific situation. And no, it doesnt work better now, hence i came here, and its magento after all xD
    – CT.
    Commented Oct 29, 2019 at 16:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.