From the client’s spreadsheet data to browseable content — how to create, populate and manage your business objects in Drupal

[Note: This is a somewhat detailed description of how to leverage content types and views in Drupal, most useful for beginner to intermediate experience level in Drupal Web Application development, or to those evaluating Drupal. It is designed to show how the power of the framework is only apparent after gaining just a little experience with its features].

[Note: This is a somewhat detailed description of how to leverage content types and views in Drupal, most useful for beginner to intermediate experience level in Drupal Web Application development, or to those evaluating Drupal. It is designed to show how the power of the framework is only apparent after gaining just a little experience with its features].

So your client has just sent you a spreadsheet of all the car dealers in Transylvania, and we need to create a business object to handle that in our Drupal web application. We need CRUD functions, a listing function and an interactive car dealer browser, and a way to quickly import the client’s data spreadsheet! On the quick! Drupal is its own greatest prototyping tool.

So the steps are:

  1. Designing the content type fields of each business object. (Easy in this example, there’s only one!)
  2. Creating the content type.
  3. Adding some data records and viewing them.
  4. Creating a listings view. Stick it on the menu.
  5. Creating a neat little custom CRUD menu block.
  6. Importing the spreadsheet.
  7. Creating an interactive browser. Stick it on the menu.
    1. Using a helper module.
    2. Writing a custom script for this: because you can and it’s easy: hey it’s only PHP.
  8. Enhancing the content type

Design the content type fields of each business object. (Easy in this example, there’s only one!)

What does it mean to design the content type fields? On the one hand we take into account that in "Drupal" (Drupal core plus the essential modules that make Drupal Drupal, used by 99% of Drupal users: CCK, Views, Panels, Image…) there is a list of "field modules" (data type + widget) to choose from. So designing involves the creative reuse of these modules.

Opening up the client’s spreadsheet to see what we have, we see:

Dealer Address City Province Telephone Website Contact Email

This breaks down into the following field design (we avoid the Address field for CCK for now, along with various localized telephone modules, since this is an initial example, we leave that as an exercise for the reader):

Field label Machine readable name Field type Widget Configuration Required Required module
Dealer title Text Text Field   yes cck
Address dealer_address Text Text Field multiple   cck
City dealer_city Text Text Field     cck
Province dealer_province Text Text Field     cck
Telephone dealer_telephone Text Text Field     cck
Website dealer_website Link       link
Contact dealer_contact Text Text Field     cck
Email dealer_email E-Mail Text Field     email
In business since dealer_biz_start_date Date Text Field with javascript pop-up calendar     date, dateapi, jscalendar

OK, let’s do it.

Creating the content type.

  1. First of all, install the necessary modules: cck, date, dateapi (comes with date), email, link, jscalendar (part of jstools).
  2. Create the "dealer" content type.
    Go to /admin/content/types
    Click on the Add content type tab
    Type in "Dealer" in the required Name field, type in "dealer" in the required Type field (this is the data type, the way the system identifies the content type). Type in a brief description.
    Type "Dealer" into the Title field label, make the deprecated Body field label blank (we will roll our own text fields, this is not a blog or article content type), type in the optional explanation or guidelines. In Default options, deselect the blog behavior "Promoted to front page" (which will turn your site into a dealer listing blog), and select the Create new revision option to leverage the fantastic, built-in Drupal version control system. Configure the remaining options at will and hit the Save content type button. That’s it, we have created the content type.
  3. This takes us to admin/content/types and we see our newly created content type is listed along with Story, Page and the others. We could start creating Dealer nodes (records) right now, but it won’t do us any good without first creating and configuring the fields.
  4. Hit the edit link for the Dealer content type and go to admin/content/types/dealer
  5. Hit the Manage Fields tab. We are informed of the following:
    • There are no groups configured for this content type.
    • There are no fields configured for this content type.

    So to remedy this situation we are going to create the Address field (we have already specified the Dealer field as the title field for the content type).

  6. To create the Address field, click on the Add field tab.
    Ignore the Add existing field at the top of the form, and directly type in the machine-readable name for the field (anything that is not an unaccentuated a-z, numbers and _ will be discarded). I typed in "dealer_address" there.
    [Skipable Geek reflection: The reason I would do such a thing is simple: later on when we are configuring additional funtionality, like views, for us to be able to list dealers, we need to be able to easily differentiate fields having to do with this content type from fields having to do with others. Even if you reuse this field in another content type (you can) at least you will know where it comes from and which content type not to delete! Embarassed]
    Now, we want this to be a field of type Text using the Text Field widget, so click the corresponding option. Now click on the Create Field button.
    We are taken to the second page of the create field wizard. What we need to do now is specify our human readable label. So replace "dealer_address" with "Address". Leave Rows at "1". And a bit further down in the Data settings select the "Multiple values" checkbox, so we can optionally add two lines for the address. There are more options, but we can safely hit the Save field settings button and say we have created and configured our first field!
  7. Now to create the dealer_city (Dealer) field. We are now at the Manage fields tab, at admin/content/types/dealer/fields, and can see the title and field_dealer_address fields listed (field has been stuck in automatically by the kind folks from the CCK development group). We click on the Add field tab, and under name type in dealer_city. We select the Text Field widget option corresponding to the Text type, as we did before, and hit the Create field button. We type "City" into the Label field, leave Rows at 1, and actually just hit the Save field settings button. You should see something like the following:
    Create content type
  8. Go ahead and add the Province, Telephone and Contact fields in exactly the same way as we did for City.
  9. Adding the dealer_website field is simply a matter of choosing the appropriate widget. We click on the Add field tab and type in dealer_website, but this time we choose the Text fields for Title and URL widget option for the Link data type. Hitting the Create field button, on the second page we specify the Label ("Website"). Now, in the Data settings section, we can see from the wealth of options why it is a good idea to use the additional functionality provided by the links module, rather than simply a textfield. But of course that brings in a bunch of decisions we have to make (welcome to Drupal!), so anyway what I did for now was to leave the Optional Title selected, initially deselect "Allow tokens" (never mind, we’ll talk about tokens next time Cool, although they are powerful stuff!), leave the URL display cutoff length at 80 characters, leave Rel Attribute blank, and stick in a CSS class of dealer-url for the class attribute, then hit Save field settings.
  10. For the Email field, we hit the Add field tab, type in dealer_email for the name and then select the Textfield widget provided for the E-Mail data type. Hit Create field, then specify the Label… and ponder the rich option set (Drupal Cool!). Let’s just leave it at a good old mailto direct link, and just hit Save field settings.
  11. For the dealer_biz_start_date field, again, Add field tab, specify machine readable name and this time select the Text Field with javascript pop-up calendar widget corresponding to the Date data type, and hit Create field. On the second page we fill in "In business since" for the label, and out of all the options you will spend much happy time experimenting with (Drupal Cool!), we will just mention here specifying a date/time granularity of Year, Month and Day (in the Data settings section).
  12. Taken back automatically to admin/content/types/dealer/fields, we can see that our content type has grown. For now we have all the fields set up, we should type in a few records of data to see if it flies. We notice one thing though, the fields are in alphabetical order, and that’s not how we want our input form to look. So we adjust the Weight values for each field, click on the Update button and end up with something like the following (notice the Weight column values!):
    Create content type 2Create content type 2

Adding some data records and viewing them

From your navigation menu, select Create content, then Dealer, which will take you to node/add/dealer, the Submit Dealer form. Enter some data for the first one, then optionally preview, and finally hit Submit. Repeat that operation a few times.

Then, to view your records, go to Administer > Content management > Content (admin/content/node), apply a filter to show only Dealer content (Show only items where + option type + is + option Dealer and hit the Filter button). This will enable you to browse your data records!. Of course, we need something better than this for people visiting the site (see below), but at least for now we can rest assured that our hard work is not going into the bit bucket.

Creating a listings view. Stick it on the menu.

[Note: Drupal 5.x assumed]

We now need to install the famous views module, and get a way to list our Dealers in various ways.

Download and install the stable version 5.x-1.6 of views that will run in Drupal 5.x.

When you enable permissions for the newly installed module at Adminster > User management > Access control, be sure to enable permissions for the views_ui module, otherwise… you cannot create any views interactively Cool.

Now, go to Administer > Site building > Views and hit the Add tab. Enter "list_dealers" in the Name field, and open up the Page section. Select the Provide Page View checkbox and enter "view/list/dealers" (or whatever you want, actually) in the URL field. Select "Teaser List" for View Type. Then, open up the Filters section, select "Node: Type" from the Add filter selection list, and hit the Add Filter button next to it. Now select "Dealer" from the Value column, so that the view will just list Dealer nodes only, which is what we are looking for. And just hit the Save button at the bottom of the page, and you have created your first automatically generated listing of a node type: a view (look Ma, no SQL!).

We see the existing views listed, and if you click on the view/list/dealers link you will see your dealers listings.

Creating a neat little custom CRUD menu block.

What we actually need now is a little CRUDy menu block we can put at the top of the sidebar, so people who have no admin privileges can actually see the listings, and those who maybe have an editor role can add dealer records to the system also.

To do this, it pays to gather up a little info first:


Menu Item (Title) Path Weight
Add Dealer node/add/dealer 1
List Dealers view/list/dealers 2
Browse Dealers view/browse/dealers 3


Now we go to Administer > Site building > Menus

Click on the Add menu tab and create a new Menu called "Dealers CRUD" (or some other colorful expression). [Note: I suppose it is unnecessary to mention that CRUD is datageek for Create/Retrieve/Update/Delete…].

Now click on the Add Item link right under the words "Dealers CRUD", under Title put "Add Dealer", under path, put node/add/dealer, under Weight select 1, then hit Submit. Repeat for the remaining menu items. So, we have a menu, something like the following:

Create content type 3

Now it so happens that whenever you make a Menu like this in Drupal, it automatically becomes available as a Block which can be inserted in a sidebar, for example, or any other enabled region (Header, Footer, left sidebar, etc.)!

So we go to Administer > Site building > Blocks, and select the "left sidebar" for the "Dealers CRUD" menu block,

Create content type 4

Now hit submit and we have a convenient and practical little menu block in the left-hand sidebar to work on our dealerships!

Importing the spreadsheet.

  1. Using a helper module.

    1. "Well, now we’re gonna find out if this puppy really works"

    2. I’m gonna look for and download a module that says it can import stuff, take a client’s Microsoft Excel spreadsheet emailed to me this morning, open it up in Open Office, save it as CSV (first line blank, second line headers in Spanish that have nothing to do with the field names I just created for the Dealer content type, third line blank, then just over sixty records to import). Then I want to see them listed on the screen, and if I can do it in 15 minutes I get a prize!

    3. Blow by blow account:

      7:45pm: search on Drupal for the module
      I go to and do a search on "import nodes".
      First one up is Node import: "This module allows you to import a set of nodes from a Comma Seperated Values (CSV) or Tab Seperated Values (TSV) text file." All right, let’s go with this!

    4. 7:48pm: download version 5.x-1.5, give a quick once over the README, backup database, install
      7:49pm: installed on local system, quick twice over the module README again:
      7:52pm: permissions thing: Go to Administration > User management > Access control and give the user who is going to be importing
      the nodes permission to create and edit Dealer node types (it’s a new
      content type, that may not have been done yet) and permission to import
      nodes (in the node_import section).
    5. 7:55: Open Excel spreadsheet in Open Office. Save as CSV without giving a hoot as to configuration. UTF-8? Heck yes!
    6. 7:56: Go to Administer > Content Management > Import content (admin/content/node_import). Browse for the local CSV file I just saved on my local workstation, leave File format at "autodetect", specify "Dealer" for Type. Hit the "Next (mapping)" button.
    7. 7:58: Mapping spreadsheet columns to field. Wow this is moving along amazingly.

      import content with node import module 1

      Hit "Next (options)" button.

    8. 8pm: Hit "Next (preview)" without setting unique fields or putting in any defaults – looking good! Hit "Apply (import nodes)" right off the bat to get in under the wire (15 minutes), then immediately clicked on "List Dealers" from the handy Dealers CRUD menu: see these three snapshots (next time take a longer look but things came out pretty good):

      import content with node import module 2

      After import:

      import content with node import module 3

      Hit "List Dealers" from "Dealers CRUD" menu block (look at this!):

      import content with node import module 4

      Guess we won the prize: Drupal Cool!

  2. Writing a custom script for this: because you can and it’s easy: hey it’s only PHP.

    Forget it!!!!!!!! (Drupal Cool !).

Creating an interactive browser. Stick it on the menu.

So let’s make a night of it:

  1. First we go to Administer > Site building > Views .
  2. Click on the clone link corresponding to the "list_dealers" view.
  3. We are presented with an "Add a view" form but with all the details of the cloned form already filled in.
  4. Change the name to "browse_dealers".
  5. In the Page section, change the URL to "view/browse/dealers".
  6. Change View Type to "Table View".
  7. Change Nodes per Page to 25.
  8. Open the Fields section (since the view is not of type Full or Teaser, we have to specify which fields should be shown).
  9. In the Add Field drop down list, select "Node Title" and click on the "Add Field" button. In the label field type "Dealership".
  10. Next, choose "Text: City (field_dealer_province)" and click on the "Add Field" button. (See what I meant about the machine readable field names and keeping them recognizably associated with the content type?). Type "City" in the label field.
  11. Add the province field in the same way.
  12. Type Email in the Label field, and in the Options field, select Email-Invisimail (wow!).
  13. The Fields section should look like this:

    browse dealers views fields section

  14. Now we need to add some filters and expose some of them, creating an interactive browser.
    Open the Filters section. We already have the "Node type is one of…" filter in place. Now select "Node: Title" from the Add Filter drop down list and hit the Add Filter button. Change the Operator to "Contains" (or any other you like). In the same way, add "Text: Province (field_dealer_province)". You should see a total of three filters at this point. Now, next to each filter, you should see an "Expose" button. Press this button once for the second filter, and once for the third. Open the Exposed Filters section. You should see something like this:

    browse dealers views fields section 2

    Put something meaningful in the Exposed Filters Label fields (Dealership, Province) and scroll down and click the Save button.

  15. Go ahead and invoke the "Browse Dealers" link off of the Dealers CRUD menu block, and you can play around with it, for example:
    browse dealers views fields section 3
  16. Well, that’s all for this trip round the block, I guess we accomplished quite a lot, don’t you think?

Enhancing the content type

  1. Separate out contacts into a separate table erm, content type; (many contacts for one dealership) first level of normalization.
  2. Complex address field
  3. Google maps integration with address field
  4. Automatic generation of dealer name (auto node title).
  5. Shrouding display of email addresses.
  6. … (next time, or let us know what you did!)