doctrine2 find method return the same object - php

$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

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

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.

How can you update and create a new row in Laravel from a JSON object with relationships?

I started using Laravel yesterday, the ORM seems powerful. Does it have any way of updating rows in related models? This is what I tried:
Step 1: Generate a JSON object with the exact structure the database has. The JSON object has certain fields that are subarrays which represent relationships in the database.
Step 2: Send the JSON object via POST to Laravel for processing, here it gets tricky:
I can change the JSON object into an array first
$array = (array) $JSONobject;
Now I need to update, I would expect this to work:
Product::update($JSONobject->id,$array);
But because the array has subarrays, the update SQL that is executed cannot find the sub-array column in the table, it should instead look for the associated table. Can this be done? Or do I have to call the other models as well?
Thanks in advance!
This is something that Eloquent does not handle for you. The array that you supply to the update() method should contain columns only for, in your case, the Product model. You might try something like this to update relations. This is all off the top of my head and is by no means tested. Take it with a grain of salt.
$update = (array) $JSONobject;
$relations = [];
foreach ($update as $column => $value)
{
// If the value is an array then this is actually a relation. Add it to the
// relations array and remove it from the update array.
if (is_array($value))
{
$relations[$column] = $value;
unset($update[$column]);
}
}
// Get the product from the database so we can then update it and update any of the
// the products relations.
$product = Product::find($update['id']);
$product->update($update);
foreach ($relations as $relation => $update)
{
$product->{$relation}()->update($update);
}
The above code assumes that the key for your nested relation arrays is the name of the relation (method name used in your model). You could probably wrap this up in a method on your Product model. Then just call something like Product::updateRecursively($JSONobject); I'm terrible with names but you get the idea.
This probably won't work with more complex relations either. You'd have to take it a few steps further for things like many to many (or probably even one to many).

Symfony/Doctrine

I am currently learning Symfony and Doctrine by reading the docs.
I don't understand the difference between find and findOneById. I tried to use them both in this simple example and it looks they do the same thing to me.
$product = $this->getDoctrine()
->getRepository('AcmeStoreBundle:ProductEntity')
->findOneById($id);
Are they really the same thing or there is some difference? And where I can find the detailed documentation for all these methods?
In your case, they happen to do the same thing. Looking at this example, you'll notice that find() looks for the field named after the primary key. findOneBy<Field>() will explicitly use the field in the name of the method, even if it's not the primary key, and will return the first record. So, in the end, if the primary key is indeed named id, then both will do the same thing.
// query by the primary key (usually "id")
$product = $repository->find($id);
// dynamic method names to find based on a column value
$product = $repository->findOneById($id);
$product = $repository->findOneByName('foo');
There is an API here I don't think there is any difference: the two methods, when call the way you call them, do this:
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id);
But find will be quicker and far quicker in some cases, because it doesn't use the __call magic method, and because find() checks a map of the current unit of work before whereas load() doesn't (see the #todo):
/**
* Loads an entity by a list of field criteria.
* ...
*
* #todo Check identity map? loadById method? Try to guess whether $criteria is the id?
*/
public function load(array $criteria, $entity = null, $assoc = null, array $hints = array(), $lockMode = 0)
So prefer find(), findOneById() is just a less efficient method to do the same thing.
In fact, is not the same thing.
Think about it. If you call "findBy()" you assume you'll receive a collection of entities ( 0, 1 or more than one ). So, to get all results, you'll need to iterate ArrayCollection or just get first ( $result->first() ).
If your query is by a unique key ( As this case ), you can just get unique entity by calling "getOneById()" and you will receive the entity as result.
/**
* Retrieving Product with 'findOneBy'
*/
$product = $this->getDoctrine()
->getRepository('AcmeStoreBundle:ProductEntity')
->findOneById($id);
/**
* Retrieving Product with 'findBy'
*/
$product = $this->getDoctrine()
->getRepository('AcmeStoreBundle:ProductEntity')
->findById($id)
->first();
Semantically, the first one it the best.
*TIP
Entity should be called just Product.
Why? Because is under "/Entity" folder ( Almost, should... ), and namespace will contain info about "What is exactly Product"
// query by the primary key (usually "id")
$product = $repository->find($id);
// dynamic method names to find based on a column value
$product = $repository->findOneById($id);
// $foo is any name which you want to find from database
$product = $repository->findOneByName($foo);
It calls the same method in the end.
findByKey('value')
Is basically the same as
findBy(array('key' => 'value'))
Where key is the property of the entity and value is the value of the property.
findById($id)
Is a special case of the above. And so is
find($id)
All of these methods execute the same query in the end. However, there is a difference in
findBy()
and
findOneBy()
Where findOneBy() only returns a single result and findBy will return all the results satisfying the demands.
However, in general it is considered good practice to use DQL queries instead. Consider lazy loading, array hydration, prepared statements, etc.
This is an interesting article on the topic:
Some Doctrine 2 Best Practices
Is the same thing, but I prefer the findOneBy method. It's more clear.

Zend: Two Objects, one Row

I've recently started using Zend Framework (1.8.4), to provide admin tools for viewing the orders of a shopping cart site.
What I'd like to do is to efficiently create multiple model (Zend_Db_Table_Row_Abstract) objects from a single database result row.
The relationship is simple:
an Order has one Customer (foreign key order_custid=customer.cust_id);
a Customer has many Orders.
Loading the orders is easy enough. Using the method documented here:
Modeling objects with multiple table relationships in Zend Framework
...I could then grab the customer for each.
foreach ($orderList as $o)
{
cust = $o->findParentRow('Customers');
print_r ($cust); // works as expected.
}
But when you're loading a long list of orders - say, 40 or more, a pageful - this is painfully slow.
Next I tried a JOIN:
$custTable = new Customers();
$orderTable = new Orders();
$orderQuery = $orderTable->select()
->setIntegrityCheck(false) // allows joins
->from($orderTable)
->join('customers', 'cust_id=order_custid')
->where("order_status=?", 1); //incoming orders only.
$orders = $orderTable->fetchAll($orderQuery);
This gives me an array of order objects. print_r($orders) shows that each of them contains the column list I expect, in a protected member, with raw field names order_* and cust_*.
But how to create a Customer object from the cust_* fields that I find in each of those Order objects?
foreach ($orders as $o) {
$cols = $o->toArray();
print_r ($cols); // looks good, has cust_* fields...
$cust = new Customer(array( 'table' => 'Customer', 'data' => $cols ) );
// fails - $cust->id, $cust->firstname, etc are empty
$cust->setFromArray($cols);
// complains about unknown 'order_' fields.
}
Is there any good way to create an Order and a Customer object simultaneously from the joined rows? Or must I run the query without the table gateway, get a raw result set, and copy each of the fields one-by-one into newly created objects?
Zend_Db doesn't provide convenience methods to do this.
Hypothetically, it'd be nifty to use a Facade pattern for rows that derive from multiple tables. The facade class would keep track of which columns belong to each respective table. When you set an individual field or a whole bunch of fields with the setFromArray() method, the facade would know how to map fields to the Row objects for each table, and apply UPDATE statements to the table(s) affected.
Alternatively, you could work around the problem of unknown fields by subclassing Zend_Db_Table_Row_Abstract, changing the __set() behavior to silently ignore unknown columns instead of throwing an exception.
You can't have an OO interface to do everything SQL can do. There must be some line in the sand where you decide a reasonable set of common cases have been covered, and anything more complex should be done with SQL.
I use this method to assign database row fields to objects. I use setter methods, but this could probably be also done with only properties on object.
public function setOptions(array $options){
$methods = get_class_methods($this);
foreach ($options as $key => $value) {
$method = 'set' . ucfirst($key);
if (in_array($method, $methods)) {
$this->$method($value);
}
}
return $this;
}

Categories