I have Two entity
Comments and BannedComments
the entity have the same field, and when one comment is banned, delete object from Comments Entity and Copy in BannedComments
For Now i use this script
Symfony 2 - Clone entity to different table
$oldEntity = $oldEntity;
$newEntity = new NewEntity();
$oldReflection = new \ReflectionObject($oldEntity);
$newReflection = new \ReflectionObject($newEntity);
foreach ($oldReflection->getProperties() as $property) {
if ($newReflection->hasProperty($property->getName())) {
$newProperty = $newReflection->getProperty($property->getName());
$newProperty->setAccessible(true);
$newProperty->setValue($newEntity, $property->getValue($oldEntity));
}
}
but i have to change all variable to public...
There is a better way to copy the contents ?
I try to use clone
$BannedComments = new BannedComments();
$BannedComments = clone $Comments;
$em->persist($BannedComments);
But save in Comments non in BannedComments because when i do Clone Comments, BannedComments is enitity of Comments
As I see it, it's a very specific use case. Does the Comment entity has many attributes? If not, you could write an ad hoc function that receives a Comment object and returns a BannedComment object.
Use getters and setters to avoid having to make them public.
If you insist in the generic approach, use method calls instead of property access, but
sometimes the generic approach is a bit overkill and a waste of time for concrete, non repetitive use cases.
Related
I have a strange problem with \Doctrine\ORM\UnitOfWork::getScheduledEntityDeletions used inside onFlush event
foreach ($unitOfWork->getScheduledEntityDeletions() as $entity) {
if ($entity instanceof PollVote) {
$arr = $entity->getAnswer()->getVotes()->toArray();
dump($arr);
dump($entity);
dump(in_array($entity, $arr, true));
dump(in_array($entity, $arr));
}
}
And here is the result:
So we see that the object is pointing to a different instance than the original, therefore in_array no longer yields expected results when used with stick comparison (AKA ===). Furthermore, the \DateTime object is pointing to a different instance.
The only possible explanation I found is the following (source):
Whenever you fetch an object from the database Doctrine will keep a copy of all the properties and associations inside the UnitOfWork. Because variables in the PHP language are subject to “copy-on-write” the memory usage of a PHP request that only reads objects from the database is the same as if Doctrine did not keep this variable copy. Only if you start changing variables PHP will create new variables internally that consume new memory.
However, I did not change anything (even the created field is kept as it is). The only operations that were preformed on entity are:
\Doctrine\ORM\EntityRepository::findBy (fetching from DB)
\Doctrine\Common\Persistence\ObjectManager::remove (scheduling for removal)
$em->flush(); (triggering synchronization with DB)
Which leads me to think (I might be wrong) that the Doctrine's change tracking method has nothing to do with the issue that I'm experiencing. Which leads me to following questions:
What causes this?
How to reliably check if an entity scheduled for deletion is inside a collection (\Doctrine\Common\Collections\Collection::contains uses in_array with strict comparison) or which items in a collection are scheduled for deletion?
The problem is that when you tell doctrine to remove entity, it is removed from identity map (here):
<?php
public function scheduleForDelete($entity)
{
$oid = spl_object_hash($entity);
// ....
$this->removeFromIdentityMap($entity);
// ...
if ( ! isset($this->entityDeletions[$oid])) {
$this->entityDeletions[$oid] = $entity;
$this->entityStates[$oid] = self::STATE_REMOVED;
}
}
And when you do $entity->getAnswer()->getVotes(), it does the following:
Load all votes from database
For every vote, checks if it is in identity map, use old one
If it is not in identity map, create new object
Try to call $entity->getAnswer()->getVotes() before you delete entity. If the problem disappears, then I am right. Of cause, I would not suggest this hack as a solution, just to make sure we understand what is going on under the hood.
UPD instead of $entity->getAnswer()->getVotes() you should probably do foreach for all votes, because of lazy loading. If you just call $entity->getAnswer()->getVotes(), Doctrine probably wouldn't do anytning, and will load them only when you start to iterate through them.
From the doc:
If you call the EntityManager and ask for an entity with a specific ID twice, it will return the same instance
So calling twice findOneBy(['id' => 12]) should result in two exact same instances.
So it all depends on how both instances are retrieved by Doctrine.
In my opinion, the one you get in $arr is from a One-to-Many association on $votes in the Answer entity, which results in a separate query (maybe a id IN (12)) by the ORM.
Something you could try is to declare this association as EAGER (fetch="EAGER"), it may force the ORM to make a specific query and keep it in cache so that the second time you want to get it, the same instance is returned ?
Could you have a look at the logs and post them here ? It may indicates something interesting or at least relevant to investigate further.
I'm working on a tool for concurrency with Doctrine 2.
I'm facing a "best practice" issue to retrieve a new instance of an entity without cache (the idea after that is to be able to compare some properties from 2 differents objects of the same entity and return the differences)
Some code might help (+ the doc: (http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/transactions-and-concurrency.html)):
// This is my current implementation.
$entity = $em->find(1);
$entity->setName('TEST');
// This entity as a "version" field equal to 2 in DB for example
try {
$em->lock($entity, LockMode::OPTIMISTIC, 1); // Will throw an OptimisticLockException
} catch(OptimisticLockException $e) {
$em->detach($entity);
$dbEntity = $this->find($entity->getId());
$em->detach($dbEntity);
$entity = $em->merge($entity);
var_dump($entity->getName()); // TEST
var_dump($dbEntity->getName()); // The old value
... do more stuff, like comparing the two objects ...
}
Is using the detach + merge methods a good practice for this behavior ? Any better idea to improve this code ?
--
Edit 1:
Actually, after adding some tests, the "merge" method is not what I expected: the object is not "re-attach" to the unit of work.
This behaviour is not what I want because the developer can't performs changes + flush on his entity after using my tool.
--
Edit 2:
After digging in the documentation and the source code, the "merge" method is actually what I wanted: a new instance of the entity is attached, not the one I provided ($entity in my example).
Since this code (In my tool) is in a method which purpose is to returns the $dbEntity object of my $entity object, passing the $entity reference (&$entity) solve my "Edit 1" issue.
In my CakePHP 3 app, I have a somewhat elaborate tree of entities that I need to clone and save.
The root of the structure is a Questionnaire, a Questionnaire hasMany Questions, each Question hasMany Fields, etc. (it goes deeper). Now I want the user to be able to define a new questionnaire by copying an old one. Then they can change whatever they need.
I can get a dump of what I need to copy by using $questionnaire->$this->Questionnaires->get($id) with the appropriate contain fields. Is there a clever way to save this as a bunch of new entities while preserving the data and the structure between them?
I think the best possible way would be following work flow:
Get object you want to clone
Go through the collection and remove all ID's
Convert to array and use that in $this->Questionnaires->newEntity($arrayData, ['associated' => ['Questions', '...']]);
Now save the new entity with all the related data you want to keep
AFAIK there's no "smarter" way of cloning an entity with associations in Cake 3 :-)
You could also use this plugin. You only have to configure the behavior and it gives you other functionalities, like setting default values or appending text to some field.
use EntityInteface toArray() to get all its fields:
$newEtity = $someTable->newEntity($oldEtity->toArray());
unset($newDevice->created);
unset($newDevice->id);
$someTable->save($newEntity);
$original = $this->Table->get($id)->toArray();
$copy = $this->Table->newEntity();
$copy = $this->Table->patchEntity($copy, $original);
unset($copy['id']);
// Unset or modify all others what you need
$this->Table->save($copy);
Work perfectly like this :)
Here's the way I did it when I needed something similar (adapted to the Questionnaire example):
// load the models
$this->loadModel('Questionnaire');
$this->loadModel('Questions');
// get the questions to questionnaire association - will need the PK name
$qAssoc = $this->{'Questions'}->associations()->get('Questionnaire');
// get the existing entity to copy
$existingEntity = $this->{'Questionnaire'}->get($id, [
'contain' => ['Questions'],
]);
// clean up the existing entity
$existingEntity->unsetProperty($this->{'Questionnaire'}->getPrimaryKey());
$existingEntity->unsetProperty('created');
$existingEntity->unsetProperty('modified');
// clean up the associated records of the existing entity
foreach ($existingEntity->questions as &$q) {
$q->unsetProperty($this->{'Questions'}->getPrimaryKey());
$q->unsetProperty($qAssoc->getForeignKey());
}
// create a new entity and patch it with source data
$newEntity = $this->{'Questionnaire'}->patchEntity(
$this->{'Questionnaire'}->newEntity(),
$existingEntity->toArray()
);
// save the new entity
$result = $this->{'Questionnaire'}->save($existingEntity);
References:
https://api.cakephp.org/3.0/class-Cake.ORM.Entity.html
https://api.cakephp.org/3.0/class-Cake.ORM.Table.html
I have a custom class that populates a controller's action parameters based on the typehint of the parameter. This works well for documents (using public properties and setters).
My aim is to make the controller simple:
function updateAction(Article $article)
{
$dm = new DocumentManager(); // code elsewhere
$dm->merge($article);
$dm->flush();
return $this->redirect('/article/' . $article->getId());
}
The problem is that the input supplying the fields to programatically populate the Article class doesn't contain all of the properties of an Article class (perhaps the edit form only contains Title and Content, but disregards Author, etc).
I was hoping that the presence of an ID would allow the document to be merged gracefully with what is currently in the database. However, any fields that are missing at the time of a merge will be removed from the document in the database.
Is there a way to update a document in such a way that only the fields that are present (non-null, I guess) are updated?
Rather than hitting the db twice - once for the find, and once for the update, you can use a FIND_AND_UPDATE query.and do it all in one step.
See this docs page for details: http://docs.doctrine-project.org/projects/doctrine-mongodb-odm/en/latest/reference/find-and-update.html
It seems that a clean way would be to bind the model AFTER retrieving it from the database. Something along the lines of ASP.NET MVC's UpdateModel.
function updateAction($id)
{
$dm = new DocumentManager(); // code elsewhere
$article = $dm->getRepository('Article')->find($id);
$this->updateModel($article);
$dm->flush();
return $this->redirect('/article/' . $article->getId());
}
If there are any better suggestions, feel free to answer...
This is an extension of this question: Understanding how to inject object dependencies. Since it is a bit different, I wanted to separate them, to make it, hopefully, easier to answer. Also, this is not a real system, just a simplified example that I thought we'd all be familiar with. TIA. :
DB
threads: thread_id, thread_name, etc
posts: post_id, thread_id, post_name, post_contents, post_date, post_user_id, etc
Overview
Basically I'm looking at the most maintainable way to load $post_id and have it cascade and load the other things I want to know about and I'm trying to keep the controller skinny. BUT:
I'm ending up with too many dependencies to inject
I'm passing in initialized but empty objects
I want to limit how many parameters I am passing around
I could inject $post(->many) into $thread(one<-), but on that page I'm not looking at a thread, I'm looking at a post
I could combine/inject them into a new object
Detail
If I am injecting an object into another, is it best to have it fully created first? I'm trying to limit how many parameters I have to pass in to a page, but I end up with a circle.
// 1, empty object injected via constructor
$thread = new Thread;
$post = new Post($thread); // $thread is just an empty object
$post->load($post_id); // I could now do something like $post->get('thread_id') to get everything I want in $post
// 2, complete object injected via constructor
$thread = new Thread;
$thread->load($thread_id); // this page would have to have passed in a $thread_id, too
$post = new Post($thread); // thread is a complete object, with the data I need, like thread name
$post->load($post_id);
// 3, inject $post into $thread, but this makes less sense to me, since I'm looking at a post page, not a thread page
$post = new Post();
$post->load($post_id);
$thread = new Thread($post);
$thread->load(); // would load based on the $post->get('post_id') and combine. Now I have all the data I want, but it's non-intuitive to be heirarchially Thread->Post instead of Post-with-thread-info
// Or, I could inject $post into $thread, but if I'm on a post page,
// having an object with a top level of Thread instead of
// Post-which-contains-thread-info, makes less sense to me.
// to go with example 1
class post
{
public function __construct(&$thread)
{
$this->thread=$thread;
}
public function load($id)
{
// ... here I would load all the post data based on $id
// now include the thread data
$this->thread->load($this->get('thread_id'));
return $this;
}
}
// I don't want to do
$thread = new Thread;
$post = new Post;
$post->load($post_id);
$thread->load($post->get('post_id'));
Or, I could create a new object and inject both $post and $thread into it, but then I have object with an increasing number of dependencies.
These are not dependencies, just parts of the same model, so there is no need to inject anything. Think about your model and it's usage. What are the main entities (building blocks) - post or thread? Or are they equally important/used?
You should deal (load/store) only with main entities, preferably using external class usually called Repository. E.g. you could have method ThreadRepository::findById($id), and that's enough, leave the rest to repository internals... Load and set whatever you want inside repository but never expose it to other parts of application. You could also implement some form of lazy loading (internal resource loader, proxy pattern, etc.) if performance is an issue
If you have free time in your life, download and read this book (or buy original):
http://www.infoq.com/minibooks/domain-driven-design-quickly
Your coding problems are already solved and well explained there.