Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

mikebarwick's avatar

Route::bind() to decode hashid

I'm using the Laravel library for Hashids, which I have it working as it should. However, the way I'm going about it is probably wrong.

Right now, I'd decoding the hashid as below. But I'm finding I'm repeating this step across multiple functions throughout controllers. This can't be the most efficient way:

public function edit($id)
{
    $id = Hashids::decode($id)[0];

    $task = Auth::user()->tasks()->findOrFail($id);

    ...
}

Now, I've heard and seen people using Route::bind() in similar situations. But I can't get it to work properly (i.e. decode hashid before id is passing t controller method).

Here's what I'm trying (in my routes.php file)...anything stand out that's wrong? Here's a portion of my code - with it's structure, etc. In short, I want the bind function to work for ALL routes that have an {id}:

Route::group(['middleware' => 'auth'], function()
{
    Route::get('tasks', ['as' => 'tasks.index', 'uses' => 'TaskController@index']);
    Route::get('tasks/create', ['as' => 'tasks.create', 'uses' => 'TaskController@create']);
    Route::post('tasks/store', ['as' => 'tasks.store', 'uses' => 'TaskController@store']);
    Route::get('tasks/{id}', ['as' => 'tasks.show', 'uses' => 'TaskController@show']);
    Route::get('tasks/{id}/edit', ['as' => 'tasks.edit', 'uses' => 'TaskController@edit']);
    Route::patch('tasks/{id}', ['as' => 'tasks.update', 'uses' => 'TaskController@update']);
    Route::delete('tasks/{id}/delete', ['as' => 'tasks.destroy', 'uses' => 'TaskController@destroy']);

    Route::bind('tasks/{id}', function($id, $route)
    {
        return \Hashids::decode($id)[0];
    });
});

BONUS QUESTION: I'm encoding my id's in the view themselves. Is there a better way? This also seems wrong.

In controller, I'm grabbing the tasks like so:

$tasks = Auth::user()->tasks()->orderBy('created_at', 'desc')->get();

And then I'm hashing the id in my view itself, for example like so. Would be good to have this already hashed when I grab the collection, etc.

<a href="{{ route('tasks.markascompleted', Hashids::encode($task->id)) }}">
0 likes
6 replies
mikebarwick's avatar

I was close on my Route::bind() function...agh. Hate when it all of the sudden just comes to me (yet love it lol). Instead of Route::bind('tasks/{id}', function.., I just needed to use id (which now makes complete sense), like so:

Route::bind('id', function($id, $route)
    {
        return \Hashids::decode($id)[0];
    });

Question still remains (i.e. my BONUS question), how can I grab a collection and have the IDs hashed? Versus doing it in my view foreach. Holla...

mikebarwick's avatar

No. In my models I do the following. So for every collection I'm grabbing I'm hashing the id, etc.

/**
     * Get the value of the model's route key.
     *
     * @return mixed
     */
    public function getRouteKey()
    {
        return Hashids::encode($this->getKey());
    }
xsmalbil@icloud.com's avatar

@mikebarwick Hi there, better late then never. You could use a Transformer class. which extends:

<?php namespace App\Transformers;

abstract class Transformer
{
    /**
     * [transformCollection description]
     * @param  array  $collection [description]
     * @return [type]             [description]
     */
    public function transformCollection(array $collection = [])
    {
        return array_map([$this, 'transform'], $collection);
    }
    /**
     * [transform description]
     * @param  [type] $item [description]
     * @return [type]       [description]
     */
    abstract public function transform($item);
    /**
     * [links description]
     * @param  [type] $item [description]
     * @return [type]       [description]
     */
    abstract protected function links($item);
}

like this:

<?php namespace App\Transformers;

use Hashids;

class HashidsTransformer extends Transformer
{
    /**
     * Transform the returned values as needed.
     *
     * @param $item
     *
     * @return array
     */
    public function transform($item)
    {
        $item['id'] = Hashids::encode($item['id'])
        return $item;
    }

Or you could try before and after middleware:


<?php namespace App\Http\Middleware;

use Closure;

class HashidsDecode
{
    //After Middleware
    public function handle($request, Closure $next)
    {
        if($request->has('id'))
        {
            Hashids::decode($request->input('id')); 
        }

        return $next($request);
    }
}

and

<?php namespace App\Http\Middleware;

use Closure;

class HashidsEncode
{
    //Before Middleware
    public function handle($request, Closure $next)
    {
        if($request->has('id'))
        {
            Hashids::encode($request->input('id')); 
        }

        return $next($request);
    }
}

or maybe use Model mutators:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * Get the user's id as hashids.
     *
     * @param  $value
     * @return string
     */
    public function getIdAttribute($value)
    {
        return Hashids::encode($value);
    }
}

// OR AS A TRIAT

<?php namespace App\Traits;

use Hashids;

trait HashidsEncode
{
    /**
     * Get the  id as hashids.
     *
     * @param  $value
     * @return string
     */
    public function getIdAttribute($value)
    {
        return Hashids::encode($value);
    }
}
// like this
class User extends Model
{
    use Traits\HashidsEncode;
}

Or use Events;

2 likes
Nospoon's avatar

I'm facing a similar situation but for me the Route::bind is not working at all. What I have is:

Route::bind('application', function ($id, $route) {
                return \Hashids::decode($id)[0];
            });

But it's still passing through hashed id. It makes absolutely no difference whether I disable or enable the binding block.

xsmalbil@icloud.com's avatar

@Nospoon

Right now you are binding {application} and decoding $id.

You should OR decode $application OR change the binding.


// create a route like this

Route::get('projects/{id}', 'ProjectsController@show');


// Put this in your provider
// It will check if it can decode, else it will just return what was put in the url
Route::bind('id', function ($id) {
    return (new Hashids())->decode($id)[0] ?? $id;
});


// Example:

// 1).  http://site.com/projects/3   

public function show($id)
{
   dd($id);   // will return 3
}

// 2). http://site.com/projects/j5 

public function show($id)
{
   dd($id);   // will return 3
}


@mikebarwick .. what do you think of this, as an addition of what you proposed above?

1 like

Please or to participate in this conversation.