The THIS Scope


The THIS Scope

The THIS scope is a special scope that exists inside an object type component. We've already seen how you can create a component with the <cfobject> tag. When you create a copy of a component with <cfobject>, there's nothing to stop you from doing it again.

Consider Listing 19.7; let's say we have a component that just takes a name, and outputs it back out.

If you invoke this component with <cfinvoke>, it gets loaded, executed, and then immediately dropped. Therefore, since what we're trying to do is keep the data around with the component, we need to use <cfobject> to do that.

Listing 19.7. THISDemo.cfcSimple Component
 <!---   THISDemo.cfc - Demonstrate use of THIS scope   Ken Fricklas (kenf@fricklas.com)   Modified: 2/15/2005   Listing 19.7 ---> <cfcomponent displayName="ThisDemo" hint="Demo THIS Usage"> <cffunction name="aFunction" output="true">   <cfargument name="myName" type="String" required="Yes">   My Name is #arguments.myName# </cffunction> </cfcomponent> 

If you created this object with:

 <cfobject name="cfcComponent" component="THISDemo"> <cfobject name="cfcAnother" component="THISDemo"> 

you'd have a copy of it in the variable cfcComponent, and another copy of it in cfcAnother. The code is identicalthe CFC doesn't contain anything that makes cfcComponent any different from cfcAnother. It's just using up more space. The only way your object knows the value of myName is because you pass it in every time you invoke aFunction. We don't want our components to be identical, howeverwe want to create a component that is a separate, distinct object. The THIS scope allows our components to have data that is unique to them.

Just as the SESSION variable scope "lives" for the duration of a user session, and the SERVER scope "lives" for the duration of a server between restarts, THIS "lives" for the duration of the component's existence. And, like those other variable scopes, it can be used to store any type of data. In Listing 19.8, we've modified our simple component so it has some data unique to it.

Listing 19.8. THISDemo.cfcComponent with Simple THIS Assignment
 <cfcomponent> <!--- set instance data ---> <cfset THIS.myName="Ignatius"> <!-- return instance data ---> <cffunction name="aFunction" output="true">   My Name is #THIS.myName# </cffunction> </cfcomponent> 

Why does our component need the THIS scope? Because it doesn't have a name internal to itself; there's no way for cfcComponent to know it's any different from cfcAnother. THIS tells the component that the data is its own internal data. THIS for cfcComponent is separate from THIS for cfcAnother. To put this in object jargon, we've created two instances of our component, and each has unique properties.

There are two solutions to getting the Name property from this component: access it with a function in the component, or use the object style of invocation.

Functions within a component have free use of anything in the THIS scope. They can read and write to the scope, return values from it, or use them as attributes when invoking other methods. aFunction just returns a string with the value of myName in it; another function in our component could likewise use <cfset> to change the value of THIS.myName or do anything else with CFML that one may do with a variable.

Although this approach works, the real power of THIS comes when using components as objects. Since we've created two objects that both are copies of our component, let's access the component's unique properties by name. From the inside of a component, we use the THIS scope; from outside, we use the name of the component instance followed by the same variable name. Let's demonstrate that our CFCs really are unique by setting and displaying the myName properties (Listing 19.9).

Listing 19.9. SetProperty.cfmSetting an Object Property
 <!---   SetProperty.cfm   Instantiate a component, and set a property   As modified by Ken Fricklas (kenf@fricklas.com)   Modified: 2/15/2005   Code from Listing 19.9 ---> <!--- Get an instance of THISDemo component into cfcComponent variable, and another copy into cfcAnother ---> <cfobject name="cfcComponent" component="THISDemo"> <cfobject name="cfcAnother" component="THISDemo"> <!--- Set the myName properties in each component instance ---> <cfset cfcComponent.myName = "Johnny Bemoan"> <cfset cfcAnother.myName = "Gelatin Biafra"> <!--- Now use the object's name to refer to the THIS scope ---> <cfoutput>#cfcComponent.myName#<BR> #cfcAnother.aFunction()# </cfoutput> 

The first output is going to give us the value of the property directly, "Johnny Bemoan". The second line calls the aFunction method, which will write out "My Name is Gelatin Biafra".

Subsequent code can write to the THIS scope in this way, too. If the instance of the component were placed in a scope that existed beyond the life of this page (such as SESSION), everything in its THIS scope will persist with it. Thus my component on my page (or in my application) can have the myName "Ignatius," and yours can be "Mary." You can think of the word this like the word my if the component were able to talk about itself. "My name is Ignatius," or, "My name is Mary." If the Orange Whip Studio actors were to speak about themselves, they might make some statements like, "My first name is Harrison. My last name is Fjord. My date of birth is April 3, 1944." So, then, would one instance of the Actor component have variables in the THIS scope to correspond:

 THIS.firstName="Harrison" THIS.lastName="Fjord" THIS.birthDate="4/3/1944" 

The values of variables held in the THIS scope can be accessed and manipulated from methods within the component and from any CFML page in which the component instance exists as an object. And again, as object instances of components can exist in application scopes, like the SESSION scope, the variables in the THIS scope can "stick" with the component instance as it exists through the life of the application.

NOTE

Frequently you won't want your data to be available to stuff outside your component. For example, you might have an internal counter, or stuff that's specific to the implementation of your component, and is not data you'd want seen or manipulated outside the body of your component. If you create variables in a component without the THIS scope, the variables are hidden and only available within the component itself.


In order for users to use properties of a component, they need to know about them. Therefore, ColdFusion provides the <cfproperty> tag. It creates entries in the introspection of a component for your properties defined with the THIS scope. The syntax is exactly the same as for <cfargument>:

[View full width]

<cfproperty name="firstName" displayname="First Name" hint="Actor's First Name" required="No" default="Fred" type="string">

cfproperty tags must exist at the top of your component, right after the cfcomponent tag and before any code or functions.

This tag lets users know what the name, type, and use of a property are, as well as whether it's always going to exist and whether it has a default value. <cfproperty>, however, doesn't actually do anything. Adding a cfproperty tag doesn't actually create the variable; it only provides documentation for the component. This becomes very important in Web services, as you'll see in Chapter 24.

TIP

cfproperty tags show up in the metadata exposed by the getMetaData() function, but they don't create values. Values that you create with the THIS scope will show up when you CFDUMP a component, but won't be visible with introspection unless you add cfproperty tags. Generally you'll want to use cfproperty for every variable you define with the THIS scope; if you don't want users to read and write your variables directly, avoid using the THIS scope.


A common component that might make heavy use of the THIS scope is a shopping cart component. Why? A shopping cart persists for a given period, usually a user's session; it holds a set of data unique to that particular instance (the cart's contents); and it has a very common set of actions that are always part of its implementation (adding items, removing items, and so on).

Begin planning the shopping cart component by visualizing a real shopping cart. What is it and what do you do with it? It's a container into which you place a certain number of things to buy. While shopping, you may decide that fewer will do and then take some things out, or you may remember that company's coming and you need more. The main things to know are what's in there and how much it's going to cost.

NOTE

The shopping cart component demonstrated here is based on the one from the "eCommerce" chapter of the Macromedia ColdFusion MX 7 Web Application Construction Kit. We'll examine how the code works here, but you'll want to read the original text to see the component in the context of a real e-commerce application.


From this initial analysis, a picture of the shopping cart component is pretty clear. What methods are needed? Well, one to add an item, one to change the quantity of a given item in the cart, and one to remove an item altogether. To plan what goes into the THIS scope, again look to the words that would be preceded by the word my when describing it. "My planned purchases are in the shopping cart." So the THIS scope will contain an array of the items. Listing 19.10 is the complete CFC.

Listing 19.10. ShoppingCart.cfcThe Shopping Cart Component

[View full width]

 <!---   ShoppingCart.cfc - Orange Whip Studio Component   Ben Forta (ben@forta.com)   As modified by Ken Fricklas (kenf@fricklas.com)   Modified: 2/15/2005   Code from Listing 19.10 ---> <cfcomponent displayName="Shopping Cart" hint="This component contains the basic  functionality for a typical shopping cart."> <!--- Initialize and array to hold the cart items ---> <cfset THIS.cartItems = ArrayNew(1)> <!--- Add an item to the cart ---> <cffunction   name="Add"   hint="Adds an item to the shopping cart"> <!--- Two ARGUMENTS: ItemID and quantity --->   <cfargument name="itemID" type="numeric" required="Yes">   <cfargument name="quantity" type="numeric" required="No" default="1"> <!--- Get structure that represents this item in cart, ---> <!--- then set its quantity to the specified quantity --->   <cfset CartItem = GetCartItem(itemID)>   <cfset CartItem.quantity = CartItem.quantity + ARGUMENTS.quantity> </cffunction> <!--- Update the quantity of a given item in the cart ---> <cffunction   name="Update"   hint="Updates an item's quantity in the shopping cart"> <!--- Two ARGUMENTS: itemID and quantity --->   <cfargument name="itemID" type="numeric" required="Yes">   <cfargument name="quantity" type="numeric" required="Yes">   <!--- If the new quantity is greater than zero --->   <cfif quantity GT 0>     <!--- Get structure that represents this item in cart, --->     <!--- then set its quantity to the specified quantity --->     <cfset CartItem = GetCartItem(itemID)>     <cfset CartItem.quantity = ARGUMENTS.quantity>     <!--- If new quantity is zero, remove the item from cart --->   <cfelse>     <cfset THIS.Remove(itemID)>   </cfif> </cffunction> <!--- Remove a given item from the cart ---> <cffunction   name="Remove"   hint="Removes an item from the shopping cart">   <!--- One Argument: itemID --->   <cfargument name="itemID" type="numeric" required="Yes">   <!--- What position is this item occupying in the cart? --->   <cfset CartPos = GetCartPos(itemID)>   <!--- Assuming the item was found, remove it from cart --->   <cfif CartPos GT 0>     <cfset ArrayDeleteAt(THIS.cartItems, CartPos)>   </cfif> </cffunction> <!--- Empty the cart completely ---> <cffunction   name="Empty"   hint="Removes all items from the shopping cart">   <!--- Empty the cart by clearing the THIS.cartItems array --->   <cfset ArrayClear(THIS.cartItems)> </cffunction> <!--- Return the contents of the cart as a query object ---> <cffunction   name="List"   hint="Returns a query object containing all items in shopping cart. The query object has  two columns: itemID and quantity."   returnType="query">   <!--- Create a query, to return to calling process --->   <cfset q = QueryNew("itemID,quantity")>   <!--- For each item in cart, add row to query --->   <cfloop FROM="1" TO="#ArrayLen(THIS.cartItems)#" INDEX="i">     <cfset QueryAddRow(q)>     <cfset QuerySetCell(q, "itemID", THIS.cartItems[i].itemID)>     <cfset QuerySetCell(q, "quantity", THIS.cartItems[i].quantity)>    </cfloop>   <!--- Return completed query --->   <cfreturn q> </cffunction>  <!--- Internal GetCartItem() Method ---> <cffunction   name="GetCartItem"   returnType="struct"   access="private">   <!--- One Argument: itemID --->   <cfargument name="itemID" type="numeric" required="Yes">   <!--- Get the position of the item in THIS.cartItems --->   <cfset CartPos = GetCartPos(itemID)>   <!--- If item for this itemID was found, we will return it --->   <cfif CartPos GT 0>     <cfset CartItem = THIS.cartItems[CartPos]>     <!--- If item was not found, create new one and add to cart --->   <cfelse>     <cfset CartItem = StructNew()>     <cfset CartItem.itemID = ARGUMENTS.itemID>     <cfset CartItem.quantity = 0>     <cfset ArrayAppend(THIS.cartItems, CartItem)>   </cfif>   <!--- In either case, return the item --->   <cfreturn CartItem> </cffunction>  <!--- Internal GetCartPos() Method ---> <cffunction   name="GetCartPos"   returnType="numeric"   access="private">   <!--- Argument: itemID --->   <cfargument name="itemID" type="numeric" required="Yes">   <!--- Get position, if any, of itemID in cart query --->   <cfset CurrentArrayPos = 0>   <cfloop from="1" to="#ArrayLen(THIS.cartItems)#" index="i">     <cfif THIS.cartItems[i].itemID EQ ARGUMENTS.itemID>       <cfset CurrentArrayPos = i>       <cfbreak>     </cfif>   </cfloop>   <!--- Return the position --->   <cfreturn CurrentArrayPos> </cffunction> </cfcomponent> 

At the very beginning of the component, before any <cffunction> blocks, an array called cartItems is created in the component's THIS scope. That array will hold all the information we need for the cart. All that remains are functions to add, remove, and modify the quantity of items in it.

NOTE

See the section "Persistence and Constructors" later in this chapter for more information about code that falls outside of <cffunction>.


Let's begin by following what happens when an item is added to the cart:

 <cfscript> theCart = createObject("component","shoppingCart"); theCart.add(123,2); </cfscript> 

This code creates a new instance of the cart and invokes the add method to add 2 of item #123. The add method really only has two lines of code (besides the argument definition tags):

 <cfset CartItem = GetCartItem(itemID)> <cfset CartItem.Quantity = CartItem.Quantity + ARGUMENTS.Quantity> 

The first line is using an internal method of the component, GetCartItem(), to return a structure representing the item in the cart. If the item is already in the cart, it is returned; otherwise, a new structure representing the item is created. The code for the former, in the GetCartItems() function, illustrates how the THIS scope is used to hold any items in the cart:

 <cfset CartItem = StructNew()> <cfset CartItem.itemID = Arguments.itemID> <cfset CartItem.Quantity = 0> <cfset ArrayAppend(THIS.cartItems, CartItem)> 

NOTE

GetCartItems()is an example of a "private" function, which will be discussed in more detail later in "Access Control" in the later section on security for components


Any item in the cart is represented as a struct with its itemID and quantity and is kept as a member of the THIS.cartItems array. Beyond that, it's pretty much just a matter of adding or removing members of the array and/or updating their quantities. For example, how do you empty the cart? All the Empty() function needs to do is clear the array:

 <!--- Empty the cart by clearing the THIS.cartItems array ---> <cfset ArrayClear(THIS.cartItems)> 

When the Remove() function removes a specific item from the cart, it simply finds its position in the array and deletes it:

 <!--- What position is this item occupying in the cart? ---> <cfset cartPos = GetCartPos(itemID)> <!--- Assuming the item was found, remove it from cart ---> <cfif cartPos GT 0>   <cfset ArrayDeleteAt(THIS.cartItems, CartPos)> </cfif> 



Advanced Macromedia ColdFusion MX 7 Application Development
Advanced Macromedia ColdFusion MX 7 Application Development
ISBN: 0321292693
EAN: 2147483647
Year: 2006
Pages: 240
Authors: Ben Forta, et al

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net