Magento 2 : get rewrite product url

Here is the method I used to get the product url.

It is definitely not optimal as I have to load the entire product to get it so very bad in terms of performance.

First you need to inject a Magento\Catalog\Model\ProductRepository in your constructor:

use Magento\Catalog\Model\ProductRepository;
//...
public function __construct(
    ProductRepository $productRepository
) {
    $this->_productRepository = $productRepository;
}

Then you load the product based on the product id:

$product = $this->_productRepository->getById($productId);

Finally you can get the URL model to retrieve the rewritten URL:

return $product->getUrlModel()->getUrl($product);

This may not really answer the question but chances are that, if you are missing the URL rewrite, you may be getting your product out of a product collection. And adding URL rewrite info is not automatic as you can see in \Magento\Catalog\Model\ResourceModel\Product\Collection::$_addUrlRewrite.

The way I managed forcing the addition of URL rewrites is by creating a plugin on the create() method of \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory. And as soon as your code (or Magento's core code) uses this factory for instanciating a product collection (and it should as of best practices), this plugin forces the \Magento\Catalog\Model\ResourceModel\Product\Collection::$_addUrlRewrite to true.

Then, products URL rewrites are successfully added to the products without the need to loop on them and reload them. It thus fixes the perf downside that @Raphael spoke about.

Here is the plugin XML definition (in your di.xml file):

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Catalog\Model\ResourceModel\Product\CollectionFactory">
        <plugin name="your_plugin_unique_nane" type="Your\Plugin\Namespace\Plugin" />
    </type>
</config>

And the plugin code:

namespace Your\Plugin\Namespace;

use Magento\Catalog\Model\ResourceModel\Product\Collection;
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory as CoreCollectionFactory;

class Plugin
{
    /**
     * @param CoreCollectionFactory $subject
     * @param Collection $collection
     * @return Collection
     */
    public function afterCreate(CoreCollectionFactory $subject, Collection $collection)
    {
        $collection->addUrlRewrite();

        return $collection;
    }
}