|
|
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.