Support the ongoing development of Laravel.io →
Database Eloquent
Last updated 2 years ago.
0

Respectful bump.

Last updated 2 years ago.
0

A bump a day.

Last updated 2 years ago.
0

There is no standard way to do this so whatever suits you will be ok.

Last updated 2 years ago.
0

I'm still stuck on this. This is what I have so far. I get the car and then...

$car_copy = $car->replicate();
$car_copy->save();

if ($car->parts->count() > 0)
{
    $parts_array[] = null;

    foreach ($car->parts as $part) {
        $part_copy = $part->replicate();
        $part_copy->car_id = $car_copy->id;
        array_push($parts_array, $part_copy);
    }

    $car_copy->parts()->saveMany($parts_array);
}

I get an error on the saveMany

"Argument 1 passed to Illuminate\Database\Eloquent\Relations\HasOneOrMany::save() must be an instance of Illuminate\Database\Eloquent\Model, null given"
Last updated 2 years ago.
0
$parts_array[] = null; // Pushes null into the first key of the array.

This is why you are getting the error specific to the first element of the $parts_array.

$parts_array = []; // Probably what you intended.

This should fix your problem!

Last updated 2 years ago.
0

Actually, there IS a way to do it with Laravel. There is a little-known and not-documented method that can do this for you.

View the method here: https://github.com/laravel/framework/blob/4.2/src/Illuminate/Database/Eloquent/Model.php#L2600-L2619

$user->load('all', 'of', 'the.relationships');
$newUser = $user->replicate();
$newUser->save();

Disclaimer: I haven't tried this before, but I came across it while browsing the source. Good luck!

Last updated 2 years ago.
0

Just read your current situation a little bit more. Looks like what you need to do is eager load the relationships into the model before calling replicate(). As you can see on line 2618, it pulls $this->relations, which is an empty array until loaded into the model.

Last updated 2 years ago.
0

adamgoose said:

Just read your current situation a little bit more. Looks like what you need to do is eager load the relationships into the model before calling replicate(). As you can see on line 2618, it pulls $this->relations, which is an empty array until loaded into the model.

This would probably be the best solution to this if this is true! Nice find!

Last updated 2 years ago.
0

Unfortunately this won't create related objects just like that.

And it also won't create relations on the existing ones, despite it will copy them to $replicated->relations.

Last updated 2 years ago.
0

After some tinkering I made that replicate recursive and it does basically what OP needs. However it's raw code that does sooooo many queries, that it requires some real improvements and refactoring.

I'll update this, when it's done.

Last updated 2 years ago.
0

jarektkaczyk said:

After some tinkering I made that replicate recursive and it does basically what OP needs. However it's raw code that does sooooo many queries, that it requires some real improvements and refactoring.

I'll update this, when it's done.

I have to disagree.... If you use $user->load('relation.one.two.three'), it assigns $user->relations to a nested array. Then, if you call replicate() on the user, it will replicate all of the nested relations as well.

Last updated 2 years ago.
0

Adam, not really, no. It will just set the relations, but it won't really link them (associate, attach or whatever). No push method or anything alike can make it work as expected, so basically they are there only till the end of the request.

Last updated 2 years ago.
0

My code above worked just by fixing my $parts_array variable just like as jlaswell recommended.

All the properties are intact and the associations are correct.

And for informational purposes I was getting the car like this:

 $car = Car:with('parts.piece')->where('id', $car_id)->first();

Now I'm going to put another foreach within the foreach for the pieces.

Thanks jlaswell, I can start moving forward again!

Last updated 2 years ago.
0

I use DebugBar to monitor the SQL queries and just discovered that saveMany() does a separate INSERT statement for each item in the array. I hate that.

I've done everything in my power to not have to do this but I'm just going to have to dynamically create a raw SQL statement.

The bad thing about this is that you have to list each column and if your columns change you have to remember to come back to this method and update the SQL. But, oh well. It is what it is.

Last updated 2 years ago.
0

You don't need to use raw query. Instead you can do something like below, for hasMany:

$parentKey = $parent->getKey();

$parentKeyName = $parent->relation()->getForeignKey();

$except = [
  $this->getKeyName(),
  $this->getCreatedAtColumn(),
  $this->getUpdatedAtColumn()
];

$newModels = [];

foreach ($models as $model)
{
  $attributes = $model->getAttributes();

  $attributes[$parentKeyName] = $parentKey;

  $newModels[] = array_except($attributes, $except);
}

$table = $parent->relation()->getRelated()->getTable();

DB::table($table)->insert($newModels);

This way you insert all the related objects with single query (per each parent), which can make it much better.

However there is one thing you need to consider. It means that you cannot save deeper level of the relation, because you never get inserted id back, so those deeper models 'don't know' what they should be associated to..

Last updated 2 years ago.
0

If I understand your question, I think that would be useful an implementation of the Prototype Pattern for your request.

See the following link, could be useful for your needs:

http://sourcemaking.com/design_patterns/prototype/php

Hope it helps you.

Last updated 2 years ago.
0

I'm still slowly working through this. Current error is:

SQLSTATE[42000]: Syntax error or access violation: 1110 Column 'car_id' specified twice

Last updated 2 years ago.
0

jarektkaczyk,

Thank you for the code. I got it to work by changing the $parentKeyName line otherwise it would leave car_id and add parts.car_id and then throw the "specified twice" error.

$parentKey = $car->getKey();

$parentKeyName = 'car_id'; //$car->parts()->getForeignKey();

$except = [
    $car->getKeyName(),
    $car->getCreatedAtColumn(),
    $car->getUpdatedAtColumn()
];

$newModels = [];

foreach ($car->parts as $part) {
    $attributes = $part->getAttributes();
    $attributes[$parentKeyName] = $parentKey;
    $newModels[] = array_except($attributes, $except);
}

$table = $car->parts()->getRelated()->getTable();

DB::table($table)->insert($newModels);

Next is to figure out how to copy the next relationship down.

Last updated 2 years ago.
0

I did it by this way

<pre> // question $question = Question::with('answers')->find($question_id)->first(); $question_replicate = $question->replicate(); $question_replicate->save(); //question answer (many) $question_answers = $question->answers->each(function($answer) use($question_replicate){ $answer->question_id = $question_replicate->id; $answer->save(); }); </pre>
Last updated 9 years ago.
0

I did it this way FROM WITHIN THE MODEL

I only needed to do with belongsToMany (with hasMany I guess you'd actually clone the models..?)

 //copy attributes
    $new = $this->replicate();

    //save model before you recreate relations (so it has an id)
    $new->push();

    //reset relations on EXISTING MODEL (this way you can control which ones will be loaded
    $this->relations = [];

    //load relations on EXISTING MODEL
    $this->load('relation1','relation2');

    //re-sync everything
    foreach ($this->relations as $relationName => $values){
        $new->{$relationName}()->sync($values);
    }

Last updated 9 years ago.
0

@sgelbart: >sgelbart said:

I did it this way FROM WITHIN THE MODEL

I only needed to do with belongsToMany (with hasMany I guess you'd actually clone the models..?)

//copy attributes
   $new = $this->replicate();

   //save model before you recreate relations (so it has an id)
   $new->push();

   //reset relations on EXISTING MODEL (this way you can control which ones will be loaded
   $this->relations = [];

   //load relations on EXISTING MODEL
   $this->load('relation1','relation2');

   //re-sync everything
   foreach ($this->relations as $relationName => $values){
       $new->{$relationName}()->sync($values);
   }

I just tested it, and your right about the hasMany relations. All hasMany related models are cloned by calling the replicate() function.

Last updated 9 years ago.
0

Sign in to participate in this thread!

Eventy

Your banner here too?

neon5 neon5 Joined 21 Feb 2014

Moderators

We'd like to thank these amazing companies for supporting us

Your logo here?

Laravel.io

The Laravel portal for problem solving, knowledge sharing and community building.

© 2024 Laravel.io - All rights reserved.