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.
Related
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.
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.
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.
I'm trying to extend a form and add an upload file field from within a module, I can see the file field just fine, but it's empty when I submit the form, the enctype is set.
$form['#attributes'] = array(
'enctype' => "multipart/form-data"
);
$form['file_upload'] = array(
'#type' => 'file',
'#title' => 'Attach Image'
);
custom form submit hook:
$form['#submit'][] = 'user_images_handler';
is being called, but when I dump the form, the file field is empty, and when I try to access it, it's empty as well.
File uploads are special in that the 'submitted' (uploaded) data does not end up in the form, but needs to be processed separately (uploading is not really a part of form submission but a separate transmission process).
See the docs for file_save_upload(), and as an example, see how it is used on form submission usage from within the upload module.
Basically, you just try to save the upload by calling file_save_upload() with the name of the upload field (and some other arguments) and check the result of this try.
Hmm, I tried that and it didn't work...it still says the upload field is empty.
I have a debugging print statement at the submit function but it doesn't print so I don't think it is even getting to the submit function.
Where are you suppose to put the file_save_upload() call?
Note, my form is named mmil_upload_form ( $form_state ) and the submit function is named function mmil_upload_form_submit ( $form, &$form_state )
can somebody help me how to customize the search result of a apache solr search. i was only able to access these variables [comment_count] => [created] => [id] => [name] => [nid] => [title] => [type] => [uid] => [url] => [score] => [body].
how can i access other variable like status, vote .... from the index ( i don't want to access the database for retrieving these values, i want to get it from the index itself)
i need to display no of votes for that specific node in the result snippet
i need to understand
1. how to index votes field
2. how to show the vote, status... in result snippet.
Votes are a poor choice for indexing for a couple of reasons:
Votes can change quickly
When a vote is made, the node is not updated. As such, apachesolr won't know to re-index the node to pick up the change.
If by 'status' you mean the node->status value, then the answer is that it will always be 1. Unpublished nodes are never indexed.
Now, if you want to add something else to the index, you want hook_apachesolr_update_index(&$document, $node) - this hook gets called as each node is being indexed, and you can add fields to $document from $node to get the values into the solr index. However, you want to use the pre-defined field prefixes - look at schema.xml to find the list.
Below is example of code to add fields for sorting, and for output.
/**
* Implementation of hook_apachesolr_update_index()
* Here we're adding custom fields to index, so that they available for sorting. To make this work, it's required to re-index content.
*/
function somemodule_apachesolr_update_index(&$document, $node) {
if ($node->type == 'product') {
$document->addField('sm_default_qty', $node->default_qty);
$document->addField('sm_sell_price', $node->sell_price);
$document->addField('sm_model', $node->model);
foreach ($node->field_images AS $image) {
//$imagecached_filepath = imagecache_create_path('product', $image['filepath']);
$document->addField('sm_field_images', $image['filepath']);
}
}
}
/**
* Implementation of hook_apachesolr_modify_query()
* Here we point what additional fields we need to get from solr
*/
function somemodule_apachesolr_modify_query(&$query, &$params, $caller) {
$params['fl'] .= ',sm_default_qty,sm_field_images,sm_sell_price,sm_model';
}
If you want to totally customize output, you should do following:
1) Copy search-results.tpl.php and search-result.tpl.php from /modules/search to your theme's folder.
2) Use the $result object as needed within search-result.tpl.php
3) Don't forget to clear the theme registry by visiting admin/build/themes
Or as mentioned about, you can override using preprocessor hooks.
Regards, Slava
Another option is to create a view(s) of your liking with input argument nid, then create the following preprocess in your template.php file:
function MYTHEME_preprocess_search_result(&$vars) {
$vars['myView'] = views_embed_view('myView', 'default', $vars['result']['node']->nid);
}
Matching the view name 'myView' with the variable name makes sense to me. Then you can use the variable $myView in your search-results.tpl.php file.
Here's a video by the makers of the Solr Search Integration module with an overview on how to customise what nodes and fields are indexed, and what Solr spits out as a search result...
For Drupal 6:
http://sf2010.drupal.org/conference/sessions/apache-solr-search-mastery.html
And Drupal 7:
http://www.acquia.com/resources/acquia-tv/conference/apache-solr-search-mastery
It all looks very customisable!