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

whereHas does not work with ObjectId relations #2986

Open
zliebersbach opened this issue May 31, 2024 · 1 comment
Open

whereHas does not work with ObjectId relations #2986

zliebersbach opened this issue May 31, 2024 · 1 comment

Comments

@zliebersbach
Copy link

  • Laravel-mongodb Version: 4.3.0
  • PHP Version: 8.2.18
  • Database Driver & Version: 1.18

Description:

Cannot use whereHas on relations that use ObjectId. ObjectId is the best-practice method for storing related document IDs.

Loading the relation works as expected, but not whereHas.

If this is possible, I propose adding the solution to the documentation, otherwise a fix should be introduced.

Steps to reproduce

  1. EntityB has the following relation configured:
    protected $casts = [
        'entity_a_id' => ObjectId::class,
    ];

    public function entityA(): BelongsTo
    {
        return $this->belongsTo(EntityA::class);
    }
  1. EntityB::whereHas('entityA', fn ($query) => $query->where('status', Status::ADDED))->get()

Expected behaviour

The following query should be executed (collected from DB::getQueryLog()):

"entity_a.find({"status":"A"},{"projection":{"_id":true},"typeMap":{"root":"array","document":"array"}})"
"entity_b.find({"entity_a_id":{"$in":[ObjectId("66591310b42a652a7c027862"),ObjectId("66591310b42a652a7c027863")]}},{"typeMap":{"root":"array","document":"array"}})"

Actual behaviour

The following query is executed (collected from DB::getQueryLog()):

"entity_a.find({"status":"A"},{"projection":{"_id":true},"typeMap":{"root":"array","document":"array"}})"
"entity_b.find({"entity_a_id":{"$in":["66591310b42a652a7c027862","66591310b42a652a7c027863"]}},{"typeMap":{"root":"array","document":"array"}})"
Logs:
@julianbarrerar
Copy link

julianbarrerar commented Jun 6, 2024

@zliebersbach Encontré una posible solución mientras se soluciona, no se si es la mejor forma:

Creas un trait en app/Traits/EloquentWhereHas.php

<?php

namespace App\Traits;
use MongoDB\BSON\ObjectId;

use Illuminate\Http\Response;

trait EloquentWhereHas{

    public function scopeWhereHasTemp($query, $relation, $cb)
{
    $relationInstance = $this->$relation();
    $relatedModelQuery = $relationInstance->getRelated()->newQuery();
    call_user_func($cb, $relatedModelQuery);
    if ($relationInstance instanceof \Illuminate\Database\Eloquent\Relations\BelongsTo) {
        $foreignKey = $relationInstance->getForeignKeyName();
        $parentKey = $relationInstance->getOwnerKeyName();
    } else {
        $foreignKey = $relationInstance->getLocalKeyName();
        $parentKey = $relationInstance->getForeignKeyName();
    }
    if($foreignKey == 'id') $foreignKey = "_id";
    $objectIds = $relatedModelQuery->pluck($parentKey)->toArray();
    // Agregar si es necesario
    /* $objectIds = array_map(function ($id) {
        return new ObjectId($id);
    }, $objectIds); */
    $query->whereIn($foreignKey, $objectIds);
    return $query;
}

}

Usas el trait en el modelo dentro de la clase:

use App\Traits\EloquentWhereHas;

class MiModelo extends Model {
        use EloquentWhereHas;
        public $collection = "mimodelo";
        ....

En tu consulta
MiModelo::whereHasTemp('nameRelation', function($query) { $query->where('status', true);})->get()
En conclusión

Revisando el Log, la diferencia es que cambia el "id" por "_id", ejemplo log:

con whereHas

{"id":{"$in":["641a45c05908a72267057c3f"]}}

con el trait "whereHasTemp"

{"_id":{"$in":["641a45c05908a72267057c3f"]}}

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

No branches or pull requests

3 participants