Programmatically getting nodes by title in Drupal 7
Asked Answered
C

6

9

I have a module that programmatically adds a whole lot of nodes to my Drupal 7 site. To prevent duplicates I want to check whether the title of my newly created node doesn't already exist in the system. There will never be a case where two nodes have the same name. I've tried using node_load(), but that doesn't seem to allow me to load nodes based on a title.

I'm stumped on how to proceed, so I was wondering if somebody here could help me.

To summarize: How do I check whether a node with a certain title already exists?

Thanks.

Cunnilingus answered 3/12, 2011 at 18:1 Comment(2)
If you want we can move this to our [Drupal site](drupal.stackexchange.com) where it might get better coverage. Please flag if you'd like us to do this (but don't cross-post). Thanks.Shira
Related: How to load a node based on its title? at DSMakings
S
8

You could get all the node titles with a SQL query like:

 $row = db_query('SELECT nid FROM {node} WHERE title = :mytitle', array(':mytitle' => $myNewTitle))->fetchField();
 if(!isset($row['nid'])) {
    //safe to continue
 } else {
    //that title is already there and its in node with node id "nid"
 }

And if there are no results returned then you may assume you're good to go.

Or alternatively you could edit the node table to make the title field UNIQUE and handle the error thrown by MySQL. Don't really know how feasible that is though.

Stripy answered 3/12, 2011 at 21:58 Comment(2)
+1 this is the best way to accomplish this. You should consider joining the revisions table in though to get the latest version of the dataAnywhere
Please, wrap your table name in curly brace when writing custom DB query.Capernaum
C
18

Node are entities, so you can use the EntityFieldQuery class to query for them. This way, you don't have to bother how the information is stored in the database. And, shall you need to add additional filters based on field values, code evolution will be easy.

$result = (new EntityFieldQuery())
  ->entityCondition('entity_type', 'node')
  ->propertyCondition('title', $title)
  ->execute();
$title_is_unique = empty($result['node']);
Capernaum answered 4/12, 2011 at 10:16 Comment(4)
That's an awful lot of overhead for such a simple query, field storage doesn't extend to entity properties so using EntityFieldQuery here has no real benefitAnywhere
Yep, there is some overhead using EntityFieldQuery to query a single node property. But since it will only apply to a multi-nodes creation process, there is a good chance that the performance cost will be marginal. And it will be easier to maintain should the node creation process evolve into something that should check for several conditions.Capernaum
If you use Entity translation and the Title module, the code needs to be updated to query for the title_field field instead of the title property.Capernaum
I know this is many years later, but there's a bug in this code - you can't chain a PHP constructor. In 5.4 and up you can use (new EntityFieldQuery())->, but as written this should give a fatal errorAnywhere
S
8

You could get all the node titles with a SQL query like:

 $row = db_query('SELECT nid FROM {node} WHERE title = :mytitle', array(':mytitle' => $myNewTitle))->fetchField();
 if(!isset($row['nid'])) {
    //safe to continue
 } else {
    //that title is already there and its in node with node id "nid"
 }

And if there are no results returned then you may assume you're good to go.

Or alternatively you could edit the node table to make the title field UNIQUE and handle the error thrown by MySQL. Don't really know how feasible that is though.

Stripy answered 3/12, 2011 at 21:58 Comment(2)
+1 this is the best way to accomplish this. You should consider joining the revisions table in though to get the latest version of the dataAnywhere
Please, wrap your table name in curly brace when writing custom DB query.Capernaum
C
4

Here is this D7 approach utilizing the API and the count function.

$query = db_select('node', 'n')
  ->fields('n', array('nid'))
  ->condition('n.title', $myNewTitle, '=');

return $query->countQuery()->execute()->fetchField();
Crispin answered 11/3, 2013 at 18:33 Comment(0)
E
4
node_load_multiple(array(), array('title' => $title));

Source: The guts of DrupalWebTestCase::DrupalGetNodeByTitle

Embarrassment answered 16/5, 2013 at 15:17 Comment(0)
M
2

Just a friendly headsup. For security you should ALWAYS use curly brackets around tables in drupal sql querys! It prevents SQL injection attacks.

so it should be: (notice {node} )

 $row = db_query('SELECT nid FROM {node} WHERE title = :mytitle', array(':mytitle' =>   $myNewTitle))->fetchField();
 if(!isset($row['nid'])) {
    //safe to continue
 } else {
    //that title is already there and its in node with node id "nid"
 }
Margherita answered 4/12, 2011 at 22:52 Comment(2)
For a node query, it seems that in order to have node access restrictions check working, you have to use a dynamic query with the 'node_access' tag, see drupal.org/update/modules/6/7#db_rewrite_sql. For this case, this doesn't really matter as the duplicate title detection shouldn't worry about node access restrictions. But using dynamic query for nodes it is a good habit to take.Capernaum
Curly brackets are only to prefix the table if you have your prefix set in your settings file. has nothing to do with SQL Injection. I use the D7 sql API to add fields and conditions so in your example if I was to guess, your variable $myNewTitle is NOT escaped?Crispin
L
1

The correct way is to add an UNIQUE index to the database. Then you will be sure that all titles are unique. Is easy:

db_add_unique_key("{node}", "title_uniq", array("title"));
Lafreniere answered 14/3, 2017 at 18:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.