Folks,
For reasons that I won't get into too deep right now (but they are good!) I need to save a new node, but make sure it gets a specific node number (nid).
The node is new, data is filled into the node object programmatically, but in stead of just having Drupal decide the nid, I need to be able to force the system to use a nid specified by me. I have of course made sure that this nid doesn't exist already.
Alternatively I'd also be fine with saving with any new nid and then being able to change that nid to something else.
I can see all the implications that this might have, but in the specific case, there's no danger of messing up existing nodes.
I have sought all over the place for any documentation on how Drupal assigns nids and on this specific issue, but the terms are so general that all I get is trivial info on saving nodes programmatically, which is not (only) what I want.
I tried this
$node = node_load($old_nid);
$node->nid = $new_nid;
$node->title .= ' - Forced nid!';
$node = node_submit($node);
node_save($node);
$node = node_load($node->nid);
In other words: load a node, change nid, change title, save it and reload it using the new nid.
This doesn't work.
Nothing gets saved, and the subsequent node_load tries to load a node with the new nid, but finds nothing
An inspection in the database shows no trace of neither the new nid or the title with Forced! in it.
If I leave out the line that forces a new nid on the node, it loads, changes, saves and loads again as expected - with a changed title - but of course with the old nid intact.
I getting a hunch that it isn't possible, but wanted to see if anybody had suggestions or a solution.
Martin
Comments
If you really wanted to force
If you really wanted to force it to happen, you probably could. But that being said, you probably shouldn't! NIDs are automatically created through an auto-increment column in the {node} table. Each time a new node is saved, the number increases. As such, the first node on a site is node 1, then node 2 etc.
There are a few reasons you probably shouldn't do what you want.
1) You'd have to override the database and stop the table from auto-incrementing
2) You would have to create a new index as you would lose your primary index (the NID)
3) The VID (for revisions) is somewhat tied to the NID, which could lead to unforeseen consequences.
Alternatively, you'd be better off just assigning a new number to each node. For example, Ubercart adds a unique model number to each product node. This number can then be used for grabbing nodes.
If you gave a little more detail on what you are trying to achieve, maybe we can help you come up with a smoother less intrusive method of doing what you are trying.
Contact me to contract me for D7 -> D10/11 migrations.
I know I shouldn't!
Jay,
I know I shouldn't!
But in my specific case I'm pinched in the "deployment gap" that many like me have probably experienced more than once.
A production site is rolling and getting updated as productions sites tend to do.
I work on a new design/struture and the client starts adapting this development site to their needs with features not available on the production site. But during the development, the production site gets updated with changes in old nodes and additions of new ones.
The development site works on a database, which is a copy of the production database, but from a while back, and now these have gone each their way to such an extend that it makes no sense to overwrite one with the other.
I need to preserve structure and logic (views etc.) in the developement site, but update and create new content - and only content.
Exporting and importing nodes using something like node_export will not update existing nodes on their current nids, but create new (and duplicate) ones.
Deleting the old nodes and importing all nodes from the production site populates the site with content, but requires a total reconstruction of the menus (which are huge and complex) and most likely also breaks other things such as internal links made with node/nid references.
So I have built a module that exports and imports nodes, but handles conflicts, unchanged nodes, updates and new nodes in an intelligent way.
This works nicely, but encounters a sort of race condition, because while updating node by node, the system will automatically assign new nids to new nodes, but may later in the update encounter a node from the old site with a nid that was recently created for a new node during the import.
The module will then dutifully update this node with the new content in stead of creating it as new, leading to all kinds of havoc as well as of course overwrite (essentially delete) the node that was created just seconds ago.
So when I'm done, I'm missing a part of the newly created nodes. They were created and then overwritten.
All this could be avoided, if I could force the nid to become the same on import as it was on export.
This will of course only work when the development site is under full control and in most other aspects identical to the production site - which is the case here.
I know that features or maybe other modules may help here, but they don't do exactly what I need, and I don't seem to be the only person that has encountered this need. The issues under node_export mentions this in several posts.
Regarding autoincrement, I have considered brutally forcing a new and empty node into the database with SQL and then loading and updating it. Autoincrement only kicks in when nid is not set, so I can control the nid here. If I insert data into the node table with a nid value, it should work.
But as in all other Drupal matters such brutal methods are best avoided since lots of things go on in the system that are spun off by saving a node in the proper way. And you want them to kick in for your new nodes too, which they don't do when simple pumping nodes into the node table through SQL.
So there! Lots of words, but nice enough to get it penned down... for my own sake too.
I'll do a quick test using the brutal method. It may just work.
Martin
Create node with a nid
Have you found the way to create a node with given nid?
GEO, No, I gave up and merged
GEO,
No, I gave up and merged my content in such a way that I didn't need to "reuse" nid's. I think it's a fundamental no-go, and even though I spent quite a lot of time trying to code my way out of my mess, I gave up in the end. Getting everything coordinated - nodes, node revisions, fields and whatnot - was too much of a hassle to do programmatically, and not easy (if at all possible) to do using the API. Since this was a client project, I found other ways.
Martin
Possibly SOLVED
I think I've been able to get this to work. Basically you need to override/edit the node_save() function and the function it calls called drupal_write_record().
node_save() - edited and replaced with node_insert_with_defined_nid(&$node)
drupal_write_record() - edited and replaced with drupal_write_record_via_insert_with_defined_id($table, &$object, $update = array())
Importantly - before creating the new record double check that a node with that nid doesn't already exist.
Call the function with:
node_insert_with_defined_nid($node);
Here are the modified functions:
This looks very useful. Where
This looks very useful. Where is the best place to insert this code given that it's for very occasional use, and where would you run it? The ideal would be a module which implements a "set node id to:" field in the save options. But I guess it would need some kind of check routine to ensure that the NID doesn't already exist before saving, to avoid corrupting data. Or maybe a dropdown selector listing all NIDs < total node count (or < autoincrement vaule) that are empty. I assume that adding an NID > autoincrement value would be asking for trouble.
Errors when run in drush
I tried running the code above with drush scr using the following bit of code to load $node(10), change its title, and save it as $node(3) - a node that had been deleted and for which there is no redirect:
First of all I got the following error:
So I commented out the offending line which allows modules to modify the node before saving. Since I was loading an existing node I figured this was not necessary.
Re-running the script, I get the following fatal error:
Any idea what is going wrong?
Change vid too
OK I realized I need to change the vid too:
But now I'm getting the following error:
It might be worth bearing in
It might be worth bearing in mind that the code was for D6.
If you want to see how to save a node with a defined ID in D7 look in the file includes/profiles/nodes.inc in the module https://drupal.org/project/data_export_import
AFAIR - In D7 you do not need to use a modified drupal_write_record function (i.e. drupal_write_record_via_insert_with_defined_id is not needed) - you can just use drupal_write_record().
This is D6 code?
EDIT: Posted this at the same time as the post above! Thanks!
It seems the above is Drupal 6 code, not Drupal 7, which explains the errors.
See this comment for a Drupal 7 patched function.
There's a module which does exactly this
http://drupal.org/sandbox/bailey86/1278830
It works perfectly with D6 - it exports then imports nodes, users and taxonomy terms. All ID's are preserved - acts like rsync with the --delete switch set.
I'm currently working on the D7 version and have hit the same old 'saving node with defined nid issue' - will probably have to solve it in the same way - i.e. by modifying node_save().
This is now a module
This is now a module which is at:
https://drupal.org/project/data_export_import
It's currently nearly at 5,000 downloads and pretty much stable.
Creating New field
Hi Martin,
Creating new field in content type will help. In which you can assign specific number whatever you want. To avoid duplication make this field as unique field.
For D7 this will work
For D7 this will work:
Drupal developer and consultant
http://www.mikestiv.com
This WILL work for D7 -- I hope.
I needed to recreate a specific NID. I used msti's code snippet to do that.
Presumably, msti's snippet is the 'bare minimum' needed to add a new record to the node table; however, I included several additional attributes to more clearly define the node I wanted to recreate.
Fortunately, I had an old backup of the database, which I was able to query to find the data for the node that was missing from our production environment. I queried the 'node' table, on the 'title' attribute, to find the node ID of the missing page based on it's title. Fortunately, the missing node was a 'Biography page' for a specific person, whose name was in the 'title' attribute. Once I had the nid, type, language, title, and uid attributes for the node I wanted to recreate from the node table of the old database, I was able to use the script to recreate the node -- basically blank -- in the current database. This is the script I used to recreate the missing node:
Note for the truly novice, like myself, put your code in a new php file in the docroot of your Drupal site, then run the command drush scr newfile.php. Once that command is run, you do not need the php file you just created in your docroot.
Now that I had the node in the database, I could load the page on my website. However, the page only displayed the title. To get the text that should be beneath the title, I went back to the old database once again. This time I queried the field_revision_body table's 'body_value' for any reference to the subject of the Biography page I was trying to recreate. This person's name appears on a handful of pages throughout our site, so a few rows of data were returned by the query. I then drilled into the row whose 'entity_id' attribute was equal to the 'nid' attribute I collected for the missing page from the node table, AND whose 'revision_id' attribute was the largest integer (meaning I had the latest revision of the node, as it existed in the old database.) Now that I had the correct 'field_revision_body' record, I simply copied the contents of the 'body_value' attribute (using the phpMyAdmin GUI interface), into the HTML editor for the recreated page! Click Save, and viola!
In researching this issue, lots of people seemed 'hung up' on ensuring that all the data from all of the tables -- especially the 'revision tables' -- was also transferred. And if you want the historical data for the node, that would be a major concern. However, I did not require all of the previous version data, I just wanted a copy of the most current page -- missing from my production web site -- back.
I then tested the newly recreated node, by editing it slightly, and saving. No errors thrown. A first revision was added to the revision table for the newly recreated node, as well -- no problem. Finally, I verified that I had not altered the new 'nid' assignment order (counter). Brand new content pages were being assigned the next new nid available -- it was in the 8000s; not nid 3567, which would have been the next nid to follow the one I forcibly recreated using the above script.
If anyone foresees any negative consequences to this process, that I am not aware of, I would greatly appreciate the feedback.
The above code works fine but
The above code works fine but logs two errors of type "node" and "php", both with the same message:
MySql documentation simply describes the message without any additional information.
So a couple of concerns:
1. Will this new entry affect the MySql database in any way?
Any data integrity issues on MySql?
2. Does Drupal do a clean delete when a node is deleted from the UI.
Are all references created by Drupal on node creation, removed on node deletion?
Any data integrity issues on Drupal?
I don't think the code worked
I don't think the code worked for you. The message says you that you tried to add the node with nid 123 which already exists in the system. I am not sre what you are trying to achieve, but you may want to run the code with a nid that does not already exist.
Drupal developer and consultant
http://www.mikestiv.com
You're right!
You're right!
Was using a rather quick hack to run the php code, which was running the code twice.
The error was getting logged on the 2nd run.
The code works fine.
Also used some of the code from the D7 docs for setting default etc.
Thanks a lot!
Here's the whole code:
This module might be helpful,
This module might be helpful, as well–
https://www.drupal.org/project/custom_nid
Drupal samurai for hire, based in Buffalo, New York, USA.
15+ years Drupal, 20+ years web.
http://basicmagic.net
This should work in Drupal 9.
This should work in Drupal 9. It's useful when building a new site for migration.