This document is meant to address the needs of developers that want to know how to quickly start using Metastorage without going through many theoretical explanations.
- Designing a persistent component
This section is meant to teach you how to create a XML document in the CPML format that expresses the design of a persistent component to addresses the needs of your applications. It provides a detailed explanation about what these concepts and formats mean and what you should do to design your application components data model.
If you are are already familiar with these concepts but you are looking for a quick reference to something that is explained in this section, take a look at the following steps to get you started quickly with designing components with Metastorage.
- How do I create the skeleton of a component definition file?
You need to create a XML file with that starts with the tag component. Inside that tag it needs to define the name and the description of the component using tags respectively of the same name.
You also need to define each of the component data classes that your application needs, as well a factory class to create your data class objects, and optionally, a schema class to handle the creation of the schema of the database with the tables that will hold the information of the data objects.
If you would like to generate forms to handle common types of user interface interactions, you may also define the forms in the component definition file.
Here is the skeleton of a component definition:
<?xml version="1.0"?>
<component>
<name>cms</name>
<description>Content management system component</description>
<!-- classes definition go here -->
<!-- factory class definition goes here -->
<!-- schema class definition goes here -->
<!-- form handling classes definition go here -->
</component>
In the tutorial document you may learn more about the creating new component definitions.
How do I define an entity class to store my data objects?
You need to define a section inside your component definition file that starts with the tag class. It must include the definition of your class name, the class variables, validation rules if you need any, and functions that you need your class to have in order to manipulate the entity class objects from your applications.
<class>
<name>article</name>
<!-- variables definition go here -->
<!-- validation rules definition go here -->
<!-- functions definition go here -->
</class>
In the tutorial document you may learn more about the defining entity data classes.
How do I define the variables of an entity data class?
You need to define a section inside your entity class definition that starts with the tag variable. It must include the definition of your variable name and type. The type can be text, integer, boolean, float, decimal, date, time and timestamp.
<variable>
<name>title</name>
<type>text</type>
</variable>
In the tutorial document you may learn more about the defining entity class variables.
How do I specify an initial value of a class variable?
You need to use the tag initialvalue within the variable definition section to specify the initial value that the variable will have when the object of a class is created.
<variable>
<name>title</name>
<type>text</type>
<initialvalue>some initial title text</initialvalue>
</variable>
How do I specify the limit length of a text class variable?
You need to use the tag length within the variable definition section to specify the maximum number of characters that a text variable may have.
<variable>
<name>title</name>
<type>text</type>
<length>64</length>
</variable>
How do I do I define a date or time variable that is updated automatically?
You can use either the tags autocreate or autoupdate within the variable definition section to specify that the variable will be assigned automatically by its data object class either only when it is created or every time the object is updated.
<variable>
<name>created</name>
<type>timestamp</type>
<autocreate>localtime</autocreate>
</variable>
<variable>
<name>updated</name>
<type>timestamp</type>
<autoupdate>localtime</autoupdate>
</variable>
How do I establish a 1-to-1 relationship between two entity classes?
A relationship between two objects of two entity classes may be established using reference variables. Reference variables are like normal variables, except that they contain a reference to an object of a specified class.
To establish a 1-to-1 relationship, in each of the related classes define a reference variable with its name and the name of the other class.
Here follows the example of a 1-to-1 relationship established between the classes man and woman. The class man has a reference variable of the class woman that represents his wife. The class woman class has a reference variable of the class man that represents her husband.
<class>
<name>man</name>
<variable>
<name>wife</name>
<class>woman</class>
</variable>
</class>
<class>
<name>woman</name>
<variable>
<name>husband</name>
<class>man</class>
</variable>
</class>
The reference variables that you define will contain a special value that acts as unique identifier of the object of the referenced class. That Object unique IDentifier is usually known as OID. In practice, OID are usually integer unique numbers that are assigned to each object when they are created, and so they are guaranteed to be unique.
In the tutorial document you may learn more about the defining reference variables and also Object IDentifiers.
What about 1-to-0-or-1 relationships?
Actually, 1-to-0-or-1 relationships are just special cases of the 1-to-1 relationships. The difference is just in assuming that the reference variable is allowed to contain a null reference value, i.e. may not point to any object and so the reference object OID may be undefined.
The definition of a 1-to-0-or-1 relationships is not different from the definition of a 1-to-1 relationship.
How do I establish a 1-to-1 relationship between entity classes of different components?
Relationships between classes of different components are like other types of relationships. The only difference is that it must also be specified the name of the other component on which the referenced class is defined.
<class>
<name>article</name>
<variable>
<name>author</name>
<class>user</class>
<component>accounts</component>
</variable>
</class>
Additionally, each external component must be declared in a special section of the current component definition that specifies the external component file name.
<component>
<name>accounts</name>
<file>accounts.component</file>
</component>
How do I store or retrieve an object pointed by a reference variable of an external component class?
Objects pointed by reference variables that are of external component classes may not be stored or retrieved directly by common setreference or getreference functions.
However you can store or retrieve the OID of the referenced object directly. To achieve that the setreference or getreference functions must be declared using the oid parameter.
<class>
<name>article</name>
<variable>
<name>author</name>
<class>user</class>
<component>accounts</component>
</variable>
<function>
<name>getauthoroid</name>
<type>getreference</type>
<parameters>
<variable>author</variable>
<oid />
</parameters>
</function>
<function>
<name>setauthoroid</name>
<type>setreference</type>
<parameters>
<variable>author</variable>
<oid />
<reference>
<argument>user_oid</argument>
</reference>
</parameters>
</function>
</class>
With the OID of an external class object, you can use a getobject type function of a factory class of the external component to retrieve an instance of that object.
<component>
<name>accounts</name>
...
<factory>
<function>
<name>getuserbyoid</name>
<type>getobject</type>
<parameters>
<class>user</class>
<id>
<argument>user_oid</argument>
</id>
</parameters>
</function>
</factory>
</component>
And what about 1-to-many relationships?
1-to-many relationships are established between an entity class that has one reference variable and another entity class that defines a collection of objects of the first class pointing to its reference variable.
Here follows an example of a 1-to-many relationship defined between the class writer that has a collection named articles made of objects of the class article. The article class has a reference variable named author that references an object of the class writer.
<class>
<name>writer</name>
<collection>
<name>articles</name>
<class>article</class>
<reference>author</reference>
</collection>
</class>
<class>
<name>article</name>
<variable>
<name>author</name>
<class>writer</class>
</variable>
</class>
In the tutorial document you may learn more about the defining collections to establish 1-to-many relationships.
And many-to-many relationships?
Many-to-many relationships are established between two classes that define two collections, one on each of them, making the collections reference each other.
Here follows an example of a many-to-many relationship defined between the classes category and articles. The class category that has a collection named articles made of objects of the class article. The class article has a collection named categories made of objects of the class category. Each of the collections reference each other class.
<class>
<name>category</name>
<collection>
<name>articles</name>
<class>article</class>
<reference>categories</reference>
</collection>
</class>
<class>
<name>article</name>
<collection>
<name>categories</name>
<class>category</class>
<reference>articles</reference>
</collection>
</class>
In the tutorial document you may learn more about the defining mutual object collection to establish many-to-many relationships.
How do you retrieve all objects of a class that satisfy a condition?
You need to define a function of the factory class of type getallobjects, setting the filter parameter to an Object Query Language (OQL) expression that declares required condition.
Here follows an example of a factory class function to retrieve all objects of the class article that match a search condition for article objects that belong to a given category.
<function>
<name>getcategoryarticles</name>
<type>getallobjects</type>
<argument>
<name>category</name>
<class>category</class>
<oid />
</argument>
<parameters>
<class>article</class>
<filter>
<object>
<name>category</name>
<class>category</class>
</object>
<equalto />
<argument>category</argument>
<and />
<object>
<name>article</name>
</object>
<in />
<collection>
<object>category</object>
<name>articles</name>
</collection>
</filter>
</parameters>
</function>
How do I express a search condition for objects involved in relationship?
You need to define a Object Query Language (OQL) expression that uses the in operator. On the left side of the operator you must specify an object expression that should be looked up in a collection expression defined in the right side of the in operator.
Here follows a search condition that looks up for an article class object in a collection named articles of a category class object.
<object>
<name>article</name>
</object>
<in />
<collection>
<object>category</object>
<name>articles</name>
</collection>
How do I specify the validation rules that the objects of each entity class must obey?
Entity class validation rules are defined in special sections inside each class definition. These sections start with the tag validation. Each class may have multiple validation sections that correspond to each validation rule that is meant to be performed.
Each validation rule must define the validation type, an error code to be be associated with the rule and validation specific parameters.
Currently, only the validations of type notempty and unique are supported.
Here follows an example of the definition of a validation rule to verify that the title variable of the class article is not empty:
<class>
<name>article</name>
<variable>
<name>title</name>
<type>text</type>
</variable>
<validation>
<type>notempty</type>
<errorcode>1</errorcode>
<parameters>
<variable>title</variable>
</parameters>
</validation>
</class>
In the tutorial document you may learn more about the defining validation rules.
How do I specify the functions that I need to manipulate my entity class objects?
Metastorage can generate entity classes with functions that implement several types of operations to manipulate your application data objects.
Despite Metastorage can generate many types of functions, it will only generate the functions that your application really needs in each of your entity classes. This is what is called the JE WIN code generation approach: generate Just Exactly What I Need.
Since the types of functions that you may need depends on the purpose of your application, you need to declare all the functions that will be necessary.
Each function that you need has to be declared in a section within the respective class definition. The declaration of a function starts with the tag function.
Function declarations need to include the function name, the function type and eventual function type specific parameters when necessary.
Here follows an example of the declaration of a function named save to store objects of the class article.
<class>
<name>article</name>
<function>
<name>save</name>
<type>persist</type>
</function>
</class>
In the tutorial document you may learn more about the defining entity class functions.
How do I specify the functions that I need to retrieve or create new entity class objects?
That is the role of the factory class. Data objects must not be created directly instantiating the respective entity classes. Instead, you should use functions of the factory class to create or retrieve the data objects that your application needs to access.
Like with entity class functions, Metastorage will only generate the factory functions that your application really needs. Therefore, you need to declare all the factory functions that will be necessary in a special component definition section for that purpose.
Each component must have one and only one factory class. Factory class definitions start with the tag factory.
Factory function declarations are similar to entity class function declarations. Only function type specific parameters may vary. Currently, Metastorage supports the generation factory function types createobject, getobject and getallobjects.
Here follows an example of the declaration of a factory class with several types of functions:
<factory>
<function>
<name>createarticle</name>
<type>createobject</type>
<parameters>
<class>article</class>
</parameters>
</function>
<function>
<name>getallarticles</name>
<type>getallobjects</type>
<parameters>
<class>article</class>
</parameters>
</function>
<function>
<name>getarticle</name>
<type>getobject</type>
<parameters>
<class>article</class>
<id>
<argument>article</argument>
</id>
</parameters>
</function>
</factory>
In the tutorial document you may learn more about the defining the factory class..
How do I declare a schema management class?
The schema management class is a special class that is meant to assist in the installation of the database schema that will store the objects of the entity classes generated by Metastorage.
The declaration of the schema class in the component definition is mandatory. This declaration is very simple. It consists of a section that starts with the tag schema. It must also have the declaration of a function of type installschema.
Here is how to declare the schema class with the installschema function:
<schema>
<function>
<name>installschema</name>
<type>installschema</type>
</function>
</schema>
In the tutorial document you may learn more about the defining the schema class..
How do customize a generated class with handwritten code that is not lost when Metastorage regenerates all the classes?
You should never need to change the code generated by Metastorage!
If you want to add functionality to the generated classes to implement a behavior different than what is provided by the types of functions that Metastorage generates, you can define custom functions in your classes that implement the functionality you want, using code written by yourself in PHP (or other supported target language).
This way, every time the code of the classes is regenerated, it always include your customization functions.
Currently, you can add custom functions to the data classes, the factory class and report classes. Custom function definitions look like this:
<function>
<name>validateandsave</name>
<type>custom</type>
<parameters>
<return>
<type>boolean</type>
</return>
<do><![CDATA[
if( !$this->validatearticle($errorcode) )
return false;
if( $errorcode != 0 )
{
$this->factory->error = 'the article object is not valid (error code: '.$errorcode.')';
return false;
}
return $this->persistarticle();
]]></do>
</parameters>
</function>
Using the generated code
This section is meant to explain how to solve certain problems related with using the code generated by Metastorage.
- How do I reuse a database connection set before initializing a factory class?
Each factory class has a variable named database that is set by the initialize function. It contains a database access handle that is reused by all classes of the same component.
If you set this variable with a valid database access handle before calling the initialize function, that handle will be reused and the function will not setup a new database access.
If you have an application that sets a database connection using Metabase API function MetabaseSetupDatabase, you may use the resulting handle to set the factory class database variable.
You may also use the value of the database variable of of factory class to set the same variable of another factory class before calling its initialize function.
- How do I set an optional reference variable to not point to any object?
If you have class with a reference variable that may or may not point to an object of another class, just call the respective setreference type of variable to passing null in the place of the referenced object argument.
|