Skip to content

Can't use column searches in same table relations #2871

Closed
@affaron

Description

@affaron

Hi!

Recently I discovered an issue when using multiple column search with different relation using the same table. Here's what I mean.

I have a model with relations:

public function country_from()
    {
        return $this->belongsTo(Country::class, 'from_country_id');
    }

    public function country_to()
    {
        return $this->belongsTo(Country::class, 'to_country_id');
    }

and two columns in the datatable with these relation (country_from.name, country_to.name). I need to use filtering by both columns at the same time and I got a problem: when the second relation is applied the join is omitted since it's already joined and same table used in WHERE condition:

select
  `borders`.*
from
  `borders`
  left join `countries` on `borders`.`from_country_id` = `countries`.`id`
where
  LOWER(`countries`.`name`) LIKE '%south africa%'
  and LOWER(`countries`.`name`) LIKE '%zimbabwe%'

This happens because of this code:

if (! in_array($table, $joins)) {
$this->getBaseQueryBuilder()->join($table, $foreign, '=', $other, $type);
}

It makes since until you need to join the same table. I figured out a way to make it work like it supposed to in my case, but Im not sure it's PR worth hack and it's not thoroughly tested :) Here's what I did.
Generate an alias for the table if it's already joined and return it with performJoin method (sorry, not the cleanest code):

protected function performJoin($table, $foreign, $other, $type = 'left')
    {
        $alias = $table;
        $joins = [];

        foreach ((array) $this->getBaseQueryBuilder()->joins as $key => $join) {
            $joins[] = Str::before($join->table, ' as ');
        }

        if (in_array($table, $joins)) {
            $index = count(array_filter($joins, function ($n) use ($table) { return $n === $table; })) + 1;
            $alias = $table . '_' . $index;
            $other = str_replace($table, $alias, $other);
            $table = $table . ' as ' . $alias;
        }

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

        return $alias;
    }

and use this alias in the end of joinEagerLoadedColumn() method:

            $alias = $this->performJoin($table, $foreign, $other);
            $lastQuery = $model->getQuery();
        }

        return $alias.'.'.$relationColumn;

after that the lib can generate accurate query:

select
  `borders`.*
from
  `borders`
  left join `countries` on `borders`.`from_country_id` = `countries`.`id`
  left join `countries_2` on `borders`.`to_country_id` = `countries_2`.`id`
where
  LOWER(`countries`.`name`) LIKE '%south africa%'
  and LOWER(`countries_2`.`name`) LIKE '%zimbabwe%'

Maybe I'm missing something and there's a native or more simple way to do that without code changes? Please let me know, thanks

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions