Skip to content

$product->getUrlInStore() does not allow to override the scope in backend context #21876

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 8, 2019

Conversation

Nazar65
Copy link
Member

@Nazar65 Nazar65 commented Mar 21, 2019

Description (*)

Magento always uses the 'admin' scope (and the default base url) to retrieve product url and returns incorrect url.

By calling the method $product->getUrlInStore(['_scope' => 2, '_nosid' => true]); I'm always retreiving the product's url with default scope in it. (for example http://default.website/catalog/product/view/...)

Fixed Issues (if relevant)

  1. getProductUrl does not allow to override the scope in backend context #4247 getProductUrl does not allow to override the scope in backend context

Manual testing scenarios (*)

On backend edit page try to get product url from second website

  1. Create 2 websites
  2. Set difference web urls for websites
  3. get product Url on adminhtml in product Edit $product->getUrlInStore(['_scope' => 2, '_nosid' => true]);
  4. You must have url like http://second.website/product.html

Same steps on frontend.

Contribution checklist (*)

  • Pull request has a meaningful description of its purpose
  • All commits are accompanied by meaningful commit messages
  • All new or changed code is covered with unit/integration tests (if applicable)
  • All automated tests passed successfully (all builds on Travis CI are green)

@magento-engcom-team
Copy link
Contributor

Hi @Nazar65. Thank you for your contribution
Here is some useful tips how you can test your changes using Magento test environment.
Add the comment under your pull request to deploy test or vanilla Magento instance:

  • @magento-engcom-team give me test instance - deploy test instance based on PR changes
  • @magento-engcom-team give me 2.3-develop instance - deploy vanilla Magento instance

For more details, please, review the Magento Contributor Assistant documentation

@Nazar65 Nazar65 changed the title getProductUrl does not allow to override the scope in backend context $product->getUrlInStore() does not allow to override the scope in backend context Mar 21, 2019
Copy link
Contributor

@orlangur orlangur left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ghost ghost assigned orlangur Mar 21, 2019
@Nazar65
Copy link
Member Author

Nazar65 commented Mar 21, 2019

@orlangur thanks, i'm will refactor it.

@Nazar65
Copy link
Member Author

Nazar65 commented Mar 22, 2019

@orlangur wait a little bit i'm still working on this .

@Nazar65
Copy link
Member Author

Nazar65 commented Mar 24, 2019

Hi @orlangur all done ✔️ i'm rechecked and run some test, seems like works correctly from frontend and adminhtml

@Nazar65 Nazar65 requested a review from sivaschenko March 26, 2019 17:37
Copy link
Member

@sivaschenko sivaschenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the pull request @Nazar65 Please see my code review comments

Also, as for original issue - I believe it can be solved by setting current store to the store manager \Magento\Store\Model\StoreManager::setCurrentStore

* @param $scope string
* @return \Magento\Store\Model\Store
*/
protected function getScope($scope = null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding protected functions is backward incompatible and we cannot accept that to a patch release.

Also, the proposed implementation is against CQRS principle (getter should not change the state of the system)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sivaschenko,

Adding protected functions is backward incompatible

What? That's something new to me. Is it documented and then announced somewhere?

@ghost ghost assigned sivaschenko Mar 27, 2019
@Nazar65
Copy link
Member Author

Nazar65 commented Mar 27, 2019

Hi @sivaschenko thanks for reviewing, but setCurrentStore dosent slove the issue. As when we call url in admin magento always use hardcoded admin scope.

@Nazar65
Copy link
Member Author

Nazar65 commented Mar 28, 2019

@sivaschenko with \Magento\Store\Model\StoreManager::setCurrentStore and my solution
DeepinScreenshot_select-area_20190328123152
DeepinScreenshot_select-area_20190328123210

@Nazar65
Copy link
Member Author

Nazar65 commented Mar 28, 2019

@sivaschenko i'm found a better solution, give me some time to test it, thanks 👍

$result = $this->_getScope()->getBaseUrl($this->_getType(), $this->_isSecure());
$customScope
? $result = $this->getData('scope')->getBaseUrl($this->_getType(), $this->_isSecure())
: $result = $this->_getScope()->getBaseUrl($this->_getType(), $this->_isSecure());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Who taught you this?)

Please use ternary operator normally, $result = $customScope ? ... : ...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still not fixed.

@Nazar65
Copy link
Member Author

Nazar65 commented Mar 28, 2019

My observation, to get correct product url in adminhtml we need create the url object for the product ->

        $catalogUrl= $this->_objectManager->get(\Magento\Catalog\Model\ResourceModel\Url::class);
        $products = $catalogUrl->getRewriteByProductStore([$product->getId() => 2]);
        $urlData = new \Magento\Framework\DataObject($products[$product->getId()]);
        $prod->setUrlDataObject($urlData);

So after that we have correct url without admin in url. like this -> http://default.website/product1.html
But we not able to get second website url even if we set setCurrentStoreId becouse magento always use hardcoded admin scope.

@Nazar65
Copy link
Member Author

Nazar65 commented Apr 2, 2019

Hi @orlangur Hi @sivaschenko can you review new changes ?

Copy link
Contributor

@orlangur orlangur left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Nazar65 please check my comments. After we finalize the implementation, some tests would really not hurt here.

Please use original ticket #4247 on commit message and make it meaningful.

@@ -63,6 +63,7 @@ public function beforeSetRouteParams(
) {
if (isset($data['_scope'])) {
$subject->setScope($data['_scope']);
$subject->setRouteParam('_scope',$data['_scope']);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be a space here according to PSR-2 but I'm not sure if Travis is not broken currently.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

$result = $this->_getScope()->getBaseUrl($this->_getType(), $this->_isSecure());
$customScope
? $result = $this->getData('scope')->getBaseUrl($this->_getType(), $this->_isSecure())
: $result = $this->_getScope()->getBaseUrl($this->_getType(), $this->_isSecure());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still not fixed.

@@ -478,8 +483,8 @@ public function getBaseUrl($params = [])
) {
$this->getRouteParamsResolver()->setType(UrlInterface::URL_TYPE_DIRECT_LINK);
}

$result = $this->_getScope()->getBaseUrl($this->_getType(), $this->_isSecure());
$result = $customScope ? $this->getData('scope')->getBaseUrl($this->_getType(), $this->_isSecure())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please explain the logic. Why we cannot always use $this->getData('scope')?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@orlangur Because this metod have usages in many methods and not always sets "_scope" to the dataObjects, and method _getScope() checks if _scope not exist in dataObject then they give us a hardcoded admin "_scope", so basically I added a 1 more scenario where we can get _scope from data object, and this scenario will not break some tests, i think

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think _getScope can be used instead of getData('scope') - it's basically the same plus handling the case when scope is not set.

@Nazar65
Copy link
Member Author

Nazar65 commented Apr 5, 2019

@orlangur I'm tested this a couple day and this works perfect with my custom module, so i'm will write some tests, and will finish on this.

@Nazar65
Copy link
Member Author

Nazar65 commented Apr 8, 2019

HI @orlangur @sivaschenko i'm finished the implementation and cover with intergation tests, seems like works fine.

@Nazar65 Nazar65 requested a review from sivaschenko April 16, 2019 19:12
Copy link
Member

@sivaschenko sivaschenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Nazar65 thanks for updates, please see my review notes

@@ -478,8 +483,8 @@ public function getBaseUrl($params = [])
) {
$this->getRouteParamsResolver()->setType(UrlInterface::URL_TYPE_DIRECT_LINK);
}

$result = $this->_getScope()->getBaseUrl($this->_getType(), $this->_isSecure());
$result = $customScope ? $this->getData('scope')->getBaseUrl($this->_getType(), $this->_isSecure())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think _getScope can be used instead of getData('scope') - it's basically the same plus handling the case when scope is not set.

@Nazar65
Copy link
Member Author

Nazar65 commented Apr 19, 2019

Hi @sivaschenko thank you for you review notes that's what i needed. now finally bug in this method Framework/Url -> protected function _getScope() in some cases php use parent method in Model/Url protected function _getScope() ignore self method wich have scope in dataObject.

seems like use $tthis->_getScope() or self::_getScope() fails integrations tests, that why i use if statment, and get scope on DataObject $this->getData('_scope');

Copy link
Member

@sivaschenko sivaschenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Nazar65 thanks for the update, see my code review comments

*
* @param array $params
*/
private function setRequestScope($params) :void
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private function setRequestScope($params) :void
private function setRequestScope(array $params): void

@@ -478,8 +488,7 @@ public function getBaseUrl($params = [])
) {
$this->getRouteParamsResolver()->setType(UrlInterface::URL_TYPE_DIRECT_LINK);
}

$result = $this->_getScope()->getBaseUrl($this->_getType(), $this->_isSecure());
$result = self::_getScope()->getBaseUrl($this->_getType(), $this->_isSecure());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this change from $this->_getScope() to self::_getScope() is needed. Can you please provide more details on why do you think it is necessary

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sivaschenko with self:: we get _getScope() in Framework/Url
deepin-screen-recorder_Select area_20190425145526
2 - with $this we get _getScope() in Model/Url wich returns admin scope.
deepin-screen-recorder_Select area_20190425145648

Copy link
Member Author

@Nazar65 Nazar65 Apr 25, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But this is not good idea to use one method, when we need to call product url in admin scope, i thinks would be better to use previous solution with $this->getData('_scope') and if statement, because some tests will fails with this

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this solution will be better and not break any functional
$result = $this->setRequestScope($params)->getBaseUrl($this->_getType(), $this->_isSecure());

    private function setRequestScope($params)
    {
        $customScope = $this->getRouteParamsResolver()->getRouteParam('_scope');

        if (isset($params['_scope']) ) {
            $this->setScope($params['_scope']);
        } else if ($customScope) {
            $this->setScope($customScope);
            return $this->getData('scope');
        }
        return $this->_getScope();
    }

Copy link
Member

@sivaschenko sivaschenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Nazar65 , please see my propositions the in review comments.

{
$customScope = $this->getRouteParamsResolver()->getRouteParam('_scope');

if (isset($params['_scope'])) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about introducing a setScope method to \Magento\Backend\Model\Url to be able to assign custom scope to _scope property? (it's better to also keep parent method call for backward compatibility purposes)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sivaschenko yes, that's what i needed, now it looks fine, i'm little bit confused by overriding method _getScope()

@Nazar65
Copy link
Member Author

Nazar65 commented Apr 28, 2019

@sivaschenko done ✔️

@magento-engcom-team
Copy link
Contributor

Hi @sivaschenko, thank you for the review.
ENGCOM-4843 has been created to process this Pull Request

@soleksii
Copy link

soleksii commented May 3, 2019

✔️ QA Passed

Copy link
Member

@sivaschenko sivaschenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Nazar65 can you please recheck the integration tests, they are falling with the following results

Magento\Catalog\Model\Product\UrlTest::testGetUrlInStoreWithSecondStore
Magento\Framework\Exception\NoSuchEntityException: The product that was requested doesn't exist. Verify the product and try again.

/var/www/html/app/code/Magento/Catalog/Model/ProductRepository.php:276
/var/www/html/dev/tests/integration/tmp/sandbox--3c335e531fab12e41ef1a983a92177df/generated/code/Magento/Catalog/Model/ProductRepository/Interceptor.php:24
/var/www/html/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/UrlTest.php:68

Copy link
Member

@sivaschenko sivaschenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Nazar65 thanks for the update!

Please correct the test to utilize the data provider functionality

@Nazar65
Copy link
Member Author

Nazar65 commented May 15, 2019

@sivaschenko done ✔️

@magento-engcom-team
Copy link
Contributor

Hi @sivaschenko, thank you for the review.
ENGCOM-4843 has been created to process this Pull Request

@m2-assistant
Copy link

m2-assistant bot commented Jun 8, 2019

Hi @Nazar65, thank you for your contribution!
Please, complete Contribution Survey, it will take less than a minute.
Your feedback will help us to improve contribution process.

magento-engcom-team pushed a commit to okorshenko/magento2 that referenced this pull request Jun 8, 2019
@magento-engcom-team magento-engcom-team added this to the Release: 2.3.3 milestone Jun 8, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants