|
|
1.1 ! root 1: /* ModelSavyController.m created by cfeder on Tue 01-Nov-1994 */ ! 2: ! 3: /* ! 4: Interesting master-detail relationships: ! 5: M Detail Insert rule Delete Types ! 6: PK -> PK D->PK := M->PK delete D to-one ! 7: FK -> PK M->FK := D->PK M->FK=NULL to-one ! 8: PK -> FK D->FK := M->PK Delete D to-many ! 9: Many to many create link obj Delete link to-many ! 10: */ ! 11: #import "SavvyControllerDelegate.h" ! 12: #import "RelationshipKeySetter.h" ! 13: #import "ResetRelationships.h" ! 14: #import "KeyGenerator.h" ! 15: #import "ValueForKey.h" ! 16: ! 17: ! 18: @interface EODetailDatabaseDataSource (databaseChannel) ! 19: - (EODatabaseChannel *)databaseChannel; ! 20: @end ! 21: ! 22: @implementation EODetailDatabaseDataSource (databaseChannel) ! 23: - (EODatabaseChannel *)databaseChannel ! 24: { ! 25: return [[self masterDataSource] databaseChannel]; ! 26: } ! 27: @end ! 28: ! 29: ! 30: ! 31: @implementation SavvyControllerDelegate ! 32: ! 33: - (EORelationship *)detailRelationshipForController:controller; ! 34: { ! 35: // Look up ! 36: id dataSource = [controller dataSource]; ! 37: if ([dataSource respondsToSelector:@selector(masterObject)]) { ! 38: EOEntity *entity = [dataSource entity]; ! 39: id masterObject = [dataSource masterObject]; ! 40: EOEntity *masterEntity = [[entity model] entityForObject:masterObject]; ! 41: return [masterEntity relationshipNamed:[dataSource detailKey]]; ! 42: } ! 43: return nil; ! 44: } ! 45: ! 46: - (void)updateDetailObjectsForController:(EOController *)controller ! 47: { ! 48: EORelationship *relationship = [self detailRelationshipForController:controller]; ! 49: if (relationship) { ! 50: // if this is a detail controller, then we may have to update ! 51: // the relationship property of the master EO, since we ! 52: // may have supressed insert and delete operations bound ! 53: // for the detailDataSource ! 54: id masterObject = [(id)[controller dataSource] masterObject]; ! 55: NSArray *objects = [controller allObjects]; ! 56: if ([relationship isToMany]) { ! 57: objects = [[[NSArray alloc] initWithArray:objects] autorelease]; ! 58: } else { ! 59: objects = [objects count] ? [objects objectAtIndex:0] : [EONull null]; ! 60: } ! 61: [masterObject takeValue:objects forKey:[relationship name]]; ! 62: } ! 63: } ! 64: ! 65: ! 66: ! 67: // EOController delegation methods ! 68: ! 69: - (BOOL)controller:(EOController *)controller willInsertObject:object atIndex: (unsigned)newIndex; ! 70: { ! 71: // Handles primary key assignment for newly inserted objects. ! 72: EOEntity *entity = [(id)[controller dataSource] entity]; ! 73: [object assignPrimaryKeyIfNotAlreadyPresentForEntity:entity]; ! 74: ! 75: return YES; ! 76: } ! 77: ! 78: // Handles insertions into a detail controller. The tricky part here is ! 79: // that if we assign a new department in the detail controller for ! 80: // an employee, we don't want to actually add the department to the ! 81: // database (it's already there). Instead, we want to update the foreign ! 82: // key in the master object to point at the new department. ! 83: // Similarly if we insert a project in the toProjects (many-to-many) detail ! 84: // controller, we don't want to insert the project, but rather a "link" ! 85: // object in the intermediate table emp_projects that joins employee and ! 86: // project. ! 87: - (EODataSourceOperationDelegateResponse)controller:(EOController *)controller ! 88: willInsertObject:object ! 89: inDataSource:dataSource; ! 90: { ! 91: EODataSourceOperationDelegateResponse response = EOPerformDataSourceOperation; ! 92: EORelationship *relationship = [self detailRelationshipForController:controller]; ! 93: ! 94: // This stuff only works if our dataSource is an EODetailDatabaseDataSource. ! 95: // Otherwise we just pass the operation through ! 96: if (relationship) { ! 97: id masterObject = [dataSource masterObject]; ! 98: id modifiedObject; ! 99: EODatabaseDataSource *masterDataSource = [dataSource masterDataSource]; ! 100: ! 101: // Invoke the magic of the ModelRelExtensions to figure out what to ! 102: // do for this relationship ! 103: modifiedObject = [relationship updateKeysForSourceObject:masterObject destinationObject:object]; ! 104: ! 105: if (modifiedObject == object) { ! 106: modifiedObject = nil; ! 107: } else if (modifiedObject == masterObject) { ! 108: // we need to update this one as well ! 109: NSLog(@"Updating master object '%@'", masterObject); ! 110: if (![masterDataSource updateObject:masterObject]) ! 111: response = EORollbackDataSourceOperation; // put up error? ! 112: } else { ! 113: // link object that we need to insert ! 114: NSLog(@"Inserting link object '%@'", modifiedObject); ! 115: if (![masterDataSource insertObject:modifiedObject]) ! 116: response = EORollbackDataSourceOperation; // put up error? ! 117: } ! 118: ! 119: if (modifiedObject) { ! 120: // do we really need to insert this object? Or did the insert just indicate ! 121: // a reference to an existing object? ! 122: EODatabaseContext *context = [[dataSource databaseChannel] databaseContext]; ! 123: if ([context snapshotForObject:object]) ! 124: response = EODiscardDataSourceOperation; ! 125: } ! 126: } ! 127: if (response == EOPerformDataSourceOperation) ! 128: NSLog(@"Allowing insert of object '%@'", object); ! 129: else { ! 130: NSLog(@"Suppressing insert of object: '%@'", object); ! 131: } ! 132: return response; ! 133: } ! 134: ! 135: - (EODataSourceOperationDelegateResponse)controller:(EOController *)controller ! 136: willDeleteObject:object ! 137: inDataSource:dataSource; ! 138: { ! 139: /*Deletion of detail record may dictate one of the following... ! 140: M Detail Insert rule Delete Types ! 141: PK -> PK D->PK := M->PK delete D to-one ! 142: FK -> PK M->FK := D->PK M->FK=NULL to-one ! 143: PK -> FK D->FK := M->PK Delete D to-many ! 144: Many to many create link obj Delete link to-many ! 145: */ ! 146: EODataSourceOperationDelegateResponse response = EOPerformDataSourceOperation; ! 147: EORelationship *relationship = [self detailRelationshipForController:controller]; ! 148: ! 149: // This stuff only works if our dataSource is an EODetailDatabaseDataSource. ! 150: // Otherwise we just pass the operation through ! 151: if (relationship) { ! 152: EOEntity *entity = [relationship destinationEntity]; ! 153: id masterObject = [dataSource masterObject]; ! 154: id modifiedObject; ! 155: ! 156: // Invoke the magic of the ModelRelExtensions to figure out what to ! 157: // do for this relationship ! 158: modifiedObject = [relationship updateKeysForDeleteOfDestinationObject:object fromSourceObject:masterObject]; ! 159: ! 160: if (modifiedObject == object) { ! 161: // just go ahead and delete this one ! 162: } else if (modifiedObject == masterObject) { ! 163: // we need to update the master to reflect the delete ! 164: // and leave this object alone ! 165: response = EODiscardDataSourceOperation; ! 166: NSLog(@"Updating master object '%@'", masterObject); ! 167: if (![dataSource updateObject:masterObject]) ! 168: response = EORollbackDataSourceOperation; // put up error? ! 169: } else { ! 170: // we need to delete the link object ! 171: // and leave this object alone ! 172: EODatabaseContext *context = [[dataSource databaseChannel] databaseContext]; ! 173: EODatabase *database = [context database]; ! 174: EOEntity *linkEntity = [[entity model] entityForObject:modifiedObject]; ! 175: NSDictionary *pkDict; ! 176: id registeredObject; ! 177: ! 178: response = EODiscardDataSourceOperation; // leave destination object alone ! 179: ! 180: // to delete the object we need to make sure that it's in the uniquing ! 181: // tables. Check if it's already there, and if not, record this one ! 182: pkDict = [modifiedObject valuesForKeys:[linkEntity primaryKeyAttributeNames]]; ! 183: registeredObject = [context objectForPrimaryKey:pkDict entity:linkEntity]; ! 184: if (!registeredObject) { ! 185: [database recordObject:modifiedObject primaryKey:pkDict ! 186: snapshot:pkDict]; ! 187: registeredObject = modifiedObject; ! 188: } ! 189: NSLog(@"Deleting link object '%@'", registeredObject); ! 190: if (![[dataSource masterDataSource] deleteObject:registeredObject]) ! 191: response = EORollbackDataSourceOperation; // put up error? ! 192: } ! 193: } ! 194: if (response == EOPerformDataSourceOperation) ! 195: NSLog(@"Allowing delete of object '%@'", object); ! 196: else { ! 197: NSLog(@"Suppressing delete of object: '%@'", object); ! 198: } ! 199: return response; ! 200: } ! 201: ! 202: - (void)controllerDidSaveToDataSource:(EOController *)controller ! 203: { ! 204: [self updateDetailObjectsForController:controller]; ! 205: } ! 206: ! 207: @end ! 208: ! 209: ! 210: // This is probably evil. ! 211: // This code checks modifications to foreign key properties in an EO, and ! 212: // reassigns any corresponding relationship pointers using methods in ! 213: // ResetRelationships.h. ! 214: // ! 215: // THIS CODE IS NOT ACTUALLY USED IN THE EXAMPLE PROGRAM, but is here as ! 216: // an illustration. ! 217: @implementation SavvyControllerDelegate (ResetRelationshipsForChangedKeyProps) ! 218: static NSArray *changedKeys = nil; ! 219: ! 220: - (NSDictionary *)controller:(EOController *)controller willSaveEdits: (NSDictionary *)edits toObject:object ! 221: { ! 222: // update any related relationship properties (in case we update foreign keys) ! 223: changedKeys = [edits allKeys]; ! 224: return edits; ! 225: } ! 226: ! 227: - (void)controller:(EOController *)controller didSaveToObject:object ! 228: { ! 229: EODatabaseDataSource *ds = (EODatabaseDataSource *)[controller dataSource]; ! 230: EOEntity *entity = [ds entity]; ! 231: ! 232: // update relationships for key changes ! 233: [[ds databaseChannel] updateRelationshipsInObject:object forModifiedAttributes:[entity attributesNamed:changedKeys]]; ! 234: ! 235: // update keys for relationship changes ! 236: { ! 237: int i, count = [changedKeys count]; ! 238: for(i=0; i<count; i++) { ! 239: NSString *key = [changedKeys objectAtIndex:i]; ! 240: EORelationship *relationship = [entity relationshipNamed:key]; ! 241: if (relationship) { ! 242: [relationship updateKeysForSourceObject:object ! 243: destinationObject:[(NSObject *)object valueForKey:key]]; ! 244: } ! 245: } ! 246: } ! 247: } ! 248: @end
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.