How to deep clone in laravel? - php

I found one solution that is just related to one relationship but I have a hierarchy of models is ...
Board -> Task -> Card ->{coments,files,labels},{checklist}-> checklistitems
my question is that whenever I copy board or task or anything subsequent childrens should get copied and referenced to new Item.
Give me just Hint to do that...!!

You have to clone the model then loop the original's relations and set them to the clone. Use the replicate method on the model to start.
https://laravel.com/api/5.5/Illuminate/Database/Eloquent/Model.html#method_replicate
$clonedBoard = $board->replicate();
foreach($board->tags as $tag)
{
$clonedBoard->tags()->attach($tag);
// you may set the timestamps to the second argument of attach()
}

Related

How to persist cloned entity object in Symfony/Doctrine

I am trying to clone an entity record along with the relationships it holds among other entities. I have successfully cloned some entity objects but this one to many entity relationship has challenged me. I have reviewed similar questions regarding the error message I have been given without progress to the challenge.
The correct records are queried out, looped through and cloned then stored in an array. I have tried to persist the array but get error
EntityManager#persist() expects parameter 1 to be an entity object,
array given
I then tried to encode the array and persist but I get error
The class 'Symfony\Component\HttpFoundation\JsonResponse' was not
found in the chain configured namespaces NameOfBundle\Entity.
This below code is in my controller
$quoteItemAddWorkCollection = $em->getRepository('UniflyteBundle:QuoteItemAdditionalWork')->findBy($params);
$quoteItemDeliverableCollection = $em->getRepository('UniflyteBundle:QuoteItemDeliverable')->findBy($params);
if (!empty($quoteItemAddWorkCollection)) {
$quoteItemAddWorkArray = [];
foreach ($quoteItemAddWorkCollection as $quoteItemAddWorkItem) {
$quoteItemAddWorkItemClone = clone $quoteItemAddWorkItem;
array_push($quoteItemAddWorkArray, $quoteItemAddWorkItemClone);
}
$quoteItemAddWorkCollection = new JsonResponse($quoteItemAddWorkArray);
$em->persist($quoteItemAddWorkCollection);
I can't persist an array, I have to encode it to json first I believe. What am I doing wrong?
I think you have a misunderstanding of Doctrine concepts here. In terms of Doctrine, each entity:
UniflyteBundle:QuoteItemAdditionalWork
and
UniflyteBundle:QuoteItemDeliverable
, and any of its relationships, could get persisted, using a configuration named Mapping.
To get this into work, any In-Memory object, MUST be an instance of a managed entity class.
There is not such a magic in Doctrine, to persist so many unknown objects at once. You may persist them, one-by-one inside a loop:
foreach ($quoteItemAddWorkCollection as $quoteItemAddWorkItem) {
$quoteItemAddWorkItemClone = clone $quoteItemAddWorkItem;
$quoteItemAddWorkItemClone->setId(null);
// Set relationships here ...
$em->persist($quoteItemAddWorkItemClone);
}
Keep in mind to set any required relationships, before persisting your new cloned objects.
If you want to use, one persist, you can assign their relationships, inside a loop:
foreach ($quoteItemAddWorkCollection as $quoteItemAddWorkItem) {
$quoteItemAddWorkItemClone = clone $quoteItemAddWorkItem;
$quoteItemAddWorkItemClone->setId(null);
$someParentCollection->add($quoteItemAddWorkItemClone);
}
$em->persist($someParentCollection);
the latter method, needs you to set cascade on mapping configuration:
class SomeParent
{
// #ORM\OneToMany(targetEntity="QuoteItemAdditionalWork", mappedBy="parent", cascade={"persist"})
private $quoteItemAddWork;
}

How can I implement projects and tasks with DDD

I have two entities: Projects and Task. I can implements this object as Value Object but I wonder about the whether that is good approach? Task might change own title or status and VO should be immutable. How implements this object?
I wonder about the in Project entity I should add addTask method or I should add Tasks via TaskController? Whether TaskController is necessary when Project entity has addTask method ?
Read this documentation on Doctrine Associations / Relations:
http://symfony.com/doc/current/doctrine/associations.html
It should explain what you need to do.
Essentially, your Project Entity should have an addTask() method where you add the task. Your Project will have an ArrayCollection of Tasks. Then you can use you getTask() method (you create this) to get the Task (if you need it).
The documentation gives good examples, so take a at that first.
EDIT #2 Based on comments.
So, it's seems you don't understand the article very well. You would have separate methods in each of your Entities to do what you need that is related to that particular Entity. I'm not certain what methods you actually want.
So for example, you gave in the comments two type of methods: changeTask and changeNameTask.
In you code, you could do something like this:
$project = new Project();
$task1 = new Task();
$task1->setName("My Task Name");
... // Do other things with task1
$project->addTask($task1);
$em = $this->getDoctrine()->getManager();
$em->persist($project); // Save to db.
$em->persist($task1);
$em->flush();
// Now let's add a new Task (different name).
$task2 = new Task();
$task2->setName("Another Task");
...
$project->addTask($task2);
// Remove the old Task...
$em->remove($task1);
$em->persist($project); // Save to db.
$em->persist($task2);
$em->flush();
// You can also get the Task if you need it.
$task2 = $project->getTask(); // Presumes that this is an object not an array.
The above should make sense...

Tracking database changes on related models... Laravel 5.1

We are trying to detect the changes in Laravel related models at attribute level, as we have to keep audit trail of all the changes which are made via the application.
We can track the changes via isDirty method on the Eloquent model for single model that is not related to any other model, but there is no way that we can track the changes on the related eloquent models. isDirty doesn't work on related models attributes. Can some one please help us on this?
Update to original question:
Actually we are trying to track changes on the pivot table that has extra attributes as well defined on it. IsDirty method doesn't work on those extra attributes which are defined in the pivot table.
Thanks
As much I understand your question, It's can achieve through Model Event and some sort of extra code with current and relation model.
Laravel Model Events
If you dont want to use any additional stuff, you can just use the Laravel Model Events (that in fact Ardent is wrapping in the hooks). Look into the docs http://laravel.com/docs/5.1/eloquent#events
Eloquent models fire several events, allowing you to hook into various
points in the model's lifecycle using the following methods: creating,
created, updating, updated, saving, saved, deleting, deleted,
restoring, restored.
Whenever a new item is saved for the first time, the creating and
created events will fire. If an item is not new and the save method is
called, the updating / updated events will fire. In both cases, the
saving / saved events will fire.
If false is returned from the creating, updating, saving, or deleting
events, the action will be cancelled:
Finally, reffering to your question you can utilize the above approaches in numerous ways but most obviously you can combine it (or not) with the Eloquent Models' getDirty() api docs here method and getRelation() api docs here method
It will work for example with the saving event.
Model::saving(function($model){
foreach($model->getDirty() as $attribute => $value){
$original= $model->getOriginal($attribute);
echo "Changed";
}
$relations = $model->getRelations();
foreach($relations as $relation){
$relation_model = getRelation($relation);
foreach($relation_model->getDirty() as $attribute => $value){
$original= $relation_model->getOriginal($attribute);
echo "Relation Changed";
}
}
return true; //if false the model wont save!
});
Another Thought might help you. when you saving
save() will check if something in the model has changed. If it hasn't it won't run a db query.
Here's the relevant part of code in Illuminate\Database\Eloquent\Model#performUpdate:
protected function performUpdate(Builder $query, array $options = [])
{
$dirty = $this->getDirty();
if (count($dirty) > 0)
{
// runs update query
}
return true;
}
The getDirty() method simply compares the current attributes with a copy saved in original when the model is created. This is done in the syncOriginal() method:
public function __construct(array $attributes = array())
{
$this->bootIfNotBooted();
$this->syncOriginal();
$this->fill($attributes);
}
public function syncOriginal()
{
$this->original = $this->attributes;
return $this;
}
check model is dirty isDirty():
if($user->isDirty()){
// changes have been made
}
Or check certain attribute:
if($user->isDirty('price')){
// price has changed
}
I did not check this code but hopeful to use as your answer by thoughts, if you have any confusion to deal such requirement or something need to optimize or change please let me know.

Laravel: Create or update related model?

Please be gentle with me - I'm a Laravel noob.
So currently, I loop through a load of users deciding whether I need to update a related model (UserLocation).
I've got as far as creating a UserLocation if it needs creating, and after a bit of fumbling, I've come up with the following;
$coords = $json->features[0]->geometry->coordinates;
$location = new UserLocation(['lat'=>$coords[1],'lng'=>$coords[0]]);
$user->location()->save($location);
My issue is that one the second time around, the Location may want updating and a row will already exist for that user.
Is this handled automatically, or do I need to do something different?
The code reads like it's creating a new row, so wouldn't handle the case of needing to update it?
Update - solution:
Thanks to Matthew, I've come up with the following solution;
$location = UserLocation::firstOrNew(['user_id'=>$user->id]);
$location->user_id = $user->id;
$location->lat = $coords[1];
$location->lng = $coords[0];
$location->save();
You should reference the Laravel API Docs. I don't think they mention these methods in the "regular docs" though so I understand why you may have not seen it.
You can use the models firstOrNew or firstOrCreate methods.
firstOrNew: Get the first record matching the attributes or instantiate
it.
firstOrCreate: Get the first record matching the attributes or create it.
For Example:
$model = SomeModel::firstOrNew(['model_id' => 4]);
In the above example, if a model with a model_id of 4 isn't found then it creates a new instance of SomeModel. Which you can then manipulate and later ->save(). If it is found, it is returned.
You can also use firstOrCreate, which instead of creating a new Model instance would insert the new model into the table immediately.
So in your instance:
$location = UserLocation::firstOrNew(['lat'=>$coords[1],'lng'=>$coords[0]]);
$location will either contain the existing model from the DB or a new instance with the attributes lat and lng set to $coords[1] and $coords[0] respectively, which you can then save or set more attribute values if needed.
Another example:
$location = UserLocation::firstOrCreate(['lat'=>$coords[1],'lng'=>$coords[0]]);
$location will either contain the existing model from the DB or a new model with the attributes set again, except this time the model will have already been written to the table if not found.

Silverstripe 3.1 - Can't create a new many_many relation

while extending the CsvBulkUploader to fit my needs, I cam across the problem, that Silverstripe doesn't let me create a new entry for a many_many relation.
My dataobject is ShopItems and has a many_many relation called Visuals. So in my MySQL database I get ShopItems_Visuals.
Now I want to create a new entry for this with the following code, and I think here's the place I made some mistake.
...
$visual = ShopItem_Visuals::create();
$visual->ImageID = $file->ID;
$visual->ShopItemID = $obj->ID;
$visual->write();
...
after adding this to my function, I receive Class 'ShopItem_Visuals' not found after hitting the import button.
Is that because the database Table was created through the many_many relation in ShopItem and has no ClassName itself?
Can someone tell me how to create a new entry for this relation?
Thank you in advance.
I don't think that there's a Class for the mapping table itself.
The entry in it should be created automagically, when adding a related Object via add.
$visual = new Visual();
...
$visual->write();
$ShoptItem->Visuals()->add($visual);
$ShoptItem->write();
If the many-many-relation name is Visuals, calling ->Visuals() should return an instance of ManyManyList on which you can call add, remove etc.
see http://api.silverstripe.org/3.0/class-ManyManyList.html

Categories