5

I have 3 storeviews in multiple languages (English, Dutch and German). The all have the same categories but the english names must be translated. So i select a storeview in the categorie editor in the backend. But when I rename a category in let's say 'kitchen' to 'Küchen' in german (or to any other name) I get the following error:

"URL key for specified store already exists."

So and i can't change the name to anything whithout getting this error. I tried to manually add a random url key lets say 'foo1234' but still not working. so i changed the category name and url key to random text. but there is no way i get it changed

I have no idea why i keep getting this error.

2
  • Tried to re-index your database, also clear your Magento Cache? Commented Aug 29, 2016 at 15:34
  • Thanks for commenting, i've tried that but it doesn't make a difference
    – Rickert
    Commented Aug 30, 2016 at 6:08

4 Answers 4

4

I've fixed this issue. And I have 2 solutions for that.

The first one: Clean up your database in table url_rewrite (Change the url_key of all category). You can write UpgradeData script for this solution.

The second one: Remove the duplication data when saving category. This data is throw in method doReplace($urls) in \vendor\magento\module-url-rewrite\Model\Storage\DbStorage.php file.

protected function doReplace($urls)
    {
        foreach ($this->createFilterDataBasedOnUrls($urls) as $type => $urlData) {
            $urlData[UrlRewrite::ENTITY_TYPE] = $type;
            $this->deleteByData($urlData);
        }
        $data = [];
        foreach ($urls as $url) {
            $data[] = $url->toArray();
        }
        $this->insertMultiple($data);
    } 

After debugging, I found out $data variable has a duplicate record. If you want this method to work without any errors. Rewrite this method above to:

protected function doReplace($urls)
{
    foreach ($this->createFilterDataBasedOnUrls($urls) as $type => $urlData) {
        $urlData[UrlRewrite::ENTITY_TYPE] = $type;
        $this->deleteByData($urlData);
    }
    $data = [];
    $storeId_requestPaths = [];
    foreach ($urls as $url) {
        $storeId = $url->getStoreId();
        $requestPath = $url->getRequestPath();
        // Skip if is exist in the database
        $sql = "SELECT * FROM url_rewrite where store_id = $storeId and request_path = '$requestPath'";
        $exists = $this->connection->fetchOne($sql);

        if ($exists) continue;

        $storeId_requestPaths[] = $storeId . '-' . $requestPath;
        $data[] = $url->toArray();
    }

    // Remove duplication data;
    $n = count($storeId_requestPaths);
    for ($i = 0; $i < $n - 1; $i++) {
        for ($j = $i + 1; $j < $n; $j++) {
            if ($storeId_requestPaths[$i] == $storeId_requestPaths[$j]) {
                unset($data[$j]);
            }
        }
    }
    $this->insertMultiple($data);
}

If you want to get more details. Please, read my comment in

https://github.com/magento/magento2/issues/7298

Hope this will help you.

3
  • Showing error "Something went wrong while saving the category." Commented Aug 11, 2017 at 11:17
  • same error @pushpendra singh did you get the solution? Commented Mar 14, 2018 at 5:46
  • Sorry for the late response, in your case it might be some other bugs. You should debug to the save category function and see what is causing the problem.
    – Toan Tam
    Commented Mar 21, 2018 at 1:53
3

This is an acknowledge issue of Magento 2, it's fixed permanently in 2.2 but it wasn't backported to 2.1. So I will provide you a workaround for it and let see how does it go :)

app/code/Stackoverflow/UrlRewrite/etc/di.xml

<?xml version="1.0"?>
<!--
  ~ Copyright © 2017 Toan Nguyen. All rights reserved.
  ~ See COPYING.txt for license details.
  -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <preference for="Magento\UrlRewrite\Model\Storage\DbStorage" type="Stackoverflow\UrlRewrite\Model\Storage\DbStorage"/>
</config>

app/code/Stackoverflow/UrlRewrite/Model/Storage/DbStorage.php

<?php
/**
 * Copyright © 2017 Toan Nguyen. All rights reserved.
 * See COPYING.txt for license details.
 */

namespace Stackoverflow\UrlRewrite\Model\Storage;

use Magento\UrlRewrite\Service\V1\Data\UrlRewrite;
use Magento\UrlRewrite\Service\V1\Data\UrlRewriteFactory;

class DbStorage extends \Magento\UrlRewrite\Model\Storage\DbStorage
{
    /**
     * Save new url rewrites and remove old if exist. Template method
     *
     * @param \Magento\UrlRewrite\Service\V1\Data\UrlRewrite[] $urls
     *
     * @return void
     * @throws \Magento\Framework\Exception\AlreadyExistsException
     */
    protected function doReplace($urls)
    {
        foreach ($this->createFilterDataBasedOnUrls($urls) as $type => $urlData) {
            $urlData[UrlRewrite::ENTITY_TYPE] = $type;
            $this->deleteByData($urlData);
        }
        $data = [];
        foreach ($urls as $url) {
            $urlArray = $url->toArray();
            $urlPath = $urlArray['request_path'];
            $storeId = $urlArray['store_id'];
            $dataKey = $storeId . '..' . $urlPath;
            $data[$dataKey] = $urlArray;
        }

        $this->insertMultiple($data);
    }
}

I tried myself a lot of ways/workaround on GitHub and this one seems fixed the problem permanently. Gods know why...

Hope it helps.

1
  • It's not true. It still is a bug in 2.2.x
    – WaPoNe
    Commented Apr 5, 2019 at 10:39
0

As it is reported on github also, check these issues, hope the tricks suggested here, will help you out regarding this issue:-

#1471 #3268 #4487

0

I have fixed this issue in more than 3-4 projects.

If you want to resolve this issue, you can upgrade your version with latest one or try below code logic:

Try this solution by overriding below function in your custom module :)

Path: vendor/magento/module-url-rewrite/Model/Storage

protected function doReplace(array $urls){ 
    $this->deleteOldUrls($urls); 
    $data = [];
    $storeId_requestPaths = [];
    foreach ($urls as $url) {
        $storeId = $url->getStoreId();
        $requestPath = $url->getRequestPath();
        $sql = "SELECT * FROM url_rewrite where store_id = $storeId and request_path = '$requestPath'";
        $exists = $this->connection->fetchOne($sql); 
        if ($exists) continue;
        $storeId_requestPaths[] = $storeId . '-' . $requestPath;
        $data[] = $url->toArray();
    }

    // Remove duplication data;
    $n = count($storeId_requestPaths);
    for ($i = 0; $i < $n - 1; $i++) {
        for ($j = $i + 1; $j < $n; $j++) {
            if ($storeId_requestPaths[$i] == $storeId_requestPaths[$j]) {
                unset($data[$j]);
            }
        }
    }
    try {
        $this->insertMultiple($data);
    } catch (\Magento\Framework\Exception\AlreadyExistsException $e) {
        /** @var \Magento\UrlRewrite\Service\V1\Data\UrlRewrite[] $urlConflicted */
        $urlConflicted = [];
        foreach ($urls as $url) {
            $urlFound = $this->doFindOneByData(
                [
                    UrlRewriteData::REQUEST_PATH => $url->getRequestPath(),
                    UrlRewriteData::STORE_ID => $url->getStoreId(),
                ]
            );
            if (isset($urlFound[UrlRewriteData::URL_REWRITE_ID])) {
                $urlConflicted[$urlFound[UrlRewriteData::URL_REWRITE_ID]] = $url->toArray();
            }
        }
        if ($urlConflicted) {
            throw new \Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException(
                __('URL key for specified store already exists.'),
                $e,
                $e->getCode(),
                $urlConflicted
            );
        } else {
            throw $e->getPrevious() ?: $e;
        }
    }

    return $urls;
}

Happy Coding :)

Thanks,

Ketan T.

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.