laravel-datatables icon indicating copy to clipboard operation
laravel-datatables copied to clipboard

laravel relation where clause not apply in laravel datatable sorting

Open aliworkshop opened this issue 7 years ago • 14 comments

Summary of problem or feature request

hi Sorry I'm not good at English; search is working with related models but when i want to sort as related field it's duplicated because translate where clause not applying in sorting

any solution ?

Code snippet of problem

System details

  • linux
  • php version : 7.1
  • Laravel Version : 5.4
  • Laravel-Datatables Version : 7.0

aliworkshop avatar Aug 15 '17 06:08 aliworkshop

 class OrderFood extends Model
{
    protected $table = 'order_foods';
    protected $fillable = [
        'order_id','food_id','quantity','food_price'
    ];

    public function order()
    {
        return $this->belongsTo(Order::class);
    }

    public function food()
    {
        return $this->belongsTo(Food::class)
            ->with('translate');
    }
}


class Food extends Model
{
    protected $table = 'foods';
    protected $fillable = [
        'title','date','tax','tax_enabled','tax_included','images','star','description','recipes','status','lang'
    ];

    public function translate()
    {
        return $this->hasOne(FoodTranslator::class,'food_id')
            ->where('lang',app()->getLocale());
    }
}

    public function query()
    {
        DB::statement(DB::raw('set @rownum=0'));
        DB::statement(DB::raw('SET collation_connection = utf8_general_ci'));
        DB::statement(DB::raw('SET character_set_connection = utf8'));
        $order_id = $this->order_id;
        $order_foods = Order::find($order_id)->foods()->with(['food.translate'])->select(['*','order_foods.created_at',DB::raw('@rownum  := @rownum  + 1 AS rownum')]);
        return $this->applyScopes($order_foods);
    }


    private function getColumns()
    {
        return [
            ['title' => trans('form.row'),'name' => 'rownum', 'data' => 'rownum','searchable'=>false],
            ['title' => trans('order.order_food'),'name' => 'food.translate.title', 'data' => 'food.translate.title','orderable'=>false],
            ['title' => trans('order.quantity'),'name' => 'quantity', 'data' => 'quantity'],
            ['title' => trans('order.food_price'),'name' => 'food_price', 'data' => 'food_price'],
            ['title' => trans('form.created_at'),'name' => 'created_at', 'data' => 'created_at'],
            ['title' => trans('form.action'),'name' => 'action', 'data' => 'action','searchable'=>false,'exportable'=>false,'printable'=>false],
        ];
    }

aliworkshop avatar Aug 15 '17 06:08 aliworkshop

This issue was already fixed on v8. It's compatible to Laravel 5.4 but have breaking changes. Note though that it's not yet tagged as stable. Thanks!

yajra avatar Aug 17 '17 01:08 yajra

thanks alot!

aliworkshop avatar Aug 19 '17 06:08 aliworkshop

this issue does not fixed in v8

i should do change in my codes ?

aliworkshop avatar Sep 12 '17 08:09 aliworkshop

The issue here is on translate relation right?

public function translate()
{
    return $this->hasOne(FoodTranslator::class,'food_id')
        ->where('lang',app()->getLocale());
}

The issue is the where clause not applied? This was addressed on v8 unless there is another issue I'm not seeing?

yajra avatar Sep 12 '17 08:09 yajra

on ordering 3 queries is executed

1:
bindings:[]
query:"select count(*) as aggregate from (select '1' as `row_count` from `foods`) count_row_table"
time:0.29
2:
bindings:[]
query:"select *, @rownum  := @rownum  + 1 AS rownum from `foods` left join `food_translator` on `food_translator`.`food_id` = `foods`.`id` order by `food_translator`.`title` desc limit 10 offset 0"
time:0.81
3:
bindings:["fa", 1]
query:"select * from `food_translator` where `lang` = ? and `food_translator`.`food_id` in (?)"
time:0.46

maybe where clause must be in second query not 3

aliworkshop avatar Sep 12 '17 08:09 aliworkshop

this query

query:"select * from `food_translator` where `lang` = ? and `food_translator`.`food_id` in (?)"

is for

public function translate()
{
    return $this->hasOne(FoodTranslator::class,'food_id')
        ->where('lang',app()->getLocale());
}

that not effect on results

aliworkshop avatar Sep 12 '17 13:09 aliworkshop

@aliworkshop I think I'm seeing the issue now. Will dig this further when I got the chance.

yajra avatar Sep 13 '17 01:09 yajra

thanks alot master :)

aliworkshop avatar Sep 13 '17 04:09 aliworkshop

error is not fixed?

aliworkshop avatar Sep 20 '17 06:09 aliworkshop

I fixed issue with edit EloquentDataTable class in namespace Yajra\DataTables


protected function joinEagerLoadedColumn($relation, $relationColumn)
    {
        $table     = '';
        $lastQuery = $this->query;
        foreach (explode('.', $relation) as $eachRelation) {
            $model = $lastQuery->getRelation($eachRelation);
            $fillable = [];
            switch (true) {
                case $model instanceof BelongsToMany:
                    $pivot   = $model->getTable();
                    $pivotPK = $model->getExistenceCompareKey();
                    $pivotFK = $model->getQualifiedParentKeyName();
                    $this->performJoin($pivot, $pivotPK, $pivotFK);

                    $related = $model->getRelated();
                    $table   = $related->getTable();
                    $tablePK = $related->getForeignKey();
                    $foreign = $pivot . '.' . $tablePK;
                    $other   = $related->getQualifiedKeyName();

                    $lastQuery->addSelect($table . '.' . $relationColumn);
                    $this->performJoin($table, $foreign, $other);

                    break;

                case $model instanceof HasOneOrMany:
                    $table   = $model->getRelated()->getTable();
                    $foreign = $model->getQualifiedForeignKeyName();
                    $other   = $model->getQualifiedParentKeyName();
                    $fillable = $model->getRelated()->getFillable();
                    break;

                case $model instanceof BelongsTo:
                    $table   = $model->getRelated()->getTable();
                    $foreign = $model->getQualifiedForeignKey();
                    $other   = $model->getQualifiedOwnerKeyName();
                    break;

                default:
                    throw new Exception('Relation ' . get_class($model) . ' is not yet supported.');
            }
            $this->performJoin($table, $foreign, $other,'inner',$fillable);
            $lastQuery = $model->getQuery();
        }

        return $table . '.' . $relationColumn;
    }

protected function performJoin($table, $foreign, $other, $type = 'left', $fillable = [])
    {
        $joins = [];
        foreach ((array) $this->getBaseQueryBuilder()->joins as $key => $join) {
            $joins[] = $join->table;
        }

        if (! in_array($table, $joins)) {
            $this->getBaseQueryBuilder()->join($table, $foreign, '=', $other, $type);
            if (in_array('lang',$fillable))
                $this->query->where('lang', app()->getLocale());
        }
    }

this change manually apply lang column where clause to join query

aliworkshop avatar Oct 07 '17 08:10 aliworkshop

if a way would have existed that relation where clauses would applied as this
it would very good

aliworkshop avatar Oct 07 '17 08:10 aliworkshop

@aliworkshop thanks, but the proposed solution will only work on your case. Can you try maybe using the code below?

Replace:

$this->getBaseQueryBuilder()->join($table, $foreign, '=', $other, $type);

With:

$this->query->join($table, $foreign, '=', $other, $type);

A bit busy on work projects and can't dedicate much time on oss lately. Thanks!

yajra avatar Oct 08 '17 01:10 yajra

hi again master i am find another solve for this problem if we edit EloquentDataTable class in namespace Yajra\DataTables as this , It probably works

protected function joinEagerLoadedColumn($relation, $relationColumn)
    {
        $table     = '';
        $lastQuery = $this->query;
        foreach (explode('.', $relation) as $eachRelation) {
            $model = $lastQuery->getRelation($eachRelation);
            switch (true) {
                case $model instanceof BelongsToMany:
                    $pivot   = $model->getTable();
                    $pivotPK = $model->getExistenceCompareKey();
                    $pivotFK = $model->getQualifiedParentKeyName();
                    $this->performJoin($pivot, $pivotPK, $pivotFK);

                    $related = $model->getRelated();
                    $table   = $related->getTable();
                    $tablePK = $related->getForeignKey();
                    $foreign = $pivot . '.' . $tablePK;
                    $other   = $related->getQualifiedKeyName();

                    $lastQuery->addSelect($table . '.' . $relationColumn);
                    $this->performJoin($table, $foreign, $other);

                    break;

                case $model instanceof HasOneOrMany:
                    $table   = $model->getRelated()->getTable();
                    $foreign = $model->getQualifiedForeignKeyName();
                    $other   = $model->getQualifiedParentKeyName();
                    break;

                case $model instanceof BelongsTo:
                    $table   = $model->getRelated()->getTable();
                    $foreign = $model->getQualifiedForeignKey();
                    $other   = $model->getQualifiedOwnerKeyName();
                    break;

                default:
                    throw new Exception('Relation ' . get_class($model) . ' is not yet supported.');
            }
            $wheres = $model->getQuery()->getQuery()->wheres;
            $this->performJoin($table, $foreign, $other,'inner',$wheres);
            $lastQuery = $model->getQuery();
        }

        return $table . '.' . $relationColumn;
    }


    protected function performJoin($table, $foreign, $other, $type = 'left', $wheres = [])
    {
        $joins = [];
        foreach ((array) $this->getBaseQueryBuilder()->joins as $key => $join) {
            $joins[] = $join->table;
        }

        if (! in_array($table, $joins)) {
            $this->query->join($table, $foreign, '=', $other, $type);
            foreach ($wheres as $where) {
                if ($where['boolean'] == 'and')
                    $this->query->where($where['column'], $where['operator'], $where['value']);
                else
                    $this->query->orWhere($where['column'], $where['operator'], $where['value']);
            }
        }
    }

please apply this code in next version

aliworkshop avatar Jun 04 '18 13:06 aliworkshop