Annotation of Examples/EnterpriseObjects/MasteringDetails/EOFExtensions.subproj/SavvyControllerDelegate.m, revision 1.1.1.1

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.