Import a magic record (next step)

I took the next step in the title, since this is not the same problem as my previous question, with almost the same name.

I have a Person object.

 Person -------- name - mappedKeyName: FullName email - mappedKeyName: EmailAddress personID - mappedKeyName: Id -------- photos 

And the Photo object.

 Photo -------- image createDate - mappedKeyName: Date photoID - mappedKeyName: Id -------- owner (type Person) - mappedKeyName: UserId - relatedByAttribute: personID 

There are other objects related to Person , and JSON for them - so ...

 { ObjectId : blah, Owner : { Id : 12345asdfg, FullName : Oliver, EmailAddress : oliver@oliver.com } } 

With this JSON, my setup works with import. Any person records that do not exist (with identifier) ​​are created. And everything that exists is updated.

However, the JSON object of the photos is as follows:

 { Id : thisIsThePhotoID, Date : today, UserId : 12345asdfg } 

When objects go down like this, the import of the magic record stops when it hits the user.

Code failure on ...

 - (id) MR_relatedValueForRelationship:(NSRelationshipDescription *)relationshipInfo { NSString *lookupKey = [self MR_lookupKeyForRelationship:relationshipInfo]; return lookupKey ? [self valueForKeyPath:lookupKey] : nil; // it stops here. } 

The value of lookupKey is @ "personID".

A listing of the info relationship at the breakpoint gives ...

 $6 = 0x1fd695e0 (<NSRelationshipDescription: 0x1fd695e0>), name owner, isOptional 0, isTransient 0, entity Photo, renamingIdentifier owner, validation predicates (), warnings (), versionHashModifier (null) userInfo { mappedKeyName = UserId; relatedByAttribute = personID; }, destination entity Person, inverseRelationship photos, minCount 1, maxCount 1, isOrdered 0, deleteRule 1 

I really don't understand why this is not working. I cannot get any reasonable errors.

+3
source share
1 answer

MagicalRecord cannot automatically match relationships with this JSON format:

 { Id : thisIsThePhotoID, Date : today, UserId : 12345asdfg } 

For MagicalRecord to map the relation to the Person object, it also had to be an object in JSON, for example:

 { Id : thisIsThePhotoID, Date : today, User : { UserId : 12345asdfg } } 

Thus, MagicalRecord knows that this is an object, and it will do a corresponding search in your existing database to record Person with the identifier above and match the relationship.

So there are two problems with this. If you cannot change the JSON output, you need to create a category class on Photo , where you manually map this relationship yourself. I will get to this after the second problem.

The second problem is that the aforementioned JSON format assumes that you have already analyzed the users and saved the records in your database. If you do not have MagicalRecord, you will create a new Person record with the identifier above, but since there are no other attributes on this object (note that the UserId key is the only attribute in the dictionary), it will be quite empty and will not contain the name and email address mail. You can always expand your JSON (if you have such an opportunity) to include these attributes in the Person dictionary in the photo dictionary:

 { Id : thisIsThePhotoID, Date : today, User : { UserId : 12345asdfg, FullName : Oliver, EmailAddress : oliver@oliver.com } } 

The JSON payload is pretty small, so it doesn't hurt to do this if you can. In addition, it will create a new Person record if it no longer exists in the database.

And then for manual matching. If you cannot change the JSON to the above format, you need to manually override the relationship mapping, since JSON is not prepared for how MagicalRecord does.

Create a category class for Photo called Photo+Mapping.h/.m . I like to stick +Mapping for them. Then the class should be Photo (Mapping) in the header and implementation file, and you're good to go.

MagicalRecord has several instance methods available for redefinition (see the last part of this article on importing MagicalRecord , written by the author of MagicalRecord), among them import<;attributeName>;: and import<;relationshipName>;: . There are also willImport: didImport: and shouldImport: the class itself, which allows you to override any mapping.

In your case, you can use import<;relationshipName>;: or shouldImport: I took these two because you have a few advantages depending on whether you have already matched all your Person objects, and they are available for matching relationships in the Photo object.

Here are examples of what you can do (you can choose a combination of several of them, if you want, it will not hurt to do this). Note: ALWAYS use the current NSManagedObjectContext when overriding the display (easily accessible with MagicalRecord via self.managedObjectContext ), otherwise you will run into context problems.

Be sure to import Person:

 #import "Photo+Mapping.h" #import "Person.h" // Assuming you only want to import the Photo object if you already have a Person stored this is a great method to tell MagicalRecord whether to continue with importing or not -(BOOL)shouldImport:(id)data { Person *person = [Person findFirstByAttribute:data[@"UserId"] value:@"personID" inContext:self.managedObjectContext]; if (!person) { // no Person object exists so don't import the Photo object - again this is up to you since you might want to create the record if not return NO; } // you can set the relationship here (you might as well) or use the importPerson: method below (doing a second lookup, which is unnecessary at this point) [self setPerson:person]; return YES; } // If you use this method you're doing the lookup to check whether a record exist when MagicalRecord is trying to map the Person relationship -(void)importPerson:(id)data { Person *person = [Person findFirstByAttribute:data[@"UserId"] value:@"personID" inContext:self.managedObjectContext]; if (!person) { // if no Person record exists for the associated UserId, you should create one (or not - if you choose not to, it wise to throw away this Photo object) person = [Person createInContext:self.managedObjectContext]; [person setPersonID:data[@"UserId"]]; } // set the relationship [self setPerson:person]; } // finally you can also use the following method when MagicalRecord is done mapping and get rid of the Photo object if the Person relationship is nil: -(void)didImport:(id)data { if (!self.person) { [self deleteInContext:self.managedObjectContext]; } } 

Hope this helps! Let me know if you have any questions.

+17
source

All Articles