Framework: [Proposal] Copy/Clone Eloquent\Builder with Copy of Underlying Query\Builder

Created on 17 May 2013  路  10Comments  路  Source: laravel/framework

I'm interested in cloning \Illuminate\Database\Eloquent\Builder instances, and along with it, getting a fresh copy of the underlying Illuminate\Database\Query\Builder instance.

The use case is that I am building up a series of queries that all start with the same series of scopes being passed the same arguments, but which then diverge in the conditions appended to each query I'd like to instantiate some $builder instance based on specific scopes/arguments, and then create copies based on it onto which I could append separate conditions.

Trying to clone $builder below does not clone the underlying Illuminate\Database\Query\Builder instance, causing the below where conditions to be both added to the same query builder.

$builder = MyModel::at([1,2,3])->active();

$one = clone $builder;
$two = clone $builder;

$one->where('title', 'one');
$two->where('title', 'two');

echo $one->toSql(); // ... where title = 'one' and title = 'two'
echo $two->toSql(); // ... where title = 'one' and title = 'two'

The following works

$builder = MyModel::at([1,2,3])->active();

$one = new \Illuminate\Database\Eloquent\Builder(clone $builder->getQuery());
$one->setModel($builder->getModel());

$two = new \Illuminate\Database\Eloquent\Builder(clone $builder->getQuery());
$two->setModel($builder->getModel());

$one->where('title', 'one');
$two->where('title', 'two');

echo $one->toSql(); // ... where title = 'one
echo $two->toSql(); // ... where title = 'two'

I'm interested in a method like copy on \Illuminate\Database\Eloquent\Builder to provide this functionality.

$builder = MyModel::at([1,2,3])->active();

$builder->copy()->where('title', 'one')->toSql(); // ... where title = 'one'
$builder->copy()->where('title', 'two')->toSql(); // ... where title = 'two'

Most helpful comment

Does this work?

with(clone $bookingsWhere)
    ->select('created_at', 'first_name', 'last_name')
    ->limit(Input::query('iDisplayLength', 25))
    ->offset(Input::query('iDisplayStart', 0));

All 10 comments

Put a __clone method on a base model.

__clone is nice but it can't be chained. Can we get a copy() method as originally suggested? It can just call __clone and return it.

@taylorotwell fixed this for #1842.

I'm confused. Is this on master? Was a copy() method added? __clone is unchainable; its return type is void.

Bump. How do we chain things? I've got stuff like this all over the place

$bookingsLimited = clone $bookingsWhere;
$bookingsLimited
    ->select('created_at', 'first_name', 'last_name')
    ->limit(Input::query('iDisplayLength', 25))
    ->offset(Input::query('iDisplayStart', 0));

because we can't clone and chain. ->newQuery() doesn't actually return a new query either.

Does this work?

with(clone $bookingsWhere)
    ->select('created_at', 'first_name', 'last_name')
    ->limit(Input::query('iDisplayLength', 25))
    ->offset(Input::query('iDisplayStart', 0));

@franzliedke Huh? This is PHP, there's no with keyword.

@mnbayazit It's a laravel helper function.

@GrahamCampbell Oh... been writing too much OOP, almost forgot bare functions existed in PHP. Yes, that does work. Thanks guys :-)

I feel like this should be in the docs somewhere.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

gabriellimo picture gabriellimo  路  3Comments

felixsanz picture felixsanz  路  3Comments

Fuzzyma picture Fuzzyma  路  3Comments

RomainSauvaire picture RomainSauvaire  路  3Comments

digirew picture digirew  路  3Comments