RSS
 

Drupal 7: программное создание нод, влкючающее field colletion

I had to import a few thousand items from a legacy database to a Drupal 7 site and found that it was quite easy to do so programmatically. Here I'll first show you the basic code for adding nodes and then I'll talk about different field types, including how to add images and term references (taxonomy). If you have any questions, just ask in the comments and I'll be happy to help!
Basic node creation - example

Put the code below in e.g. foo.php in your Drupal root directory, and execute it either through the browser or from the command line. I prefer the latter, running the script from the Drupal directory (php foo.php). Note that you have to make sure that the input is valid!

<?php
define('DRUPAL_ROOT', getcwd());
$_SERVER['REMOTE_ADDR'] = "localhost"; // Necessary if running from command line
require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
 
$bodytext = "Foo m bar fnord?";
 
$node = new stdClass(); // Create a new node object
$node->type = "article"; // Or page, or whatever content type you like
node_object_prepare($node); // Set some default values
// If you update an existing node instead of creating a new one,
// comment out the three lines above and uncomment the following:
// $node = node_load($nid); // ...where $nid is the node id
 
$node->title    = "A new node sees the light of day";
$node->language = LANGUAGE_NONE; // Or e.g. 'en' if locale is enabled
 
$node->uid = 1; // UID of the author of the node; or use $node->name
 
$node->body[$node->language][0]['value']   = $bodytext;
$node->body[$node->language][0]['summary'] = text_summary($bodytext);
$node->body[$node->language][0]['format']  = 'filtered_html';
 
// I prefer using pathauto, which would override the below path
$path = 'node_created_on' . date('YmdHis');
$node->path = array('alias' => $path);
 
if($node = node_submit($node)) { // Prepare node for saving
    node_save($node);
    echo "Node with nid " . $node->nid . " saved!\n";
}
?>

$node->language is important! If you don't have the locale module enabled, the node will not be assigned any particular language. Or rather, the language code used then is LANGUAGE_NONE, which is a constant with the value und (undefined) in Drupal. If you have locale enabled nodes can exist in more than one language, and you should specify the language code. Go to Configuration -> Regional and language -> Languages to configure languages and see what code you should use. For English, it would be en; for Swedish, it would be sv; etc.

Other things you might want to set:

$node->status (1 or 0): published or not
$node->promote (1 or 0): promoted to front page
$node->sticky (1 or 0): sticky at top of lists
$node->comment: 2 = comments on, 1 = comments off

This all you need for basic node creation. I will continue with different field types. The code below should be placed before the node_submit line. All fields can be accessed with $node->field_fieldname, where fieldname is the name you find on Structure -> Content types -> Manage fields.

The best way to see how Drupal nodes are made up, and how to work with the various fields, is to simply look at the structure of an existing node. Just create a new PHP file with the first bunch of lines above (including drupal_bootstrap) and add print_r(node_load($nid)), where $nid is the node id. Or use drush.
Setting various field types
Text or integer field

Nothing special:

$node->field_fnordtext[$node->language][0]['value'] = "Fnord fnord fnord";
 
Multiple values (make sure the field is configured to allow this):
$node->field_author[$node->language][]['value'] = "Ford, Tom";
$node->field_author[$node->language][]['value'] = "Fnord, Dom";

Creation time

To set a node's creation time, you'd think you could just set $node->created. And you can - if you don't use node_submit() (or if you set it after node_submit()). Looking at the node_submit() function in node.module, we find this line:
$node->created = !empty($node->date) ? strtotime($node->date) : REQUEST_TIME;

Which means that node_submit() will set $node->created to the current time if $node->date doesn't exist. So, if you want to set the creation time, you have to do something like this:
$node->date = "2009-05-27";

This also means that if you are updating a node programmatically, don't forget to set $node->date; otherwise node_submit() will change the creation time. (I will write another post about updating nodes.)
Date field (datetime, date, datestamp)

If you use the date module, you get three new field types - date, datetime and datestamp. (See this page to read about the differences; also check out this discussion.)

If your field is named datetest, you could do:

// For datetime
$node->field_datetest[$node->language][0][value] = "2011-05-25 10:35:58";
 
// For date
$node->field_datetest[$node->language][0][value] = "2011-05-25T10:35:58";
 
// For datestamp
$node->field_datetest[$node->language][0][value] = strtotime("2011-05-25 10:35:58");

Note that you don't need to specify a complete date; for datetime and date you can just pad with zeros, e.g. "2011-05-00 00:00:00" (datetime), "2011-00-00T00:00:00" (date), etc. For datestamp you could just do e.g. strtotime("2011-05-25").

Important: Also note that while the exact value you specify will be stored in the database, the actual time displayed on the site might be different depending on timezone settings. When you create a new datetime/date/datestamp field, you get to choose between five different timezone handling methods. The default one is "site's time zone":
When entering data into the field, the data entered is assumed to be in the site's time zone. When the data is saved to the database, it is converted to UTC.

However, if you set a date field programmatically like in the above example then no conversion takes place, so make sure you account for the field's timezone settings. Or in other words, if you use "site's time zone", make sure the time is in UTC.
Boolean field

Single on/off checkbox:
$node->field_bork[$node->language][0]['value'] = 1;
Term reference (taxonomy) field

Set the term reference field tags to taxonomy term id 25 (for more than one term, just repeat the line; and note that it doesn't matter whether the widget type is select list, check boxes/radio buttons or autocomplete):
$node->field_tags[$node->language][]['tid'] = 25;

(If you are trying this with the tags field in the default article content type, and have locale enabled, and have set $node->language to e.g. 'en', and it doesn't seem to work: I also encountered this oddity (bug?). For some reason tags are saved to $node->field_tags[und] instead of $node->field_tags[en], while e.g. body is saved to $node->body[en] as expected. If I create a new term reference field, it works as it should. So either do that, or change the above to $node->field_tags[und][]['tid'].)

As you can see, you need the know the taxonomy term's id. Fortunately, there’s a Drupal function to help us with this: taxonomy_get_term_by_name(). You supply the name ("Italy") and it returns an array of matching term objects, so you can do something like this:

if($foo = taxonomy_get_term_by_name('Italy')) {
    $foo_keys = array_keys($foo);
    $node->field_tags[$node->language][]['tid'] = $foo_keys[0];
}

Unfortunately, you can't specify a certain vocabulary with this function. This means that if you have the same term name in more than one vocabulary, the above code will just use whatever happens to come first. If you want to specify a certain vocabulary, say the one with id 9, you could do something like this:

$foo = taxonomy_get_term_by_name('Italy');
foreach($foo as $term) {
    if($term->vid == 9) {
         $node->field_tags[$node->language][]['tid'] = $term->tid;
    }
}

(One way of figuring out the vocabulary id is to run print_r(taxonomy_get_vocabularies());)

But what if you want to create new vocabulary terms for those that aren’t already in the database? You can use taxonomy_term_save(), like this:

$new_term = array(
    'vid' => 1,
    'name' => 'Fugazi',
    // You can optionally also set id of parent term, e.g. 'parent' => 25
);
$new_term = (object) $new_term;
taxonomy_term_save($new_term);

$new_term is very conveniently updated with the tid of our newly created term, which you can get with $new_term->tid.
Node references

If you use the references module for node/user references you can set a reference like this:

// 453 is the id of the referenced node
$node->field_author[$node->language][]['nid'] = 453;

This way you can also add multiple references in the same field - just repeat.
Image field

Attaching an image to any given image field is easy. Create a file object, copy the file and associate the file object with the image field:

$file_path = drupal_realpath('foo.jpg');
$file = (object) array(
            'uid' => 1,
            'uri' => $file_path,
            'filemime' => file_get_mimetype($file_path),
            'status' => 1,
);
// You can specify a subdirectory, e.g. public://foo/
$file = file_copy($file, 'public://');
$node->field_image[$node->language][0] = (array) $file;

To update a node simply load it, make the changes, and then save it. The following example assumes there is a pre-existing node with the node id (nid) of 1.

<?php
/**
 * Basic Node Update Example for Drupal 7
 *
 * This example:
 * - Assumes a standard Drupal 7 installation
 * - Assumes there is a node with a nid of 1
 */
  $nid = 1;
  $node = node_load($nid);
  $node->title = 'Updated Title Text';
  node_save($node);
?>

Remember, you are responsible for insuring the data being saved is valid.

How to programmatically create a taxonomy term

This one is the most easiest part of the tutorial. To create a term you just need to do the following:

$term = new stdClass();
$term->name = ‘Term Name’;
$term->vid = 1; // ‘1’ is a vocabulary id you wish this term to assign to
$term->field_custom_field_name[LANGUAGE_NONE][0]['value'] = ‘Some value’; // OPTIONAL. If your term has a custom field attached it can added as simple as this
taxonomy_term_save($term); // Finally, save our term

Related links that are formed upon LINK module + text field + field collection module can be added like this:

     foreach ($related as $rel) {
       if (empty($rel['url'])) {
         continue;
       }
 
       $link = array();
       $link['field_name'] = 'field_news_related_links';
       $link['is_new'] = 1;
 
       $link['field_news_links_description']['und'][0]['value'] = $rel['desc'];
       $link['field_news_related_links_link']['und'][0]['url'] = $rel['url'];       
       $link['field_news_related_links_link']['und'][0]['title'] = $rel['title'];
 
       $tt = new FieldCollectionItemEntity($link);
       $tt->setHostEntity('node', $node);
       $tt->save();
     }

Used links:
http://fooninja.net/2011/04/13/guide-to-programmatic-node-creation-in-dr...
http://timonweb.com/how-programmatically-create-nodes-comments-and-taxon...
http://drupal.org/node/1158602
http://drupal.org/node/1106182
http://drupal.org/node/1179088