How to persist cloned entity object in Symfony/Doctrine - php

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;
}

Related

Doctrine Error saving two records at once (new entity was found through the relationship)

When I want to save 1 record to the database, everything works correctly.
When I want to save 2 records right after each other - an error occurs:
[Doctrine\ORM\ORMInvalidArgumentException]
A new entity was found through the relationship 'App\Entity\User#directory' that was not configured to cascade persist
operations for entity:. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity
or configure cascade persist this association in the mapping for example #ManyToOne(..,cascade={"persist"}). If you
cannot find out which entity causes the problem implement 'Main\Entity\Directory#__toString()' to get a clue.
I just started learning symfony and can't figure out what the problem is.
In my case: I create a new user who takes certain information from a directory that is already in my database.
For example, my new user is a cat lover, then the entry "loves cats" should pull up from the directory.
The text of the error says that I have to set up a connection and save a new record about the love of cats. But my reference book is already full, it does not need to be supplemented, just take information from there.
When I create one user, everything is fine. When I create two users who are supposed to take information from the directory and save the data, an error occurs.
Help me please.
I tried in different places to use method: $this->em->clear(). But it did not help.
class User:
ManyToOne(targetEntity="App\Entity\Directory")
JoinColumn(nullable=true)
private $directory;
public function createData($data) {
$this->setData($data);
$this->em->flush();
}
public function setData($data) {
$user = new User();
$directory = $this->em->getRepository(Directory::class)->findOneBy(['id' => $data['id']]);
$user->setDirectory($directory);
return $this;
}
You need to configure cascade operations for Directory entity which relates to user. Or you can do $em->persist($directory) before flushing

Doctrine 2 - Persist Entity with Joined Table's Foreign Key Opposed to Foreign Entity

I'm trying to persist a TradeEntity. A TradeEntity has a OneToOne relationship with a CurrencyEntity.
/**
* #ORM\OneToOne(targetEntity="Repositories\Currency\CurrencyEntity")
* #ORM\JoinColumn(name="currency", referencedColumnName="id")
*
* #var CurrencyEntity
*/
protected $currency;
I have recieved a CurrencyEntity from another object which I'm trying to insert in this new TradeEntity and persist it to the database but get an exception:
Type: Doctrine\ORM\ORMInvalidArgumentException
Message: Expected value of type "Repositories\Currency\CurrencyEntity"
for association field "Repositories\Trade\TradeEntity#$currency", got "integer" instead.
Is there no other way of me persisting TradeEntity without fetching the CurrencyEntity from the database and setting it that way?
In light of my recent discovery, i felt the need to update this answer.
Reading about Doctrine's advanced configuration, i came across Reference Proxies.
The method EntityManager#getReference($entityName, $identifier) lets you obtain a reference to an entity for which the identifier is known, without loading that entity from the database.
This is useful, for example, as a performance enhancement, when you want to establish an association to an entity for which you have the identifier. You could simply do this:
<?php
// $em instanceof EntityManager, $cart instanceof MyProject\Model\Cart
// $itemId comes from somewhere, probably a request parameter
$item = $em->getReference('MyProject\Model\Item', $itemId);
$cart->addItem($item);
Old answer:
What you can do is (it defeats the purpose of an ORM, but it is possible):
$conn = $entityManager->getConnection();
$conn->insert(
'table_name',
array(
'column_name' => 'column_value',
// define all the columns here
)
);
See Doctrine's Data Retrieval And Manipulation

Symfony Doctrine exclude persisted entity

I have an entity Key (Not the real name, I know Key is forbidden) and I need, in a loop, get a Key with state=1, and change it to state=2. This is my script :
/* Each object */
for ($i=0; $i < $order->getQuantity(); $i++) {
/* get available key */
$key = $this->getDoctrine()->getRepository('AppBundle:Key')->findOneBy(array('state' => 1));
$key->setState(2); // On la rend active
$this->_em()->persist($key);
}
}
My probleme is with this line : $key = $this->getDoctrine()->getRepository('AppBundle:Key')->findOneBy(array('state' => 1));
Doctrine always get the same first key with state=1. If I flush directly in the loop it's ok, but I can have a very big loop and I don't want to flush XXXX times.
Is there a way to don't get already persisted entity ? How can I say to Doctrine to get a Key with state=1 ONLY if I don't already persisted ?
Thanks !
Why don't you do this:
$keys = $this->getDoctrine()->getRepository('AppBundle:Key')->findBy(array('state' => 1));
foreach($keys as $key) {
$key->setState(2);
$this->_em()->persist($key);
}
$this->_em()->flush();
Thereby each key will only be persisted once and because persisting things is symfony logic only you have only one DB write action during the flush-function where all persisted items will be stored
In addition to retrieving and looping over your entities, you can also use DQL (unless I am missing context from your question that precludes this).
For example:
$dql = 'UPDATE AppBundle:Key k SET k.state = 2 WHERE k.state = 1';
$query = $this->_em->createQuery($dql);
$result = $query->getResult();
This is untested obviously. It's been a while since I wrote DQL so you might want to consult the docs. Hope this helps :)
Persisting means "Hey Doctrine, let's be aware of that entity instance!".
It's used (as you surely already know) when you create a new entity instance ($key = new Key();), and then you want Doctrine to be aware of it ($em->persist($key);) to be able to add a new record in the database on flush ($em->flush()).
All entity instances retrieved with Doctrine are already persisted (Doctrine is already aware of them).
So, in your code, the persist call is useless. And as you don't flush, the database is not updated.
Then, in the next loop, when you request from the database (->findOneBy(...)), you will get again the same entity instance, with state still equals to 1.
Finally, to answer your questions "Is there a way to don't get already persisted entity ? How can I say to Doctrine to get a Key with state=1 ONLY if I don't already persisted ?":
No, it's just impossible.

Symfony2 serialize entity object to session

I want to save one of my entity objects into the session, but as I'm doing so, I'm getting the following two errors:
Exception:
Symfony\Bundle\FrameworkBundle\DataCollector\RequestDataCollector::serialize()
must return a string or NULL
and
ErrorException: Notice: serialize(): "id" returned as member
variable from __sleep() but does not exist in
/var/www/clients/client71/web256/web/_dev_fd/kkupon/vendor/symfony/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php
line 29
My code goes like this:
$offer = $this->getEntityManager()->getRepository('KkuponMainBundle:Offer')->find($offer_id);
$request->getSession()->set('offer', $offer);
How could I get it right?
Thank you.
UPDATE
With Rowgm's help I could fix this problem by setting properties protected instead of private. The only problem I have is after reading the entity from the session the EntityManager does not know about it, and if I add the object(from the session) to another object(there is OneToMany relationship between them), it won't work.
<?php
$offer = $this->get('session')->get('offer');
$coupon = new Coupon();
$coupon->setOffer($offer);
$this->em->persist($coupon);
$this->em->flush();
This raises an error, because coupon has an object property which according to the EntityManager is not in the database(actually it is in the DB, I put to the session from the DB).
<?php
$offer = $this->get('session')->get('offer');
echo $this->em->getUnitOfWork()->isInIdentityMap($offer) ? "yes":"no"; //result: no
One solution can be:
$offer = $this->em->merge($offer);
But this doesnt seem to be the best one. I'd like my EntityManager to perceive entity objects stored in session without telling it each time.
Any idea?
You can serialize any entity by setting all their properties and relationships from private to protected.
You could have a common issue with symfony2, even if you have set all properties to protected: You have to re-generate the proxies of those entities you have changed. To do so, simply clear the cache. For dev enviroment:
app/console cache:clear
It works even if "it contains many foreign objects and even ArrayCollections of foreign entities" as you said.
Serializing entities is not recommended, as you can see in the Doctrine-documentation. You should implement the Serializable-interface and serialize/deserialize the entity-data manually.
You can exclude unnesseary fields by overridding __sleep method:
public function __sleep() {
// these are field names to be serialized, others will be excluded
// but note that you have to fill other field values by your own
return array('id', 'username', 'password', 'salt');
}

doctrine2 find method return the same object

$test have other $type and price for it different. In cart can be $test with same id, but other $type and that's a different product (object).
Example:
foreach ($cart as $order) {
$test = $em->getRepository('OrfosCoreBundle:Test')->find($order['test_id']);
$test->setType($order['test_type']);
$tests[] = $test;
$test = null;
}
and in $tests array doctrine return the same object if $order['test_id'] == previous $order['test_id']
array
0 =>
object(Orfos\CoreBundle\Entity\Test)[105]
1 =>
object(Orfos\CoreBundle\Entity\Test)[105]
How I can get new object?
Entities are uniquely identified by an identifier (primary key, in other terms).
Two entities with same identifier are the same object when you're thinking in ORMs.
So what you will have there is just an array full of references to the same object (Doctrine\ORM\EntityRepository#find will get also the same instance if one is registered!)
What you should probably do there is creating new instances of your Test entity, assign all fields values to them by calling all the different setters you have defined, persist them via Doctrine\ORM\EntityManager#persist() and populate the array. Flushing the EntityManager via Doctrine\ORM\EntityManager#flush() will save all elements in the array into db on separate records.
If you want to clone the object instead, then you'll have to work with Doctrine\ORM\EntityManager#detach(), which I wouldn't suggest you if you are just starting with the ORM. In this case, consider reading my solution posted on the Doctrine mailing list about Cloning Persisted Entities

Categories