TransWikia.com

Multiple buscador en modelos laravel

Stack Overflow en español Asked on January 5, 2022

Estimados estoy trabado en la realización de un buscador por favor agradecería toda clase de orientación.

En la vista tengo dos inputs.
Uno es un select que el usuario debe elegir el tipo de propiedad:
Alquiler – Venta – Temporal – Comercial

En el otro input debe escribir la ubicación.

VISTA BUSCADOR

{{ Form::open(['route' => 'busquedas', 'method' => 'get', 'class' => 'form-inline'])}}

                <select id="categoria" name="categoria" class="form-control">
                    {{-- <option value="" disabled>Seleccionar</option> --}}
                    @foreach($categorias as $index => $categoria)
                        <option value="{{ $categoria->nombre_categoria }}">
                            {{ $categoria->nombre_categoria }}
                        </option>
                    @endforeach
                </select>

                <div class="form-group">
                    {{ Form::text('ubicacion', null, ['class' => 'form-control', 'placeholder' => '¿Que ubicación está buscando? ']) }}
                </div>

                <div class="form-group">
                    <button type="submit" class="btn btn-default question-btn">
                        <span class="fa fa-search">Buscar</span>
                    </button>
                </div>
            
            {{ Form::close() }}

En el controlador deseo filtrar en el modelo Category.php en base a la selección del usuario
Alquiler – Venta – Temporal – Comercial y además debo buscar en dos tablas la ubicación escrita.

En dos tablas por que por un lados está ubicación y por otra localidad que a su vez las mismas están relacionadas.

Modelo Propiedad:

public function category(){
    return $this->belongsTo(Category::class);
}

public function ubicacion(){
    return $this->belongsTo(Ubicacion::class);
}

Modelo Category:

public function properties(){
    return $this->hasMany(Property::class);
}

Modelo Ubicación:

public function properties(){
    return $this->hasMany(Property::class);
}

public function area(){
    //hasOne: una ubicacion tiene un area
    return $this->hasOne(Area::class);
}


public function scopeUbicacion($query, $ubicacion){
    if($ubicacion)
        return $query->where('ubicacion', 'LIKE', "%$ubicacion%");
}

Modelo Área:

public function properties(){
    return $this->hasMany(Property::class);
}

public function scopeArea($query, $area){
    if($area)
        return $query->where('name', 'LIKE', "%$area%");
}

En el controlador tengo hasta el momento lo siguiente:

$categoria = $request->get('categoria');
    $ubicacion = $request->get('ubicacion');

    $categoria_id = Category::where('nombre_categoria', $categoria)->pluck('id');


    $propiedadesObtenidas = Property::with(['category', 'ubicacion'])

        ->when($categoria_id, function ($query) use ($categoria_id) {
            $query->where('category_id', $categoria_id);
        })
        ->when($ubicacion, function ($query) use ($ubicacion) {
            $query->whereHas('ubicacion', function ($q) use ($ubicacion) {
                $q->ubicacion($ubicacion);
        });
    })
    ->get();



    dd($propiedadesObtenidas);

No termino de encontrar una solución para unir las busquedas.

Por favor cualquier sugerencia será bienvenida. ¡Muchas gracias!

One Answer

Suponiendo que todo lo demás está funcionando como quieres, respondo a

"¿Cómo unir las busquedas?"

Para el caso usaría la cláusula when() de Query Builder:

Clausulas condicionales

En ocasiones, es posible que desees que las cláusulas se apliquen a una consulta solo cuando algo más sea verdadero. Por ejemplo, es posible que solo desees aplicar una instrucción where si un valor de entrada dado está presente en la solicitud entrante. Puedes lograr esto utilizando el método when:

Property::when($categoria_id, function ($query) use ($categoria_id) {
    $query->where('category_id', $categoria_id);
})->get();

El método when solo ejecuta la función de cierre dada cuando el primer parámetro ($categoria_id) es verdadero (funciona como un if). Si el primer parámetro es falso, el cierre no se ejecutará.

Entonces, en tu consulta sobre el modelo Property, puedes poner un when para cada filtro, que se ejecutará condicionalmente si el valor del filtro resulta en true (ten en cuenta que null resultará en false, por lo tanto si el usuario no rellenó el input de ese filtro, no buscas por ese valor).

$propiedadesObtenidas = Property::with(['category', 'ubicacion'])
    ->when($categoria_id, function ($query) use ($categoria_id) {
        // cuando $categoria_id tenga algún valor que resuente en true, hacer este where
        $query->where('category_id', $categoria_id);
    })
    ->when($ubicacion, function ($query) use ($ubicacion) {
        // cuando $ubicacion tenga algún valor que resuente en true, hacer este where
        $query->whereHas('ubicacion', function ($q) use ($ubicacion) {
            $q->ubicacion($ubicacion);
        });
    })
    ->get();

Para la ubicación verás que usé whereHas, aquí la explicación:

Consultar existencia de relación

Al acceder a los registros de un modelo, es posible que desees limitar sus resultados en función de la existencia de una relación, para el caso, puedes pasar el nombre de la relación al método has, o puedes usar el método whereHas para poner condiciones "where" en tus consultas de has. Este método te permite agregar restricciones personalizadas a una restricción de relación.


EDIT: Si la categoría va a estar siempre presente, no hace falta el condicional, puedes poner directamente el where. Para las otras dos tendrías que dejar el condicional y agrupar los parámetros (como ponerle paréntesis) y un OR para que devuelva datos si coincide una o la otra. Algo así:

    $propiedadesObtenidas = Property::with(['category', 'ubicacion', 'area'])
        ->where('category_id', $categoria_id)
        ->when($ubicacion || $area, function ($query) use ($ubicacion, $area) {
            $query->where(function ($que) use ($ubicacion, $area) {
                $que->whereHas('ubicacion', function ($q) use ($ubicacion) {
                    $q->ubicacion($ubicacion);
                })->orWhereHas('area', function ($q) use ($area) {
                    $q->area($area);
                });
            });
        })
        ->get();

EDIT2

O esta otra, que si el filtro tiene $ubicacion y $area, que busque una Property que cunpla las dos condiciones. Si el filtro no tiene las dos ($ubicacion y $area), que busque por la que tenga ($ubicacion o $area), y si no tiene ninguna, que devuelva todas las de la $categoria.

$propiedadesObtenidas = Property::with(['category', 'ubicacion', 'area'])
            ->where('category_id', $categoria_id)
            ->when($ubicacion && $area, function ($query) use ($ubicacion, $area) {
                
                return $query->where(function ($que) use ($ubicacion, $area) {
                    
                    $que->whereHas('ubicacion', function ($q) use ($ubicacion) {
                        $q->ubicacion($ubicacion);
                    })->whereHas('area', function ($q) use ($area) {
                        $q->area($area);
                    });
                    
                });
                
            }, function ($query) use ($ubicacion, $area) {

                return $query
                    ->when($ubicacion, function ($que) use ($ubicacion) {

                        $que->whereHas('ubicacion', function ($q) use ($ubicacion) {
                            $q->ubicacion($ubicacion);
                        });

                    })
                    ->when($area, function ($que) use ($area) {

                        $que->whereHas('area', function ($q) use ($area) {
                            $q->area($area);
                        });

                    });
            })
            ->get();

EDIT3

Algunas observaciones sobre los Modelos, las relaciones y la base de datos:

Modelo Propiedad:

public function category(){
    // para esta relación necesitas tener un columna 'category_id'
    // en la tabla del modelo Propiedad
    return $this->belongsTo(Category::class);
}

public function ubicacion(){
    // para esta relación necesitas tener un columna 'ubicacion_id'
    // en la tabla del modelo Propiedad
    return $this->belongsTo(Ubicacion::class);
}

// esta es la relación que te falta declarar para hacer la consulta,
// pero necesitas esa columna 'area_id', 
// si no está en esta tabla, hay que hacer otra cosa, 
// como buscarla a través del modelo Ubicación
public function area(){
    // para esta relación necesitas tener un columna 'area_id'
    // en la tabla del modelo Propiedad
    return $this->belongsTo(Ubicacion::class);
}

Modelo Category:

public function properties(){
    // para esta relación necesitas tener un columna 'category_id'
    // en la tabla del modelo Propiedad
    return $this->hasMany(Property::class);
}

Modelo Ubicacion:

public function properties(){
    // para esta relación necesitas tener un columna 'ubicacion_id'
    // en la tabla del modelo Propiedad
    return $this->hasMany(Property::class);
}

// esta está rara, ¿no será al revés? 
// "Una Ubicacion pertenece a un Area" 
// en vez de "Una Ubicacion tiene a un Area"
public function area(){
    // para esta relación necesitas tener un columna 'ubicacion_id'
    // en la tabla del modelo Area
    return $this->hasOne(Area::class);
}

// este es un scope, generalmente se usa para segmentos de consultas
// que se repiten, y para que consultas complejas sean más fácil de leer
public function scopeUbicacion($query, $ubicacion){
    // para esta consulta necesitas tener un columna 'ubicacion'
    // en la tabla del modelo Ubicacion
    if($ubicacion)
        return $query->where('ubicacion', 'LIKE', "%$ubicacion%");
}

Modelo Area:

public function properties(){
    // para esta relación necesitas tener un columna 'area_id'
    // en la tabla del modelo Propiedad
    return $this->hasMany(Property::class);
}

public function scopeArea($query, $area){
    // para esta consulta necesitas tener un columna 'name'
    // en la tabla del modelo Area
    if($area)
        return $query->where('name', 'LIKE', "%$area%");
}

Answered by porloscerros Ψ on January 5, 2022

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP