Most of the set up is done already, but you have to make one more modification to the model file, and you have to add a few more methods. In this section, you'll add methods to the MovieDetails component that:
A role in a movie cannot exist if the movie itself does not exist, so if a movie is deleted, all of its related movie roles should be deleted as well. This is what the Cascade delete option does. Owns Destination means that if you delete a movie role from the selected movie, it is removed from the database as well. (Recall that in this movieRoles releationship, Movie is called the source entity and MovieRoles is the destination entity. Owns Destination means that Movie owns its MovieRoles.)
Next, because Movie and MovieRole share the movieId primary key, you select Propagate Primary Key. Now when you insert a new movie role, that new object will automatically be assigned the movieID of the selected movie.
The Hyperlink command adds a hyperlink element as the selected element's parent. Now the string element is nested inside the hyperlink.
- selectObject { [movieroles selectObject:movieRole]; }
The movieRole variable is bound to the repetition element's item attribute. In the selectObject method above, movieRole represents the role a user clicked on.
talent's displayedObjects method returns an array of all of the talent objects in the database.
WebObjects Builder creates a new variable named talent in the MovieDetails component and binds it to the browser's item attribute.
Strictly speaking, this step is unnecessary, but it makes it easier to see what's going on. Now when you select talent variable in the object browser, you can see that it has lastName and firstName attributes.
- fullName { id lastName = [talent valueForKey:@"lastName"]; if (![lastName length]) { /* some actors don't have two names */ return [talent valueForKey:@"firstName"]; else return [NSString stringWithFormat:@"%@ %@", [talent valueForKey:@"firstName"], lastName]; }
As the browser iterates through its list, it sets its item (in this case, talent) to the current Talent object and then invokes the fullName method. When fullName is invoked, it returns a string containing the full name of the current actor.
- talentSelection { id selectedTalent = [[movieroles selectedObject] valueForKey:@"talent"]; if (selectedTalent) return [NSArray arrayWithObject:selectedTalent]; else return nil; }
Because the browser expects an array for its selections attribute, this method packages the talent object for the selected MovieRole in an array. If the selected MovieRole object is nil, talentSelection simply returns nil to indicate that the browser shouldn't set a selection.
- setTalentSelection:array { if ([array count]) [[movieroles selectedObject] takeValue:[array objectAtIndex:0] forKey:@"talent"]; }
Again because the browser uses an array for its selections attribute, the setTalentSelection: method must take an array as its argument. If array's count is non-zero, then this method sets the selected MovieRole's talent object to the first object in the array. (Note that a user can select more than one actor. A production-quality application would probably alert the user that a MovieRole can only be played by one actor, but this simple tutorial application won't take that extra step.)
Given this binding, the browser invokes talentSelection to determine what the selection is. It invokes setTalentSelection: when a user changes the selection. As a result, the talent instance variable of the selected MovieRole object is set to the newly selected actor.
Note: If you save your application after deleting the buttons, WebObjects Builder automatically deletes the submit method.
When you use the Database Wizard and choose the Selected Record layout, the wizard sets up three active image elements just as you're doing on this page. As a part of that set up, it adds a method called saveChanges to the component script. You can copy the saveChanges method generated by the Database Wizard for the Main component and paste it into the MovieDetails component script.
- saveChanges { id exception; exception = self.session.defaultEditingContext.tryToSaveChanges(); if (exception != nil) { [exception raise]; } }
self.session (the same as [self session]) refers to a WOSession object that represents a connection to the application by a single user. WOSession objects provides access to an EOEditingContext object. The expression
self.session.defaultEditingContext.tryToSaveChanges()
which is the same as the expression:
[[[self session] defaultEditingContext] tryToSaveChanges]
sends a tryToSaveChanges message to the WOSession's defaultEditingContext. This default EOEditingContext object manages graphs of objects fetched from the database, and all changes to the database are saved through it. For more information, see the EOEditingContext class specification in the Enterprise Objects Framework Reference.
EOEditingContext's tryToSaveChanges method uses other Enterprise Objects Framework objects to analyze its graph of enterprise objects (Movie, MovieRole, and Talent objects referenced by the application) for changes and then to perform a set of corresponding operations in the database. If an error occurs during this process, tryToSaveChanges returns an NSException object. (See the NSException class specification in the Foundation Reference for more information on exceptions.) By default, the saveChanges method simply raises the exception, having the effect of returning a diagnostic page. You could return an error page that explains the reason for the save failure instead, but the application in this tutorial uses the default behavior.
Table of Contents Next Section