1. Introduction
X2O is a web-based data modeling platform for rapidly developing database-driven Adobe® Flex® and Flash® applications.
With it, you don’t need to create your own database, build stored procedures, write server-side code, or even integrate your AS3 code to data services. All of this is smartly managed by X2O.
Your job is to create a data model, configure as much (or as little) as you’d like, and generate the framework. You get the code and tools you need to start building your app right away.
Because it’s all done over the Web, there is no installation. Simply import the generated SWC library into your Flex or Flash CS4 project and you’re ready to go. You can modify your data model and regenerate as many times as you need, making X2O an incredibly agile platform.
This document will take you through X2O from start to finish. It’s geared toward Flex and Flash developers who crave a more rapid, seamless, and maintainable approach to database-driven application development.
1.1 What you should already know
To use X2O, you must already know AS3 and MXML as well as the basic principles of object-oriented programming. Beyond that, the following concepts are integral to take full advantage of the power of X2O:
- Data modeling concepts like objects and relationships (we’ll teach you the basics in this tutorial)
- Concepts like value objects, array collections, event handling, and data binding (what you’ll be mostly doing with your generated X2O code)
- Advanced users will know how to write SQL (to create more customized queries that integrate with the generated AS3 code)
- If you are not as familiar with the concepts above, this document will step you through them.
The term data modeling is something we’ll talk about a lot in this document. Your data model describes how data is organized and related to each other. A data model is the DNA of an X2O project – it provides the instructions X2O needs to generate the framework and tools specific to your application.
1.2 What tools you need
X2O is completely web-based. The generated SWC will run on the Flex 3.0 SDK or Flash CS4. If developing in Flex, we do recommend that you use Adobe® Flex® Builder™ 3 to do your development.
2. Overview
When you sign up with X2O, you will be given your own account available at the URL provided in your registration email. X2O is the web-based software you use to create and configure a data model. X2O then creates the underlying framework and tools you need to develop your Flex or Flash application.

Figure 1. X2O workflow diagram
Figure 1 shows you the general workflow. The items in gray are created behind-the-scenes. You never work directly with the database, stored procedures, server code, data services, or the web server itself. You’ll get the tools and code for you to develop against the framework.
- CMS – A secured content management system to manage the records in your data model
- SWC – A complied AS3 library containing value objects and methods to interact with your data model
- DOC – Full documentation of the packages in your SWC library
X2O is a time-saver for several reasons. Not only does it generate an infrastructure that would take several days (if not weeks) to build, but it provides a standard API for working with data. Once you’ve mastered the basic concepts of data modeling and working with the AS3 library, pushing and pulling data from the client to server will be second nature.
Also, when you need to modify your data model, X2O regenerates the entire infrastructure without overwriting any past work you’ve done. This combination of standards and agility makes X2O remarkably powerful, yet easy-to-use.
Here’s a high-level view of what’s to come.
- Section 3. The next section will take you through creating a new project.
- Sections 4-8. Next, we’ll dig into data modeling with the following sections: Entities, attributes, relationships, and custom methods.
- Sections 9-10. We’ll then step through using your generated CMS and documentation web site.
- Sections 11-19. Finally, we’ll show you how to use the API for the generated SWC to create data-driven Flex and Flash applications.
Throughout this tutorial, we’ll refer to a sample recipe project to describe the various facets of X2O. The recipe project comes
as a default pre-built X2O project when you sign up for an account. The Flex source code to the application is available at http://code.google.com/p/x2osamplerecipeapplication.
You can view the sample recipe application in action at http://recipebar.x2oframework.com.
3. Projects
Projects are where an X2O-driven Flex or Flash CS4 application starts.
Within a project, you build your data model and configure your generated CMS tool. Then, you’ll use the generated components of your project to build your Flex or Flash application against.
When you login to X2O, you will see a listing of your projects. You can then click "New" to create a new project. By default, we provide you with our sample recipe project. Its goal will be to store and categorize different cooking recipes. Here’s how we created the recipe project.

Figure 2. New project screen
In Figure 2, we provide a name, description, and credentials to login to the generated CMS tool.
Once you’ve created a new project, you will be forwarded to the data model section. Let’s start building the data model for this simple recipe project by taking a look at entities.
4. Entities
Entities are the concepts that comprise your data model.
They can represent tangible things (e.g. people, engines, albums, blog entries), non-tangible concepts (e.g. activities, questions, categories) or general types of information (e.g. marital statuses, account types, U.S. states).
For our recipe project, we want to create a data model that will sufficiently store all the recipe information we might need. We’ll start with the following entities:
- Recipes
- Ingredients
- Styles
The Recipes entity will represent all the recipes in our application. The Ingredients entity will represent all the potential ingredients that will be used in every recipe. The Styles entity will describe the type of recipe (e.g. Italian, French, Thai, etc.).
Entities ultimately provide custom AS3 value objects you will use to interact with data in your Flex or Flash applications. Each entity will generate a pair of VOs: an item VO and a collection VO. An item VO holds properties representing a particular entity, while a collection VO is a container for many item VOs of the same entity. Think of an item VO as a singular record of one entity, and a collection VO as a collection of an entity’s records. In the sample set of entities above, six VOs will be generated:
- RecipeVO – stores the properties of a Recipe entity
- RecipeCollectionVO – stores a collection of RecipeVOs
- IngredientVO
- IngredientCollectionVO
- StyleVO
- StyleCollectionVO
We’ll be diving into detail about these objects later on. In the next few sections, we will occasionally refer back to them, but not in great detail.
Next, let’s create an entity.
4.1 Creating an entity
To create a new entity, click the "Add" link from the data model section of your project.
Figure 3 shows you how to create a new entity. You give the entity a friendly plural and singular name, meaning that you may include spaces and punctuation. X2O will prep these names when creating your generated code to avoid any compiler issues. You may also provide a description for the entity. Descriptions ultimately appear in the generated CMS and documentation. Therefore, it's good practice to give meaningful descriptions to your entities.
In addition, you may make an entity a defined type or associative entity from the dropdown underneath the name fields. We’ll re-investigate these kinds of special entities in Section 7.0.
After adding them, entities will appear in the list below the form. You can then configure these entities by clicking on the row in the list. There is also the option to delete an entity from the listing screen by clicking the "Delete" links on the right.
In our sample recipe project, we’ve added the Recipes, Ingredients, and Styles entities. You’ll also notice a fourth entity, Recipe Ingredients. We’ll discuss its purpose when we get to Section 6.3 – "Many-to-many Relationships."

Figure 3. Creating a new entity
Next, we’ll teach you all about attributes and relationships. Attributes are the properties that define an entity, while relationships bind two entities together. Most of the instruction in the next sections will reference the Recipes entity. To follow along, click on the Recipes row in the entities list.
5. Attributes
Attributes are the properties that define an entity.
Attributes will become properties of the generated item VO for a particular entity. In addition, all attributes must be assigned a name and a type. For recipes, we’ve created the following attributes, with their types in parentheses:
- Name (string) – This represents the name of the recipe.
- Instructions (rich text field) – This will house instructional content describing the recipe.
- Serving Size (integer) – This number tells you how many people the recipe should serve.
- Picture (file upload) – This will be a picture of the recipe.
Let’s see how we created them in the attributes section. From the Recipe entity page, click the "Add" button next to Attributes. You should see a foldout appear as it does below. Once you add an attribute, it will appear as a new row in the list below. Like entities, you can edit attributes by clicking the row or delete an attribute by clicking "Delete."

Figure 4. Creating a new attribute
5.1 Naming an attribute
Attributes must be given a friendly name. Just like entities, you can include spaces or punctuation. X2O will ensure the names can be safely converted to ActionScript code later on.
5.2 Types
In addition, attributes can be assigned one of the following types:
- Boolean
- Date
- Decimal
- File Upload
- Integer
- Money
- Phone Number (US – 10-digit based)
- Zip Code (US – 5 or 9-digit based)
- Rich Text
- Rich Text Foreign (allows non-western-based characters)
- String
Based on the attribute type chosen, X2O will provide validation rules on the generated AS3 code and CMS tool to ensure that these attributes adhere to their specified types.
For a couple of types, there are a few extra parameters you must provide.
5.2.1 Decimals
When selecting Decimal, you may also provide a precision and scale for the attribute. Precision is the total number of allowable digits for the decimal while scale refers to the number of digits allowed to the right of the decimal place. X2O sets the default precision to 10 and the default scale to 2.
For example, the number 3590.426 would have a precision of 7 and scale of 3.
5.2.2 Strings
When selecting String, you may also provide a maximum length for the string. No string may be greater than 4000 characters. If you require the string be longer than 4000 characters, you should select "Rich Text" or "Rich Text Foreign" (to support non-western characters) type instead. By default, the max length is set to 255 characters.
5.3 Required fields
You can make an attribute required by checking the "Make this a required field" checkbox (see Figure 4). This will ensure that this field is set with a valid value before a record is inserted or updated in both the AS3 code and CMS tool.
5.4 Filtering methods
You can also mark an attribute as filterable by checking the "Make filter methods on this field" checkbox (see Figure 4). This will create a series of methods in the generated AS3 code to filter the entity’s records by this attribute. Based on the type you’ve selected, X2O will create different filtering methods.
For a hypothetical attribute called "My Attribute", here are the kinds of methods you can expect in your generated AS3 code.
5.4.1 Boolean
Method:
- LoadAllWhereMyAttributeEqualTo – Returns entity records where MyAttribute is equal to a particular value.
5.4.2 Date, Decimal, Integer, Money
Methods:
- LoadAllWhereMyAttributeEqualTo – Returns entity records where MyAttribute is equal to a particular value.
- LoadAllWhereMyAttributeLessThanEqualTo – Returns entity records where MyAttribute is less than a particular value.
- LoadAllWhereMyAttributeGreaterThanEqualTo – Returns entity records where MyAttribute is less than a particular value.
- LoadAllWhereMyAttributeInRange – Returns entity records where MyAttribute is inclusively in a range of a low and high value.
5.4.3 Email, File Upload, Phone Number, Zip Code, String
Methods:
- LoadAllWhereMyAttributeEqualTo – Returns entity records where MyAttribute is equal to a particular value.
- LoadAllWhereMyAttributeLike – Returns entity records where MyAttribute contains a match for a particular value.
Note: Rich Text and Rich Text Foreign attributes are not filterable.
We’ll talk about how to use these filtering methods and where they reside in your AS3 code in Section 15.7.
5.5 Sorting
You may also determine how collections of records in an entity will sort by specifying a sort order for a particular attribute. Enter in the sort order priority in the "Sort Order is:" field (see Figure 4), and whether to sort ascending or descending on this attribute.
For example, in the Recipes entity, we have given the "Name" attribute a sort value of 1 (ascending). In the CMS, as well as in code, all loaded recipes will be sorted by Name from A-to-Z. To sort in reverse order, you check the descending checkbox. For dates, ascending sorts will go from earliest to latest while descending sorts from latest to earliest. For numerical values, ascending sorts will go from lowest to highest while descending sorts from highest to lowest.
You can also cascade sorts. Notice how we’ve given "Serving Size" a sort value of 2 (ascending). Any collection of recipes we load in ActionScript will come back ordered first by Name, and, in the event of two recipes with the same name, by Serving Size. The example below shows how four recipe records would be sorted by default based on our choice of sort ordering for Recipes.
Example sort:
1. Chicken Cordon Bleu (Serving Size of 3)
2. Chicken Cordon Bleu (Serving Size of 4)
3. Fettuccine Alfredo (Serving Size of 2)
4. Spaghetti and Meatballs (Serving Size of 4)
5.6 The ID attribute
By default, every entity comes with a unique integer attribute called "ID". This attribute cannot be modified or deleted. The ID will increment each time a new record is created, starting at 1.
If you’re familiar with databases, the ID concept in X2O is similar to a primary key. It gives us a way to uniquely identify each record in a particular entity. You’ll find we use the ID field over and over again in our code discussion to come.
The ID field will not show up on your list of attributes on the entity configuration pages. So, our recipe will not only have name, instructions, a serving size and picture, but also an ID.
6. Relationships
Relationships link entities together.
You may be wondering why Style is not as an attribute of Recipe, even though a recipe has an associated style. Since Style is an entity, we will, instead, create a relationship between Recipe and Style. In this case, a single Recipe has only one Style, but a Style can belong to many recipes. This is a classic "many-to-one" relationship between Recipes (many recipes…) and Styles (...have one style).
- Recipes have only one style.
- A style may belong to many recipes.
6.1 Related items (Many-to-one relationships)
Because recipes have only one style, a Recipe has a Related Style item. To add a new related item, click the "Add" button above the Related Items section of the Recipe detail page, as in Figure 5.

Figure 5. Creating a related item
From here, you select the related entity. In our case, we would select the Style entity.
In the two input fields below the dropdown, you name the relationship. X2O will create default names for the relationship which you can modify. The first input field names the relationship going from the current entity to the related entity (in this case, recipes own or have one "Related Style"). The second input field names the relationship going from the related entity back to the current entity (in this case, a style owns or has many "Related Recipes"). More on this in Section 6.1.2.
You can optionally make the relationship required. This only enforces that the entity must have a related item. More on required relationships in Section 6.1.4.
This relationship now means that a recipe record must have one related style item, and conversely, a style record may have many recipe records.
Once you add a relationship, it appears as a record in the related items list below. You can delete the relationship by clicking the "Delete" link to the right of a record.
6.1.1 How records are "linked" in a relationship
When you add a related item to an entity, X2O will make the related item a property of the entity’s item VO.
In the example we just discussed, the generated RecipeVO class will have a property called RelatedStyleItem (of type StyleVO). In addition, the RecipeVO will have a property called RelatedStyleID (of type int) which represents the unique ID value of the related StyleVO instance. If you’re familiar with object-relational databases, the RelatedStyleID is much like the foreign key of the Style entity.
Any time you create a related item, two new properties appear in the generated entity’s item VO class. Namely:
- A VO to the related item, and
- An int value representing the related item’s ID.
You can also have more than one of relationship between the same two entities. For instance, suppose we added a new People entity. We could create both a "Related Favorite Style" item and a "Related Least Favorite Style" item between People (many people…) and Styles (…have one favorite and one least favorite style). X2O simply requires that you name your relationships uniquely.
6.1.2 Relationships are always a two-way street
As you’ve seen, by creating a related item, you automatically create a related collection on the other entity in the relationship. In this case, once you’ve created a related Style in the Recipe entity, the Style entity will now have a RelatedRecipesCollection(of type RecipeCollectionVO) in the Recipe in its resulting StyleVO.
When loading a StyleVO record, all recipes whose RelatedStyleID matches the StyleVO will then be populated into the RelatedRecipesCollection. Again, we’ll get into working with your generated AS3 code later.
So, keep in mind that X2O relationships are like life’s relationships – there are always two sides to the story.
6.1.3 How to handle one-to-one relationships
Suppose we have two entities named Students and Lockers. A student can only have one locker, and similarly, one locker can only be assigned to a single student. Here, we have a "one-to-one" relationship as opposed to a "many-to-one" relationship.
- A student has only one locker.
- A locker only belongs to one student.
X2O does not explicitly handle one-to-one relationships. If you have a one-to-one relationship, simply pick one of the two entities from which to construct the related item. In the case just described, you could assign Lockers as a Related Item to Students. This means that a student will have one locker, but that a locker will have a related collection of Students (even though the developer should know that the collection will never have more than one record). You could, just the same, assign Students as a Related Item to Lockers. This means a locker will have one related student, but a student will have a collection of related lockers.
As a side note, if you find a need to create a one-to-one relationship, you may also want to consider combining the two related entities together. For example, suppose Lockers is composed of the attributes "Locker Number" and "Locker Combination.” You could, alternatively, add those two fields into Students and get rid of Lockers and corresponding relationship altogether. If there aren’t specific reasons to separate Lockers into its own entity, this might be a worthwhile alternative.
6.1.4 Making a relationship required
Like attributes, relationships can also be required.
By making a related item required, you are ensuring that a record cannot be inserted into the database unless that record has a related item. In our example, you would not be able to insert a new recipe without first relating it to an associated style. However, the flip-side of the relationship is not required. You can have styles that are not associated with a recipe.
In conclusion, a required relationship guarantees an entity has a related item but does not guarantee that the other entity’s related collection contains at least one item. Each recipe must have a style. Each style may or may not have a collection of related recipes.
6.2 Related collections (One-to-many relationships)
Related collections represent the "other side" of a many-to-one relationship. When you create a related collection, you’re simply creating the reverse of a many-to-one relationship. You can call this a one-to-many relationship. There are also cases where both entities share a related collection with one another, a many-to-many relationship. We'll cover this in section 6.3. For now, we'll focus on the one-to-many relationship.
For example, instead of creating the related Style in the Recipes entity earlier, we can create the same relationship by adding a Related Recipes collection inside of the Styles entity. To do this, go to the Styles entity’s detail page and click the "Add" button above the Related Collections panel as in Figure 6.
Just like related items, you select the entity you want to make as a related collection in the dropdown. X2O will then ask you whether many styles also belong to one recipe. This determines whether you should create a one-to-many relationship, or a many-to-many relationship. In this case, only one style belongs to a recipe, so the answer is no.

Figure 6. Creating a related collection
After selecting "no", you will get to name both sides of the relationship. X2O will create default names for the relationship which you can modify. The first input field names the relationship going from the current entity to the related entity (in this case, styles own or have many "Related Recipes"). The second input field names the relationship going from the related entity back to the current entity (in this case, recipes own or have many "Related Style").
There is no difference between creating a related item from entity A to entity B and creating a related collection from entity B to entity A. It comes down to personal preference. Often times, it’s just easier to "see" the relationship from one side rather than the other. Most people will find the statement "A recipe must have a related style" more clear than "A related style may belong to many recipes" and choose to create the relationship from the Recipe entity rather than from the Style entity.
Additionally, when we answer "yes" to the required relationship question for Related Collections, we are still following the same rules as when creating the relationship from the "Related Items" side. In our example, a style does not have to have an associated collection of recipes (e.g. the collection can be empty), but each recipe must have an associated style.
6.2.1 Relating an entity to itself
You can also relate an entity to itself. Suppose we have an entity called Categories. A classic example would be a navigation tree of categories, where a category may have many sub-categories. A category, in turn, may also be a sub-category of another categyory.
- A category can have many sub-categories.
- Categories can also have one parent category.
In this case, you could create a related collection of categories in the Category entity page. For clarity’s sake, you should rename the relationships from the given defaults to something like "Related Sub-Categories" for the related collection and "Related Parent Category" for the related item.
6.3 Many-to-many relationships
So far we have covered how to handle one-to-one, one-to-many (and many-to one) relationships. You’ll find that the vast majority of relationships in your data model will fall under one of these scenarios.
However, there is one final relationship type that X2O handles – the many-to-many relationship. Many-to-many relationships let two entities contain related collections of each other. Let’s take another look at our sample project for an example.
In our recipe application, we’ll want the capability to relate recipes to ingredients. After all, recipes without ingredients taste pretty bland. When we determine which entity is the "many" and which entity is the "one" in this relationship, we discover something new.
- A recipe can have many ingredients.
- An ingredient can belong to many recipes.
Oh no! In this case, a recipe needs a related collection of ingredients, and likewise, an ingredient can have a related collection of recipes it belongs to. In this case, we have a many-to-many relationship between ingredients and recipes. What now?
If you’ve ever designed databases, X2O handles many-to-many relationships in much the same way as a database designer would. You create a new entity that associates the two related collections together. In database lingo, you create a relational table that stores two foreign keys pointing to the two related tables. In X2O lingo, you create an associative entity.
In our case, we’ve created an entirely new entity called Recipe Ingredients. We’ve also marked this entity as an associative entity by selecting "This is an associative entity" in Figure 3. Then, we created two related items in the entity detail page:
- Related Recipe Item
- Related Ingredient Item

Figure 7. Recipe Ingredient’s related items
As you learned earlier, by creating a related recipe item in the entity Recipe Ingredients, X2O automatically creates a related collection of Recipe Ingredients in the Recipes entity. Similarly, by creating a related ingredient item in the entity Recipe ingredients, X2O automatically creates a related collection of Recipe Ingredients in the Ingredients entity.
Now, both recipes and ingredients contain a related collection of Recipe Ingredients. To create a "many-to-many" relationship is to create two "many-to-one" relationships where the related item, in both cases, is Recipe Ingredients.
[Recipes] <--> [Recipe Ingredients] <--> [Ingredients]
A recipe has many recipe ingredients.
An ingredient has many recipe ingredients.
Each recipe ingredient has one related recipe and one related ingredient.
As a developer, you can create associative entities manually (as we have here), or have X2O do the work for you. Recall in section 6.2, we created a one-to-many related collection between styles and recipes ("A style has many recipes, but a recipe can have only one style.").
In Figure 6, we answered "no" to the question "In this relationship, can many styles also belong to one recipe?". Now let's take the example where the answer is "yes." Namely, we'll go to the Recipes detail page and create a related collection to Ingredients. In this case, we'll say "yes" to the question "In this relationship, can many recipes also belong to one ingredient?".
By answering yes, X2O will inform you that it will create an associative entity for you. It will also create the two many-to-one relationships as well. X2O defaults the name of the newly created associative entity by concatenating the names of the two related entities together. This is a great time-saver as you data model.

Figure 7a. Automatic creation of an associative entity for a many-to-many relationship.
6.3.1 Residual benefits of creating an "associative entity"
One of the residual benefits of an associative entity is that now we can add additional information to the relationship if we’d like. For Recipe Ingredients, it’s not just important to know that you need eggs, flour, and sugar to bake a cake. It’s also important to know how much of each.
In the Recipe Ingredients entity, we’ve added a new string attribute called "Amount" to determine how much of one ingredient we need for a recipe. That way, we can store a value of "2" for the Recipe Ingredients record that relates eggs to the cake recipe, or "4 cups" for the Recipe Ingredients record that relates flour to the cake recipe and so forth.
Approaching it a different way, consider where else you could possibly place the "Amount" attribute in this data model. It would not make sense for a recipe to have an amount (as it doesn’t let you determine which of the many ingredients you are referring to), nor does it make sense for an ingredient to have an amount (as it doesn’t tell you which recipe to apply this amount).
Just like a regular entity, we can also decide to filter, sort, and require any of the attributes of an associative entity.
6.3.2 Why we mark an entity as an "associative entity"
You may be asking why we explicitly marked the Recipe Ingredients entity as associative. By marking it as associative, X2O will generate underlying procedures and objects to enhance the performance of data loads that aren’t normally done for regular entities. We’ll get to this more in Section 11 – Working with your generated SWC.
7. Special Entities
There are two special kinds of entities, associative entities and defined type entities.
7.1 Associative Entities
We’ve already covered associative entities in the previous section.
To recap, associative entities are a special kind of entity whose main job it is to link two or more other entities together. For those of you familiar with database design, associative entities are equivalent to relational tables that contain foreign keys to two or more tables.
In X2O, this means that associative entities will contain two or more related items. Associative entities handle many-to-many relationships by creating many-to-one relationships from itself to other entities.
7.2 Defined Types
Defined types are entities whose records can be referenced in your AS3 code without having to first load them directly from the database. You can mark an entity as a defined type by checking "Make this a defined type" (see Figure 3).
Defined types are just like regular entities, except that they will generate an additional static class that will store static const int variables representing each record in that entities’ collection.
For example, we’ve made Styles a defined type. After you’ve added new style records in the generated CMS, you can regenerate the project to see these styles appear in an ActionScript class called Styles. By making Styles a defined type, we can take shortcuts in our code later.
For instance, without defined types, we’d have to know what the ID of an Italian style record is before we could load all Italian recipes. With defined types, we can load Italian recipes without having any idea of what the Italian style ID is.
How does this work? When you mark an entity as a defined type, X2O will automatically create an attribute called Key Name. Every subsequent record you make must have a unique value for Key Name because it is used to name the static const int variables in the generated defined type class. The value of these variables is the ID of the record. Because Styles was marked as a defined type, it automatically has a "Key Name" attribute included.
It’s far easier to see the benefit of defined types in code. We’ll see examples of how defined types work in code in Section 16 – Defined Types.
8. Custom Methods
Custom methods let you customize queries in your data model using SQL.
So far, you’ve seen how to create a data model for your application and customize the entities and relationships in your data model. As you’ll soon see, the resulting generated value objects will provide you ways to customize how you want to load records from the database. For most applications, this is enough to get you from start to finish.
However, there likely will be times when the generated methods won’t give you the versatility you need. For more customized ways to mine your data, you will want to write your own custom methods.
Custom methods are nothing more than SQL (Structured Query Language) SELECT statements against the database for your X2O project. Obviously, you need to know how to write SQL in order to use custom methods.
Adobe’s Developer Center has a nice SQL primer article available at http://www.adobe.com/devnet/dreamweaver/articles/sql_primer_print.html.
By piggy-backing off of SQL, custom methods give you a full breadth of querying options including:
- Applying any number of custom parameters to query a record set.
- Joining two or more entities together.
- Using SQL operations like SUM or COUNT against attributes.
- Complex grouping and ordering using GROUP BY and ORDER BY.
Custom methods convert your SQL statements into corresponding item VO and collection VO pairs in your generated SWC. They will be named after the name you give the custom method. Think of these generated VOs as wrappers to your SQL statements.
X2O will turn your SQL parameters into parameters in a load() method off of the generated collection VO. In turn, each item VO will contain attributes that correspond to the fields retrieved from the SELECT portion of the SQL statement. We’ll explore these objects in detail in Section 17 – Custom Method VOs.
Think of custom method VOs as object representations of your customized SQL select statement.
Currently, X2O only allows you to write custom methods using the SQL "SELECT" action. Custom INSERT, UPDATE, or DELETE statements are not allowed.

Figure 8. Creating a new custom method
Custom methods are located in their own tab within a project. From there, you click the "Add" button to create a new custom method. Figure 8 shows you the resulting panel.
First, you give the custom method a name. You may include spaces or punctuation, but these will get removed by X2O during generation.
You then write your SQL statement in the main body below. The "Select fields from data model diagram" link is available to help you choose the entities and attributes you want returned in your SELECT statement (see Figure 9). The resulting overlay also allows you to see how X2O has converted your attributes and relationships into database field names.
If your custom method will include passed-in parameters, they must be prefaced by the @ symbol. When you click off the SQL statement box, X2O will update your parameter list below (see the parameter list in Figure 8) with the parameters you’ve passed in. You then choose the appropriate type for each parameter. Since you are typing in raw SQL text, X2O will not deduce these types automatically for you.

Figure 9. Overlay to select entities and attributes that will be return in your custom method
When X2O generates the corresponding VOs based off a custom method, it first attempts to run the SQL statement to ensure that it is properly written. If not, it will skip the method and continue with the rest of the framework, but will alert you with a warning upon completing a generation.
Because custom methods are fairly open-ended in their construction, here are a few good rules to live by to ensure they are properly generated:
- Be sure that the returned values in your SELECT statement do not have any naming conflicts. If two attributes have the same base name (e.g. The attribute "Name" is both an attribute of Recipe and Ingredient), you should cast them to distinct names (e.g. [Recipes].[Name] AS [RecipeName]).
- As mentioned earlier, all parameters should be prefaced with an @ symbol.
- Be sure you give the proper types to the parameters you create. X2O does not attempt to assign proper types automatically, so it’s up to you to ensure they are set correctly.
In Figure 8, we’ve created a custom method named GetAllRecipesByStyleAndMinimumServingSize. The SQL statement we’ve written will return records with all Recipe properties plus a property called StyleName (the recipe’s related style’s Key Name). It will also filter these records based on a passed-in minimum serving size and a style ID.
SELECT [Recipes].*, [Styles].[KeyName] AS [StyleName] FROM [Recipes] INNER JOIN [Styles] ON [Recipes].RelatedStyleID = [Styles].[ID] WHERE [Recipes].[ServingSize] >= @MinimumServingSize AND [Styles].[ID] = @StyleID
In summary, custom methods let you pinpoint specific queries you need that the generated X2O VOs will not provide by default.
9. Customizing the CMS
So far, we’ve focused heavily on building your data model. The other half to working with X2O is customizing your CMS.
Outside of the generated SWC, the CMS will become your central place for working with data. In this section, we’ll look at manipulating entities and navigation within the CMS to customize your user experience.
9.1 Customizing Entities
Each entity you create will be modifiable from the CMS generated with your project. The CMS is a light-weight, secured site where you can administer records. While we won’t go into full detail about the CMS until Section 10.1, this section will explain three ways you can customize the appearance of each entity’s set of management pages.
Within an entity’s detail page, click on the "CMS Setup" tab on the left-hand menu. You will then see the three panels we will discuss below.
9.1.1 Add/Edit Page – Field Ordering
For each entity, X2O will generate an "Add" and "Edit" page within the CMS. These pages will convert the entity’s attributes and relationships into form fields. By default, X2O will display the attribute form fields in the same order you created them. However, this is modifiable.
On the "Add/Edit Page – Field Ordering" panel within the "CMS setup" section, you can drag-and-drop attribute fields as you see fit. The order you place your attributes will be reflected in the CMS.

Figure 10a. Ordering fields for Recipe’s add/edit pages within the CMS
Here’s how the generated CMS edit page for Recipes looks, with the attribute ordering applied. Notice that related items and collections always display after the attributes.

Figure 10a. Ordered fields for Recipe’s Add/Edit pages in generated CMS
9.1.2 Dropdown Selector View
X2O converts an entity’s related items into standard dropdown menus in the add/edit screens. For example, because a recipe ingredient (our associative entity) has a related recipe item, the Recipe Ingredient’s "Add" and "Edit" pages will contain a dropdown menu of recipes to relate to the recipe ingredient.
The "Dropdown Selector View" panel, let’s you enforce how to display the records of an entity when it is in the dropdown. For Recipes, we may want to show its Name and related style’s Key Name, with a dash in between them. In the textbox in Figure 11a, you use bracket notation to encase the attributes and related item attributes you want to display. Below the text box is a list of available attributes and related item attributes in bracket notation.

Figure 11a. Configuring dropdown display panel for Recipes
Here’s how the generated CMS edit page for Recipe Ingredients looks, with the related Recipe dropdown displayed according to the configuration in Figure 11a. (e.g. "Chicken Cordon Bleu – French")

Figure 11b. Dropdown display of Recipes in generated CMS
By default, the "Dropdown Selector View" text box (Figure 11a) will be set to the first attribute you create for the entity. For Recipes, because we added Name first, it would default to the [Name] field.
9.1.3 Listing Page View
X2O also lets you customize the display of each entity’s CMS listing page. The listing page is where all records for a given entity live. They are sorted according to the sort orders you gave attributes in the entity (or, if there are no sort orders applied, in the order the records are entered in the CMS).
In the "Listing Page View" panel, you can customize two things.
First, you can optionally paginate records by setting the records allowed per page. If you leave this blank, the listing page will display all records for a given entity.
Second, you can decide what attributes display on the listing page. For Recipes, we may want to show its Name and related style’s Key Name. In the textboxes in Figure 12a, we’ve typed in the appropriate headers and values to display. X2O allows you three different items to display per record.

Figure 12a. Listing page view panel
Here’s how the generated CMS listing page for Recipes looks according to the configuration in Figure 12b.

Figure 12b. CMS listing page for Recipes.
9.2 Customizing CMS Navigation
By default, the CMS navigation will group defined types under one navigation tab "Defined Types" and all other entities flow to the right of it.
For projects with lots of entities, this might get a bit cluttered. X2O lets you optionally re-organize the entity navigation tabs by clicking the "CMS Navigation" tab within a project.
In CMS Navigation, you can create any number of top-level navigation items. As soon as you create one, the entity tabs will get overwritten by your own custom tabs. In Figure 13a, we’ve added two top-level navigation tabs called "Top Nav 1" and "Top Nav 2". These tabs will appear as containers in the green panel below. Then, we drag and drop entities into these top-level navigation tabs. The entities will now appear as sub-navigation links under each top-level navigation tab.
Figure 13b shows you how the re-generated CMS handles this customized navigation.

Figure 13a. Customizing top-level navigation

Figure 13b. Resulting CMS navigation in generated CMS
So far, we’ve taken you through all the basic knowledge to set up a data model and configure your CMS display. We’re roughly half-way through your full X2O indoctrination. The second half of this document will focus on how to use the generated tools and code.
Next up, we’re ready to generate the project and work with the framework!
10. Generating (and re-generating) your project
The real legwork of X2O happens when you generate your project. Look for the green "Generate" button at the bottom of most every page in X2O.

Once you click on it, X2O will re-confirm your settings and let you know if there are any issues with your project. If not, click "Generate" again.
While you take a breather, this is where the heavy-lifting begins. If this is the first time the project has been generated, it will create a new database. If you’ve generated previously, it will negotiate the changes to the database and apply the appropriate updates while still keeping your old data intact. In addition, it will regenerate the necessary underlying server-side code, stored procedures, XML services, CMS tool, Actionscript docs, and SWC to tie the whole project together.
The generation (or regeneration) of a project takes about 1-2 minutes on average, though it may take longer depending on the complexity of your project.
If you close your browser while a project is still generating, the process will still complete on the server. However, you will not know immediately when the process has completed. As a safety net, you may want to wait a few minutes before logging in and checking on your project generation status. So, try to avoid accidentally leaving the X2O client when you are in the middle of generating.
Note: If an error occurs when generating, the generated material will roll itself back to its previous state (unless this is the first time you’ve generated this project) and you should be able to view the error.
When generation completes, you will see a completion screen. The completion screen contains links to the CMS tool, documentation of the generated AS3 code, and a compiled SWC. Table 1 shows you where these components live within your X2O project.
All generated content management systems are secured under SSL.
Table 1. Location of generated X2O framework components
| Generated component | Location |
| CMS | https://cms-[project name]-[company name].x2oframework.net |
| AS3 Documentation | http://doc-[project name]-[company name].x2oframework.net |
| Flex or Flash CS4 SWC | https://cms-[project name]-[company name].x2oframework.net/downloads/[project name].swc |
After you’ve generated the first time, your links to the CMS, AS3 documentation, and SWC will always be available on the "Project Info" tab.
Next, we’ll go over the CMS. In the following section, we’ll assume you’ve already generated our sample project.
10.1 Content Management System
When the generation has completed, you may directly link to the CMS tool from the completion screen. The CMS tool will contain pages to manage all the entities and relationships that you built in your project.
Login using the credentials you provided when you created your project. Our sample recipe project’s credentials are U: sample / P: recipe. If you do not remember your login credentials, they are displayed on the completion screen and on the project info page.
As discussed in Section 9.2, by default, the top-level navigation contains tabs for all the non-defined-type entities you’ve created. The "Defined Types" tab will store all entities you’ve made as defined types, for better organization.
You may modify the default navigation in X2O by going to the "Custom Navigation" section of your project and following the instructions provided. See Section 9.2.
10.1.1 Record listing
Clicking on any entity tab will take you to that entity’s record listing page. The display and number of records on the listing page is determined by how you’ve customized the entity’s Listing Page View in the entity’s CMS setup screen.
10.1.2 Adding new records
From the listing page, there will be a link on the right to add new records. The add screen will contain a form for adding a new record. Note that the ordering of the form elements is determined by how you’ve customized the Add/Edit Page — Field Ordering in the entity’s CMS setup.
The add screen will contain dropdown boxes for related items. For instance, the "Add New Recipe" screen will have a dropdown of styles to select from. The display of the style dropdown is determined by how you’ve customized the Dropdown Selector View in the style’s CMS setup screen.
The form also validates required fields and ensures that type requirements are met (e.g. valid zip codes, emails, integers, etc.). For defined types, an error will be thrown if you add a record with a Key Name already used in another record of that defined type. Remember, defined types’ Key Name attribute must be unique across all records of that defined type.
When you add new records, X2O will automatically assign IDs to them. The IDs auto-increment (starting at 1) and are unique for each record in a given entity.
For the purposes of this tutorial, we’ve mocked up 5 recipes and 3 styles as seen below. Feel free to add these to the Sample Recipe project CMS in your X2O account so you can follow the code samples later. You should add records in the order shown below to ensure the IDs match the IDs we reference in our sample code later on.
Styles
| ID | Name |
| 1 | Italian |
| 2 | French |
| 3 | Thai |
Recipes
| ID | Name | Related Style |
| 1 | Fettuccine Alfredo | Italian |
| 2 | Chicken Parmagiana | Italian |
| 3 | Chicken Cordon Bleu | French |
| 4 | Tofu Pad See Eiw | Thai |
| 5 | Spaghetti and Meatballs | Italian |
Ingredients
| ID | Name |
| 1 | Flour |
| 2 | Tomatoes |
| 3 | Chicken |
| 4 | Tofu |
| 5 | Eggs |
When we get into programming with the generated SWC, we’ll reference these records throughout our code examples. You may want to bookmark the tables above as a reference if you do not wish to insert these records yourself.
10.1.3 Editing existing records
You edit records by clicking on them from the listing page. The edit record screens look and behave similar to the "Add" record screens. The same validation rules and form ordering apply to edit screens.
10.1.4 Deleting records
To delete a record, simply click the "Delete" link to the right of a record on the entity’s listing page.
10.2 AS3 Docs
X2O will create a documentation website that details the entire generated SWC API including every generated item and collection VO. The documentation is a handy reference to remind yourself of the specific objects, relationships, and methods you’ve created with X2O.
While we will cover the most important methods are covered in this document, please consult the documentation site for the full API.
10.3 ActionScript Code
Finally, X2O will generate a compiled SWC file that you simply download and place inside the /libs folder of a Flex project (or import into a Flash CS4 project). You should immediately have all the classes and services needed to start building your app. There is absolutely no further configuration needed.
In the next sections, we’ll take you through the SWC and how you use the value objects and classes within the SWC.
11. Working with your generated SWC
Each entity you create will ultimately generate two ActionScript value objects (VOs): An item VO and a collection VO. From the four entities in our recipe project’s data model, eight VOs will be generated:
- RecipeVO
- RecipeCollectionVO
- IngredientVO
- IngredientCollectionVO
- StyleVO
- StyleCollectionVO
- RecipeIngredientVO
- RecipeIngredientCollectionVO
Item VOs hold properties representing a particular entity, while collection VOs hold a collection of item VOs. For example, an instance of StyleCollectionVO can hold a collection of StyleVOs. These VOs live in the com.x2o.gen.vo package in your downloadable SWC. The attributes and relationships you made in X2O get converted to strongly-typed properties in each item VO.
All generated value objects live in the com.x2o.gen.vo package.
In addition, if you’ve created any custom methods, corresponding collection VO and item VO pairs will live in the com.x2o.gen.customservices.vo package.
Table 2. How X2O concepts translate to AS3 concepts
| X2O | AS3 | X2O Example | AS3 Examples |
| Entities | Item and collection VOs | Recipes |
RecipeVO; RecipeCollectionVO; |
| Attributes | Properties of an item VO | Name, Serving Size | RecipeVO.Name; RecipeVO.ServingSize; |
| Relationships | Properties of an item VO | Recipes’ related style | RecipeVO.RelatedStyleItem; StyleVO.RelatedRecipeCollection; |
| Custom methods | Item and collection value objects | GetAllRecipesByStyleAndMinimumServingSize | GetAllRecipesByStyleAndMinimumServingSizeVO; GetAllRecipesByStyleAndMinimumServingSizeCollectionVO; |
In Table 2, you can see how concepts in X2O are translated to AS3 code.
11.1 Login and Security
Before you can begin using any X2O-generated SWC, you must explicitly call the login() method found in the X2OManager class. Under the hood, the login() method calls a service for your X2O project to ensure that your application can communicate with the remote database.
All communications between your generated SWC and the X2O database is done over normal http, but responses and requests are encrypted. Here’s sample code you would start every X2O-driven application with:
import com.x2o.X2OManager;
import mx.rpc.events.FaultEvent; // Use com.x2o.core.base.X2OFaultEvent for Flash CS4
private function init():void
{
X2OManager.getInstance().addEventListener(Event.COMPLETE, onLogin);
X2OManager.getInstance().addEventListener(FaultEvent.FAULT, onLoginFailed); // Use X2OFaultEvent.FAULT for Flash CS4
X2OManager.getInstance().login();
}
private function onLogin(e:Event):void
{
//Now you can start using the X2O value objects...
}
private function onLoginFailed(f:FaultEvent):void // Use X2OFaultEvent for Flash CS4
{
//Handle the fault here...
}
Let’s now roll up our sleeves and see how to work with the generated SWC. First, we’ll go over the general behavior of X2O value objects before we dig into how we use them.
12. X2O Value Objects
Traditionally, Actionscript VOs store data but do not perform any actions (like calling remote services or commands). In X2O, this is not the case.
X2O VOs are "self-actionable" objects. A collection VO (e.g. RecipeCollectionVO) can load item VO records (e.g. RecipeVO instances) into itself from its own methods. An item VO (e.g. RecipeVO) can load a record into itself by its ID, insert itself as a new record into the database, update its record in the database, or delete itself from the database.
Each generated VO will come with a standard set of methods as well as specific filtering methods based on how you’ve customized their corresponding entities in X2O. In addition, VOs handle their own validation, throw errors, and dispatch fault events, so you can be assured not to ever have bad data in your database.
In this section, we’ll go over the basic methods and recommended ways to use your generated VOs. For a more comprehensive look at the X2O API, you should consult the documentation site for your X2O project.
13. Item VOs
Every entity created in X2O will generate a corresponding item VO. Item VOs will contain properties based on how you configured the entities in X2O. Based on their attribute types, a representative AS3 type is created for each of the attributes in an item VO.
13.1 Item Properties
Here’s how X2O attribute types are converted into ActionScript types:
- Boolean – Boolean
- Date – Date
- Decimal – Number
- Email – String
- File Upload – String
- Integer – int
- Money – Number
- Phone Number (US – 10-digit based) – String
- Zip Code (US – 5 or 9-digit based) – String
- Rich Text – String
- Rich Text Foreign (allows non-western-based characters) – String
- String – String
Let’s take recipes as an example. The RecipeVO class will contain the following primitive properties:
- Name:String
- Instructions:String
- ServingSize:int
- Picture:String
- RelatedStyleID:int
13.2 Getting and setting properties
Each of the attributes created for the Recipes entity now manifest as properties in the RecipeVO class. Accessing and assigning data is no different than how you’re used to working with other ActionScript objects.
var someRecipe:RecipeVO = new RecipeVO();
// Set/Get the name of a variable called someRecipe
someRecipe.Name = "My tasty recipe";
// Set/Get the serving size of a variable called someRecipe
someRecipe.ServingSize = 4;
In addition, because we created a related item from Recipes to Styles (a recipe has one style), the RecipeVO also creates a RelatedStyleID property to store the ID of the related style. See Section 6.1.1 for a refresher on how related items link to an entity.
Item VOs also contain references to related classes. The RecipeVO class also has the following additional properties:
- RelatedStyleItem:StyleVO
- RelatedRecipeIngredientsCollection:RecipeIngredientsCollectionVO
When working with an instance of RecipeVO, you can easily access and assign information about its related entities (in this case, its related style and related collection of recipe ingredients) using the same familiar dot notation you’re used to. This is what makes programming with X2O feel so fluid and natural. To access a particular recipe’s style name, see below.
// Create a new style object...
var someStyle:StyleVO = new StyleVO();
someStyle.Name = "Chinese";
// Assign the recipe this style...
someRecipe.RelatedStyleItem = someStyle;
// Will output "Chinese"...
trace(someRecipe.RelatedStyleItem.Name);
We’ll show you how to access data from related collections (and collections in general) in the next section.
13.3 Validating setters
Recall that X2O allows you to create attributes of with a wide array of types. While some types naturally map to an ActionScript type, some X2O attribute types are more high-level.
- Email – String
- Phone Number (US – 10-digit based) – String
- Zip Code (US – 5 or 9-digit based) – String
While there is no equivalent Email type in ActionScript, the generated setter will validate whether the attribute was set to a valid email address. The same validation goes for phone numbers (it must be a 10-digit phone number) and zip codes (it must be 5 or 9 digits). If you try to assign one of these specially-typed attributes to an invalid value, the setter will throw an error. Suppose we create an entity with an attribute named "My Email Address" of type Email. Below shows the error thrown if you attempt to assign it a non-email string.
myObject.MyEmailAddress = "not an email address";
---
Error thrown: MyEmailAddress is not in the correct format. Email addresses must be in a typical format, such as: username@domain.com
In addition, setters ensure that string values are within the specified maximum lengths. Suppose we created an entity with an attribute named "String With 10 Character Max Length" and set its length to 10. Below shows the error thrown if you attempt to assign a string with a length greater than 10.
myObject.StringWith10CharacterMaxLength = "abcdefghijklmnopqrstuvwxyz";
---
Error thrown: StringWith10CharacterMaxLength cannot be longer than 10 characters long.
By default, errors thrown will appear in a pop-up dialog box if you are testing your application on the debug version of Flash player.
13.4 Working with File Upload attributes
Recall that our Recipe entity has a file upload attribute called "Picture". In the RecipeVO, the Picture property doesn’t store the actual file. Instead, it stores a relative path to the location of that file, if that file was uploaded via the CMS or throughout the generated X2O FileUpload class (more on the latter in Section 19.2).
To get a file upload’s full path, you simply append the base path of the file repository for your project in front of the relative path. The base path is the assetsURL property found in the static com.x2o.X2OManager class. Here’s a quick example:
var anImage:mx.controls.Image = new mx.controls.Image();
// The full path to the uploaded Picture for the given recipe
anImage.source = com.x2o.X2OManager.assetsURL + myRecipe.Picture;
14. Database Operations with Item VOs
Item VOs are capable of four basic database operations: Loads, inserts, updates, and deletes. Because X2O VOs are self-actionable, they perform these operations on themselves. In other words, the methods to load, insert, update and delete an entity’s record are contained within the item VO that holds the record.
Like other event-based objects, item VOs also allow you to add and remove event listeners using the addEventListener and removeEventListener methods. Before we get into each action, let’s first take a look at the events an item VO can dispatch. Item VOs dispatch custom events from the com.x2o.core.util.X2OEvent class.
14.1 Dispatched Events
- X2OEvent.LOADCOMPLETE – This event is dispatched when the loadByID() method has successfully returned data from the database. The X2OEvent class contains a property called "VO" which refers to the VO that called the load method.
- X2OEvent.INSERTCOMPLETE – This event is dispatched when the insert() method has successfully executed against the database. The X2OEvent class contains a property called "VO" which refers to the VO that called the load method.
- X2OEvent.UPDATECOMPLETE – This event is dispatched when the update() method has successfully executed against the database. The X2OEvent class contains a property called "VO" which refers to the VO that called the load method.
- X2OEvent.DELETECOMPLETE – This event is dispatched when the delete() method has successfully executed against the database. The X2OEvent class contains a property called "VO" which refers to the VO that called the load method.
- mx.rpc.events.FaultEvent.FAULT (com.x2o.core.base.FaultEvent.FAULT for Flash CS4) – This event is dispatched if any errors have occurred while attempting to load data from the X2O database.
Next, let’s look at the methods available to every generated item VO. We’ll show you when these events are dispatched, and how to use event listeners to listen to the dispatched events.
14.2 Loads
Method signature: loadByID(ID:int, _bLoadDeep:Boolean = false):void
Item VOs contain a load method called loadByID(). As its name suggests, you pass the ID value as a parameter and it will load the corresponding record from the database into itself. In addition, you can optionally pass in a Boolean to _bLoadDeep. Loading deep will pull not just the attributes of the record into the item VO, but also grab the first-level relationship records as well. This might not make 100% sense right now, but it is a pretty important feature of X2O. We’ll talk about this more in Section 14.2.1.
For now, we will just load the recipe whose ID is 1 (without setting _bLoadDeep to true).
1 var myRecipe:RecipeVO;
2
3 private function loadOneRecipe():void
4 {
5 myRecipe = new RecipeVO();
6 myRecipe.addEventListener(X2OEvent.LOADCOMPLETE, onRecipeLoaded);
7 myRecipe.addEventListener(FaultEvent.FAULT, onRecipeLoadFailed);
8 myRecipe.loadByID(1); // Load recipe whose ID is 1
9 }
10
11 private function onRecipeLoaded(e:X2OEvent):void
12 {
13 //Trace out info using the myRecipe object…
14 trace("Output ID and Recipe Name for myRecipe after it is loaded");
15 trace("ID: " + myRecipe.ID + ", Recipe Name: " + myRecipe.Name);
16 trace("-----");
17
18 //Equivalently, trace out info using the event’s VO object…
19 trace("Output ID and Recipe Name for e.VO after it is loaded");
20 trace("ID: " + e.VO.ID + ", Recipe Name: " + e.VO.Name);
21 }
22
23 private function onRecipeLoadFailed(e:FaultEvent):void
24 {
25 // Trace out what went wrong...
26 trace(e.fault.message);
27 }
On line 1, we create a new instance of a RecipeVO called myRecipe. On line 6, within the loadOneRecipe() method, we add an event handler called onRecipeLoaded which will be executed when the X2OEvent.LOADCOMPLETE event is dispatched. The event will dispatch once the data has successfully returned from the database and has been loaded into myRecipe. On line 7, we add an event handler called onRecipeLoadFailed which will be executed if a service error occurs while loading data. We implement onRecipeLoadFailed at the bottom of the code snippet by simply tracing out the fault message.
On line 8, we call the loadByID() method on myRecipe, passing in the ID value of 1.
In the onRecipeLoaded method, we must pass an instance of X2OEvent as a parameter (e). The event itself contains a reference to the myRecipe object in a property named VO. Thus, e.VO is a reference to the object myRecipe. The only difference is that e.VO is typed as a generic Object instead of a RecipeVO. You may want to cast this object as a RecipeVO to take advantage of code-hinting and faster execution:
e.VO as RecipeVO
On line 15, we trace out both the ID and Name properties of the myRecipe object. On line 20, we trace out the exact same values, but use the event’s VO property instead. You can always use the X2OEvent instances’ VO property (e.VO) instead of the originating VO itself (myRecipe). This can be useful if, for example, we did not scope myRecipe to the class and therefore did not have it available in the event handler method.
Here’s what the sample output should look like:
Output ID and Recipe Name for myRecipe after it is loaded
ID: 1, Recipe Name: Fettuccine Alfredo
-----
Output ID and Recipe Name for e.VO after it is loaded
ID: 1, Recipe Name: Fettuccine Alfredo
14.2.1 What is this "load deep" business?
Every load method allows you to pass in an optional _bLoadDeep Boolean parameter to determine whether you want to load the item "deeply.” By default, the _bLoadDeep parameter is set to false. This tells X2O to just return the values of the primitive properties of an item VO instance, without including the properties for its related entities.
For example, in the sample code we showed you, the myRecipe object would only contain values for Name, Instructions, ServingSize, Picture, and RelatedStyleID (the primitive types) because we did not set _bLoadDeep to true. If we attempted to access, say, the Related Style Name (myRecipe.RelatedStyleItem.Name), it would return null.
By setting _bLoadDeep to true, you will return not just the base properties of the item, but also the first-level relationships of the item VO (for a RecipeVO, these are the RelatedStyleItem and RelatedRecipeIngredientsCollection).
In the previous example, had we passed in true, myRecipe.RelatedStyleItem.Name will return "Italian". Let’s modify the previous example to pass in true to _bLoadDeep and trace out the recipe’s related style name.
var myRecipe:RecipeVO;
private function loadOneRecipe():void
{
myRecipe = new RecipeVO();
myRecipe.addEventListener(X2OEvent.LOADCOMPLETE, onRecipeLoaded);
myRecipe.loadByID(1, true); // Load recipe whose ID is 1
}
private function onRecipeLoaded(e:X2OEvent):void
{
//Trace out info using the myRecipe object…
trace("Output ID, Recipe Name, and Style Name for myRecipe after it is loaded");
trace("ID: " + myRecipe.ID + ", Recipe Name: " + myRecipe.Name + ", and Style Name:" + myRecipe.RelatedStyleItem.Name);
trace("-----");
//Equivalently, trace out info using the event’s VO object…
trace("Output ID, Recipe Name, and Style Name for e.VO after it is loaded");
trace("ID: " + e.VO.ID + ", Recipe Name: " + e.VO.Name + ", and Style Name:" + e.VO.RelatedStyleItem.Name);
}
The resulting trace output should be:
Output ID, Recipe Name, and Style Name for myRecipe after it is loaded
ID: 1, Recipe Name: Fettuccine Alfredo, Style Name: Italian
-----
Output ID, Recipe Name, and Style Name for e.VO after it is loaded
ID: 1, Recipe Name: Fettuccine Alfredo, Style Name: Italian
Keep in mind that deep loads have a limitation. They will only return the first level relationship data and not go any further into the database. In other words, if a StyleVO, in turn, had a relationship to another object, that data will not get returned from the load methods on myRecipe. Only the immediate descendent relationships of a RecipeVO will be returned.
The only instance where deep loads go beyond one-relationship deep is when an entity is related to an associative entity. As we mentioned earlier, associative entities form many-to-many relationships between two or more objects by having many-to-one relationships from itself to these other objects.
In the case of related associative entities, a load deep returns not only the related associative entity collection, but all of its related items as well. This should make sense – an associative entity’s main value is to link other entities together. So, we should expect that by loading an associative entity, we would automatically get back its related entity records from the database.
Recall that Recipe Ingredient was marked as an associative entity. In the previous example, by loading deep, we would be able to access not only myRecipe’s RelatedRecipeIngredientsCollection (which contains a collection of RecipeIngredientVO objects), but, in turn, each RecipeIngredientVO instance’s RelatedIngredientVO data as well.
The diagram below shows you the difference between a regular shallow load (_bLoadDeep = false) and a deep load (_bLoadDeep = true).

Note: One drawback of setting _bLoadDeep to true is that the resulting collection load will perform slower due to the extra data transfer. For most applications, the performance hit is marginal, but we recommend you only load deep when you need access to an item VO’s related entity data.
14.3 Inserts
Method signature: insertVO(_bReturnDeepLoad:Boolean):void
Every item VO contains an insert method that lets you add an item VO instance as a new record in the database. The insert method contains an optional parameter, _bReturnDeepLoad, to re-load the resulting inserted VO deeply. We’ll see how this works later.
For now, we will do a simple insert of a new recipe style ("Mexican") into the database.
1 private var myNewStyle:StyleVO;
2
3 private function insertStyle():void
4 {
5 myNewStyle = new StyleVO();
6 myNewStyle.addEventListener(X2OEvent.INSERTCOMPLETE, onInsertCompleted);
7 myNewStyle.Name = "Mexican";
8 myNewStyle.insertVO();
9 }
10
11 private function onInsertCompleted(e:X2OEvent):void
12 {
13 trace("Insert has completed! The new ID is:" + myNewStyle.ID);
14 }
The flow of the code goes much the same as for loading an item VO. In the insertStyle method (line 3), we instantiate the myNewStyle object. Then, on line 6, we add the onInsertCompleted function as an event listener to X2OEvent.INSERTCOMPLETE. This event will dispatch as soon as the new style has successfully inserted itself into the database.
On line 7, we set the Name of the style we are about to insert. On line 8, we insert the record represented by myNewStyle into the database.
Notice that we do not assign any value to ID. IDs are managed by X2O itself. The myNewStyle object will be given an ID when it has been successfully inserted into the database. In the onInsertCompleted method, we can access the generated ID for the newly inserted myNewStyle object.
Just as with the loadByID() method, the insertVO() method acts upon the object itself. In this case, once we insert myNewStyle as a record in the database, it updates its ID property from the ID field in the database.
14.3.1 Inserting records with related items
So far, we’ve seen how to do an insert of an instance of a basic item VO – StyleVO. A StyleVO’s only primitive attribute is the Name property. Now, let’s insert a new recipe record, linking the recipe to a particular style. In the example below, we’ll add the "Mexican" style record again. This time, once the record style is inserted, we’ll insert a recipe whose RelatedStyleItem is the Mexican style.
private var myNewStyle:StyleVO;
private var myNewRecipe:RecipeVO;
private function insertStyle():void
{
myNewStyle = new StyleVO();
myNewStyle.addEventListener(X2OEvent.INSERTCOMPLETE, onStyleInsertCompleted);
myNewStyle.Name = "Mexican";
myNewStyle.insertVO();
}
private function onStyleInsertCompleted(e:X2OEvent):void
{
myNewStyle.removeEventListener(X2OEvent.INSERTCOMPLETE, onStyleInsertCompleted);
myNewRecipe = new RecipeVO();
myNewRecipe.addEventListener(X2OEvent.INSERTCOMPLETE, onRecipeInsertCompleted);
myNewRecipe.Name = "Chicken Fajitas";
myNewRecipe.Instructions = "Blah blah for now.";
myNewRecipe.ServingSize = 2;
myNewRecipe.RelatedStyleID = myNewStyle.ID;
myNewRecipe.InsertVO();
}
private function onRecipeInsertCompleted(e:X2OEvent):void
{
trace("Done inserting new Mexican recipe!");
}
In onStyleInsertCompleted(), we instantiate the myNewRecipe object, and set the appropriate primitive values for a Chicken Fajita recipe.
To assign the Mexican style to this recipe, we simply assign myNewRecipe.RelatedStyleID to the ID of the just-inserted style. The RelatedStyleID property represents the link between this recipe and the associated style.
14.3.2 Assigning relationships for inserts
For inserts, you must assign the ID of the related entity rather than the related entity itself. In the previous code snippet, if we assigned myNewStyle to the RelatedStyle property, the insert will not link the recipe to the associated style in the database.
myNewRecipe.RelatedStyle = myNewStyle; // Will not insert the relationship
myNewRecipe.RelatedStyleID = myNewStyle.ID; // Will insert the relationship
Always set the values of the related IDs of the object you are inserting or else the relationships will not insert themselves.
Inserts only act upon the object you are inserting. In other words, if we wanted to add a new German recipe (e.g. Schpatzel), we must insert the German style first before we can insert the Schpatzel record. This is why the relationships of the object you are inserting must already have valid IDs before you can successfully insert the record.
This also means that you cannot explicitly insert related collections. In other words, while we can link a new recipe with a related style on insert (by assigning a valid RelatedStyleID), we can’t link a new style with a related collection of recipes (the reverse side of the many-to-one relationship) on an insert. Inserting relationships must always occur from the "many" side (e.g. Recipes) to the "one" side (e.g. Styles) of a relationship in all cases.
This means that in the case of many-to-many relationships, you must insert the associative entity record to tie the other entities together. To link the Fettucine Alfredo recipe to the Flour ingredient we again create the relationship on the "many" side (Recipe ingredients) to the "one" sides (recipe and ingredients) by assigning the appropriate related IDs as seen below:
var myRecipeIngredient:RecipeIngredientVO = new RecipeIngredientVO();
myRecipeIngredient.RelatedRecipeID = 3; //Fettucine Alfredo;
myRecipeIngredient.RelatedIngredientID = 1; //Flour;
myRecipeIngredient.Amount = "3 cups";
myRecipeIngredient.InsertVO();
14.3.3 Returning a deep load after insert
The insertVO() method has an optional parameter called _bReturnDeepLoad which defaults to false. This Boolean works much like the _bLoadDeep Boolean for load methods; by setting it to true, the item you’ve just inserted will re-load itself deeply (i.e. with all first-level relationships brought back from the database).
When we inserted the Chicken Fajita recipe into the database, because we did not set _bReturnDeepLoad to true in the myRecipe.InsertVO() command, the related style item’s data would not be available once onRecipeInsertCompleted() is run. If we explicitly passed in true, you could then access the newly inserted recipe’s related style data.
14.4 Updates
Method signature: updateVO(_bReturnDeepLoad:Boolean):void
Updating an item VO in X2O works almost identically to inserting an item VO. Like inserts, you can optionally set the _bReturnDeepLoad parameter to true. This will return the just updated item VO with first-level relationships included.
Compared to inserts, there are only a few small differences:
- An item VO must have a valid ID set in order to be updated. Remember that for an insert, ID is automatically returned to the item VO once the item has successfully inserted itself into the database.
- The updateVO()method will dispatch the X2OEvent.UPDATECOMPLETE event when it has successfully updated the record in the database.
- Let’s see how this works. Assume, in the code below, that the chickenFajitaRecipe references the Chicken Fajita recipe we just inserted in the previous section and that the italianStyle references the Italian style record.
We’ll update the chickenFajitaRecipe’s corresponding database record in the code that follows.
private function updateRecipe():void
{
trace("Before updating chicken fajita record:");
trace("Recipe Name: " + chickenFajitaRecipe.Name);
trace("Style Name: " + chickenFajitaRecipe.RelatedStyleItem.Name);
trace("-----");
chickenFajitaRecipe.addEventListener(X2OEvent.UPDATECOMPLETE, onUpdateCompleted);
chickenFajitaRecipe.Name = "Meat Lasagna";
chickenFajitaRecipe.Instructions = "This is how to make tasty lasagna.";
chickenFajitaRecipe.ServingSize = 4;
chickenFajitaRecipe.RelatedStyleID = italianStyle.ID;
chickenFajitaRecipe.UpdateVO();
}
private function onUpdateCompleted(e:X2OEvent):void
{
trace("After updating chicken fajita record:");
trace("Recipe Name: " + chickenFajitaRecipe.Name);
trace("Style Name: " + chickenFajitaRecipe.RelatedStyleItem.Name);
}
Here’s the resulting output:
Before updating chicken fajita record:
Recipe Name: Chicken Fajitas
Style Name: Mexican
-----
After updating chicken fajita record:
Recipe Name: Meat Lasagna
Style Name: Italian
14.5 Insert/Update Validation
Item VOs handle their own validation before it attempts to insert or update themselves into the database. If there are any validation issues upon submission, the VOs will throw an error. We recommend that you handle all potential thrown errors by wrapping your insert and update methods in a try/catch block.
- Any required properties (as specified from the X2O client) that have not been set will throw an error.
- Any required related items whose related ID property has not been set will throw an error.
14.6 Deletes
Method signature: deleteVO():void
The deleteVO() method will delete the current item VO from the database. Let’s again use the chickenFajitaRecipe RecipeVO as a guinea pig and delete it from the database.
private function deleteRecipe():void
{
chickenFajitaRecipe.addEventListener(X2OEvent.DELETECOMPLETE, onDeleteCompleted);
chickenFajitaRecipe.deleteVO();
}
private function onDeleteCompleted(e:X2OEvent):void
{
trace("Chicken fajita deleted from database.");
}
Like the other actions, deletes will dispatch their own event (X2OEvent.DELETECOMPLETE) when the record has been successfully deleted from the database. Just like updates, the item VO to be deleted must have a valid ID set, or else a delete will not occur.
When an item is deleted, it still lives in memory on the ActionScript side. In the onDeleteCompleted() method, even though the chicken fajita record no longer exists in the database, the chickenFajitaRecipe object still exists. X2O does not explicitly remove the object from the client side. We leave it to the developer to ensure that objects deleted from the database are properly handled in code.
14.6.1 Cascading Deletes
X2O cascades deleted records from the database. This means that, if a record is deleted and that record is a required related item of another record, the other record will be deleted as well. This cascading delete only occurs with required relationships.
Here’s a quick example. Suppose we delete the Italian style. Because recipes require an associated style, any recipes related to the Italian style will also be deleted. In turn, because a recipe ingredient record requires one recipe, any recipe ingredients linking to a recipe will also be deleted. This way, deleting a record will not cause any "orphaned" records – records whom do not have a related item where one is required.
14.7 Update from VO
Method signature: updateFromVO(itemToCopy:ItemVO*, updateID:Boolean = true)
The updateFromVO() method allows you to copy the contents of one item VO instance into another item VO instance. This is different from simply assigning one item VO instance to another item VO instance. When you assign two items, they reference each other – a change to one item VO instance changes the other one too. By using updateFromVO(), you are copying the contents but not copying references. A change to one item VO instance will not change the other item VO instance.
The updateFromVO() method will also copy the contents of any related entities as well. Again these will not assign the references of the related objects.
Optionally, you can decide to not copy over the IDs of the item (and the IDs of the related objects) by passing false to the updateID parameter.
*- ItemVO is not the actual type, but refers to a specific entity’s item VO (e.g. RecipeVO, IngredientVO)
15. Collection VOs
Every entity created in X2O will generate a corresponding collection VO. Collection VOs extend the mx.collections.ArrayCollection class for Flex, and fl.data.DataProvider for Flash. If you’re familiar with using array collections with Flex, collection VOs will offer a similar level of flexibility.
15.1 Accessors
Collection VOs all have the following methods for manipulating and accessing its items. Most of these methods are similar to the methods found in the base ArrayCollection class.
- x2o_addItem(item:ItemVO):void
- x2o_addItemAt(item:ItemVO, index:int):void
- x2o_contains(item:ItemVO):Boolean
- x2o_getByID(ID:int):ItemVO
- x2o_getItemAt(index:int):ItemVO
- x2o_getItemIndex(item:ItemVO):int
- x2o_removeItemAt(index:int):ItemVO
- x2o_removeVO(item:ItemVO):void
- x2o_removeVOByID(ID:int):void
- x2o_setItemAt(item:ItemVO, index:int):ItemVO
*- ItemVO is not the actual type, but refers to a specific entity’s item VO (e.g. RecipeVO, IngredientVO)
Collection VOs utilize the length property of ArrayCollection. In a typical usage scenario, you will write something like the following to access each item VO within a collection VO.
// Loop through all RecipeVO instances for myRecipeCollectionVO...
for (var i:int=0; i < myRecipeCollectionVO.length; i++)
{
// Trace out name of recipe...
trace(myRecipeCollectionVO.x2o_getItemAt(i).Name);
}
Alternatively, you can access item VOs via the for each declarative. This is the method we suggest for its more readable notation, and we’ll use this technique throughout the code samples.
// Loop through all RecipeVO instances for myRecipeCollectionVO...
for each (var recipe:RecipeVO in myRecipeCollectionVO)
{
// Trace out name of recipe...
trace(recipe.Name);
}
Now, let’s move onto loading items into your collection VOs.
15.2 Load Methods
Every collection VO comes with a standard set of load methods to extract a filtered collection of item VOs from the database. When you generate your project, you can reference the full X2O API via the generated ActionScript Documentation. As stated earlier, when calling load methods, a collection VO instance will load items from that method into itself.
Let’s take a look at the standard sets of methods available for collection VOs.
15.2.1 Three standard parameters
One thing you’ll notice is that every load method allows you to pass in three optional parameters.
- _bLoadDeep – Boolean
- _x2o_startIndex – int
- _x2o_endIndex – int
The _bLoadDeep parameter behaves similarly to the _bLoadDeep parameter for an item VO’s loadByID() method. It lets you decide if you want to just grab a collection of base item VOs or if you’d like to grab the collection of base item VOs plus each of their first-level related items and collections loaded as well(by setting the parameter to true). We’ll show you an example implementation of both later.
The _x2o_startIndex and _x2o_endIndex parameters let you optionally specify which record indexes to start and end at respectively. The indexes are zero-based. For instance, if you pass in 0 for _x2o_startIndex and 2 for _x2o_endIndex, the load method will return a collection of the first three item VOs in the collection. If the entire collection has less than three records, it will only return those records.
Also, suppose you pass in 3 and 5 respectively. If the collection has 4 records, it will only return the record at index 3 (which is the 4th item in the collection). Since that is the last record, the resulting collection will have only one item VO in it.
If you do not specify any indexes, X2O will just return the entire filtered collection from the database.
15.3 Dispatched Events
All load methods dispatch the following events:
- X2OEvent.LOADCOMPLETE – This event is dispatched when a load method has been executed and data has successfully returned from the database. The X2OEvent class contains a property called VO which is a generic object that contains a reference to the VO that called the load method. You can either use the VO returned from the X2OEvent instance, or just use the collection VO that you are calling the load method on.
- mx.rpc.events.FaultEvent.FAULT (com.x2o.core.base.X2OFaultEvent.FAULT for Flash CS4) – This event is dispatched if any errors have occurred while attempting to load data from the database.
15.4 The "Load All" method
The most basic load method is the loadAll() method. As its name suggests, it will simply load all items from the database into the collection without doing any specific filtering of the returned items. Here’s a sample implementation:
1 private var myRecipes:RecipeCollectionVO;
2
3 private function loadRecipes():void
4 {
5 myRecipes = new RecipeCollectionVO();
6 myRecipes.addEventListener(X2OEvent.LOADCOMPLETE, onRecipesLoaded);
7 myRecipes.loadAll();
8 }
9
10 private function onRecipesLoaded(e:X2OEvent):void
11 {
12 for each (var recipe:RecipeVO in myRecipes)
13 {
14 // Trace out the name of each recipe...
15 trace("ID:" + recipe.ID + ", Recipe Name: " + recipe.Name);
16 }
17 trace("-----");
18 for each (var recipe:RecipeVO in e.VO)
19 {
20 // Trace out the name of each recipe...
21 trace("ID:" + recipe.ID + ", Recipe Name: " + recipe.Name);
22 }
23 }
On line 1, we create a new instance of a RecipeCollectionVO called myRecipes. On line 6, within the loadRecipes() method, we add an event handler method called onRecipesLoaded which will be executed when the LOADCOMPLETE event is dispatched. On line 7, we call the loadAll() method on myRecipes. Because we are not passing in any of the standard parameters, the collection will use the default values.
In the onRecipesLoaded method, we pass an X2OEvent instance as a parameter. As mentioned before, the event itself stores a reference to the myRecipes object in its VO property. Within the method, e.VO is ostensibly the same object as myRecipes. The only difference is that e.VO is typed as a generic Object. You may want to cast this object as a RecipeCollectionVO to take advantage of code-hinting.
(e.VO as RecipeCollectionVO)
On line 12, we write a for each loop to extract each RecipeVO instance from the just-loaded collection into a local variable recipe. From there, we trace the Name property of recipe. This shows the self-actionable qualities of X2O value objects. By caling loadAll(), myRecipes loads the resulting collection of RecipeVOs into itself.
Beginning on line 18, we’ve written the exact same for each loop, except that we are looping through the X2OEvent instances’ VO object as opposed to myRecipes, with the exact same result. You can always use the X2OEvent instances’ VO object instead of the collection VO itself. This can be useful if you have not scoped the collection VO (in this case, myRecipes) to the class and therefore do not have it available in the event handler method (however, in this case, myRecipes has been scoped to the class.
If we ran this code, we should get the sample output below (the data is taken from the CMS tool section). Notice that, because we placed the primary sort order on the "Name" attribute for the Recipes entity in X2O, the resulting records come back ordered by Name.
ID: 3, Recipe Name: Chicken Cordon Bleu
ID: 2, Recipe Name: Chicken Parmagiana
ID: 1, Recipe Name: Fettuccine Alfredo
ID: 5, Recipe Name: Spaghetti and Meatballs
ID: 4, Recipe Name: Tofu Pad See Eiw
-----
ID: 3, Recipe Name: Chicken Cordon Bleu
ID: 2, Recipe Name: Chicken Parmagiana
ID: 1, Recipe Name: Fettuccine Alfredo
ID: 5, Recipe Name: Spaghetti and Meatballs
ID: 4, Recipe Name: Tofu Pad See Eiw
15.5 The "Load All" method – Take 2
Let’s modify the code to load recipes deeply. Once again, by loading a recipe deeply, we will get not just each recipe’s base properties, but also the first-level relationship objects as well. For good measure, let’s only load the first three recipes instead of all of them.
private var myRecipes:RecipeCollectionVO;
private function loadRecipes():void
{
myRecipes = new RecipeCollectionVO();
myRecipes.addEventListener(X2OEvent.LOADCOMPLETE, onRecipesLoaded);
myRecipes.loadAll(true, 0, 2);
}
private function onRecipesLoaded(e:X2OEvent):void
{
for each (var recipe:RecipeVO in myRecipes)
{
// Trace out the name of each recipe...
trace("ID:" + recipe.ID + ", Recipe Name and Style: " + recipe.Name + "," + recipe.RelatedStyleItem.Name);
}
trace("-----");
for each (var recipe:RecipeVO in e.VO)
{
// Trace out the name of each recipe...
trace("ID:" + recipe.ID + ", Recipe Name and Style: " + recipe.Name + "," + recipe.RelatedStyleItem.Name);
}
}
If we ran this code, we might get the sample output below.
ID: 3, Recipe Name and Style: Chicken Cordon Bleu, French
ID: 2, Recipe Name and Style: Chicken Parmagiana, Italian
ID: 1, Recipe Name and Style: Fettuccine Alfredo, Italian
-----
ID: 3, Recipe Name and Style: Chicken Cordon Bleu, French
ID: 2, Recipe Name and Style: Chicken Parmagiana, Italian
ID: 1, Recipe Name and Style: Fettuccine Alfredo, Italian
Note that, because we passed-in the optional start and end index parameters of 0 and 2, only the first three records are loaded into the collection.
15.6 Loading by a related entity
X2O collection VOs will also have methods to load a collection of item VOs by a related item. For instance, in our recipe application, we may want to display a list of just Italian or French recipes. One way we could do this is to load the entire collection of recipes using loadAll(), and then subsequently loop through each RecipeVO instance to find Italian recipes. Let’s suppose we’ve loaded all recipes into a RecipeCollectionVO instance called allRecipes, and we want to extract only the Italian recipes into a RecipeCollectionVO instance called myItalianRecipes.
var myItalianRecipes:RecipeCollectionVO = new RecipeCollectionVO();
for each (var recipe:RecipeVO in myRecipes)
{
if (recipe.RelatedStyleItem.Name == "Italian")
{
myItalianRecipes.x2o_addItem(recipe);
}
}
While this is a feasible way of accomplishing this task, it can prove somewhat inefficient – especially if you are loading thousands of records from the database at once.
Because a RecipeVO instance has a RelatedStyleItem property (a recipe has one related style), X2O will generate a method called LoadAllByRelatedStyleID() which will accept an ID (int) of the related style item to filter by, along with the standard optional parameters. Here’s how we would do it using the LoadAllByRelatedStyleID method. Let’s assume that the ID of the Italian record is 1:
var myItalianRecipes:RecipeCollectionVO = new RecipeCollectionVO();
myItalianRecipes.addEventListener(X2OEvent.LOADCOMPLETE, onCollectionLoaded);
myItalianRecipes.loadAllByRelatedStyleID(1); // 1 = Italian style ID
In summary, all related items created off of an entity will have a corresponding load method to filter by that item VO’s unique ID value.
15.7 Filtered loading
Based on which attributes of an entity you marked as a filtered attribute, collection VOs will generate methods to filter against an attribute. The following shows the methods created for a filtered attribute named "My Attribute" in an entity named "My Entity" in X2O. The type of methods generated is based upon the type of attribute.
15.7.1 Boolean
Method:
- LoadAllWhereMyAttributeEqualTo – Fills MyEntityCollectionVO with records where MyAttribute is equal to passed-in value.
15.7.2 Date, Decimal, Integer, Money
Methods:
- LoadAllWhereMyAttributeEqualTo – Fills MyEntityCollectionVO with records where MyAttribute is equal to passed-in value.
- LoadAllWhereMyAttributeLessThanEqualTo – Fills MyEntityCollectionVO with records where MyAttribute is less than or equal to passed-in value.
- LoadAllWhereMyAttributeGreaterThanEqualTo – Fills MyEntityCollectionVO with records where MyAttribute is greater than or equal to passed-in value.
- LoadAllWhereMyAttributeInRange – Fills MyEntityCollectionVO with records where MyAttribute is inclusively in the passed-in low and high values.
15.7.3 Email, File Upload, Phone Number, Zip Code, String
Methods:
- LoadAllWhereMyAttributeEqualTo – Fills MyEntityCollectionVO with records where MyAttribute is equal to passed-in value.
- LoadAllWhereMyAttributeLike – Fills MyEntityCollectionVO with records where MyAttribute has a match with passed-in value (using an asterisk as a pre-or-post wildcard filter).
Note: Rich Text and Rich Text Foreign fields are not filterable.
Just like the loadAll() method, you can also pass in the three standard parameters, _bLoadDeep, _x2o_startIndex, and _x2o_endIndex to customize the level of returned data and a range of records.
Let’s do a quick example. In our sample application, we’ve placed a filter on ServingSize. Because ServingSize is an integer, we can load all recipes >=, <=, =, or in a specified range of the ServingSize. Here’s how we can use the filtered methods on ServingSize to just get back recipes for two, and recipes for four or more respectively. For brevity, we won’t include the event listener methods in this code snippet.
private var recipesForTwo:RecipeCollectionVO;
private var recipesForFourOrMore:RecipeCollectionVO;
private function loadRecipesForTwo():void
{
recipesForTwo = new RecipeCollectionVO();
recipesForTwo.loadAllWhereServingSizeEqualTo(2);
}
private function loadRecipesForFourOrMore():void
{
recipesForFourOrMore = new RecipeCollectionVO();
recipesForFourOrMore.loadAllWhereServingSizeGreaterThanEqualTo(4);
}
15.8 Update from VO
Similar to the item VO’s updateFromVO() method, the updateFromVO() method allows you to overwrite the items within one collection VO instance into another collection VO instance. This is different from simply assigning one collection VO instance to another collection VO instance. When you assign two collections, they reference each other – a change to one collection VO instance changes the other one too. By using UpdateFromVO(), you are replacing the contents but not copying references. A change to one collection VO instance will not change the other collection VO instance.
The updateFromVO() method will also overwrite the contents of any related entities as well. Again these will not assign the references of the related objects.
Optionally, you can decide to not copy over the IDs of the items in the collection (and the IDs of the related objects) by passing false to the updateID parameter.
16. Defined Type Classes
In Section 7.0, we explain defined type entities. Here, we’ll show you its real value here.
In the explanation below, we assume that we’ve regenerated the project after already adding the Style records. In addition, we’ve related the appropriate recipes to these styles (either in the CMS or when adding/updating the recipes in ActionScript). Regenerating will update the static defined type classes with the latest records in the defined type.
Earlier, you saw how X2O generates load method by related items. Because a recipe has a related style, the RecipeCollectionVO had a load method called loadAllRecipesByRelatedStyleID. Here was our snippet from earlier, when we assumed that the Italian style had an ID of 1.
var myItalianRecipes:RecipeCollectionVO = new RecipeCollectionVO();
myItalianRecipes.addEventListener(X2OEvent.LOADCOMPLETE, onCollectionLoaded);
myItalianRecipes.loadAllByRelatedStyleID(1);
By marking an entity as a defined type, when you generate your framework, your AS3 code will contain a static class of public const int variables with friendlier names so you don’t have to hardcode the value of 1 in your code. Instead, you can write:
var myItalianRecipes:RecipeCollectionVO = new RecipeCollectionVO();
myItalianRecipes.addEventListener(X2OEvent.LOADCOMPLETE, onCollectionLoaded);
myItalianRecipes.loadAllByRelatedStyleID(Styles.ITALIAN);
The static class will be named the plural name of the entity in question. For Styles, we will have a Styles class to go along with the StyleCollectionVO and StyleVO classes in the com.x2o.gen.vo package.
The const variable Styles.ITALIAN is created by having entered in the word "Italian" in the "Key Name" field for that record when inputting values in the CMS. You will also have available all the other defined type records for styles:
- Styles.ITALIAN
- Styles.FRENCH
- Styles.THAI
- Styles.MEXICAN
…and so forth.
Hopefully, you’re now seeing the real value of defined types. Without it, we would need to either hardcode a value of 1 to get the Italian recipes or load the entire StyleCollectionVO and sift through each record to find the Italian style record. Then, we’d store the Italian style’s ID in a variable for later use.
Defined types let us take a shortcut when we just need to reference the IDs of an entity –instead of having to make a call to the database to get these IDs, we can just grab them from the static defined type class.
17. Custom Method VOs
You saw earlier how to create custom methods for your project. Custom methods let you freely create SQL statements against your generated database for more customized querying. While the standard load methods of a collection VO can get you pretty far in your development, sometimes you’ll find it better to write more customized queries.
Recall the Custom Method example we created earlier.
GetRecipeInfoByNameAndStyle
SELECT [Recipes].*, [Styles].[KeyName] AS [StyleName] FROM [Recipes] INNER JOIN [Styles] ON [Recipes].RelatedStyleID = [Styles].[ID] WHERE [Recipes].[Name] LIKE @Name AND [Styles].[ID] = @StyleID
This query will pull all of the recipe’s properties, plus the name of the associated style where the name has a match against the passed-in name and the style equals the passed-in style ID. You wouldn’t be able to do this customize load simply with just the standard VOs.
For every custom method you create, X2O will generate another collection VO and item VO pair. In this example, two classes get created in the com.x2o.gen.customservices.vo package:
- GetRecipeInfoByNameAndStyleCollectionVO
- GetRecipeInfoByNameAndStyleVO
Since you can only write SELECT statements for custom methods, the generated custom method collection VO contains only one public method called load(). The parameters of the load() method are based off the parameters you provided in the SQL statement. In this case, GetRecipeInfoByNameAndStyleCollectionVO.load() accepts a Name string and ID integer parameter.
Just like regular collection VOs, custom method collections dispatch the X2OEvent.LOADCOMPLETE when it has been successfully loaded. In this case, GetRecipeInfoByNameAndStyleCollectionVO instance will have a collection of GetRecipeInfoByNameAndStyleVO items once loaded.
Unlike regular item VOs, custom method item VOs don’t have any self-actionable methods. You cannot load, update, insert, or delete item VOs. In this sense, they are more like true ActionScript value objects – objects that store data but do not perform any actions.
The properties of item VOs exactly mirror the returned fields in the SELECT statement. Because we are returning all primitive Recipe values ([Recipes].*) and the style name ([Styles].[Name] as [StyleName]), the GetRecipeInfoByNameAndStyleVO object will have the following attributes:
- Name
- Instructions
- ServingSize
- Picture
- RelatedStyleID
- StyleName (Style.KeyName)
Let’s take a look at how to work with custom methods in code. Here, we’ll load all French recipes that contain the word "Chicken". Notice that we’re also employing defined types to get the ID for the French style here.
private var myCustomRecipes:GetRecipeInfoByNameAndStyleCollectionVO;
private function loadCustomRecipes():void
{
myCustomRecipes = new GetRecipeInfoByNameAndStyleCollectionVO();
myCustomRecipes.addEventListener(X2OEvent.LOADCOMPLETE, onCustomRecipesLoaded);
// Passing in the values for Name and Style ID. Use * as a wildcard for name
myCustomRecipes.load("Chicken*", Styles.FRENCH);
}
private function onCustomRecipesLoaded(e:X2OEvent):void
{
for each (var customRecipe:GetRecipeInfoByNameAndStyleVO in myCustomRecipes)
{
// Trace out the name of each recipe...
trace("Recipe: " + customRecipe.Name + ", Style: " + customRecipe.StyleName);
}
}
This code should look familiar to you if you saw how we handle loads for regular collection VOs. Notice the name of the style attribute is found by simply the attribute "StyleName" since this is what we aliased the Style.Name attribute as in the SQL statement.
In summary, custom methods turn into strongly-typed collection VO and item VO pairs. Collection VOs will provide a load method with parameters that match the parameters you give in your SQL statement. The properties of your item VOs will mirror the properties you create in the SELECT portion of your SQL statement.
18. Flex Binding
The combination of X2O and Flex data binding means you can literally develop a fully-functional database-driven Flex app in minutes.
All X2O VOs can be bound to Flex components. Because collection VOs extend mx.collections.ArrayCollection, they are automatically bindable by using the [Bindable] metadata tag above the Flex component instance. Item VOs are also bindable.
Let’s take a look at a simple MXML script that showcases this binding capability.
1 <mx:Application creationComplete="init();">
2
3 <mx:Script>
4 <![CDATA[
5
6 import com.x2o.gen.vo.RecipeCollectionVO;
7 import com.x2o.gen.vo.StyleVO;
8
9 [Bindable]
10 private var myRecipes:RecipeCollectionVO;
11
12 [Bindable]
13 private var myItalianStyle:StyleVO;
14
15 private function init():void
16 {
17 loadItalianStyle();
18 loadRecipesDeep();
19 }
20
21 private function loadItalianStyle():void
22 {
23 myItalianStyle = new StyleVO();
24 myItalianStyle.loadByID(Styles.ITALIAN);
25 }
26
27 private function loadRecipesDeep():void
28 {
29 myRecipes = new RecipeCollectionVO();
30 myRecipes.loadAll(true);
31 }
32 ]]>
33 </mx:Script>
34
35 <mx:Canvas>
36 <mx:VBox>
37 <mx:Text id="txtItalianRecipes" text="{myItalianStyle.Name}" />
38 <mx:Repeater id="repRecipes" dataProvider="{myRecipes}">
39 <mx:Text text="{(repRecipes.currentItem as RecipeVO).Name + " " + repRecipes.currentItem as RecipeVO).RelatedStyleItem.Name}" />
40 </mx:Repeater>
41 </mx:VBox>
42 </mx:Canvas>
43 </mx:Application>
In this MXML code snippet, we demonstrate how to bind both a collection VO and item VO to Flex components. On line 1, we set the application’s creationComplete property to init(), so init() will fire when the page has rendered. On lines 9 and 12, we append the [Bindable] tag on top of the private variables myRecipes and myItalianStyle. The init() method, calls both loadItalianStyle() and loadRecipesDeep(). These two methods then load the Italian style and all recipes deep respectively.
Starting on line 35, we write out some basic MXML scripts to display the information found in myRecipes and myItalianStyle. Line 37 will display the Italian style’s Name property, and line 38 begins a repeater that will display each recipe’s Name and associated style Name properties. The style’s Name property will display because we set _bLoadDeep to true in the myRecipes.loadAll method in line 30.
Notice how we do not add event listeners because the bindablity of the VOs takes care of having to instruct the code what to do after the data has been loaded from the database.
Using VOs in MXML goes well beyond the sample code above. You can, just as easily, build forms to insert and update new records. You can also build more visually stunning UIs combining all that Flex has to offer with the ease of incorporating X2O value objects with it. The options are limitless.
19. Special Features
Every generated X2O project will come with a few convenient classes. The generated AS3 documentation will detail the technical specifics of these classes including error handling and events. In this overview, we’ll show you a quick sample implementation.
19.1 Email
The generated SWC will contain a class to handle sending emails. There is no configuration on your part because emails are sent through the default X2O SMTP server. The Email class lives in the com.x2o.core.util package.
Here’s a sample implementation:
var myEmail:Email = new Email();
myEmail.ReplyToAddress = "info@mysite.com"; //optional
myEmail.ReplyToName = "Info @ My Site"; //optional
myEmail.SendToAddress = "billg@microsoft.com";
myEmail.SendToName = "Bill Gates"; //optional
myEmail.FromAddress = "info@mysite.com";
myEmail.FromName = "Info @ My Site"; //optional
myEmail.Subject = "This is my subject";
myEmail.Body = "This is my body text.";
myEmail.IsHtmlMessage = true; //optional
myEmail.sendEmail();
19.2 File Upload
The generated SWC will also contain a class that will allow you to upload files to the server. The FileUpload class lives in the com.x2o.core.util package as well. The generated AS3 documentation will detail the technical specifics of these classes including error handling and events.
In this overview, we’ll show you a quick sample implementation. The X2O file uploading wraps the AS3 flash.net.FileReference object.
X2O does not allow you to upload potentially harmful files (e.g. EXEs, BATs). There is also a maximum of 5 MB per uploaded file.
19.2.1 Dispatched Events
The FileUpload class dispatches two events.
- FileUploadEvent.UPLOADCOMPLETE – This event is dispatched when a file has successfully uploaded via the FileUpload.upload() method.
- mx.rpc.events.FaultEvent.FAULT (com.x2o.core.base.X2OFaultEvent.FAULT for Flash CS4) – This event is dispatched if any errors have occurred while attempting to upload the file.
19.2.2 Uploading the file
Next, let’s look at how you upload a file.
1 <mx:Script>
2 <![CDATA[
3
4 import com.x2o.core.util.FileUpload;
5 import com.x2o.core.base.FileUploadEvent;
6
7 [Bindable]
8 public var fileReference:FileReference;
9
10 private var fileUpload:FileUpload;
11
12 private function onBrowseClick():void
13 {
14 fileReference = new FileReference();
15 fileReference.addEventListener(Event.SELECT, onSelectFile);
16 fileReference.browse();
17 }
18
19 private function onSelectFile(event:Event):void
20 {
21 fileUpload = new FileUpload(fileReference);
22 fileUpload.addEventListener(FileUploadEvent.UPLOADCOMPLETE, onUpload);
23 fileUpload.upload();
24 }
25
26 private function onUpload(e:FileUploadEvent):void
27 {
28 trace(e.Filename);
29 }
30
31 ]]>
32 </mx:Script>
33
34 <mx:Canvas>
35 <mx:Button label="Browse" click="onBrowseClick()"/>
36 </mx:Canvas>
In the code above, we first create two private properties, a FileReference object (line 8) and an X2O FileUpload object (line 10). On line 35, we display a button that will fire the onBrowseClick() method when clicked. In the onBrowseClick() method, we instantiate a fileReference object and call the browse() method. This will pop open a dialog box to select a file on the user’s machine.
When a file has been selected, the onSelectFile event handler (line 19) will be initiated. Here’s where we tie into the X2O FileUpload component. We simply pass the fileReference object into the construction of fileUpload. We then set an event handler to listen to the UPLOADCOMPLETE event (line 22), and then upload the file (line 23).
When the file has successfully uploaded, the onUpload() event handler is called. Here, we pass in the FileUploadEvent as a parameter. The relative path to the uploaded file is available via the event’s Filename property.
19.2.3 Assigning a file upload attribute to an uploaded file
As we mentioned in Section 13.4, an entity’s attribute of type "File Upload" stores the relative path to the file rather than the file itself. When you upload a file via the CMS, this is all handled internally. For example, if we upload an image for a particular recipe in the CMS, the resulting RecipeVO record’s Picture property would be populated with the relative path of the file on the server.
If we insert a RecipeVO record in Flex, we have to manually assign the resulting relative path. Extending upon the previous example, suppose we want to insert a new recipe after uploading a picture file. We could create a RecipeVO object in the onUpload method and then assign the Picture property to e.Filename.
private function onUpload(e:FileUploadEvent):void
{
var myRecipe:RecipeVO = new RecipeVO();
myRecipe.Name = "Cannolis";
myRecipe.Instructions = "Some instructional text.";
myRecipe.Picture = e.Filename;
myRecipe.ServingSize = 2;
myRecipe.RelatedStyleID = Styles.ITALIAN;
myRecipe.insertVO();
}
As mentioned in Section 13.4, if we want to display the picture, we’d use the full file path: X2OManager.assetsURL + myRecipe.Picture.
20. Conclusion
Hopefully, you’re now seeing the true power of X2O. We’re hoping you’ve gained a new perspective on developing database-driven Flex and Flash applications. If you’ve read through this entire document, watched our videos on our website, and viewed some of our case studies online, you should be ready to start your own X2O project!
Enjoy and welcome to X2O.