i am returning to an application I wrote a few months ago and trying to make a few minor adjustments. I currently have a model Product and a catalogue page where all products are listed using an each loop for all entries in Product.
In order to attempt to keep the site secure I have put http://.../products/... behind a authentication login, so only users with a password can see /products/index, edit the product details and also see the products one at a time (show page).
I would now like to link from the page with all products listed to a page with just ONE product’s details (i.e. an individual product’s own page).
I am aware that I can find a product from the Product model using a find function for a Product ID, however, how do I go about automatically creating a sensible URL for this product’s listing?
In summary, if I had a listing of 10 products on a page, what is the best way of enabling a user to click on the product and be routed to a page called http://..../shop/specific_products_name? I have read around a bit and it appears to_param may be of some benefit, but I am struggling to work out a sensible means of creating these pages
Thanks for your time
Here’s a basic, conventional controller setup:
First, here’s the route shortcut syntax:
Here’s the controller that loads all the Products on the
indexand just one Product on theshow.That line in the routes file gives us these routes (among others):
/productsgoes toproducts_controller#index/products/:idgoes toproducts_controller#showwith params[:id] set to the given numberThe easiest way to get a pretty URL with the least amount of work is to, as you looked into, override
to_param.to_paramis what Rails uses to turn a model into a representation in a url/request.By default, it’s this:
So when Rails does a Product.find() on the result of that
to_param,find()turns it into an integer withto_i.If
params[:id]is"24", thenProduct.find(params[:id])converts “24” to the integer 24.But consider how
to_iworks with other examples:In other words, if our parameter representation of a Product is
"#{id}-the-product-name",Product.find(params[:id])will still work even though our products#show URLs look like/products/56-ford-taurus.One more thing:
Solution A
The easiest solution to your problem is to override to_param like this:
The benefit of this method is that you change one method in the Product model and the rest of your code Just Works.
The downside is that your URLs looks like:
Instead of:
Solution B
If you want the latter example, you can maintain a
slugcolumn in the Products table.Example:
Then you could override to_param like this:
Now, params[:id] would look like “ford-taurus”.
Product.find("ford-taurus")wouldn’t work anymore since find() wants an integer, so you could replaceProduct.find(params[:id])withProduct.find_by_slug(params[:id]).You’d just have to make sure every product has a unique slug.
And you can automate the slug creation process with something like:
That way, you just need to set the product’s name on creation, save it, and its slug will become a URL friendly “whatever-the-product-name-was”.
Now that Rails will generate the kind of URLs you want, you can generate links to these products in a view like this:
Would product this sort of html for each product: