El encadenamiento de Laravel Eloquent Query Builder afecta la consulta base original [duplicado]

Dec 15 2020

¿Cómo puedo hacer que esta consulta funcione?

$basic_query = Invoices::between_days(15, 7); // This is a collection of invoices from 15 days ago, to seven days in the future. Notice I am not getting this query, it´s still just a builder instance. If you do dd($basic_query->get()) you get 100 invoices, 60 of them paid, 40 of them outstanding.

$paid_invoices = $basic_query->paid()->get(); // This returns the collection of 60 paid invoices without problems. BUT MODIFIES $basic query, if you dd($basic query->get()) at this point, you only get the 60 paid invoices, not the full 100 invoices collection. ¿?!!!

$outstanding_invoices = $basic_query->paid(false)->get(); // This query will always be empty, even though there are many outstanding invoices. If you dd($basic_query->get()) at this point, you get an empty collection. The 100 invoices are lost.

Entonces, ¿cómo puedo tener una colección básica como punto de partida que no será modificada por operaciones get () subsiguientes?

¡Gracias!

Respuestas

2 lagbox Dec 15 2020 at 01:31

Si tiene un constructor y desea una nueva copia del constructor para poder continuar creando una consulta a partir de él, puede "clonar" el constructor:

$cloned = clone $query;

Ahora $clonedes su propio objeto y puede crear la consulta como desee sin afectar al $queryconstructor original.

Si realmente desea un clonemétodo en el constructor y no existe, puede aplicar una macro:

Illuminate\Database\Query\Builder::macro('clone', function () {
    return clone $this;
});

Puede incluir eso en el bootmétodo de un proveedor de servicios .

2 Donkarnash Dec 15 2020 at 01:35

Hay un método de clonación disponible en el constructor (Illuminate \ Database \ Query \ Builder) que se puede utilizar

$basic_query = Invoices::between_days(15, 7); $paid_invoices = $basic_query->clone()->paid()->get(); $outstanding_invoices = $basic_query->clone()->paid(false)->get();

Actualizar

Para las versiones de Laravel por debajo de 8.xa, la macro se puede definir en AppServiceProvider o en un nuevo MacroServiceProvider : método de arranque.

Con MacroServiceProvider, no olvide agregarlo a la matriz de proveedores en config/app.php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Database\Query\Builder;

class MacroServiceProvider extends ServiceProvider
{
    public function boot()
    {
        Builder::macro('clone', function() {
            return clone $this;
        });
    }

    public function register()
    {
        //
    }
}