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].
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:
- Designing the content type fields of each business object. (Easy in this example, there's only one!)
- Creating the content type.
- Adding some data records and viewing them.
- Creating a listings view. Stick it on the menu.
- Creating a neat little custom CRUD menu block.
- Importing the spreadsheet.
- Creating an interactive browser. Stick it on the menu.
- Enhancing the content type
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:
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|
OK, let's do it.
- First of all, install the necessary modules: cck, date, dateapi (comes with date), email, link, jscalendar (part of jstools).
- 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.
- 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.
- Hit the edit link for the Dealer content type and go to admin/content/types/dealer
- 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).
- 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! ]
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!
- 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:
- Go ahead and add the Province, Telephone and Contact fields in exactly the same way as we did for City.
- 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 , 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.
- 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 !). Let's just leave it at a good old mailto direct link, and just hit Save field settings.
- 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!):
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.
[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 .
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.
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|
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:
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,
Now hit submit and we have a convenient and practical little menu block in the left-hand sidebar to work on our dealerships!
"Well, now we're gonna find out if this puppy really works"
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!
- Blow by blow account:
7:45pm: search on Drupal for the module
I go to http://drupal.org 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!
- 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).
- 7:55: Open Excel spreadsheet in Open Office. Save as CSV without giving a hoot as to configuration. UTF-8? Heck yes!
- 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:58: Mapping spreadsheet columns to field. Wow this is moving along amazingly.
Hit "Next (options)" button.
- 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):
Hit "List Dealers" from "Dealers CRUD" menu block (look at this!):
Guess we won the prize: Drupal !
Forget it!!!!!!!! (Drupal !).
So let's make a night of it:
- First we go to Administer > Site building > Views .
- Click on the clone link corresponding to the "list_dealers" view.
- We are presented with an "Add a view" form but with all the details of the cloned form already filled in.
- Change the name to "browse_dealers".
- In the Page section, change the URL to "view/browse/dealers".
- Change View Type to "Table View".
- Change Nodes per Page to 25.
- Open the Fields section (since the view is not of type Full or Teaser, we have to specify which fields should be shown).
- In the Add Field drop down list, select "Node Title" and click on the "Add Field" button. In the label field type "Dealership".
- 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.
- Add the province field in the same way.
- Type Email in the Label field, and in the Options field, select Email-Invisimail (wow!).
- The Fields section should look like this:
- 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:
Put something meaningful in the Exposed Filters Label fields (Dealership, Province) and scroll down and click the Save button.
- Go ahead and invoke the "Browse Dealers" link off of the Dealers CRUD menu block, and you can play around with it, for example:
Well, that's all for this trip round the block, I guess we accomplished quite a lot, don't you think?
- Separate out contacts into a separate
tableerm, content type; (many contacts for one dealership) first level of normalization.
- Complex address field
- Google maps integration with address field
- Automatic generation of dealer name (auto node title).
- Shrouding display of email addresses.
- ... (next time, or let us know what you did!)