Cómo funciona la reescritura de URLs en Magento
===============================================

La reescritura de URLs de Magento es una característica del core. Vamos a ver cómo funciona en detalle.
-------------------------------------------------------------------------------------------------------

 

 

    By Way2 Ecommerce  

 

   29 Oct 2015  · Actualizado: 06 Mar 2020 La *reescritura de URLs de [Magento](https://way2ecommerce.com/magento-ecommerce)* es una característica del core. Principalmente se utiliza para generar URLs amigables para buscadores (SEO friendly) en productos, categorías, páginas CMS, etc. La reescritura es posible además a través del archivo etc/config.xml cuando creamos un módulo de Magento. Vamos a ver cómo funciona en detalle.

La tabla core\_url\_rewrite
---------------------------

Magento guarda las reescrituras de URLs en la tabla «core\_url\_rewrite»:

- url\_rewrite\_id: Clave primaria
- store\_id: Id de la tienda para la que se hace la reescritura
- id\_path: Identificador único interno. Normalmente es "product" + id de producto, o "category" + id de categoría, etc.
- request\_path: La url solicitada en el navegador
- target\_path: La url que debería abrirse si la url solicitada corresponde con la anterior
- is\_system: Si la reescritura de url ha sido generada por Magento o ha sido creada de forma manual
- options: Diferentes opciones de redirección, como "RP" (redirección permanente 301), "R" (redirección temporal 302), o null/empty (nulo/vacío)
- description: Para agregar cualquier descripción
- category\_id: El id de la categoría si la redirección es para una categoría
- product\_id: El id del producto si la redirección es para un producto

Implementación de reescrituras de URLs
--------------------------------------

Vamos a ver en detalle cómo Magento implementa estas reescrituras cuando abrimos una URL en el navegador. Cuando se abre una URL de Magento en el navegador, el control principal pasa a

```
Mage_Core_Controller_Varien_Front::dispatch()
```

 donde está este código:

```php
$this->_getRequestRewriteController()->rewrite();
```

Esto básicamente crea un objeto de la clase 'core/url\_rewrite\_request' y llama a la función rewrite que hace lo siguiente:

```php
public function rewrite()
{
     
    if (!$this->_request->isStraight()) {
        $this->_rewriteDb();
    }
    $this->_rewriteConfig();
    return true;
}
```

Como podemos ver aquí, se llama a las funciones `_rewriteDB()` y `_rewriteConfig()`, las cuales controlan las reescrituras de base de datos o de config.xml respectivamente.

### Reescrituras de URLs por base de datos

Vamos a ver la reescritura de base de datos primero:

```php
protected function _rewriteDb()
{
    if (null === $this->_rewrite->getStoreId() || false === $this->_rewrite->getStoreId()) {
        $this->_rewrite->setStoreId($this->_app->getStore()->getId());
    }
 
    $requestCases = $this->_getRequestCases();
    $this->_rewrite->loadByRequestPath($requestCases);
 
    $fromStore = $this->_request->getQuery('___from_store');
    if (!$this->_rewrite->getId() && $fromStore) {
        $stores = $this->_app->getStores(false, true);
        if (!empty($stores[$fromStore])) {
            /** @var $store Mage_Core_Model_Store */
            $store = $stores[$fromStore];
            $fromStoreId = $store->getId();
        } else {
            return false;
        }
 
        $this->_rewrite->setStoreId($fromStoreId)->loadByRequestPath($requestCases);
        if (!$this->_rewrite->getId()) {
            return false;
        }
 
        // Load rewrite by id_path
        $currentStore = $this->_app->getStore();
        $this->_rewrite->setStoreId($currentStore->getId())->loadByIdPath($this->_rewrite->getIdPath());
 
        $this->_setStoreCodeCookie($currentStore->getCode());
 
        $targetUrl = $currentStore->getBaseUrl() . $this->_rewrite->getRequestPath();
        $this->_sendRedirectHeaders($targetUrl, true);
    }
 
    if (!$this->_rewrite->getId()) {
        return false;
    }
 
    $this->_request->setAlias(Mage_Core_Model_Url_Rewrite::REWRITE_REQUEST_PATH_ALIAS,
        $this->_rewrite->getRequestPath());
    $this->_processRedirectOptions();
 
    return true;
}
```

La primera cosa que se hace aquí es crear los "Casos de solicitud" ($requestCases). Los casos de solicitud son básicamente un array que contiene las diferentes combinaciones de la solicitud por orden de prioridad. Por ejemplo, si la URL es "http://tumagento.com/pagina.html?param=1" estos serían los casos de solicitud generados:

1. pagina.html?param=1
2. pagina.html/?param=1
3. pagina.html
4. pagina.html/

De igual forma, si la URL es "http://tumagento.com/pagina.html/?param=1" estos serían los casos generados:

1. pagina.html/?param=1
2. pagina.html?param=1
3. pagina.html/
4. pagina.html

Véase la diferencia en el orden de la barra "/" basada en la URL solicitada. Aquí está el código que utiliza Magento para esto:

```php
protected function _getRequestCases()
{
    $pathInfo = $this->_request->getPathInfo();
    $requestPath = trim($pathInfo, '/');
    $origSlash = (substr($pathInfo, -1) == '/') ? '/' : '';
    // If there were final slash - add nothing to less priority paths. And vice versa.
    $altSlash = $origSlash ? '' : '/';
 
    $requestCases = array();
    // Query params in request, matching "path + query" has more priority
    $queryString = $this->_getQueryString();
    if ($queryString) {
        $requestCases[] = $requestPath . $origSlash . '?' . $queryString;
        $requestCases[] = $requestPath . $altSlash . '?' . $queryString;
    }
    $requestCases[] = $requestPath . $origSlash;
    $requestCases[] = $requestPath . $altSlash;
    return $requestCases;
}
```

Con cada caso de solicitud generado, Magento consulta la base de datos para ver si hay coincidencias con la función:

```php
$this->_rewrite->loadByRequestPath($requestCases);
```

Esta función está definida en el recurso modelo "Mage\_Core\_Model\_Resource\_Url\_Rewrite".

```php
public function loadByRequestPath(Mage_Core_Model_Url_Rewrite $object, $path)
{
    if (!is_array($path)) {
        $path = array($path);
    }
 
    $pathBind = array();
    foreach ($path as $key => $url) {
        $pathBind['path' . $key] = $url;
    }
    // Form select
    $adapter = $this->_getReadAdapter();
    $select  = $adapter->select()
        ->from($this->getMainTable())
        ->where('request_path IN (:' . implode(', :', array_flip($pathBind)) . ')')
        ->where('store_id IN(?)', array(Mage_Core_Model_App::ADMIN_STORE_ID, (int)$object->getStoreId()));
    $items = $adapter->fetchAll($select, $pathBind);
 
    // Go through all found records and choose one with lowest penalty - earlier path in array, concrete store
    $mapPenalty = array_flip(array_values($path)); // we got mapping array(path => index), lower index - better
    $currentPenalty = null;
    $foundItem = null;
    foreach ($items as $item) {
        if (!array_key_exists($item['request_path'], $mapPenalty)) {
            continue;
        }
        $penalty = $mapPenalty[$item['request_path']] << 1 + ($item['store_id'] ? 0 : 1);
        if (!$foundItem || $currentPenalty > $penalty) {
            $foundItem = $item;
            $currentPenalty = $penalty;
            if (!$currentPenalty) {
                break; // Found best matching item with zero penalty, no reason to continue
            }
        }
    }
 
    // Set data and finish loading
    if ($foundItem) {
        $object->setData($foundItem);
    }
 
    // Finish
    $this->unserializeFields($object);
    $this->_afterLoad($object);
 
    return $this;
}
```

Lo que este código hace es crear una query SQL como esta:

```sql
SELECT `core_url_rewrite`.* FROM `core_url_rewrite` WHERE (request_path IN (:path0, :path1)) AND (store_id IN(0, 1))
```

y lo devuelve en un array como este:

```php
Array
(
    [path0] =>
    [path1] =>
)

```

Lo siguiente que hace el código es calcular la penalización mínima usando operadores bit a bit, pero lo que hace en resumen es devolver la preferencia de rutas en un array y de una tienda concreta. Así que una vez que tengamos la ruta final, Magento hace la redirección final utilizando:

```php
$this->_processRedirectOptions();
```

Entre medias hay también otro código que se explica por sí solo. Así que esta es la forma de funcionar de las redirecciones de base de datos. Una cosa a tener en cuenta es cuándo Magento crea estas reescrituras de URL, esto ocurre en "Mage\_Catalog\_Model\_Indexer\_Url" usando la función "reindexAll()".

### Reescrituras de URLs por config.xml

Vamos a ver ahora cómo funcionan las redirecciones de fichero etc/config.xml, aquí está el código para las reescrituras basadas en config.xml

```php
protected function _rewriteConfig()
{
    $config = $this->_config->getNode('global/rewrite');
    if (!$config) {
        return false;
    }
    foreach ($config->children() as $rewrite) {
        $from = (string)$rewrite->from;
        $to = (string)$rewrite->to;
        if (empty($from) || empty($to)) {
            continue;
        }
        $from = $this->_processRewriteUrl($from);
        $to   = $this->_processRewriteUrl($to);
 
        $pathInfo = preg_replace($from, $to, $this->_request->getPathInfo());
        if (isset($rewrite->complete)) {
            $this->_request->setPathInfo($pathInfo);
        } else {
            $this->_request->rewritePathInfo($pathInfo);
        }
    }
    return true;
}
protected function _processRewriteUrl($url)
{
    $startPos = strpos($url, '{');
    if ($startPos !== false) {
        $endPos = strpos($url, '}');
        $routeName = substr($url, $startPos + 1, $endPos - $startPos - 1);
        $router = $this->_getRouterByRoute($routeName);
        if ($router) {
            $frontName = $router->getFrontNameByRoute($routeName);
            $url = str_replace('{' . $routeName . '}', $frontName, $url);
        }
    }
    return $url;
}
```

Este sería el xml que escribiríamos en el archivo etc/config.xml de nuestro módulo de Magento:

```xml
<global>
    <rewrite>
        <designer_url>
            <from><![CDATA[#^/author/id/#]]></from>
            <to><![CDATA[/designer/index/index/id/]]></to>
            <complete>1</complete>
        </designer_url>
    </rewrite>
</global>
```

Como podemos ver este código explica bastante por sí solo cómo funciona. Una cosa a tener en cuenta es que Magento usa "preg\_replace" para buscar urls coincidentes, así que podemos usar tambíen como ruta expresiones regulares sin ningún problema.

   Way2 EcommerceExpertos en Magento ecommerce. Definimos, desarrollamos, mantenemos y potenciamos tiendas online de la más alta calidad para que tu ecommerce supere todas tus expectativas.

 

 

 

 Otras entradas
