Drupal 9 - Why is my body not saved with Node::create() - drupal

Thanks for taking the time to read my problem.
I'm trying to write a module that import content from an old version of Drupal to a new Drupal 9 website.
I managed to extract all content from the old database. It is stored in an array that I pass to a function reponsible for creating the node in the new DB. The problem I'm having is that the body of the node is not saved. Records are created in the tables node, node_field_data, node_revision, node_field_revision but nothing is created in node__body.
I tried two different methods :
1-
$node = Node::create(['type' => 'article_epingle']);
$node->langcode = $a["lang"];
$node->title = $a["title"];
// ... status, promote, ...
$node->body = array("value" => $a["body"], "format" => 'full_html');
// ... a few custom fields
$node->enforceIsNew();
$node->save();
Or
$node = Node::create(['type' => 'article_epingle']);
$node->langcode = $a["lang"];
$node->title = $a["title"];
// ... status, promote, ...
$node->body->value = $a["body"];
$node->body->format = 'full_html';
// ... a few custom fields
$node->enforceIsNew();
$node->save();
And 2 -
$node = \Drupal::entityTypeManager()
->getStorage('node')
->create(['type' => 'article_epingle',
'title' => $a["title"],
'body' => $a["body"],
// ... other fields
]);
$node->save();
The result is the same everytime, the body is not saved in DB. The save() method returns "1" and the newly created node appears in /admin/content but can't be displayed. The following error is returned if I try to display the node :
Error: Call to a member function displaySubmitted() on null in template_preprocess_node() (line 528 of core/modules/node/node.module).
Does anyone already had the same problem ?
Thanks in advance for your input !

I solved my problem.
I guess I did something wrong while installing Drupal because my code works fine after installing everything again.

Related

Drupal - Importing a taxonomy with migrate module from a table and creating/updating existing terms

I need to import a list of terms into my taxonomy from a source I loaded in the database.
The problem is I allready have this taxonomy on my site (loaded wihtout migrate) with terms that are used by reference in other content, so I got to keep existing term and update them or create the new ones.
To link my taxonomy source and the existing taxonomy I have an unique code for each term, so I added a code field to my vocabulary and filled it for each existing term.
I am currently able to create and update terms with my current Migration class, but if the name of my term on the site and the name of the term in my source is different, the import will create a new term instead of updating its name even if the code is the same.
Here my Migration Class :
class TotoMigration extends Migration {
private $list_term = array();
public function __construct($arguments) {
parent::__construct();
$this->softDependencies = array('TotoParent');
// get data from the custom table containing the new terms to create or update
$query = db_select('toto', 'f')
->fields('f', array(
'CODE', // code
'LIBLONG', // name
'PARENT', // parent
)
);
$this->source = new MigrateSourceSQL($query);
$this->destination = new MigrateDestinationTerm('toto_tax');
$this->map = new MigrateSQLMap($this->machineName,
array(
'CODE' => array('type' => 'varchar',
'length' => 5,
'not null' => TRUE,
'description' => 'Code',
)
),
MigrateDestinationTerm::getKeySchema()
);
$this->addFieldMapping('name', 'LIBLONG');
$this->addFieldMapping('field_code', 'CODE');
$this->addFieldMapping('parent', 'PARENT')
->arguments(array('source_type' => 'tid'))
->sourceMigration('TotoParent');
// create a list of existing toto terms with code => tid
$list_term = db_query("select fc.field_code_value, ttd.tid
from taxonomy_term_data ttd
left join taxonomy_term_hierarchy tth on tth.tid=ttd.tid
left join field_data_field_code fc on fc.entity_id = ttd.tid
where ttd.vid=10
and tth.parent!=0;")->fetchAllKeyed();
}
public function prepareRow($row) {
// Always include this fragment at the beginning of every prepareRow()
// implementation, so parent classes can ignore rows.
if (parent::prepareRow($row) === FALSE) {
return FALSE;
}
// if the destination is not mapped in migrate we tell him where to go
if (!isset($row->migrate_map_destid1) && isset($list_term[$row->CODE])) {
$row->migrate_map_destid1 = $list_term[$row->CODE];
}
}
}
I then load the import with drush (and --update option).
I must be missing something, if anyone got a clue it will be welcome.
After many tries, the problem reside in the fact the module Migrate does not support Creating content and Updating content in the same migration class (I even read it will sometime claim to update content and just do nothing).
So the solution is pretty simple, create 2 classes :
One for Creating content
One for Updating content
Your Creating class will be the same.
Your Updating class will need to have a systemeOfRecord set to DESTINATION :
$this->systemOfRecord = Migration::DESTINATION;
So it knows to only update and not recreate the content, it will keep current fields not mapped and update fields mapped that are not part of the MigrateSQLMap :
$this->map = new MigrateSQLMap($this->machineName,array(...));
The tricky part will be to find corresponding nid/tid of your content so you can map it to your imported data and then to separate data used to update or create content.

foreign key not persisted symfony2 after a clone

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 !

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.

Drupal7 copy filefield to another node at submit

I have a content type that has unlimited filefield fields in addition to other fields. At node save/submit I would like to create an additional node for each file in the field, and assign that filefield to it. I'm fine with the nodeapi hooks and progmatically creating the node, but I can't access the content of the filefield from the node. When I print the filefield contents from within hook_node_insert I get:
...
(
[fid] => 38
[display] => 1
[description] =>
[upload_button] => Upload
[remove_button] => Remove
[upload] =>
)
....
Not the formatted and proceed field I would normally see. My suspicion is that I can access this somehow from the form and do a form submit after modifying it, but I'm not sure how to do this and it may not the best way. Let me know if you have any tips on this, greatly appreciated.
since we have [fid] populated we can use file_load($fid) to load the file object. Then you can cast this file object to array and then attach it to the file field of newly created node.
Loop through the array and for each $fid you encounter..
$file = file_load($fid);
$new_node= new StdClass();
$new_node->type = 'image';
$new_node->language = LANGUAGE_NONE;
node_object_prepare($new_node);
// add additional data about new node.
$new_node->field_custom_files[LANGUAGE_NONE][] = array($file);
node_submit($new_node);
node_save($new_node);
I have not tested this, but do let me know if you face any issues.

Creating new event in a module

I'm trying to execute some code right before content is deleted. The Rules module has events for
After updating existing content
Before saving content
After saving new content
After deleting content
However, none of these execute my code at the right time.
I discovered a simple module called Predelete, which provides hooks for executing code before deletion. This seemed like an excellent candidate to call the Rules event from.
So, I created a very simple module based on the "predelete_field" example module contained within Predelete. The folder is called "predelete_field", is in the "modules" folder, and contains the following files:
1: predelete_field.info
core = "7.x"
dependencies[] = "rules"
dependencies[] = "list"
dependencies[] = "predelete"
description = "Example for the predelete module with a content type and a node"
name = "Predelete Field"
package = Other
project = "predelete_field"
version = "7.x-1.0"
; Information added by drupal.org packaging script on 2011-07-11
version = "7.x-1.1"
core = "7.x"
project = "predelete"
datestamp = "1310360219"
2: predelete_field.module
<?php
/**
* Implements hook_predelete_node().
*/
function predelete_field_predelete_node($node) {
drupal_set_message( "PREDELETE HOOK CALLED", 'warning' );
rules_invoke_event('predelete_field', $node);
$deletable = TRUE;
$reason = t('Deletable by default.');
return array('result' => $deletable, 'reason' => $reason);
}
3: predelete_field.rules.inc
<?php
/**
* Implements hook_rules_event_info() on behalf of the predelete_field module.
*/
function rules_predelete_field_event_info() {
$items = array(
'predelete_field_predelete' => array(
'label' => t('Before deleting content'),
'group' => t('Node'),
),
);
return $items;
}
Unfortunately, this does not appear to work: The event does not show up in the event list, even after clearing cache and disabling and re-enabling the module. Likewise, the drupal_set_message function does not appear to fire.
Is anyone able to spot any errors I may have made, or provide a solution?
The Predelete module just seems to hook into the confirmation form for multiple node deletions, it won't ever be fired using Rules as Rules doesn't invoke the form but uses the node API instead.
If you look at the node_delete_multiple() function though you'll see that several hooks are called before any content is actually deleted from the database (namely hook_node_delete and hook_entity_delete). One of these is the hook you'll want to implement in your custom module, like so:
function mymodule_node_delete($node) {
// Perform some action based on values in $the node object.
// Nothing has been deleted from the database at this point.
}
You'll need to clear Drupal's caches again when you've added that hook but according to the documentation it should work.

Resources