L5 How to use trait that hashes id but keep pivot

2019-09-11 20:24发布

I added hashes to my ID's using a trait. However by doing that now I can no longer use attach() or relationships.

For example this relationship does not work in my view anymore:

@foreach ($invoice->items as $item)
   {{ $item->item }}
@endforeach

Here is the trait that hashes the id for me

<?php
namespace App\Traits;

use Hashids\Hashids;
use Illuminate\Database\Eloquent\Builder;

trait HashedId
{


    /**
     * Get the user's id as hashids.
     *
     * @param  $value
     * @return string
     */
    public function getIdAttribute($value)
    {
        $hashids = new \Hashids\Hashids(env('APP_KEY'),10);
        return $hashids->encode($value);
    }

    public function scopeHashId(Builder $query, $id)
    {
        $hashIds = new Hashids(env('APP_KEY'), 10);
        $id = $hashIds->decode($id)[0];

        return $query->where('id', $id);
    }
}

Invoice Model:

<?php

namespace App;

use App\Traits\HashedId;
use Illuminate\Database\Eloquent\Model;
use HipsterJazzbo\Landlord\BelongsToTenants;

class Invoice extends Model
{
    use BelongsToTenants;
    use HashedId;
    //
    protected $fillable = [
        'client_id',
        'invoice_number',
        'purchase_order',
        'invoice_note',
        'invoice_status',
        'invoice_total',
        'invoice_type',
        'sub_total',
        'balance_due',
        'due_date',
        'invoice_type',
        'user_id',
    ];

    protected $hidden = [
        'user_id'
    ];

    public function items()
    {
        return $this->belongsToMany('App\LineItem', 'invoice_items', 'invoice_id', 'item_id');
    }

    public function client()
    {
        return $this->belongsTo('App\Client');
    }

}

I have tried doing this from a controller but it feels more like a hack than the right way to do it and I still lose the ability to use things like $invoice->attach($lineItem) or $invoice->items

//Currently I have to unhash the ids in order to save them as a pivot
$hashIds = new \Hashids\Hashids(env('APP_KEY'), 10);
$invoiceId = $hashIds->decode($request->invoice_id)[0];
$lineItemId = $hashIds->decode($request->item_id)[0];

//Should have been able to use $invoice->attach($lineItemId)
DB::table('invoice_items')->insert(
  ['invoice_id' => $invoiceId, 'item_id' => $lineItemId]
);

How can I continue to use $invoice->attach($lineItem) or $invoice->items from controllers while still using the trait that hashes my ids?

2条回答
Evening l夕情丶
2楼-- · 2019-09-11 21:05

I've re-written the trait as follows (this assumes you're using PHP 5.6 or above):

<?php 

namespace App\Traits;

use Hashids\Hashids;
use Illuminate\Database\Eloquent\Builder;

trait HashedId
{
    /**
     * Get model ID attribute encoded to hash ID.
     *
     * @return string
     */
    public function getHashIdAttribute()
    {
        $hashIds = new Hashids(env('APP_KEY'), 10);
        return $hashIds->encode($this->getKey());
    }

    /**
     * Restrict query scope to find model by encoded hash ID.
     * 
     * @param  \Illuminate\Database\Eloquent\Builder  $query
     * @param  integer  $id
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeHashId(Builder $query, $id)
    {
        $hashIds = new Hashids(env('APP_KEY'), 10);
        $id = $hashIds->decode($id)[0];

        return $query->where('id', $id);
    }

    /**
     * Restrict query scope to find models by encoded hash IDs.
     * 
     * @param  \Illuminate\Database\Eloquent\Builder  $query
     * @param  array  $ids
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeHashIds(Builder $query, ...$ids)
    {
        $hashIds = new Hashids(env('APP_KEY'), 10);

        $ids = array_map(function ($id) use ($hashIds) {
            return $hashIds->decode($id)[0];
        }, $ids);

        return $query->whereIn('id', $ids);
    }
}

You may notice that I've renamed the accessor, getIdAttribute() to getHashIdAttribute(). You can therefore now get the hash ID of a model instance by calling $model->hash_id instead of $model->id.

This is where I think your problem was, because Laravel was expecting an integer key to be returned by $model->id, whereas it would have been getting the hash ID instead.

If after implementing the changes above you're still getting an error, can you show what the specific error is?

查看更多
甜甜的少女心
3楼-- · 2019-09-11 21:19

Just like you commented, you couldn't use attach because id is hashed cos of getIdAttribute. I would like to suggest you to use getOriginal().

For example,

$invoice->attach($lineItem->getOriginal()['id']);

I think that could be the only way to attach that.

查看更多
登录 后发表回答