Script variables are an extremely important addition to scripting in FileMaker 8. If you've worked with other languages or development environments, you're familiar with a variable as a type of named, temporary storage. For example, in the PHP programming language, you might write this:
$x = 7; $y = 9; $z = $x + $y;
Here $x, $y, and $z are all variablestemporary names to which values are then assigned (the $ in PHP indicates that these are variable names, and FileMaker 8, as you'll soon see, uses a similar convention for variable names). In later expressions, the variable names stand in for the values stored in them. So you'd expect that when the preceding program is run, the variable $z will end up storing a value of 16.
Often, as you build up a program or routine, you'll find yourself wanting to rely on named, temporary storage elements like these. In previous versions of FileMaker, the only place to cache such data has been within FileMaker's database structures, by putting the data into one or another kind of field.
Consider the simplistic example of a script that beeps 10 times in succession. Previously, you might have defined a field with global storage to act as a counter, and written the script like this:
Set Field [Loop::gCounter; 1] Loop Beep Pause/Resume Script [Duration (seconds):1] Set Field [Loop::gCounter; Loop::gCounter + 1] Exit Loop If [Loop::gCounter > 10] End Loop
This has always worked fine, but it has some drawbacks:
Global fields are still useful for many things in FileMaker 8, as you'll see, but they're not ideally suited to the kind of temporary named storage we need for something like a loop counter. This is where FileMaker 8's variables come to the forefront. FileMaker 8 script variables are defined within scripts, so they don't appear in the list of defined fields. And script variables can be truly temporary, coming into existence within a specific script and disappearing when the script completes.
About Local Variables
A local variable is one that exists and has meaning only within a single script: exactly what you'd want for the loop counting example shown previously. If you were to rewrite the looping script using FileMaker 8 script variables, it might look like this:
Set Variable [$counter; Value:1] Loop Beep Pause/Resume Script [Duration (seconds):1] Set Variable [$counter; Value: $counter + 1] Exit Loop If [$counter > 10] End Loop
Local variables are named using a single dollar sign ($), and they're created and manipulated using the Set Variable script step. The options for the Set Variable script step are shown in Figure 15.5.
Figure 15.5. The capability to set variables is a powerful new feature in FileMaker 8.
You'll notice that the Set Variable script step allows you to set the variable's value dynamically, using the Specify option. This means that a variable can be used to hold the results of any expression you can define using FileMaker's Calculation dialog. You could store the current username, the current date, or the results of an expression such as $counter + 1. You'll note also, by the way, that variables can be referenced from within such calculations just by using the variable name. For example, the following are perfectly valid calculation expressions:
$counter + 1 "Name: " & $userName
If a variable of the specified name is not currently defined, a reference to the variable returns an empty string (or a null result, if that term is more familiar). The first expression of the preceding two would give a result of 1, whereas the second would give a result of "Name:", if the respective variables were not defined.
About Variable Scope
You'll often hear computer-science types talking about the scope of a variable. It's a concept worth mastering. Scope can be defined as "that portion of a program for which a variable is defined or has meaning." Variables in FileMaker have one of two kinds of scope: either local scope or global scope.
So far, the variables we've examined have local scope. It's most common to refer to them as local variables. A local variable exists and has meaning only within the context of a single script. Consider the example of the $counter variable discussed previously. This variable exists only within the script in which it's defined and manipulated. After the script completes, the $counter variable in effect disappears. Likewise, if you called a subscript from within that script, the subscript would not have access to the value of $counter contained in the parent script.
Because local variables exist only within the context of a single script, this can lead to subtle confusion. Consider a parent script that uses a $counter variable that then also calls a subscript. If you were to try to access the value of $counter within the subscript, you'd get a null value because you'd be trying to access a variable that had never been set within the context of the subscript. And if you were to try to set the value of $counter within the subscript, using the Set Variable script step, this would create a new variable, local to the subscript, with the same name, $counter. There would thus be a total of two $counter variables: one local to the parent script, one local to the subscript. The two exist simultaneously and independently; they don't conflict, and they don't affect one another.
Local Variables Summary
So, to summarize what has been said about local variables in FileMaker 8:
About Global Variables
Global variables, denoted with a double dollar sign ($$userName, $$currentLayout) share many features with local variables. The only difference is in their scope. Whereas local variables are limited in scope to a single script, global variables retain their value no matter what script is running, or whether a script is running at all. They can be used to store values that persist across any or all scripts, for the duration of a user's session.
The last point bears repeating. Whereas local variables have script scope, meaning that they are limited in scope to a single script, global variables have file/session scope. Like globally stored fields, global variables are unique to an individual user: Each user has his own copy of a global variable, so the variable $$userName can have a different value for each active user. And global variables also cease to exist when a user session ends. If you work with a global variable, quit FileMaker, and then open the same file again, the global variable will disappear, until some logic in the files creates it again.
Global variables also have scope only within a single file. There is no way to "reach across" to pull a global variable's data from one file into another. (Such a thing is, by contrast, possible with globally stored fields.) Global variables from other files cannot be accessed via relationships, because they don't appear in the field list.
So what good are global variables? When does it make sense to use them? We recommend that, by and large, you use global variables for user session data: data specific to one user that is intended to persist for just that user session. Examples include things like the name of the currently logged-in user, or user preferences such as a user's chosen default layout, or any other user-specific data you might be storing, such as a user's department or sales region.
It is also tempting to use global variables for general data transfer between scripts. For example, in a script that prompts the user for a dialog box choice, you might be tempted to store the user's choice in a global variable so that other scripts (a parent script, for example) could have access to the user's choice and act accordingly.
We can't stress too strongly that this is not a good use for global variables! FileMaker 8 provides a set of what we call script (input/output) capabilities that let you pass data into scripts, and now, in FileMaker 8, return results from scripts. If you simply need to move data from one script to another, script parameters and script results (discussed in the preceding section of this chapter) are the best way to accomplish that task. Using global storage, whether global variables or globally stored fields, for general data transfer between scripts will make your programs more fragile and less maintainable. Global storage, by definition, can be accessed from anywhere, so you can never quite be sure that you know where your data is coming from; it might have been modified by scripts other than those with which you're trying to communicate, perhaps even by accident.
In the dialog box example discussed previously, we chose to return the user's choice as a script result, rather than tossing it into a global variable. Using script results, we were able to avoid creating any temporary storage along the way.
Global variables cannot completely obviate the need for globally stored fields. Globally stored fields have several capabilities not shared by global variables:
For example, if you were implementing a filtered portal (a portal the contents of which change in response to user input), you would need to use a globally stored field to do so, both because you would need to capture user input, and because you would need to use that input to drive the portal relationship.
For more on filtered portals, see "Filtered Portals," p. 489.
Other Ways to Work with Variables
Variables in FileMaker 8 can be used in quite complex ways. When you're first starting out with variables, we recommend you try to stick to the following precepts until you feel you've mastered the basics:
Now that we've said all of that, if you have mastered the basic concepts of variables, there are some advanced points to be made about them. If you feel you're ready, read on!
Accessing Variables Across Files
We've said previously that there's no way to share global variables across files. This is not entirely true. Using FileMaker 8's new capability to return script results, you can have a script in file A return the value of a global variable from file A back to a script in file B. Consider the following one-line script:
Exit Script[Result: Evaluate( Get( ScriptParameter ) )]
This one-liner may appear puzzling at first. It simply takes whatever script parameter was passed in, uses Evaluate on it, and returns the result.
Now suppose that this script, living in file A, is called from file B with a parameter of "$$currentUserName" (quotes included!). The Evaluate function will evaluate the string "$$currentUserName", so if a global variable of that name exists in file A, Evaluate will return the value of that variable. This value in turn gets passed out of the script as a script result, and is returned to the calling function in file B.
The principal drawback to this technique is that it's scripted, so it doesn't give a means to access variables in other files in such a way that they could be used in calculation expressions. It's a highly specialized technique, but might come in handy from time to time.
Accessing Variables from Within a Let Statement
If our editors had offered us a way to put a big yellow flag over an entire section, to signal "Caution! Slow Down! Dangerous Curves Ahead!" we would probably have applied it to this section. You may work with variables for a while before you need to delve into these techniques, but because the capability does exist in FileMaker 8, we discuss it here for completeness.
It's important to know that script variables can be referenced and used within a Let statement, both within the variables block of the Let statement and within the body of the Let formula itself. For example, the following Let statement incorporates the value of a global variable called $$currentUserName:
Let ( [ dayNm = DayName( Get(CurrentDate)) ] ; "Hello, " & $$currentUserName & ", it is " & dayNm )
In this example, we are only reading the value of $$currentUserName. But it's also possible to set the value of a variable from within the variables block of a Let statement, like this:
Let ( [ $$currentUserName = Get ( AccountName ); dow = DayOfWeek( Get(CurrentDate)) ] ; "Hello, " & $$currentUserName & ", it is " & dow )
This operation will actually set the value of $$currentUserName, and because this is a global variable, the value of the variable will persist even beyond the bounds of the Let statement!
The same considerations apply to local variables (those denoted with a single $); they can be read from within a Let statement, and can be set or reset within the variable block of a Let statement.
The capability of reading and writing script variables from within a Let statement is intriguing, but we would argue that this technique is best used with caution, and only when you're certain that there's no better or equally practical way to accomplish your goals. There are actually several scenarios here, and each is slightly different:
The story is different, though, for Let statements within calculations that are defined on the fly within scripts, such as those you might enter in the Specify box of many script steps. These are transient, script-only formulas that it may make sense to bind closely to the script by referencing script variables.
About Dynamic File Paths
There's another nice feature of variables in FileMaker 8 that's very much worth mentioning. Certain script steps, such as Export Records, as well as the new Save Records as Excel/PDF script step, allow you to specify the location of an output file by typing in a file reference. In FileMaker 8, that file reference may be taken from a variable, rather than being hard-coded.
If the usefulness of that isn't obvious, let it sink in for a moment. In the past, it hasn't been possible to create names for exported files on the fly: You either had to let the user enter a filename, or had to hard-code a single specific filename into the script step. If you wanted to name exported or saved files dynamically (say you wanted to include the current date in the filename), you were out of luck, unless you chose to use a third-party plug-in.
To save files to a dynamically specified file path, you'll need to create that file path in your script and put it into a variable. That variable can then be used in specifying a file path, as the following script example illustrates:
Go to Layout [ "Contacts" ] Show All Records Set Variable [ $filePath; Value: Let ( [ theDate = Get(CurrentDate); theYear = Year(theDate); theMonth = Month(theDate); theDay = Day(theDate); dateText = theYear & "_" & theMonth & "_" & theDay; filePath = Get ( FileMakerPath ) & "Export_" & dateText ]; filePath ) ] Save Records as PDF [ File Name: "$filePath"; Records being browsed ]
This script example uses no fewer than three new FileMaker 8 features: script variables, the capability to use script variables for dynamic file paths, and one of FileMaker 8's several new Get() functions, Get(FileMakerPath).Get(FileMakerPath) will return the path to the FileMaker application directory. This function is part of a family of new path functions in FileMaker 8 that also includes Get(DesktopPath), Get(DocumentPath), Get(PreferencesPath), and Get(SystemDrive). By using this suite of new functions, along with the new dynamic file path capability, you should now be able to save files with custom filenames to a wide variety of locations on a user's disk.
Viewing Your Variables
One final note on variables in FileMaker 8. We've made the point a few times that variables are beneficial in that they don't add clutter to the database schema: They don't appear in Define Database dialog, nor in the field lists that go along with operations such as Sort or Import Records. There's a disadvantage to this as well: There's currently no way to see a list of all the variables that are currently active in a FileMaker solution.
It is possible to view the values of individual variables in the FileMaker Pro Advanced Data Viewer, but you must enter the variable names one at a time, as with any other expression.
For more on the Data Viewer, see "Debugging Scripts," p. 517.
Part I: Getting Started with FileMaker 8
Using FileMaker Pro
Defining and Working with Fields
Working with Layouts
Part II: Developing Solutions with FileMaker
Relational Database Design
Working with Multiple Tables
Working with Relationships
Getting Started with Calculations
Getting Started with Scripting
Getting Started with Reporting
Part III: Developer Techniques
Developing for Multiuser Deployment
Advanced Interface Techniques
Advanced Calculation Techniques
Advanced Scripting Techniques
Advanced Portal Techniques
Debugging and Troubleshooting
Converting Systems from Previous Versions of FileMaker Pro
Part IV: Data Integration and Publishing
Importing Data into FileMaker Pro
Exporting Data from FileMaker
Instant Web Publishing
FileMaker and Web Services
Custom Web Publishing
Part V: Deploying a FileMaker Solution
Deploying and Extending FileMaker
FileMaker Server and Server Advanced
Documenting Your FileMaker Solutions