Add the ability to modify the database
In this final section, you'll add the ability to insert, update, and delete movie roles:
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:
- Select a movie role when a user clicks a link.
- Manage the selected actor for the page's browser element.
- Return the full name of an actor for display in the browser.
- Save changes to movie roles to the database.
Update the movieRoles relationship
The modification you need to make in the model file are to the relationship between a movie and its movie roles.
- In EOModeler, double-click the open entity icon for the Movie entity.
- Select the movieRoles relationship in the bottom view.
- Choose Tools Inspector to open the Relationship Inspector.
- In the inspector, click the middle icon to display the Advanced Relationship Inspector.
- Select the Cascade Delete Rule.
- Enable the Owns Destination and Propagate Primary Key check boxes.
- Save the model file.
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.
Add a hyperlink around the role name string
Now you need to make the role name a hyperlink so that users can select it. When the user clicks one of the movie role hyperlinks, the application should select the corresponding MovieRole object in the movieroles display group.
- Select the string element that displays role names.
- Choose Format Abstract Element Hyperlink.
- Add the following method to MovieDetails' script:
- Bind the selectObject method to the hyperlink action attribute.
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.
Add a form to the MovieDetails page
- Drag a form element from the Form Elements palette into the MovieDetails page.
- Replace the form's first label and text field with a browser element.
- Type Role Name as the label for the remaining text field.
- Bind movieroles roleName to the Role Name text field.
Add a Talent display group
The browser you just created is going to display a list of actors, or talent. You need a new WODisplayGroup to manage objects associated with the Talent entity.
- Drag the Talent entity from EOModeler into the MovieDetails page.
- Select the talents display group in the object browser, and then click the check mark button to open the DisplayGroupEditor panel.
- Configure the newly created talents display group to sort its objects alphabetically (ascending) by lastName.
- Configure it to fetch on load.
- Set its Entries per batch to 0.
Create list and item bindings for the browser element
Like a repetition element, a browser has list and item attributes. As the browser moves through its list, it sets item to the object at the current index.
- Bind talents displayedObjects to the browser element's list attribute.
- In the browser inspector, select the item attribute.
- Type talent in the text field.
- Click Connect.
- Set the class of the talent variable to be the Talent class.
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.
Create the value binding for the browser element
The value attribute tells the browser what string to display. For each item in its list, the browser evaluates its value. Typically you bind an attribute of the item variable to the value attribute. For example, you could bind talent lastName to the value attribute. However, the browser in the MovieDetails page should display the full name for each of the actors. Since the Talent class doesn't provide an attribute for a full name, you need to write a method to create and return a string containing the full name for the current actor.
- Add the following method to the MovieDetails script.
- Bind the fullName method to the browser's value attribute.
- 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.
Create the selections binding for the browser element
Browser elements have a selections attribute that should be set to an array of objects. A browser's selection can be zero, one, or many objects; but in this talent browser element, the selection should refer to a single object. Consequently, you need to add two methods to manage the browser's selection: one to return an array containing the selected actor and one to set the selected actor from an array object.
- Add the following method to the MovieDetails script:
- Add the following method:
- Bind the talentSelection method to the browser's selections attribute.
- Configure the browser's appearance.
- Open the browser inspector.
- Set the number of rows shown to 10.
- 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.
Add Insert, Update, and Delete buttons
- Delete the Submit and Reset buttons.
- In the MovieDetails script, delete the submit method associated with the Submit button.
- Drag three active image elements from the Form Elements palette into the form element.
Note: If you save your application after deleting the buttons, WebObjects Builder automatically deletes the submit method.
Assign images to the active image elements
- Select the first active image element.
- Open the active image inspector.
- Click Browse.
- In the Open panel navigate to the Main.wo directory in the Movies.woa application directory.
- Select the DBWizardInsert.gif file.
- Click Open.
- Follow the same procedure to set the second image's source to DBWizardUpdate.gif.
- Set the last image's source to DBWizardDelete.gif.
Create bindings for the active image elements
The WODisplayGroup class defines the methods insert and delete that you'll bind to the insert and delete active image elements, respectively. It doesn't, however, provide a save method. You'll have to provide that.
- In the MovieDetails script, delete the three submit... methods that are bound to the active images.
- Copy the saveChanges method from the Main script and paste it into the MovieDetails script.
- Bind the saveChanges method to the update image's action attribute.
- Bind movieroles insert to the insert image's action attribute.
- Bind movieroles delete to the delete image's action attribute.
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.
Save and run your application
You've now completed the entire application. You can use the Browse Movies and Movie Search pages to insert, update, and delete movies, and you can use the MovieDetails page to inspect the list of roles in a movie and insert, update, and delete movie roles as well.
Table of Contents Next Section