Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[5.4] whereHas on polymorphic relations not work #18523

Closed
lkmadushan opened this issue Mar 27, 2017 · 18 comments
Closed

[5.4] whereHas on polymorphic relations not work #18523

lkmadushan opened this issue Mar 27, 2017 · 18 comments
Labels

Comments

@lkmadushan
Copy link
Contributor

lkmadushan commented Mar 27, 2017

  • Laravel Version: 5.4
  • PHP Version: 7.1
  • Database Driver & Version: PostgreSQL 9

Description:

when try whereHas method on a polymorphic relation does not works as expected.

Statistics::whereHas('applicable', function ($query) {
        $query->where('employee.id', 1);
    })->toSql();
select * from "statistics" where exists (select * from "statistics" as "laravel_reserved_0" where "laravel_reserved_0"."id" = "laravel_reserved_0"."applicable_id" and "employee"."id" = ?)
@Dylan-DPC-zz
Copy link

@lkmadushan what's the output? any error? or does it give an empty response?

@themsaid
Copy link
Member

Please provide more details, doesn't work is not a bug report.

@lkmadushan
Copy link
Contributor Author

Sorry. I have updated the description. Currently whereHas on polymorphic relations doesn't add constraints to the relationship query. It always check existence in same table.

@themsaid
Copy link
Member

This won't be an easy fix afaik, we'll need to run several queries on several related tables in order for this work.

@paulofreitas
Copy link
Contributor

This is related to #5429. There are some workarounds discussed there, like this one: #5429 (comment)

It's a known limitation that won't probably be easy to fix. Workarounds indeed work but they involve some tricky engineering.

@ghost
Copy link

ghost commented Apr 1, 2017

Marked as No-fix when polimorph relations has not intersect collumns

@bunnypro
Copy link

bunnypro commented Apr 13, 2017

i'm currently using this code in my own project

my Model (\App\Production\Models\Production Model)

public function detail()
{
    return $this->morphTo('detail');
}

public function scopeDetailable($query, $callable = null)
{
    list($type, $key) = $this->getPolymorphicFields();

    $this->newQuery()->distinct()->get([$type])->keyBy($type)->map(function ($model)  use ($type) {
        return (new $model->{$type})->getTable();
    })->each(function ($table, $modelClass) use (&$query, $key, $callable) {
        $model = new $modelClass;

        $query = $query->orWhereExists(function ($query) use ($table, $model, $key, $callable) {
            $query->select('*')->from($table)->whereRaw("{$this->getTable()}.{$key} = {$table}.{$model->getKeyName()}")
                ->when($callable instanceof \Closure, $callable);
        });
    });

    return $query;
}

protected function getPolymorphicFields()
{
    $relation = $this->detail();

    return [$relation->getMorphType(), $relation->getForeignKey()];
}

my Controller

\App\Production\Models\Production::detailable(function ($query) {
    $query->whereDate('panen_future_date', '2017-02-08');
})->with('detail')->get()

and it works for my use case.

please note that all model related to \App\Production\Models\Production model have 'panen_future_date' filed in its table.

@Dylan-DPC-zz
Copy link

@bunnypro can you try it on a new project with just the STR (steps to reproduce), it will be better :)

@bunnypro
Copy link

i forgot to compare the _type filed.

ok i'll try to repoduce it with fresh laravel project and also comparing the _type filed.

@bunnypro
Copy link

bunnypro commented Apr 13, 2017

i have reproduced in a fresh laravel project. here is the https://github.com/bunnypro/querying-polymorphic

just migrate and use DummySeeder class for seeding.
i didn't write any unit testing, but i wrote a route in web route (web.php) for testing, just serve and access
/?search=your search

@Dylan-DPC-zz
Copy link

Do you get the same issue or not?

@bunnypro
Copy link

yes i do
that's just my temporary solution for this issue.

@vesteves
Copy link

vesteves commented May 1, 2017

Ppl,
I solve it with strange way but it's working... I'll try explain:

code:

$agendamentos = Agendamento::where(
            'administrador_id',
            Auth::user()->userable_id)->orderBy('id', 'desc')
        ->get();

and It's the code to return object to view:

return view('agendamentos.lista', [
            'agendamentos' => $agendamentos,
            'clientes' => $clientes
        ]);

The hint and the strange thing on the same is:

$clientes = Cliente::whereHas('credito',
    function ($query) {
        $query->where('administrador_id', Auth::user()->userable_id);
    })
->get();
foreach ($agendamentos as $agendamento) {
    foreach ($agendamento->cliente as $cliente) {
        $agendamento->cliente->users = $agendamento->cliente->users;
    }
}
return view('agendamentos.lista', [
    'agendamentos' => $agendamentos
]);

If I do this loop, for NOTHING, it work well and i can use on blade it:

@foreach ($agendamentos as $agendamento)
    {{ $agendamento->cliente->users->name }}
@endforeach

and it on vue:

<ul>
<li v-for="agendamentos in agendamento">{{ agendamento.cliente.users.name }}

I hope it help

@themsaid themsaid removed the database label Jun 7, 2017
@phroggyy
Copy link
Contributor

phroggyy commented Aug 6, 2017

@themsaid I've thought of a semi-solution after discussing this with another developer. It does have a fairly big caveat, but I still think it's better than nothing... Basically:

Prerequisite:
The user must use morphMap at least for the target model (the relation we want to query our whereHas on).

Solution:
If the Relation::morphMap() is indexed, that is, has the following structure:

[
    User::class,
    Post::class,
    Comment::class,
]

We know that the {morphable}_type is the name of the related table. As such, the subquery performed by laravel should be able to use that value straight away for the select.

If the Relation::morphMap() is not indexed, that is, has a structure like:

[
    'user' => User::class,
    'post' => Post::class,
    'comment' => Comment::class,
]

We cannot be sure what the table name is. As such, the solution is... Ugly. We can loop over the morph map, running (new $model)->getTable() on each of them. After that, construct a SQL SWITCH statement that runs on the {morphable}_type column. That's pretty ugly, but it was the only solution I could think of that'd solve it for both scenarios (as long as morph map is present).

@morningmemo
Copy link

@bunnypro your solution is good.
but it is do not support for whereHas in polymorphic whereHas.
so i send to 'pull request' that solution for this problem to that repository.

@laurencei
Copy link
Contributor

@staudenmeir - does your merged PR fix this issue? Can I close it?

@staudenmeir
Copy link
Contributor

@laurencei The issue itself is basically unfixable, my PR just added a proper exception. I think this can be closed.

@staudenmeir
Copy link
Contributor

staudenmeir commented Jun 27, 2019

Laravel 5.8.27 adds whereHasMorph(): #28928

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

9 participants