Lesson 2: Data Basics
After completing Lesson 1, you know how to create and use variables, how to store and retrieve data in arrays, and that Zephyr uses if/else statements to automatically make decisions.
This lesson introduces two primary sources of data: the user profile and content data feeds. These offer pre-built, system variables you will access frequently to program decisions and display information in your templates, hosted pages, and everywhere else Zephyr is accepted.
We will look at each, and learn about another Zephyr control structure to maximize your productivity in using your data.
You will notice we refer to a singular user throughout these sections. This is because even when you are sending to a million customers, each email is built individually, one user at a time, enabling true 1-1 communication. When you construct Zephyr code, think of it as being processed for a single user, but know it is reused for each additional user.
As in Lesson 1, select Result to see the answer for each exercise.
2.1 User Profile
The system saves a lot of information about your individual users to create a unique profile in our system for each one. You are likely also saving additional custom information about your users. Those two data sets make up a User profile. If you have users set up, you can see a user profile and an overview of all that information by looking up an email address in User Lookup.
You can utilize user data to drive the display of content or to make decisions in 'if' statements. The system enables this by offering the 'profile' var in email and onsite. The profile var is filled with much of the current user's information. We'll go through the available profile var data here, but you can also visit this page to see a full listing.
You'll recall that we can see the content stored in a variable by calling it by name in Zephyr brackets. So we can see inside a profile var by using the following Zephyr statement. (The result is a more complicated form of array than we have seen, so we will break it down below.)
In general, the {profile} call above returns JSON formatted text in a single line. As that's hard to read, you can use a JSON formatting app to add line breaks and spaces to produce a much more readable copy which still has the same technical value:
{
"id":"556e949cfa13a18f208b47ea",
"email":"zephyr.trainer@sailthru.com",
"lists":[
"apitl",
"list2",
"listB",
"main",
"newsletter"
],
"lists_signup":{
"apitl":1433310478,
"list2":1449271665,
"listB":1449271665,
"main":1449271665,
"newsletter":1449271665
},
"vars":{
"hello":"dolly",
"first_name":"Jane",
"last":"2015-12-08",
"acquisition_source":"facebook",
"last_name":"Lewis"
},
"signup_time":1433310478,
"optout":"all",
"keys":{
"email":"zephyr.trainer@sailthru.com"
}
}
This form of an array contains not just one type of value, but multiple. In fact, within the array, you'll find multiple variable names, and each might have multiple values. This form of array is often referred to as an object or hash table.
Let's look at how these hash tables work and how to make one before we return to the profile and use its data. Understanding objects lays the framework for how you work with most if not all of your data in the system from the profile to data feeds and beyond.
It is easiest to understand hash tables coming from the context of a basic array. In Lesson 1, we created a simple array in the form of a grocery list:
{grocery_array = ["Peas","Apples","Chicken","Poppy Seed Bagels"]}
You access the peas entry by calling the array's name with the item's position in square brackets. The position is 0, because arrays begin their numbering with 0.
Peas
Our task is to rewrite the grocery list as a hash table. We will want to make each of our items, like Peas, the value of its own var that will be stored within a grocery hash table.
To assign values to variables within a hash table, we don't want to use the equal sign, because that is already being used for assigning the entire table's name to the object we are creating. Instead, we use a colon.
Ex: "var_name" : "value"
Use quotes around the var_name to create a new one. To reference an existing var, use it without quotes.
Let's rewrite the original array giving each item it's own var name, and since we choose the var names, lets add more useful information. Lets categorize our shopping list a bit, so starting with Peas we can indicate it is our vegetable. (Note that all the curly brackets within the outermost Zephyr brackets are part of standard JSON notation, and not considered by Zephyr to indicate new Zephyr code.)
{grocery_hash = {"vegetable" : "Peas", "fruit": "Apples", "meat": "Chicken", "bread": "Poppy Seed Bagels"} }
To make it easier to see what is going on, this is the same assignment but spread out over multiple lines.
{grocery_hash = {
"vegetable" : "Peas",
"fruit" : "Apples",
"meat" : "Chicken",
"bread" : "Poppy Seed Bagels" }
}
The hash items can now be called (rendered) with their position or name. This allows for an easier and clearer form of calling the inner vars and in some cases is much more powerful. To call an item (member) by name, you can use a syntax similar to calling by position, grocery_hash[0], only now using the inner var's name: grocery_hash["vegetable"].
Having var names also gives you another, easier way of calling by name. You can use a dot to separate the hash table's name from the desired item's variable name to get the item's value. This is known as dot notation. For example: {grocery_hash.vegetable}. This has the same effect as grocery_hash["vegetable"], but is easier to read and write in your code.
Let's try it:
Peas
The Profile object we started the section with is a hash table. It holds vars about your user. We just learned how to access the variables. So to render a user's email, call profile.email in Zephyr brackets.
zephyr.trainer@sailthru.com
The variable inside the profile object that holds the custom vars you saved on a user is called 'vars', so edit the above code and instead of profile.email, use profile.vars to view them.
"vars":{
"hello":"dolly",
"first_name":"Jane",
"last":"2015-12-08",
"acquisition_source":"facebook",
"last_name":"Lewis"},
The "vars" variable you have just accessed is itself a hash table... that is right, hash tables (like the profile) can hold other hash tables! A good analogy might be nested dolls. This behavior is indeed called nesting. (When an 'if' statement is inside another 'if' statement, it is also called a nested 'if' statement.)
The simple takeaway: You can access your custom vars with yet another dot following profile.vars:
facebook
The profile object contains several other useful vars. Here is a brief rundown:
- "vars" var : A hash table of client submitted custom vars
- "lists" var : Another hash table with the Natural lists the user is signed up to.
- "optout" var : The current opt out status of the user. See here for possible statuses
- "signup_time" var : The Unix timestamp of when the user signed up. (Unix time isn't reader friendly)
- "purchases" var : An array with the user's previous purchases.
Exercise: Form each one of the above objects in Zephyr. Try starting with the entire profile array, then limit it down to just the field you want. Try some of the deeper nested members too.
2.2 Loops
Before we go on to data feeds, lets learn about an efficient way to interact with all this data: loops. Loops make it easy to apply the same Zephyr to each item in an array without having to write the same code over and over again. It's easier to understand with an example. Lets create a nicely formatted HTML list of all the current User's lists.
First, let's get the data. Call the 'lists' var from within the profile object.
"lists":[
"apitl",
"list2",
"listB",
"main",
"newsletter"],
It is a standard array, with each list the user is on as an item in the array. We know how to call the position of each member in order to individually display each one to the screen:
- List: apitl
- List: list2
- List: listB
- List: main
- List: newsletter
This worked successfully, but we had to call each position manually (by specifying its index number) and it required us to know that the user was on five lists. What if we don't know how many lists the user is on? Loops solve both problems by letting you specify code to run for each item in the array without knowing how many items there are.
We can look at our HTML above and notice that each member of the array had a line like this:
- List: {profile.lists[0]}
So we need our loop to render that line for each of the User's lists. But we don't want to always write out {profile.lists[0]}. Instead we will specify a new variable for the loop to fill in with each individual array value, one at a time. Lets look at the code to write our loop.
- List: apitl
- List: list2
- List: listB
- List: main
- List: newsletter
The {list}
var is our loop's new variable to represent each member of the array. The starting {foreach}
and ending {/foreach}
tags define the start and end of the loop. When the system encounters a starting {foreach}
, it grabs the array specified and saves the first member into the user-specified variable {list}
. (We could have chosen any unused variable name for this.) Then, all the code before the closing foreach is run.
So the first time the lines are run, {list}
will refer to the first member of the profile.lists array. Once the closing {foreach}
is hit the system goes back to the opening {foreach}
tag, checks for the next array member, and saves it into the {list}
var. The code is run again with {list} now refering to the second member of the array. This will repeat (or loop!) until every member of the specified array has been run through the body of the loop. A best practice is to name arrays as the plural of whatever they contain, and you can likewise use the singular version as your placeholder var in loops. (For example, an array of widgets can be named widgets, and you can use widget as your loop placeholder var, which creates the friendly phrase 'foreach widget'.)
2.3 Data Feeds
Data feeds are key to the automation of your email and web content. Data feeds are files that contain structured data, such as titles and URLs. When you assign a data feed to a template, its content is put into a var (like the profile object), which is accessible in your Zephyr code. System generated data feeds will be found in a var named "content". If you just call the Zephyr statement below, it will render as a large and unformatted data set.
If you just called {content}
in an HTML template, it would look like the following image from the Blog content feed:
In the last section we saw how the profile object is a hash table--a collection of other vars (each with its own value(s)). The content var is an array--a list of values. However, the entities within aren't simple variables: they have titles, URLs and other data that need to be held in an object. Therefore, the content var is structured as an array of hash tables, where each hash table represents a content item and all of its attributes.
This doesn't change much from the content var's perspective. It isn't too different from our grocery list. Think about an array whose only member was another var, like so:
{a_var = "Peas"}
{"content" : [a_var]}
If you were to make a_var into a hash table, nothing would change about the "content" array.
Since we know the content items are just an array, we know how to call the members and start using the variables in each item's hash table. Remember, arrays count from 0!
HOW TO CREATE A SINGLE CONTENT ITEM CALL:
If you are providing an external feed from your team, you will need to discuss what fields are available. The following example uses content feeds created by the platform, administered in the Data Feeds section of the UI. Data feeds have a standard set of variables in their hash tables, things like title, date, description, etc.
Since the content item is a hash table, we can access these items using dot notation!
HOW TO ACCESS DATA INSIDE THE FIRST CONTENT ITEM WITH DOT NOTATION:
It's nice to have access to the content object and know how to render the title and URL. But what is great about the content feed being an array is how easily you can use loops with it. You can allow the repetitive sections of your email message or website to build themselves in the manner that you direct. The loops will create blocks of HTML filled in with the vars from your content feed. Lets build out the body of a hypothetical newsletter.
We are going to make one sly move to simplify our task. We are going to apply the Horizon recommendation algorithm and simultaneously cut the content array down to six items. Horizon is applied through a function. We won't delve into how it works, but in short, we give the Horizon function the content feed and tell it how many stories we want. In this example, the resulting six stories--personalized to the current user's interests--are saved in the var 'sixArticles', still as an array.
There are a lot of great things happening above. First, you only need to get the HTML right for one item and then the Zephyr loop will repeat it for you. That is, you only need to code the Zephyr variable calls once. And the best part: If you change how many stories or items you want to display, or want to change the content, you can change the number in horizon_select or manipulate the data feed in Recommendations and the updated HTML will automatically follow.
Congratulations! You've completed Lesson 2: Data Basics!