foreign key not persisted symfony2 after a clone - symfony

I'm using symfony + Doctrine and I'm stuck with a problem:
I cloned an existing object and I would like to change a FK on the clone. It should be like that:
$dafCloned = clone $daf;
$dafState = $dafStateRepository->findOneBy(
array(
'name' => 'saved',
'dafType' => 'invoice',
'company' => $daf->getSeller(),
));
$dafCloned->setDafState($dafState);
var_dump($dafState->getId());
var_dump($dafCloned->getDafState()->getId());
$this->em->persist($dafCloned);
$this->em->flush();
As you may have noticed, I got 2 var_dump here. Here are the print of the Custom Command calling this code :
int(5500)
int(5499)
5500 is the id I should have in db for $dafCloned, 5499 is the id I have for $daf.
I'd like to know WHY I got different id...My dafState should be the same. I'm probably missing something really stupid but I'm stuck on it since 9am...I even tried to delete all caches we have, moving flush() and persist() but cant help :s
EDIT : added the setDafState() method if needed, but this is basic :
public function setDafState(DafState $dafState) {
$this->dafState = $dafState;
return $this;
}
EDIT2 :
Here getDafState() :
/**
* Get dafState
*
* #return MyPath\Entity\DafState
*/
public function getDafState() {
return $this->dafState;
}
If you need more code sample, just ask for it, I'll edit ;)
For the object, both are huge (Doctrine Object) and i can't find any way to get what could be useful :s. I cant grep dafState on $daf Object, output is still huge.
EDIT 3 :
if ($daf->getId() == 8902) // daf test which should be duplicated
var_dump($dafCloned->getDafState() === $dafState);
output
bool(true)

$dafCloned = clone $daf; // Here your clone is the same object as the old one
$dafState = $dafStateRepository->findOneBy( // Here you get some fresh object
array(
'name' => 'saved',
'dafType' => 'invoice',
'company' => $daf->getSeller(),
));
$dafCloned->setDafState($dafState); // Because this object is still managed by the entity manager it will set the $dafState on the old object (tracked by Id most likely)
var_dump($dafState->getId()); // Show the Id on the fresh object
var_dump($dafCloned->getDafState()->getId()); // Show the Id on the old object
$this->em->persist($dafCloned); // overwrite the old object
$this->em->flush();
This Post will be helpful to you: How to re-save the entity as another row in Doctrine 2
I will update my answer if this doesn't solve your issue

Here we go.
Thanks to #cheesemacfly i find out i have a prePersistListener which was resetting my dafState !
So, next time you have something weird looking like the above problem, check your listener !

Related

Symfony 2.7 findOneBy() Function Finds the Wrong Entity

I've got a controller action that's supposed to be looking for semi-duplicate entries in a collection, and removing them from that Entity's list. New ones should not have an ID yet, and existing ones do, so I'm running findOneBy() with an array of parameters to match on (leaving out ID).
I am baffled and deeply troubled by the error I am getting, where it finds the wrong entity! I've got some relevant code below, I hope this is just a dream or a silly mistake, I can't reproduce the output on my own windows development environment, but my co-worker was testing on his mac, and was getting errors, so I went on his machine and did some quick echoing to see what was going on. See the code and the result below.
CODE
foreach($entity->getTechnicians() as $tech) {
//find new addtions, see if they exist
if (!$tech->getId()) {
echo "Technician " . $tech->getFirstName() . " has no ID, trying to find one that does<br/>";
$found = $em->getRepository('TechnicianBundle:Technician')->findOneBy(array(
'firstName' => $tech->getFirstName(),
'lastName' => $tech->getLastName(),
'email' => $tech->getEmail(),
'residentEngineer' => $tech->getResidentEngineer(),
'officeNumber' => $tech->getOfficeNumber()
));
//if one with an ID already exists
if ($found) {
echo "found technician " . $found->getFirstName() . " that already has id " . $found->getId() . "<br/>";
...
OUTPUT
Technician Four has no ID, trying to find one that does
found technician khjvuov that already has id 7
Probably it's not a findOneBy() issue.
If without add/remove calls it works, it may be caused by modified current pointer of ArrayCollection so when $tech->getFirstName() is called it actually points to another entry.
You may try to iterate your collection like this:
$iterator = $entity->getTechnicians()->getIterator();
while ($iterator->valid()) {
$tech = $iterator->current();
... $entity->addTechnician()/$entity->removeTechnician()
$iterator->next();
}
It will create new ArrayIterator object, so you can modify underlying object preserving ArrayCollection's internal pointer.
This may be obsolete if you are running PHP7 (reference - Note box)
Ultimately, the findOneBy() method uses the load() method of the Doctrine ORM.
In the doc, the parameter $criteria is described as such :
#param array $criteria The criteria by which to load the entity.
You should probably dig deeper into Doctrine API to make sure, but there is a big chance that in the array you are passing as an argument, only the first key => value pair is taken into account (in your example, 'firstName' => $tech->getFirstName()).
If this is the case, you'll probably need to add your own custom method in your entity repository, one that would allow you to query your database with more advanced statements (using OR/AND or LIKE() in SQL for instance).

Convert old silverstripe form to SS 3

I have an old SS2.4 site which I have updated to SS3.1.13. The only part of the old site I can't get working is a search form that filters DataObjects. The old code is:
public function doCollectionSearch($data, $form)
{
$filters = array();
...
//setup some filters based on user selections
...
$where = implode(" AND ", $filters);
if(!isset($_REQUEST['start'])) $_REQUEST['start'] = 0;
$limit = $_REQUEST['start'].",50";
return $this->customise(array(
'Collections' => DataObject::get("Collection", $where, "Genus, Species ASC", null, $limit)
))->renderWith(array('Collection_results','Page'));
}
I have updated the last part to:
return $this->customise(array(
'Collections' => Collection::get()->where($where)->sort("Genus, Species ASC")->limit($limit)
))->renderWith(array('Collection_results','Page'));
But I get a "the method 'fortemplate' does not exist on 'CollectionPage_Controller'".
I know the $where is not right yet, but if I strip that out I still get an error..
I know I am missing something obvious...can anyone suggest a fix?
You'll get that error if your template accesses a method or database field that returns an object that can't be reduced to a string. This often happens when you have a related object (say a parent page) and you do something like the following:
<p>My parent is: $Parent</p>
Instead of
<p>My parent is: $Parent.Title</p>
The same thing existed in 2.4 so this doesn't totally explain your problem, but I'd look for something like the above scenario in your templates.

Modifying a field collection programmatically missing hostEntity fields

I am trying to modify a field collection in a node that already exists so I can change an image on the first element in an array of 3. The problem is, the hostEntity info is not set when I do a entity_load or entity_load_single so when I do a:
$field_collection_item->save(true); // with or without the true
// OR
$fc_wrapper->save(true); // with or without the true
I get the following error:
Exception: Unable to save a field collection item without a valid reference to a host entity. in FieldCollectionItemEntity->save()
When i print_r the field collection entity the hostEntity:protected fields are indeed empty. My field collection is setup as follows:
field_home_experts
Expert Image <--- Want to change this data only and keep the rest below
field_expert_image
Image
Expert Name
field_expert_name
Text
Expert Title
field_expert_title
Text
Here is the code I am trying to use to modify the existing nodes field collection:
$node = getNode(1352); // Get the node I want to modify
// There can be up to 3 experts, and I want to modify the image of the first expert
$updateItem = $node->field_home_experts[LANGUAGE_NONE][0];
if ($updateItem) { // Updating
// Grab the field collection that currently exists in the 0 spot
$fc_item = reset(entity_load('field_collection_item', array($updateItem)));
// Wrap the field collection entity in the field API wrapper
$fc_wrapper = entity_metadata_wrapper('field_collection_item', $fc_item);
// Set the new image in place of the current
$fc_wrapper->field_expert_image->set((array)file_load(4316));
// Save the field collection
$fc_wrapper->save(true);
// Save the node with the new field collection (not sure this is needed)
node_save($node);
}
Any help would be greatly appreciated, I am still quite new to Drupal as a whole (end-user or developer)
Alright so I think I have figured this out, I wrote up a function that will set a field collection values:
// $node: (obj) node object returned from node_load()
// $collection: (string) can be found in drupal admin interface:
// structure > field collections > field name
// $fields: (array) see usage below
// $index: (int) the index to the element you wish to edit
function updateFieldCollection($node, $collection, $fields = Array(), $index = 0) {
if ($node && $collection && !empty($fields)) {
// Get the field collection ID
$eid = $node->{$collection}[LANGUAGE_NONE][$index]['value'];
// Load the field collection with the ID from above
$entity = entity_load_single('field_collection_item', array($eid));
// Wrap the loaded field collection which makes setting/getting much easier
$node_wrapper = entity_metadata_wrapper('field_collection_item', $entity);
// Loop through our fields and set the values
foreach ($fields as $field => $data) {
$node_wrapper->{$field}->set($data);
}
// Once we have added all the values we wish to change then we need to
// save. This will modify the node and does not require node_save() so
// at this point be sure it is all correct as this will save directly
// to a published node
$node_wrapper->save(true);
}
}
USAGE:
// id of the node you wish to modify
$node = node_load(123);
// Call our function with the node to modify, the field collection machine name
// and an array setup as collection_field_name => value_you_want_to_set
// collection_field_name can be found in the admin interface:
// structure > field collections > manage fields
updateFieldCollection(
$node,
'field_home_experts',
array (
'field_expert_image' => (array)file_load(582), // Loads up an existing image
'field_expert_name' => 'Some Guy',
'field_expert_title' => 'Some Title',
)
);
Hope this helps someone else as I spent a whole day trying to get this to work (hopefully I won't be a noob forever in Drupal7). There may be an issue getting formatted text to set() properly but I am not sure what that is at this time, so just keep that in mind (if you have a field that has a format of filtered_html for example, not sure that will set correctly without doing something else).
Good luck!
Jake
I was still getting the error, mentioned in the question, after using the above function.
This is what worked for me:
function updateFieldCollection($node, $collection, $fields = Array(), $index = 0) {
$eid = $node->{$collection}[LANGUAGE_NONE][$index]['value'];
$fc_item = entity_load('field_collection_item', array($eid));
foreach ($fields as $field => $data) {
$fc_item[$eid]->{$field}[LANGUAGE_NONE][0]['value'] = $data;
}
$fc_item[$eid]->save(TRUE);
}
I hope this helps someone as it took me quite some time to get this working.

smarty phpunit empty fetch

I need help please.
I'm using Smarty with PHPUnit, and I have troubles.
For example:
In some checks, I call fetch function many times, and I only receive right the first call, the others only return empty. Why???
I let some code here as an example:
/**
* #dataProvider provider_test
*/
public function test_field($field) {
// with this I instance smarty
$front = $this->get_template();
$front->assign('function', 'fb_user_field');
$front->assign('field', $field);
// this fetch only return right widh the first value of field
$result = $front->fetch('tests/generic.tpl');
$this->assertNotNull($result);
}
public function provider_test() {
return array(
array('field' => 'subdomain'),
array('field' => 'login')
);
}
I check $field and in every iteration receive the correct value, but after the first one, fetch return only empty.
Why???? Thanks!!!
My suggestion is to use the MakeGood or similar to step by step debug your php code. Otherwise it is guess and check till you die!
Here's some links
http://blog.loftdigital.com/running-phpunit-tests-in-eclipse-pdt
http://www.youtube.com/watch?v=1qnWL52wt58
I used to spend hours guess and check with phpunit until I discovered MakeGood. Hope it helps!

Duplicating extbase repository object

In my extbase/fluid project,in addition to standard actions such as create,delete,list etc, I want to create a duplicate of a model class object which are stored in a repository. Using findall(), all objects are displayed in a list and corresponding actions such as delete,edit are displayed next to each. For duplicating an object, I have created a duplicate action in the corresponding controller and here is the code:
public function dupcliateAction(Tx_CcCompanylogin_Domain_Model_MyObject $testObject)
{
$this->myObjectRepository->add($testObject);
$this->redirect('list');//Lists the objects again from the repository
}
Seems straitforward enough but no new object is added to the repository and I am not getting an error.I have checked the documentation and there is no explicit method available for duplicating.
For those who may concern:
You don't need to call the reflection-api at this point.
You only need to implement a method called e.g. resetUid() in your model like this:
public function resetUid() {
$this->uid = NULL;
$this->_setClone(FALSE);
}
then you can use the magic clone method to clone the object in your controller. after that you have to call the new resetUid() method and then you are able to persist the new object with the old properties.
Note : When an object is cloned, PHP 5 will perform a shallow copy of all of the object's properties. Any properties that are references to other variables, will remain references.
Alternative you can use reflection to create a (deep) copy of your object.
$productClone = $this->objectManager->create('Tx_Theext_Domain_Model_Product');
// $product = source object
$productProperties = Tx_Extbase_Reflection_ObjectAccess::getAccessibleProperties($product);
foreach ($productProperties as $propertyName => $propertyValue) {
Tx_Extbase_Reflection_ObjectAccess::setProperty($productClone, $propertyName, $propertyValue);
}
// $productAdditions = ObjectStorage property
$productAdditions = $product->getProductAddition();
$newStorage = $this->objectManager->get('Tx_Extbase_Persistence_ObjectStorage');
foreach ($productAdditions as $productAddition) {
$productAdditionClone = $this->objectManager->create('Tx_Theext_Domain_Model_ProductAddition');
$productAdditionProperties = Tx_Extbase_Reflection_ObjectAccess::getAccessibleProperties($productAddition);
foreach ($productAdditionProperties as $propertyName => $propertyValue) {
Tx_Extbase_Reflection_ObjectAccess::setProperty($productAdditionClone, $propertyName, $propertyValue);
}
$newStorage->attach($productAdditionClone);
}
$productClone->setProductAddition($newStorage);
// This have to be repeat for every ObjectStorage property, or write a service.
For me, the Solution "Clone $Object and resetUid() in the Modell" does not work .. maybe this solution had worked in older TYPO3 Versions but in 7.6. LTS it throws an exception
#1222871239: The uid "61" has been modified, that is simply too much.
so maybe someone can find my solution helpful as it is much less code than the other reflection Solution: (and you do not have to think about to set all single properties .. )
Given: an Object called $registrant with all Data.
Wanted Result: a copy of that Object with same Data, but new Uid ..
/** #var \JVE\JvEvents\Domain\Model\Registrant $newregistrant */
$newregistrant = $this->objectManager->get( "JVE\\JvEvents\\Domain\\Model\\Registrant") ;
$properties = $registrant->_getProperties() ;
unset($properties['uid']) ;
foreach ($properties as $key => $value ) {
$newregistrant->_setProperty( $key , $value ) ;
}
I think the add command ignores objects that already exist.
You could try to clone the object and then add the clone to the repository $copy_of_object = clone $object;. Or maybe create a new Object with all the same properties.

Resources