Search code examples
laravelhashlaravel-5laravel-5.3

L5 Using Hashid in middleware returns null


I am using hashid to hash the id parameters in url. I have it set up in my model to automatically hash the id. This is working fine. My problem is decoding the hash in a middleware returns null. I'm not sure if this is a problem with my middleware or because of the hashing.

Model:

public function getIdAttribute($value)
    {
        $hashids = new \Hashids\Hashids(env('APP_KEY'),10);
        return $hashids->encode($value);
    }

Middleware:

<?php

namespace App\Http\Middleware;

use Closure;

class HashIdsDecode
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        dd($request->id); //Returns null on show method - example localhost:8000/invoices/afewRfshTl
        if($request->has('id'))
        {
            $hashids = new \Hashids\Hashids(env('APP_KEY'),10);
            dd($hashids->decode($request->input('id')));
        }

        return $next($request);
    }
}

Route:

Route::resource('invoices','InvoiceController');

Controller:

public function show($id)
    {
        $invoice = Invoice::find($id);
        return view('invoices.show', [
            'invoice' => $invoice,
            'page_title' => ' Invoices',
            'page_description' => 'View Invoice',
        ]);
    }

NOTE: if I bypass the middleware and do it directly in my controller like this it works but it requires me to repeat myself over and over and probably not the best way to do it.

public function show($id)
    {
        $hashids = new \Hashids\Hashids(env('APP_KEY'),10);
        $invoiceId = $hashids->decode($id)[0];
        $invoice = Invoice::find($invoiceId);

        return view('invoices.show', [
            'invoice' => $invoice,
            'page_title' => ' Invoices',
            'page_description' => 'View Invoice',
        ]);
    }

Solution

  • Personally, I would be more inclined to write a model trait. You can then use the trait on only the models required, rather than assuming every ID argument in a request is a Hash ID.

    E.g.

    namespace App\Models\Traits;
    
    use Hashids\Hashids;
    use Illuminate\Database\Eloquent\Builder;
    
    trait HashedId
    {
        public function scopeHashId(Builder $query, $id)
        {
            $hashIds = new Hashids(env('APP_KEY'), 10);
            $id = $hashIds->decode($id)[0];
    
            return $query->where('id', $id);
        }
    }
    

    Then to use it, you'd use the trait on your Invoice model (edit):

    namespace App\Models;
    
    use Illuminate\Database\Eloquent\Model;
    
    class Invoice extends Model
    {
        use \App\Models\Traits\HashedId;
    
        // ...
    }
    

    And execute the following query in your controller:

    public function show($id)
    {
        $invoice = Invoice::hashId($id)->firstOrFail();
    
        return view('invoices.show', [
            'invoice' => $invoice,
            'page_title' => ' Invoices',
            'page_description' => 'View Invoice',
        ]);
    }