I'm going to dive into some actual code in this post to demostrate Drupal without Views. I'm only going to cover how to write the SQL — I'll cover the theming functions in a third part.
I'm starting from the assumption that you're already comfortable with SQL (particularly select statement and joins). If you don't understand what I just wrote, look up tutorials on those topics before trying to read here.
Database Abstraction Layer in Drupal
The typical pattern for getting data out of the database using SQL is:
- Examine the database structure
- Write a working SQL query
- "Drupalize" the sql syntax
- Pass the query through
db_query()
- Retrieve query results using
while
anddb_fetch_object()
For this article, we'll write a query that grabs the node ids, titles, and bodies for the last three blog posts. Let's imagine I'm rewriting my homepage view. So we'll want the blog posts in date order, starting with the most recent at the top of the list. At the end of this post, our code should return an array of three each objects with the properties nid
, title
, and body
ordered by created date.
1. Examine the Database Structure
There's a lot of tables in Drupal. Usually finding the fields we want involving poking around in the database. Never poke around the live database since you could ruin your site. Instead, download a copy of the database to your own computer and look at the database in your favorite MySQL client. If don't have one already, I highly recommend HeidiSQL for windows. You could PhpMyAdmin, but I find it slow and complicated compared to a desktop application like HeidiSQL.
After poking around in my database, I found that the node
table contained almost everything I needed: the content_type, title, nid (node id), and created. The only thing missing was the body field.
2. Write a Working SQL Query
Let's ignore the body field for a moment just get most of our query written:
SELECT nid, title
FROM node
WHERE type = "article"
AND status = 1
ORDER BY created DESC;
If you run that query in the "query" tab on HeidiSQL, you should see three results in the correct order with the nid and title fields. The only thing to explain here is that status = 1
clause filters out unpublished nodes. So far so good! Okay, let's add in the body field now.
Body Field Drupal 6
If you're on Drupal 6, the body field is always stored in the node_revision
table. Just join the node
and node_revision
tables on the vid field. Here's the SQL for Drupal 6:
SELECT nid, title, body
FROM node
JOIN node_revision
ON node.vid = node_revision.vid
WHERE type = "article"
AND status = 1
ORDER BY created DESC;
Body Field Drupal 7
Drupal 7 is a little different. The body field is in the field_data_body
table, and the field is called body_value. Here's the SQL Drupal 7:
SELECT nid, title, body_value
FROM node
JOIN field_data_body
ON node.nid = field_data_body.entity_id
WHERE type = "article"
AND status = 1
ORDER BY created DESC;
3. Drupalize the SQL Syntax
Drupal's database abstraction layer allows you to automatically prefix your table names in the queries. (It also allows parameterized queries which is vital to security, but it doesn't apply to our situation). Doing this step makes your SQL more portable and is really easy. All we need to do is wrap curly braces around the table names. By convention, queries also use aliases for table names. Here's the SQL for Drupal 6:
SELECT n.nid, n.title, r.body
FROM {node} n
JOIN {node_revision} r
ON n.vid = r.vid
WHERE n.type = "article"
AND n.status = 1
ORDER BY n.created DESC;
Here's the SQL for Drupal 7:
SELECT n.nid, n.title, b.body_value
FROM {node} n
JOIN {field_data_body} b
ON n.nid = b.entity_id
WHERE n.type = "article"
AND n.status = 1
ORDER BY n.created DESC;
4. Pass the query through db_query()
For brevity's sake I'll show you the Drupal 7 version only:
<?php
$query = "SELECT n.nid, n.title, b.body_value
FROM {node} n
JOIN {field_data_body} b
ON n.nid = b.entity_id
WHERE n.type = \"article\"
AND n.status = 1
ORDER BY n.created DESC";
$result = db_query($query);
It's important to keep the return value of db_query()
so that we can actually pull out the individual rows from the database.
5. Retrieve Query Results Using while
and db_fetch_object()
Here's how this bit works. db_fetch_object
is given the $result of a previously run query. If there is another row among the results, db_fetch_object
will return an object with each field of the SQL result as a property on the object. In our example, the object will have three properties: title, nid, and body.
However, if there's no more results from the query, then db_fetch_object()
returns false. In PHP, if you assign a "falsy" value to a variable, that whole expression evaluates to false. This is really handy in while loops. Again, I'm only showing the Drupal 7 version for brevity (db_query()
and db_fetch_object()
are pretty much the same in those versions of drupal). This code follows the code above:
<?php
// Store the results here.
$nodes = array();
// Continue pulling out result rows from the query results,
// but stop if there are no more.
while ($row = db_fetch_object($result)) {
$nodes[] = $row;
}
//Output the result to the browser
var_dump($nodes);
Again, the only thing to really explain here is that when db_fetch_object reaches that last row in the result set, it will return false. Setting $row to false makes everything inside the while conditional evaluate to false. That kills the while loop. Simple and elegant!
Conclusion
In this article, we've walked through some tools to help discover the layouts of tables in Drupal (don't be afraid to explore locally!), written a working SQL query in HeidiSQL, then ported this query to PHP/Drupal code. Ready to implement these features in the theme layer? Keep reading part 3 in the Ditch Views for SQL saga.