frezno's avatar
Level 36

seo friendly routes

by using slugs i'm getting urls eg like this.

// the route looks like this:
Route::get('cat/{slug}', 'CatsController@getCategory');

// and the url looks like this:
localhost/cat/cars/mercedes-benz


// when calling an individual product, the route:
Route::get('product/{slug}', 'ProductsController@getProduct');

// and the url looks like this:
localhost/product/mercedes-benz/sl-convertable.html

i want to get rid of the /cat/ and /product/ in the url to have a 'nicer' one:

localhost/cars/mercedes-benz
localhost/mercedes-benz/sl-convertable.html

how can i achieve this?

0 likes
21 replies
eukaryoter's avatar

You need to make the first param as a wildcard then, like this:

Route::get('{slug}', 'ProductsController@getProduct');

If you want it to be like that then you can't call a specific Controller and function depending if it's a car or a category since you can't know that.

davidwickstrom's avatar

Another idea is to write a component that handles your rewrites, and basically translates your nice urls to a less nicer one.

A table with an id, rewrite, target_url and then instead of querying the db for each request just cache them forever and renew them only when something has changed.

I guess that would require some kind of catch all route that would check for a match in your rewrite table. Then return what is returned from the translated route. I'm not clever enough to to figure out how such route would be set up but maybe someone else is...?

Magento has this type of solution. I'm just not aware of its specifics on a more detailed level.

2 likes
frezno's avatar
Level 36

then you can't call a specific Controller and function

that's exactly what i came up with as well, @eukaryoter , @lstables
and thanks for the input @davidwickstrom

Sometimes it just helps talking about something to free up the mind.

How about a kind of lookup table containing the seo-url and referencing to the appropriate tabel/id of the desired target?
sounds like a reasonable possibility. I'll have a try...

1 like
theUnforgiven's avatar

Good luck and do let us know, always helps talking about it.

1 like
Kryptonit3's avatar

You could also do something like this (may not be proper syntax just pseudo) [ using optional route parameters ]

Route::get('{category}/{product?}', 'as' => 'display', 'uses' => 'MyController@display');
// MyController
public function display($category, $product)
{
    if ($product) {
        // product display
    }
    // else only category
}

Then for showing the route

// Category only
{{ route('display', $category->id) }}

// Product (assuming product model has a parent relationship with category)
{{ route('display', [$product->category->id, $product->id]) }}
jekinney's avatar

I understand your point, but technology your routes are SEO friendly. More so then using an id or random string.

mstnorris's avatar

@frezno I created a Model for the IDs of one of my apps. I wanted everything (most things) to have a unique alphanumeric ID. I was designing a simple music database. So I set up a model for the IDs and such models like Artist, Album and Track each had one UniqueID.

The unique_ids table acted as a sort of "look-up" table.

I suppose a better approach (or I should say different approach) would be to use a Polymorphic relation as the models that share such slugs in your case could be of the sluggable type.

desi's avatar

Try this:

routes.php

Route::get('{slug}','CatsController@getCategory');
Route::get('{_slug}','ProductsController@getProduct');

BaseController : All Controllers should extends from this controller

class BaseController extends Controller{
    
    // Add this function to  BaseController
    public function dispatch($controller, $params)
    {
        $controller = explode('@', $controller);
        $method = array_pop($controller);
        return call_user_func_array([\App::make($controller[0]),"{$method}"], $params);
    }
}

CatsController.php

class CatsController extends BaseController{
//...
public function getCategory($slug)
{
// Find category by Slug
$category = Category::whereSlug($slug)->first();

// If category doesn't exists, then dispatch request to ProductController@getProduct
   if(! $category)
   {
        return $this->dispatch('\ProductController@getProduct',$slug);
   }
}
//...
}

ProductsController.php

class ProductController extends BaseController{
//...
public function getProduct($slug)
{
// Find product by Slug
$product = Product::whereSlug($slug)->first();

// If product doesn't exists, show 404 page
   if(! $product)
   {
        App::abort(404);
   }
}
//...
}

I hope this will helps

frezno's avatar
Level 36

thnaks to all of you for thinking about it

@Kryptonit3 , @desi i have to dig deeper into your examples, thanks for sharing

@jekinney i know that the url is already seo friendly. just trying to get it friendier/nicer.
maybe that's the 'bad' influence of JWay who always wants to make his methods nicer ;)

@mstnorris Polymorphic relations was something i wanted to think about.

The solution right now as follows:
slug-table with fields, slug, according table (categories and products as of now), id of the cat or product.
the called method (from the routes file) queries the slug table to find the row with the slug.
now knowing whether the slug belongs to the categories or products table a query the appropriate table for the desired result.

it may not be the best solution and there's very likely room for improvement but it's working at least.
unfortunately very often such q'n'd solutions won't be touched again too soon.

mstnorris's avatar

@frezno that is what I would do, don't worry about early optimisation :) keep it simple.

pmall's avatar

I really don't think having categories and products in your url has any impact on your google ranking.

i suggest, you can do a catchall route for categories as cat is not really informative and your categories seems to start with the category name like cars. localhost/cars/mercedes-benz is a cool url.

I feel like localhost/product/mercedes-benz/sl-convertable.html is better and more informative than localhost/mercedes-benz/sl-convertable.html. What is the latter ? A review, a test, a blog post, a product ? Maybe you will have other kind of pages concerning a mercedes-benz sl.

I would go this way :

// Cartch the product with the informative prefix
Route::get('products/{slug}', 'ProductsController@getProduct');

// Catchall for the categories
Route::get('{slug}', 'CatsController@getCategory');
1 like
frezno's avatar
Level 36

cool idea, @pmall - never though about this possibility. Sounds like a pretty good idea esp. - you are right - what if there are several different occurences of an item. I really like this idea.

liamvictor's avatar

@pmall

I really don't think having categories and products in your url has any impact on your google ranking.

Longer URLs get lower Click Through Rates (CTR) than shorter in the Search Engine Result Pages (SERP). The organic CTR has been shown to correlate with the SERP positioning so that all else being equal (which it never is of course!) having a longer URL will impact on your ranking.

Consumers apparently generally distrust longer URLs more than shorter ones.

An interesting experiment would be to buy some Pay Per Click ads and use two URL forms (remembering that you can easily redirect the display URL to the actual landing page) and then you'll see how the URL length effects your particular client base.

pmall's avatar

Longer URLs get lower Click Through Rates (CTR) than shorter in the Search Engine Result Pages (SERP).

@liamvictor This doesn't apply to categories and products. Longer url are not trusted because they are messy, here those segments improve the descriptiveness of the url.

liamvictor's avatar

@pmall I agree they improve the readability and indeed will give the end user a good idea of what the final page. Sadly though experimentation has shown that longer URLs get a lower CTR and that impacts upon ranking.

Furthermore longer URLs can't be as easily communicated and are more likely to break in email or social media which has a tertiary impact on SEO. Despite it being easy for URLs to be shrunk for Twitter (etc) the average user is still somewhat weary of sharing longer URLs.

Steven Levy mentions inn his book In The Plex that Google use all sorts of user behavioral data in their ranking algorithm(s). There are hundreds of factors involved with any particular page/site ranking so it's just one more tiny factor but URL length is something to consider when developing sites that will depend upon organic ranking.

MikeHopley's avatar

I would suggest choosing your URL structure based on what's helpful to users, rather than trying to second-guess search engines.

All other things being equal, a short URL is better than a long one. But you shouldn't be afraid to use longer URLs just because of SEO FUD.

pmall's avatar

@liamvictor I dont say what you say is wrong I say it doesn't apply here. There is no difference between products/black-shoes and /black-shoes, the quality of the overall page will always win in this case.

frezno's avatar
Level 36

btw, @pmall

I feel like localhost/product/mercedes-benz/sl-convertable.html is better and more informative than localhost/mercedes-benz/sl-convertable.html. What is the latter ? A review, a test, a blog post, a product ? Maybe you will have other kind of pages concerning a mercedes-benz sl.

that was a great input back those days. i did it that way and it turned out that i ran into the situation having eg
/product/mercedes-benz/sl-convertable.html
and for for further details on that product, there's a blog:
/blog/mercedes-benz/sl-convertable.html

pmall's avatar

@frezno Yeah of course, I don't like the "slug only" url, the slug always describe something (unless your website have only one kind of entity).

Please or to participate in this conversation.