skip to Main Content

I need to redirect (301) urls like this:

https://website.com/products/view/id

to:

https://website.com/products/product-name-slug

I know how to make the slug version of urls work, but I do not want to disable old ones completely, instead I want them to 301-redirect to its slug version.
I don’t want them both working simultaneously as it is treated as duplicated content by Google SEO.

I also do not want to write redirect for every product in routes.php like this:

Router::redirect( '/product/view/1001', [ 'controller' => 'products', 'action' => 'view', 'product-1001-name-slug' ] );

Instead I would rather have dynamic lookup function to do it for me.

How can I do it in CakePHP 2.x?

2

Answers


  1. Chosen as BEST ANSWER

    This is an example implementation of @burzum's answer:

    in routes.php:

    Router::connect(
        '/product/view/:slug',
        array( 'controller' => 'base_products', 'action' => 'view' ),
        array( 'pass' => array( 'slug' ) )
    );
    Router::connect(
        '/base_products/view/:id',
        [ 'controller' => 'product', 'action' => 'view', 'model' => 'BaseProduct' ],
        [ 'routeClass' => 'IdToSlugRoute' ]
    );
    

    in app/Routing/Route/IdToSlugRoute.php:

    public function parse($url) {
            $params = parent::parse($url);
            if (!$params) {
                return false;
            }
    
            if (!$this->response) {
                $this->response = new CakeResponse();
            }
    
            App::import('Model', $params['model']);
            $Model = new $params['model']();
            $instance = $Model->find('first', array(
                'conditions' => array($params['model'] . '.id' => $params['id']),
                'recursive' => 0
            ));
    
            $slug = getSlug( $instance[$params['model']]['id'], $instance[$params['model']]['name'] );
            $redirect = ['controller' => $params['controller'], 'action' => $params['action'], $slug];
    
            $status = 301;
            $this->response->header(array('Location' => Router::url($redirect, true)));
            $this->response->statusCode($status);
            $this->response->send();
            $this->_stop();
        }
    

    in app/Controller/BaseProductsController.php:

    function view($slug)
        {
            $id = getIdFromSlug($slug);
    
            $product = $this->BaseProduct->find('first', array(
                'conditions' => array('BaseProduct.id' => $id),
                'recursive' => 2
            ));
    
            ...
        }
    

    additionality:

    function getIdFromSlug( $slug ) {
        $pieces = explode( "-", $slug );
    
        return $pieces[0];
    }
    
    function getSlug( $id, $name ) {
        return $id . '-' . Inflector::slug($name, '-');
    }
    

    • Create a custom route class to handle this case
    • Check if the slug exists in the new slug field
      • If yes great, nothing more to do
      • If no try to lookup the slug in the old slug field
        • If it exists redirect to the new slug and set HTTP status
        • If it does not exist return false
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search