CakePHP - How to do reverse routing with slug?

2020-03-23 03:01发布

问题:

I am using CakePHP 1.3. I have a Product model. on the DB table among others there are id and slug fields.

If I have a product that is id:37 and slug:My-Product-Title I want the URL for the product to be:

products/37/My-Product-Title

Instead of the standard:

products/view/37

I created a route that looks like this:

Router::connect(
    '/products/:id/:slug',
    array('controller' => 'products', 'action' => 'view'),
    array('pass' => array('id'), 'id' => '[0-9]+')
);

Now I can go to http://server/products/37/My-Product-Title and it takes me to the right place.

But How do I get reverse routing to automatically build the correct URL in $HtmlHelper->link?

When I use:

echo $html->link(
    'Product 37', 
    array('controller'=>'products', 'action' => 'view', 37)
);

It still outputs the standard products/view/37 url.

回答1:

I don't believe that it's possible to be done auto-magically. The helper is just an "helper" who builds the link from the given parameters.

So the easiest method is to add another parameter in your link like so:

echo $html->link(
    'Product 37', 
    array('controller'=>'products', 'action' => 'view', 37, $slug)
);

where the $slug is the data from the slug field.

Probably it could be done your idea, but you need to break the MVC pattern very badly :)

Edit:

Reading your question again I understood it well. See how should be done:

in your router.php add the following rule:

Router::connect(
    '/product/*',
    array('controller' => 'products', 'action' => 'view')
);

Please note that it's /product/* rather than /products/*

Your link should be done like this:

echo $html->link(
    'Product 37', 
    array('controller'=>'products', 'action' => 'view', 37, 'my-product-title')
);

and the link would look like:

http://yourdomain.com/product/37/my-product-title

For me doing your suggestion is bad practice. Also I don't think it's good from SEO point of view redirecting always the user.



回答2:

For routing:

Router::connect(
    '/products/:id/:slug',
    array('controller' => 'products', 'action' => 'view'),
    array('pass' => array('id'), 'id' => '[0-9]+')
);

Your links should look like this:

echo $html->link(
    'Product 37', 
    array('controller'=>'products', 'action' => 'view', 'id' => 37, 'slug' => 'my-product-title')
);

You have to add additional (key => value) to your array for each :param in your routing. Then magic will work



回答3:

You should look at the following post regarding custom route classes.

The slug data doesn't need to be involved with the database at all - the field is a fake field used to simplify logic and lookups. This solution allows you to reverse route slugs, without needing a slug field in the models table.

http://42pixels.com/blog/slugs-ugly-bugs-pretty-urls



回答4:

I am not sure how bad this is but with the following code in the ProductsController:

function view($id)
{
    if( isset($_SERVER) && stristr($_SERVER["REQUEST_URI"],'view/') )
    {
        $this->Product->id = $id;
        $slug = $this->Product->field('slug');
        $this->redirect($id.'/'.$slug);
    }
    $data = $this->Product->find('first', array('conditions' => array('Product.id' => $id)));
    $this->set("data", $data);
}

If the page is accesses via /view/id it automatically redirects them to the current page using /id/slug

Now I can just use the default link scheme:

echo $html->link(
    'Product 37', 
    array('controller'=>'products', 'action' => 'view', 37)
);

and they will be redirected to the right URL.

Only problem is I am not sure how bad it is to have a redirect happening every time a user visits a product page?