I hope this is the right place to post this.
I have been working back and forth with nevets over in the post installation support forum to develop a function to be used as a module to check titles and fields in custom content types when nodes are created. You can see the thread at http://drupal.org/node/89599. Unfortunately, I've been cluttering-up the thread over there with my posts so I figured I had better start my own thread to keep things more organized.
nevets had written a nice little function that checked for duplicate titles for nodes of a specific type. I wanted to do a more robust version, and he really helped me out in with my issues (I'm really new to PHP). I'm calling the new function "Dupe-R-Scooper" since it tries to scoop-up duplicate data before it gets published.
Anyway, long story short, I'd like to get a little help with the function, and get this developed into a full module with an admin-area UI.
The basic overview of the function is this:
1) we hook nodeAPI to catch the validate operation
2) we use a switch to check a number of node types
3) we run a query to pull specified fields and node title/nid from the {node} and {content_type_*} tables.
*3b) I need to somehow write a loop here to make sure we check everything that could be a match (but don't know how).
4) we run a few if() conditions against the new and existing data
5) if we get the proper matches, meaning the data already exists, we return a form_set_error() on the field with a friendly message.
So here is the code so far. Everything works without errors, I just don't have the loop written to make sure we're checking everything or just the first found data. that's where I need the help right now. I've generalized the type and field names.
<?php
// $Id: duperscooper.module,v 0.1.0 2007/10/11 ZrO-1 Exp $
/**
* Dupe-R-Scooper implements the nodeAPI hook - duperscooper_nodeapi()
* This function is to help reduce the number of duplicate or equivalent nodes of a particular type in our system.
*/
function duperscooper_nodeapi(&$node, $op) {
if ( $op == 'validate' ) {
switch ($node->type) {
case 'mytype1': // set this to your machine-readable type (we want to check title and 1 field for this type)
$sql = "SELECT f.field_my_field_value, n.title, n.nid
FROM {node} n JOIN {content_type_mytype1} f USING(nid)
WHERE type = '%s' OR title = '%s' OR field_my_field_value = '%s'";
$results = db_query($sql, $node->type, $node->title, $node->field_my_field[0]['value']);
$existing = db_fetch_object($results);
if (!$node->nid || $existing->nid != $node->nid) { // if the node is new, OR we're not updating/editing an existing node
if ($existing->title == $node->title) { // if an existing title matches the new title
$link = l('view the existing '.$node->type, 'node/'.$existing->nid, NULL, NULL, NULL, FALSE, FALSE);
form_set_error('title', 'Oops! There is already a '.$node->type.' called '.$node->title.'. Here is a link to '.$link.'. Please be absolutely sure that you are not creating a duplicate '.$node->type.'. Thanks!');
}
if ($existing->field_my_field_value == $node->field_my_field[0]['value']) { // if an existing field matches the new field
$link = l('view the existing '.$node->type, 'node/'.$existing->nid, NULL, NULL, NULL, FALSE, FALSE);
form_set_error('field_my_field', 'Oops! There is already a '.$node->type.', called '.$node->title.', with the value '.$existing->field_my_field_value.'. Here is a link to '.$link.'. Please be absolutely sure that you are not creating a duplicate '.$node->type.'. Thanks!');
}
}
break;
case 'mytype2': // set this to your machine-readable type (we want to check a combo of title and 2 fields for this type)
$sql = "SELECT f.field_first_field_value, f.field_second_field_value, n.title, n.nid
FROM {node} n JOIN {content_type_mytype1} f USING(nid)
WHERE type = '%s' OR title = '%s' OR field_first_field_value = '%s' OR field_second_field_value = '%s'";
$results = db_query($sql, $node->type, $node->title, $node->field_first_field[0]['value'], $node->field_second_field[0]['value']);
$existing = db_fetch_object($results);
if (!$node->nid || $existing->nid != $node->nid) { // if the node is new, OR we're not updating/editing an existing node
// if an existing title matches the new title AND an existing first_field matches the new first_field
if ($existing->title == $node->title && $existing->field_first_field_value == $node->field_first_field[0]['value']) {
$link = l('view the existing '.$node->type, 'node/'.$existing->nid, NULL, NULL, NULL, FALSE, FALSE);
form_set_error('field_first_field', 'Oops! There is already a '.$node->type.' called '.$node->title.' with first_field value. Here is a link to '.$link.'. Please be absolutely sure that you are not creating a duplicate '.$node->type.'. Thanks!');
}
// if an existing title matches the new title AND an existing second_field matches the new second_field
if ($existing->title == $node->title && $existing->field_second_field_value == $node->field_second_field[0]['value']) {
$link = l('view the existing '.$node->type, 'node/'.$existing->nid, NULL, NULL, NULL, FALSE, FALSE);
form_set_error('field_second_field', 'Oops! There is already a '.$node->type.' called '.$node->title.' with second_field value. Here is a link to '.$link.'. Please be absolutely sure that you are not creating a duplicate '.$node->type.'. Thanks!');
}
}
break;
}
}
}
?>
So there are more cases in my particular application of this function, but I think the above example gives you a good idea of what I'm trying to accomplish.
Goal 1 for me is to get the checks looped so i can be sure we're checking everything
and a distant goal 2 for me is to get the function fully generalized and a UI built for this where an admin could check-off the content types and fields that they want to check and those selections get passed to the function.
I look forward to hearing from you guys.
Comments
Here is the first case
This part
needs the loop since you may have more than one recorrd returned by the query
Ah... WHILE (results)... I didn't think of that
LOL once again nevets hits one out of the park when I get stuck.
I was trying to do some weird count() foreach() combo and was totally lost.
the while() is perfect. Short sweet and exactly what was needed.
that's awesome nevets thanks!.
Lack of other responses
So is no one else here interested in preventing duplicate data?
Is there some other more-efficient way of preventing dupes in drupal and CCK?
Is this not "the drupal way" of preventing duplicates?
I don't need people to submit code and examples, I'd just like some feedback.
thanks
Yes please!
hi,
I would really like a module like this on drupal 5.6.
I know the node_import module is also checking off duplicate note-titles.
Could you please build your code into a module?
I also think you get more response when it is a downloadable 5.6 module which you just have to install.
Thanks for your effort already!
greetings,
Martijn
This is awesome. If I can get it to work
This is exactly what I need. But I cannot seem to get it to work. It would be great if it was a full module.
Cool feature to add
I would be neat if it could have the ability "as you type" So as you type the text in the title, it will display if it was all ready used. This would save from submitting it and seeing it all ready exists. This was a user does not have to fill out the node only to find out the work was a waste of time if it was all ready there.
Howdy
exactly what i am scratching right now..! Did this code ever turned into something usable? Any hint or step further appreciated, thank you