Search code examples
phplaravelhashids

Laravel is there a way to dehash the IDs of data in URL before accessing the route?


I'm using the package hashids\hashids to hash the ID of data sent through the URL (e.g .../post/bsdfs/edit, the 'bsdfs' is the encoded value). I followed Stuart Wagner's accessor method to do so. Below is how I do it:

use Hashids\Hashids;

class Post extends Model {
    protected $appends = ['hashid'];

    public function getHashidAttribute() {
        $hashid = new Hashids(config('hash.keyword'));

        return $hashid->encode($this->attributes['id']);
    }
}

Before hashing the IDs I am getting post/2/edit. After the hashing process, I'm getting post/bsdfs/edit, which is fine to me.

The problem occurs when redirecting to the encoded route. This is how my route looks like:

use App\Http\Controllers\PostController;
    
Route::get('post/{post}/edit', 'PostController@edit')->name('post.edit');

After redirecting, I get a 404 error. This is what the controller accepts:

Use App\Models\Post;

class PostController extends Controller {
    //PS: I don't actually know what this method is called...
    public function edit(Post $post) {
        return view('post.edit')->with(compact('post'));
    }
}

I know that if I'm doing this method, Laravel is searching for an ID of 'bsdfs', which does not exist in the database. What it should do is decode the hash and get the ID. Is there a way to do it WITHOUT doing this:

public function edit($id) {
    $hashid = new Hashids(config('hash.keyword'));
    $id= $hashid->decode($id);

    $post = Post::find($id);

    return view('post.edit')->with(compact('post'));
}

As you can see, my goal here is to reduce the number of lines while still maintaining the data ID in the URL to be encoded. Any help would be appreciated. Thank you.


Solution

  • You could take advantage of Model Binding.

    So your model should have something like this:

    /**
     * Retrieve the model for a bound value.
     *
     * @param  mixed  $value
     * @param  string|null  $field
     * @return \Illuminate\Database\Eloquent\Model|null
     */
    public function resolveRouteBinding($value, $field = null)
    {
        $hashid = new Hashids(config('hash.keyword'));
    
        return $this->findOrFail($hashid->decode($value)[0]);
    }
    

    So you can then have your controller like nothing happend:

    public function edit(Post $post)
    {
        return view('post.edit')->with(compact('post'));
    }