Author: Peter Keung Date: 2007-02-02 03:00:17 +0100 (Fri, 02 Feb 2007) New Revision: 4617
Log: First edit of PersistentObject article Modified: docs/articles/PersistentObject/relations/article.txt Modified: docs/articles/PersistentObject/relations/article.txt =================================================================== --- docs/articles/PersistentObject/relations/article.txt 2007-02-01 19:37:48 UTC (rev 4616) +++ docs/articles/PersistentObject/relations/article.txt 2007-02-02 02:00:17 UTC (rev 4617) @@ -1,5 +1,5 @@ -Relations where relations belong -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The PersistentObject eZ Component: Putting Relations Where Relations Belong +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :Author: Tobias Schlitt :Revision: $Rev$ @@ -11,174 +11,158 @@ Introduction ============ -One of the cool new features of the eZ Components 2006.2 release (short before -Christmas last year) was the support for relation mappings in the -PersistentObject component. Before that, PersistentObject already allowed you -to store any of your applications objects into a database and retrieve them -back from there, but you needed to care for the relations between these data -objects yourself all over your application. With the latest release 1.2 of -PersistentObject, these times are gone and you can configure the relations of -your objects in a central place and rely on PersistentObject all over your -application. +One of the cool new features of the eZ Components 2006.2 release is the support +for relation mappings in the PersistentObject component. PersistentObject +already allowed you to store objects in a database (and retrieve them from +there). With the latest 1.2 release of PersistentObject, you can configure the +object relations in a central place and benefit from using PersistentObject +throughout your application. -This article will give you an introduction into PersistentObject in general and -how the new facilities of handling relations work. As usual in my articles, I -will show you code from a simple example application. The application that will -be shown makes also heavy use of other components, like UserInput and Template, -but we will keep those out of the article and concentrate completely on -PersistentObject. +This article will give you an introduction to the PersistentObject component in +general, and explain the new feature of handling relations. To aid in the +explanations, we will look at code from a simple example application, as is +usually the case with my articles. The example application makes heavy use of +other components, such as UserInput and Template, but this article will +concentrate completely on the use of PersistentObject. The example application ======================= -This time, it will be a simple address book, that allows you to take care of -your friends their addresses and email addresses. The application will have a -web interface to test the functionality, which covers creation of new data and -deletion of objects. For simplicity, the manipulation of existing data has been -left out. +We will create a simple address book that allows you manage information about +your friends, and their addresses and email addresses. The application will have a +web interface that facilitates the creation and deletion of objects. For +simplicity, the manipulation of existing objects will be left out. The database layout ------------------- -The application relies on 5 database tables: The table "person" will store -first- and lastname of each contact. A second table, called "detail", will -contain additional details about person. -- Yes, this is not in the sense of -normalization, but it can make a lot of sense in praxis, if you reach -multi-millions of records, or if your detail table reaches hundreds of columns. -So, whatever you read and see here: Remember that this is only an example -application! -- The table "email" will be used to store the email addresses a -person and finally the table "address" will store physical addresses. Because -every person can be assigned to several addresses and several persons can live -at one address, we need a relation table: "person_address". +The application relies on five database tables: The "person" table stores the +first and last name of each person. A second table, called "detail", contains +additional details about people. The table "email" is used to store email +addresses, and the table "address" stores physical addresses. Because a person +can be assigned to several addresses and several people can live at one +address, we have a relation table: "person_address". .. include:: trunk/docs/ezccontact.sql :literal: -This database design contains several relations between the objects we want to -handle: The "person" table (represented by a Person object) is the central -instance in the application and represents the starting point for all of our -relations. The "detail" table contains information describing exactly 1 person -and every person may only have 1 detail record. This means, we have the -simplest kind of relation: A 1:1 (one-to-one) relation. A Person can have -multiple email addresses (so, how many do you have?), but 1 email address can -only belong to 1 person (at least in our example). Here we have the most common -relation type: 1:n (one-to-many). Finally a person can have multiple addresses -(a home address, an office address, ...) and multiple persons may be assigned -to the same address. This reflects the most complex relation type: A m:n -(many-to-many) relation. +This database design contains several relations between objects. The "person" +table (represented by a person object) is the central instance in the +application and represents the starting point for all of our relations. + +- The "detail" table contains information describing exactly one person and + every person may only have one detail record. Here, we have the simplest kind + of relation: a 1:1 (one-to-one) relation. -The application layout ----------------------- +- A person can have multiple email addresses (so, how many do you have?), but + one email address can only belong to one person (at least in our example). + Here, we have the most common relation type: 1:n (one-to-many). + +- A person can have multiple addresses (for example, a home address and an + office address) and multiple persons can be assigned to the same address. + This reflects the most complex relation type: a n:m (many-to-many) relation. -The example application will follow a very simple MVC (Model-View-Controller) -pattern: The main file is index.php, which defines and runs the main -controller object. This one knows several "actions", for which it will -dispatch to an action class, which will then handle the request. Beside that, +The application structure +------------------------- + +The example application follows a very simple MVC (Model-View-Controller) +pattern: The main file is index.php, which defines and runs the main +controller. This controller responds to several "actions", and dispatches them +to an action class, which will then handle the request. In addition, the controller handles the initialization and processing of a template (which -gets defined by the action object). +is defined by the action object). -We will not take care for the controller and the template mechanism in this -article, but concentrate on the actions and the configuration of the -PersistentObject component. If you downloaded the `application source`_, you -can find the action classes in the sub directory actions/. Furthermore we will -deal with the model classes used in the application (sub directory models/) and -the definition files, used by the PersistentObject component (persistent/). +We will not discuss the controller and template mechanism in this +article, but will concentrate on the configuration and usage of the +PersistentObject component. If you download the `application source`_, you +can find the action classes in the directory actions/. We will +also deal with the model classes used in the application (in the directory +models/) and the definition files used by the PersistentObject component (in +the directory persistent/). .. _`application source`: -The further directories are uninteresting for us, but I want to give you -some short hints, what they contain: +The other directories are not discussed in this tutorial. Here is a brief +summary of what they contain: autoload/ - This directory contains the autoload files for our applications, which are + This directory contains the autoload files for our application, which are used in combination with the eZ Components autoload mechanism. docs/ - You can find setup information for this application here. Especially the - database structure as a MySQL dump and some example data in the same format. + You can find setup information for the application here. For example, there + is the database structure and some example data as MySQL dumps. templates/ The example application relies on the Template component, for which the HTML template files can be found here. templatesc/ - Because the Template component compiles template code into PHP source code, - it needs this directory to cache the generated code. + Because the Template component compiles template code into PHP source code, + this directory is needed to cache the generated code. Getting things started ====================== -Now we will start digging into the application. You will see step by step, how -I defined the "persistent objects to reflect our database design and how to make -use of them in the action classes. +We will see how to define, step by step, the persistent objects to reflect our +database design and how to make use of them in the action classes. As already mentioned, the person object is the central model of our -application. The person object has a relation defined for each of the other -objects. Therefore, we will create this model in the first step, to see how the -basics of PersistentObject work. There are 3 steps to take before you can make +application. There are three steps to take before you can make use of PersistentObject: 1. Create the model class, which will be used in the application itself. -2. Create a definition for the PersistentObject component, to indicate, in +2. Create a definition for the PersistentObject component to indicate in which way this model class reflects the database structure. 3. Create an instance of ezcPersistentSession and configure it to make use of - the 2 previous points. + the two previous points. -Creating the first model ------------------------- +Creating the first model (step 1) +--------------------------------- -So, let's start with step 1. The PersistentObject component does neither -require you to inherit from a specific base class in order to make an object -persistent, nor to stick to a given naming scheme. The only thing required is, -to implement 2 methods in the object class: +The PersistentObject component does not require you to inherit from a specific +base class, nor to stick to a given naming scheme. The only requirement is to +implement two methods in the object class: getState() - Which returns an array representing the current state of the object. The - state is represented by the properties of the object, each property name is - a key in the array, having the properties value assigned. + This returns an array representing the current state of the object. The + state is represented by the properties of the object; each property name is + a key in the array, assigned to its corresponding value. setState() - Is the corresponding method which is used to set the state of an object. This - method must accept the same format, that getState() returns and must set the + This the corresponding method to set the state of an object. This + method must accept the same format that getState() returns and must set the object properties accordingly. -Let's take a look at the model for the person object. I called the class -ezcappContactPerson and it looks like this: +Let's take a look at the model for the person object, which is called +ezcappContactPerson: .. include:: trunk/models/person.php :literal: -The class contains 3 public properties (I'm quite sure, you would not want to -have all of them public in a real application, but remember, that this is just -an example): $id, $firstname and $lastname, which reflect the fields of the -person table in the database. Beside that, you see the 2 mentioned methods -getState() and setState(), which perform the desired actions: getState() simply -returns an array of all properties and setState() sets the properties from a -given array. You should note, that the implementation of setState() is a bit -sloppy (for simplicity reasons), because it would also set non-existent -properties. +The class contains three public properties: $id, $firstname and $lastname, +which reflect the fields of the "person" table in the database. (I'm quite sure +that you would not want to have all of the fields to be public in a real +application, but remember that this is just an example.) -Telling PersistentObject about it ---------------------------------- +The getState() and setState() methods perform the desired actions: getState() simply +returns an array of all object properties and setState() sets the properties from a +given array. You should note that the implementation of setState() is a bit +sloppy (for simplicity reasons) as it would also set non-existent properties. -So far, so good. Now we need to tell PersistentObject, that our new class +Telling PersistentObject about it (step 2) +------------------------------------------ + +So far, so good. Now we need to tell PersistentObject that our new class "ezcappContactPerson" should reflect the database table "person". -PersistentObject is designed to understand different formats for storing these -information, but there is only 1 format manager available so far. -The "ezcPersistentCodeManagaer" can load a PHP data structure, so that other -formats can always rely on this one as the target format. In future versions of -PersistentObject it might happen, that other code managers are added, which -could e.g. deal with some XML format. Beside that, you could write your own -code manager, which possibly creates definitions on the fly, to save you from the -work of creating definitions for all of your model classes, if you use a unique -naming scheme. +PersistentObject is designed to understand different formats for storing +information, but there is only one format manager available so far. +ezcPersistentCodeManager can load a PHP data structure. Future versions of +PersistentObject might include additional format managers that could, for +example, load an XML structure. You could also write your own format managers. -Anyway, we are using the standard definition manager. This one requires us to -provide a directory with PHP files, the manager can load when we request an -action on a certain persistent object. For this reason, the PHP files must be -named after the class names, but all in lowercase. The following definition is -stored in a file called persistent/ezcappcontactperson.php. +ezcPersistentCodeManager requires us to provide a directory with PHP files that +can be loaded when an action is requested on a certain persistent object. For +this reason, the PHP files must be named after the class names, but in +lowercase. The following definition is stored in a file called +persistent/ezcappcontactperson.php.:: -Let us take a look at how to create the basic PersistentObject definition -file. I won't show you how to define relations in this first step, so that we -have the basics here and concentrate on the really important stuff later. :: - <?php $def = new ezcPersistentObjectDefinition(); @@ -205,29 +189,31 @@ return $def; ?> -You should try to get not too confused by the explanation that follows now, -because the term "property" will occur quite often in different meanings. A -definition of a persistent object is created using an instance of -ezcPersistentObject definition. This container needs to know 2 essential -things: The database table and the class name it should interconnect. Next we -need to define the properties of our persistent object, which is done by -defining an array called "properties" in the definition object. This one has a -key for every property of the persistent object. For example: The property -ezcappContactPerson->firstname is define in "$def->properties['firstname']". We -need to tell PersistentObject, which column from the database is stored in this -property, as well as the data type the property has in and the name -of the property. +For now, we will leave out the discussion on defining relations until later in +this article. -A special case for a property is the "ID property", which is commonly called -"primary key" in terms of a relational database. The ID property is used to -identify a persistent object uniquely, e.g. to load and store it. The ID -property needs a definition object, because it needs a "generator" beside the -usual information for a property. The generator takes care of creating a unique -ID, when a new persistent object is stored into the database for the first -time. In our example we are using the "ezcPersistentSequenceGenerator", which -relies on the database to care for a new ID. In case of MySQL, this is -"auto_increment" and for Oracle (for example) it would be a sequence. +First, a definition of a persistent object is created using an instance of +ezcPersistentObject definition. You need to specify two essential things to the +definition: the database table and the equivalent class name. +Next, we define the properties of our persistent object, which is done by +defining an array called "properties" in the definition object. (Beware that the +term "property" will be used quite often in different contexts.) This array has a +key for every property of the persistent object. For example, the property +ezcappContactPerson->firstname is defined in "$def->properties['firstname']". We +need to specify the corresponding column from the database, its datatype and +the name of the property. + +The ID property, which is commonly called the "primary key" relational database +terminology, is a special case. The ID property is used to uniquely +identify a persistent object, in order to manipulate it. The ID +property needs a definition object, because it needs a "generator" in addition +to the usual information for a property. The generator takes care of creating a unique +ID when a new persistent object is stored into the database for the first +time. In our example, we are using the "ezcPersistentSequenceGenerator", which +relies on the database to provide a new ID. For MySQL, this is +"auto_increment" and for Oracle this is a sequence. + Finally, the definition we just created is returned from the file. I believe I'd see some baffled faces now that you read this , because I must have looked the same, when I saw this first. If you want to know more about this, take a @@ -235,16 +221,11 @@ .. _`PHP manual about include`: http://www.php.net/manual/en/function.include.php#AEN5250 -Using what we got ------------------ +Using what we have (step 3) +--------------------------- -Finally in this introductional part, we will see, what we need in our -application beside the configuration setup and how to perform -the first steps with the real usage of PersistentObject. - To perform any kind of action on a persistent object, we need an instance of -ezcPersistentSession, which itself needs a database connection to work -properly: :: +ezcPersistentSession, which in turn needs a database connection: :: <?php @@ -260,36 +241,33 @@ ?> The first two lines perform the database initialization and store the newly -created database connection in a singleton like mechanism (for more -information, take a look at the `Database component`_). After that, we create -the persistent session itself. Beside the database connection, it needs the -definition manager, I already talked about earlier. This one needs to know, -where our definition files reside. Finally, we store the persistent session -instance in a singleton container, too, so that we can access it from -everywhere in our application. +created database connection in a singleton-like mechanism (for more +information, see the `Database component`_). After that, we create +the persistent session itself. It also needs to load the format +manager, as discussed in step 2. Finally, we store the persistent session +instance in a singleton container so that it can be accessed throughout our +application. .. _`Database component`: http://ez.no/doc/components/view/2006.2/(file)/classtrees_Database.html Using persistent objects ======================== -In the introductional part you learned the basics of how to configure -PersistentObject. We created a simple model class, the database mapping for -it and the persistent session. In the following part you will learn how to -configure relations between persistent objects and how to work with them in -your application. We will make use of the other models in the following -examples, assuming, that you know how they are structured and how their basic -PersistentObject definition looks like. It's all the same as with the "person" -model. +In the introduction section we discussed the basics of how to configure +PersistentObject. We created a simple model class, specified the database +mapping for it and instantiated the persistent session. We will now demonstrate +how to configure relations between persistent objects and how to work with them in +an application. Other models are used, which are all similar to +the "person" model that was just created. The first steps --------------- -First of all I want to show, how a new persistent object can be stored. We +Let's examine how a new persistent object can be stored. We therefore take a look at the "create" action, but still leave out the relations -it uses. I also skip the part of the `UserInput component`_, which is -used to retrieve the POST variables in the action. Here is the very shortened -source code: :: +it uses. The usage of the `UserInput component`_, which is +used to retrieve the POST variables in the action, is skipped as well. Here is +the shortened source code: :: <?php //... @@ -305,19 +283,19 @@ //... ?> -We retrieve the instance of ezcPersistentSession in the first line of the -example. After that, we simply create a new ezcappContactPerson object, assign -its properties from the form data we received and tell the session to save it. -These few lines of code last, to make PersistentObject put our newly created -person into the database and assign the ID it retrieved to the defined ID -property. Note, that the form object already took actions to secure our data -and that PersistentObject uses bind values internally to secure the SQL queries. +In the first line, the ezcPersistentSession instance is retrieved. Then, we +create a new ezcappContactPerson object, assign +its properties from some input form data and save the object. +PersistentObject puts our newly created person object into the database and +assigns the generated unique ID to the defined ID property. Note, that the form +object already ensured that our data is secure +and that PersistentObject uses bind values to internally secure the SQL queries. -So, that was easy. But in the create action, we also retrieve the details for a -new person, which are stored in the table "detail" and therefore in another -persistent object. Basically we need to define our first relation: A one-to-one -relation. As mentioned earlier, I won't show you the class ezcappContactDetail -here, because it looks too similar to the ezcappContactPerson class. The same +So, that was easy. During the create action, however, we also retrieve the +details for a new person, which are stored in the table "detail" and therefore +in another persistent object. Basically, we need to define our first relation: +A one-to-one relation. As mentioned earlier, the class ezcappContactDetail +looks very similar to the ezcappContactPerson class. The same applies to its definition file. Relations are defined in the persistent object definitions themselves, using @@ -346,25 +324,25 @@ We create a new key for the desired class in the relations array. The configuration class for a one-to-one relation is called -ezcPersistentOneToOneRelation. Its constructor receives the names of 2 database -tables: The 1st is the name of the source table (which is actually the current -on, "person"), the second one is the name of the destination table for the +ezcPersistentOneToOneRelation. Its constructor receives the names of two database +tables: The first is the name of the source table (which is actually the current +one, "person"); the second is the name of the destination table for the relation ("detail" in this case). Furthermore, the relation needs a mapping of columns, which defines how the -objects are related to each other. In case of a one-to-one relation, this works -through a so-called ezcPersistentSingleTableMap, because we only do 1 mapping -between 1 tables in each map (in contrast to an ezcDoubleTableMap, which you -will get to know later). In our case we map the "id" column of the table -"person" to the column "person" of the table "detail". As you might have -noticed, the column map is an array, which means that you can also specify -multiple column mappings, using 1 instance of ezcPersistentSingleTableMap each. +objects are related to each other. In the case of a one-to-one relation, this +works through ezcPersistentSingleTableMap, because we only map one table on +each end (in contrast to ezcDoubleTableMap, which will be discussed later). In +our case, we map the "id" column of the table "person" to the "person" column +of the table "detail". As you might have noticed, the column map is an array, +which means that you can also specify multiple column mappings, using one +instance of ezcPersistentSingleTableMap for each mapping. -Finally, we define that this relation should cascade delete actions. This -means, that whenever we delete a person object from the database, the -corresponding detail object gets deleted, too. +Finally, we specify that this relation should cascade delete actions. This +means that whenever we delete a person object from the database, the +corresponding detail object is deleted as well. -Nice, so, we have the first relation defined and can make use of it in our +Now that we have the first relation defined, it can be used in our example from before: :: <?php @@ -388,21 +366,20 @@ // ... ?> -The first part, for storing the person object stays the same. After that, we +The first part, for storing the person object, stays the same. After that, we create the detail object and assign the necessary properties from the form -data. The following call to $session->addRelatedObject() performs the -assignment of the relation. After that, the detail belongs to the person. -Finally, we only need to save the detail to the database, too. Note, that the -addRelatedObject() method does only set the necessary properties (and for some -relations it does some more, but more about that later) and does not store the -object it manipulated automatically. +data. The $session->addRelatedObject() method assigns the relation. +Finally, the detail object is saved to the database. Note that the +addRelatedObject() method only sets the necessary properties (as you will see +later, it does a bit more for other relations) but does not actually store the +object. -Easy, wasn't that? But I have to admit, that I embezzled a small speciality for -the detail model so far. As you can see from the database structure, the detail +Easy, wasn't it? But I have to admit, I purposely left out a small point about +the detail model. As you can see from the database structure, the detail table uses the same ID as the person table. If we would define the ID property -of the detail object using an ezcPersistentSequenceGenerator, as we did for the -person object, we would run into problems: The sequence generator would try to -generate a new ID, while we want it to re-use the one from the person. +of the detail object using ezcPersistentSequenceGenerator, as we did for the +person object, we would run into problems: the sequence generator would try to +generate a new ID, while we want it to use the same one as the person object. Therefore, we need to use another generator here: :: <?php @@ -416,31 +393,31 @@ // ... ?> -Instead of the sequence generator, we are using the manual generator here, -which allows us to set the ID property on our own and make PersistentObject -don't care about it. At least not in the moment where we are calling -$session->save(). Actually, PersistentObject does take care for the ID property -in our special case, but in a different way: The call to addRelatedObject() in -the previous code piece does it, because we defined the ID property for the -relation mapping. +Instead of the sequence generator, we use the manual generator, +which allows us to manually set the ID property for the detail object. +Actually, PersistentObject takes care of this: the call to addRelatedObject(), in +the previous piece of code, set the ID property, because the "id" and "person" +columns were mapped to each other. +.. _`UserInput component`: http://ez.no/doc/components/view/2006.2/(file)/classtrees_UserInput.html + Advanced relations ------------------ -We just defined our first relation, the one-to-one relation between the person -and the detail object. Additionally, we saw where a problem for one-to-one -relations occurs: In the most cases we will use the same keys for related -objects, so that one of them needs to use the manual generator. And finally we -saw how to save newly created persistent objects and how to make 2 persistent -objects related to each other. +We just defined our first relation: the one-to-one relation between the person +and detail objects. Additionally, we saw that in most one-to-one cases the same +keys are used for related objects; as a result, one of them needs to use the +manual generator. We also saw how to save newly created persistent objects and +how to relate two persistent objects to each other. -The next step is, to define some more relation types and to see, how we can -fetch objects, which are related to each other. For that, let's take a look at -the "show" action, which is responsible for displaying a person, its details, its -email addresses and the addresses it is assigned to. Beside that, the actions -template shows a list of all addresses, so that the user can assign new ones to -the actual person. Therefore we need to fetch all address objects, too. :: +Next, we will define some more relation types and see how to +fetch objects that are related to each other. +In our example, the "show" action, is responsible for displaying a person +object, its details, its email addresses and its physical addresses. The actions +template also shows a list of all addresses so that the user can assign new ones to +a person. Therefore, we need to fetch all address objects as well. :: + <?php // ... @@ -457,20 +434,19 @@ // ... ?> -As usual we need the instance of ezcPersistentSession, for any kind of action -on a persistent object. Next we fetch the desired person from the -database, using $session->load(). If you are using this method, you should be -aware, that it will throw an exception, if the desired object does not exist. -If you cannot be sure it exists or if it does not matter to you, you should use -the method loadIfExists(). +As usual, we need an instance of ezcPersistentSession for any kind of action +on a persistent object. Next, we fetch the desired person from the +database using $session->load(). If you are using this method, you should be +aware that it will throw an exception if the desired object does not exist. +To fetch an object only if it exists, use the method loadIfExists(). -Next we need to fetch the 3 related objects: The detail object, the email -objects and the address objects. You should note especially, that I am using 2 -different methods for this: getRelatedObject() (used for fetching the detail -object) will fetch exactly 1 object and will throw an exception, if no object -was found. In contrast, fetchRelatedObjects() will fetch all related objects of -a type from the database (0 or more) and always return an array of objects, no -matter how many were found. +Then, we need to fetch the three related objects: the detail object, the email +objects and the address objects. Note that two different methods are used in +for this: getRelatedObject() (used for fetching the detail +object) will fetch exactly one object and will throw an exception if no object +is found; in contrast, getRelatedObjects() will fetch all related objects from +the database (0 or more) and will always return an array of objects (even if it +is empty), no matter how many were found. Finally, we need the list of all address objects. This has actually nothing to do with the topic of relations. The ezcPersistentSession instance can create a @@ -480,14 +456,13 @@ component`_. Using this object as the basis, we can restrict the performed search and add SQL parts to it. As you can see, the query class uses a so-called "fluent interface", where each method call returns the object itself, -so that one can call methods in a chain. We are adding 3 "ORDER BY" clauses to +so that one can call methods in a chain. We are adding three "ORDER BY" clauses to our query here. Finally, we instruct the persistent session to find all -objects, of the class ezcappContactAddress, which match the given query and -retrieve an array of them. +objects of the class ezcappContactAddress that match the given query and +return an array of them. -So, after this little excursus away from relations, let us take a look at the -definitions for the relations we just used. Again we are enhancing the -definition file of the person object: :: +Let us now take a look at the definitions for the relations we just used. +Again, we are enhancing the definition file of the person object: :: <?php // ... @@ -525,37 +500,37 @@ ?> The relation between the person and email objects is the most common relation -type, the one-to-many relation. It is defined completely analogues to the -one-to-one relation, using the specific relation class and the 2 tables we want -to connect to each other. Since we are only using 2 tables again, we need a -single table map here, too. Same as for the ezcPersistentOneToOneRelation. The -cascade option again has the same effect: If a person gets deleted, all of its -associated email addresses get deleted, too. +type, the one-to-many relation. It is defined completely analagous to the +one-to-one relation, using the specific relation class and the two tables we want +to connect to each other. Since we are only using two tables, we need a +single table map, just as for ezcPersistentOneToOneRelation. The +cascade option again has the same effect: if a person is deleted, all of its +associated email addresses are deleted too. -The second relation we define here, is the most complex type: The many-to-many -relation. It is more complex then the other 2 types we already know, because we +The second relation we define here is the most complex type: the many-to-many +relation. It is more complex than the other two types, because we need a relation table to realize it. Therefore, the constructor of -ezcPersistentManyToManyRelation requires 3 table names instead of 2: The source -table, the destination table and the relation table. We are connecting the -person table to the address table, using the person_address table. Therefore the -column map of a n:m relation is an array of ezcPersistentDoubleTableMap, which -connect the 3 named table and therefore realizes 2 table connections. The -order of the columns given to the double table map is the following: 1st the -field from the source table, which maps to (2nd) the first field of the -relation table. The 3rd item is the second field of the relation table, which -maps to a field of the destination table (4th). In other words, we are mapping -the field "id" of the table "person" to the field "person" of the table -"person_addresss" and the column "address" of the table "person_address" to the -field "id" of the table "address". +ezcPersistentManyToManyRelation requires three table names instead of two: the source +table, the destination table and the relation table. -Specialities ------------- +We are connecting the person table to the address table using the +person_address table. Therefore, the column map of a n:m relation is an array +of ezcPersistentDoubleTableMap, which connects the three named tables to +realizes two table connections. First, the field from the source table maps to +the first field of the relation table. The third item is the second field of +the relation table, which maps to the field from the destination table. In +other words, we are mapping the column "id" of the table "person" to the column +"person" of the table "person_addresss" and the column "address" of the table +"person_address" to the column "id" of the table "address". -As already mentioned, many-to-many relations are the most complex relation -type. To analyze them a bit deeper, we will now take a look at another action. -Earlier, we loaded all addresses so that the user can assign them to a person. -We will examine this action now ("address"). :: +More about relations +-------------------- +As already mentioned, the many-to-many relation is the most complex relation +type. Earlier, we loaded all addresses so that the user can assign them to a +person. To analyze this a bit deeper, we will take a look at the "address" +action: :: + <?php // ... @@ -569,16 +544,16 @@ ?> We load both desired objects from the database, using the well-known load() -method. Again we are using the addRelatedObject() method, but this time, -without saving any of the objects afterwards. Why don't we do so? Because it is +method. Again, we are using the addRelatedObject() method, but this time, +without saving any of the objects afterwards. That is actually not necessary here. For all other relation types, the -addRelatedObject() method just sets the desired object properties to the -correct values. This manipulates the objects themselves, so we need to store -them afterwards. In the case of a many-to-many relation, the original objects -are not modified in any way. Instead, a new record is inserted into the -relation table, which happens without any manual saving. +addRelatedObject() method simply sets the desired object properties to the +correct values; this manipulates the objects themselves, so we need to store +them. In the case of a many-to-many relation, the original objects +are not modified in any way. Instead, a new record is inserted into the +relation table, which occurs without any manual saving. -So, let us also examine the opposite part in the "removeaddress" action. :: +So, let us also examine the counterpart: "removeaddress" action. :: <?php // ... @@ -592,29 +567,26 @@ // ... ?> -The same procedure here: Loading both objects and afterwards a call to -removeRelatedObject() to remove the relation record again. As you might guess, +The same procedure occurs here: both objects are loaded, then a call to +removeRelatedObject() removes the relation. As you might guess, you can also use this method with all other types of relations, where it will -simply unset the relation properties, to remove the relation. Again, you will -have to manually store the object afterwards in that case. +simply unset the relation properties to remove the relation. Again, you would +have to manually store the object afterwards. -As a final word about many-to-many relations you should notice, that the -cascade option does not exist for them. If you think a second about this, it -makes perfect sense, because PersistentObject cannot know (or only with more -SQL query overhead), if related objects may be deleted, and are not referenced -by other objects. +As a final word about many-to-many relations, notice that the +cascade option does not exist for them. PersistentObject does not know (or only +with more SQL query overhead) if related objects can be deleted, as it needs to +find out whether they are referenced by other objects. -So, what is still missing is the last relation type, about which we did not -hear anything, yet: Many-to-one relations. Right now, you will wonder, where -this relation type could make sense. But it actually makes sense: With all of -the relations we configured so far, we can fetch objects from the database, -which are related to a person object. What happens if we want to fetch the -other way around? Correct: We need to configure the corresponding relation. -And for a one-to-many relation, this is consequently a many-to-one relation. +The final relation type, is the many-to-one relation. With all of +the relations we configured so far, we can fetch objects from the database that +are related to a person object. What happens if we want to fetch a person +object related to one of those other objects? We need to configure the +corresponding relation. If you want to learn more about this relation type (or so-called "reverse" relations), you should take a look at the definition files for the "email" and -"address" objects. Furthermore, the `PersistentObject tutorial`_ contains some +"address" objects. The `PersistentObject tutorial`_ also contains some information about it. .. _`PersistentObject tutorial`: http://ez.no/doc/components/view/2006.2/(file)/introduction_PersistentObject.html @@ -622,21 +594,18 @@ Final thoughts ============== -Some of the features of PersistentObject are not mentioned in this, but it -already became a lot longer than it was planned... Anyway, the example -application shipped with this article can give you a nice overview on the usage -of PersistentObject and if you dig into the whole code, you can also learn a -lot about other components. +The example application in this article gives a nice overview on the usage +of PersistentObject and if you dig a bit deeper into the code, you can also +learn a lot about some other components. -As a hint, you should also take a look at the -`PersistentObjectDatabaseSchemaTiein component`_, which gives you some comfort -for generating PersistentObject definitions +You should also take a look at the `PersistentObjectDatabaseSchemaTiein +component`_, which can help with generating PersistentObject definitions .. _`PersistentObjectDatabaseSchemaTiein component`: http://ez.no/doc/components/view/2006.2/(file)/classtrees_EventLogDatabaseTiein.html -For the next releases I expect that PersistentObject will become more and more +For future releases, I expect that PersistentObject will become more and more powerful. We will definitely enhance the usability with some API candy and a -primary goal is, to reduce the SQL overhead generated as much as possible. +primary goal is to reduce the generated SQL overhead as much as possible. I hope you enjoyed reading this article and that you try out the PersistentObject component. I would be very happy to receive some feedback! -- svn-components mailing list svn-components@lists.ez.no http://lists.ez.no/mailman/listinfo/svn-components