|
|
Sample Programs from NeXSTEP 3.3
/* ModelSavyController.m created by cfeder on Tue 01-Nov-1994 */
/*
Interesting master-detail relationships:
M Detail Insert rule Delete Types
PK -> PK D->PK := M->PK delete D to-one
FK -> PK M->FK := D->PK M->FK=NULL to-one
PK -> FK D->FK := M->PK Delete D to-many
Many to many create link obj Delete link to-many
*/
#import "SavvyControllerDelegate.h"
#import "RelationshipKeySetter.h"
#import "ResetRelationships.h"
#import "KeyGenerator.h"
#import "ValueForKey.h"
@interface EODetailDatabaseDataSource (databaseChannel)
- (EODatabaseChannel *)databaseChannel;
@end
@implementation EODetailDatabaseDataSource (databaseChannel)
- (EODatabaseChannel *)databaseChannel
{
return [[self masterDataSource] databaseChannel];
}
@end
@implementation SavvyControllerDelegate
- (EORelationship *)detailRelationshipForController:controller;
{
// Look up
id dataSource = [controller dataSource];
if ([dataSource respondsToSelector:@selector(masterObject)]) {
EOEntity *entity = [dataSource entity];
id masterObject = [dataSource masterObject];
EOEntity *masterEntity = [[entity model] entityForObject:masterObject];
return [masterEntity relationshipNamed:[dataSource detailKey]];
}
return nil;
}
- (void)updateDetailObjectsForController:(EOController *)controller
{
EORelationship *relationship = [self detailRelationshipForController:controller];
if (relationship) {
// if this is a detail controller, then we may have to update
// the relationship property of the master EO, since we
// may have supressed insert and delete operations bound
// for the detailDataSource
id masterObject = [(id)[controller dataSource] masterObject];
NSArray *objects = [controller allObjects];
if ([relationship isToMany]) {
objects = [[[NSArray alloc] initWithArray:objects] autorelease];
} else {
objects = [objects count] ? [objects objectAtIndex:0] : [EONull null];
}
[masterObject takeValue:objects forKey:[relationship name]];
}
}
// EOController delegation methods
- (BOOL)controller:(EOController *)controller willInsertObject:object atIndex: (unsigned)newIndex;
{
// Handles primary key assignment for newly inserted objects.
EOEntity *entity = [(id)[controller dataSource] entity];
[object assignPrimaryKeyIfNotAlreadyPresentForEntity:entity];
return YES;
}
// Handles insertions into a detail controller. The tricky part here is
// that if we assign a new department in the detail controller for
// an employee, we don't want to actually add the department to the
// database (it's already there). Instead, we want to update the foreign
// key in the master object to point at the new department.
// Similarly if we insert a project in the toProjects (many-to-many) detail
// controller, we don't want to insert the project, but rather a "link"
// object in the intermediate table emp_projects that joins employee and
// project.
- (EODataSourceOperationDelegateResponse)controller:(EOController *)controller
willInsertObject:object
inDataSource:dataSource;
{
EODataSourceOperationDelegateResponse response = EOPerformDataSourceOperation;
EORelationship *relationship = [self detailRelationshipForController:controller];
// This stuff only works if our dataSource is an EODetailDatabaseDataSource.
// Otherwise we just pass the operation through
if (relationship) {
id masterObject = [dataSource masterObject];
id modifiedObject;
EODatabaseDataSource *masterDataSource = [dataSource masterDataSource];
// Invoke the magic of the ModelRelExtensions to figure out what to
// do for this relationship
modifiedObject = [relationship updateKeysForSourceObject:masterObject destinationObject:object];
if (modifiedObject == object) {
modifiedObject = nil;
} else if (modifiedObject == masterObject) {
// we need to update this one as well
NSLog(@"Updating master object '%@'", masterObject);
if (![masterDataSource updateObject:masterObject])
response = EORollbackDataSourceOperation; // put up error?
} else {
// link object that we need to insert
NSLog(@"Inserting link object '%@'", modifiedObject);
if (![masterDataSource insertObject:modifiedObject])
response = EORollbackDataSourceOperation; // put up error?
}
if (modifiedObject) {
// do we really need to insert this object? Or did the insert just indicate
// a reference to an existing object?
EODatabaseContext *context = [[dataSource databaseChannel] databaseContext];
if ([context snapshotForObject:object])
response = EODiscardDataSourceOperation;
}
}
if (response == EOPerformDataSourceOperation)
NSLog(@"Allowing insert of object '%@'", object);
else {
NSLog(@"Suppressing insert of object: '%@'", object);
}
return response;
}
- (EODataSourceOperationDelegateResponse)controller:(EOController *)controller
willDeleteObject:object
inDataSource:dataSource;
{
/*Deletion of detail record may dictate one of the following...
M Detail Insert rule Delete Types
PK -> PK D->PK := M->PK delete D to-one
FK -> PK M->FK := D->PK M->FK=NULL to-one
PK -> FK D->FK := M->PK Delete D to-many
Many to many create link obj Delete link to-many
*/
EODataSourceOperationDelegateResponse response = EOPerformDataSourceOperation;
EORelationship *relationship = [self detailRelationshipForController:controller];
// This stuff only works if our dataSource is an EODetailDatabaseDataSource.
// Otherwise we just pass the operation through
if (relationship) {
EOEntity *entity = [relationship destinationEntity];
id masterObject = [dataSource masterObject];
id modifiedObject;
// Invoke the magic of the ModelRelExtensions to figure out what to
// do for this relationship
modifiedObject = [relationship updateKeysForDeleteOfDestinationObject:object fromSourceObject:masterObject];
if (modifiedObject == object) {
// just go ahead and delete this one
} else if (modifiedObject == masterObject) {
// we need to update the master to reflect the delete
// and leave this object alone
response = EODiscardDataSourceOperation;
NSLog(@"Updating master object '%@'", masterObject);
if (![dataSource updateObject:masterObject])
response = EORollbackDataSourceOperation; // put up error?
} else {
// we need to delete the link object
// and leave this object alone
EODatabaseContext *context = [[dataSource databaseChannel] databaseContext];
EODatabase *database = [context database];
EOEntity *linkEntity = [[entity model] entityForObject:modifiedObject];
NSDictionary *pkDict;
id registeredObject;
response = EODiscardDataSourceOperation; // leave destination object alone
// to delete the object we need to make sure that it's in the uniquing
// tables. Check if it's already there, and if not, record this one
pkDict = [modifiedObject valuesForKeys:[linkEntity primaryKeyAttributeNames]];
registeredObject = [context objectForPrimaryKey:pkDict entity:linkEntity];
if (!registeredObject) {
[database recordObject:modifiedObject primaryKey:pkDict
snapshot:pkDict];
registeredObject = modifiedObject;
}
NSLog(@"Deleting link object '%@'", registeredObject);
if (![[dataSource masterDataSource] deleteObject:registeredObject])
response = EORollbackDataSourceOperation; // put up error?
}
}
if (response == EOPerformDataSourceOperation)
NSLog(@"Allowing delete of object '%@'", object);
else {
NSLog(@"Suppressing delete of object: '%@'", object);
}
return response;
}
- (void)controllerDidSaveToDataSource:(EOController *)controller
{
[self updateDetailObjectsForController:controller];
}
@end
// This is probably evil.
// This code checks modifications to foreign key properties in an EO, and
// reassigns any corresponding relationship pointers using methods in
// ResetRelationships.h.
//
// THIS CODE IS NOT ACTUALLY USED IN THE EXAMPLE PROGRAM, but is here as
// an illustration.
@implementation SavvyControllerDelegate (ResetRelationshipsForChangedKeyProps)
static NSArray *changedKeys = nil;
- (NSDictionary *)controller:(EOController *)controller willSaveEdits: (NSDictionary *)edits toObject:object
{
// update any related relationship properties (in case we update foreign keys)
changedKeys = [edits allKeys];
return edits;
}
- (void)controller:(EOController *)controller didSaveToObject:object
{
EODatabaseDataSource *ds = (EODatabaseDataSource *)[controller dataSource];
EOEntity *entity = [ds entity];
// update relationships for key changes
[[ds databaseChannel] updateRelationshipsInObject:object forModifiedAttributes:[entity attributesNamed:changedKeys]];
// update keys for relationship changes
{
int i, count = [changedKeys count];
for(i=0; i<count; i++) {
NSString *key = [changedKeys objectAtIndex:i];
EORelationship *relationship = [entity relationshipNamed:key];
if (relationship) {
[relationship updateKeysForSourceObject:object
destinationObject:[(NSObject *)object valueForKey:key]];
}
}
}
}
@end
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.