Search code examples
phplaravelyajra-datatable

The most efficient way to do eagerload in Laravel Policy for yajra-datatable use


I need to eager load relationship within laravel policy The problem is i use the laravel policy inside yajra-datatable which will load it (the policies) line by line

Here's the code looks like :

public function create(User $user)
{
    $user->load('sections');
    $sections = collect($user->sections->toArray())->pluck('name');
    
    return
        in_array($user->role_id,[3,4])
        && in_array('Produksi', $sections->toArray())
        && Session::get('productionPlans-branch') !== 'all'
        && Session::get('productionPlans-period') !== 'all';
}

and i use it in my yajra-datatable like so :

public function table(Request $request)
{
    $query = ProductionPlan::with(['branch','product.category','period'])->orderBy('created_at');

    return \Yajra\DataTables\DataTables::of($query)
        ->addIndexColumn()
        ->addColumn('action', function($row) {
            if ($request->user->can('create', $row)) {
                return '<a href="javascript:void(0)" onclick="show('. $row->id .')">Add</a>';
            }
        })
        ->rawColumns(['action'])
        ->make(true);
}

so every line will load the relation again and again

I'm expecting more efficient way to load them just once instead of load the relation line by line How can i achieve this ?

Update :

I tried to use accessor on User model to append the relations with sections table

protected $appends = ['sections'];

public function getSectionsAttribute ()
{
    return $this->attributes['sections'] = $this->sections()->first();
}

This was success for only the FIRST relation only, i tried to remove the first() method but got the error PDO serialization instead

Serialization of 'PDO' is not allowed

Solution

  • I think Laravel policies uses the exact object it is called on. So $this->user->can(...) actually is the same object that is being passed as first parameter to the create(User $user) policy method.

    In that case, I would try to load it before you call the ->can method inside the closure.

    The code could look like this:

    public function table(Request $request)
    {
        $query = ProductionPlan::with(['branch','product.category','period'])->orderBy('created_at');
        $user = $request->user;
        $user->load('sections');
    
        return \Yajra\DataTables\DataTables::of($query)
            ->addIndexColumn()
            ->addColumn('action', function($row) use ($user) {
                if ($user->can('create', $row)) {
                    return '<a href="javascript:void(0)" onclick="show('. $row->id .')">Add</a>';
                }
            })
            ->rawColumns(['action'])
            ->make(true);
    }
    

    And then you also have to remember to remove the $user->load('sections'); from inside the create() method in the policy. This is very important :-)