wvteijlingen / Spine

A Swift library for working with JSON:API APIs. It supports mapping to custom model classes, fetching, advanced querying, linking and persisting.
MIT License
264 stars 109 forks source link

Fetching relationships objects #127

Open markst opened 7 years ago

markst commented 7 years ago

Let's say I have an object Timesheet which has a relationship Entries of 100 objects.

Our API expects a URI as: /timesheets/1234/entries

Is there a way for Spine to generate the appropriate URL without concating a URL as so:

var query = Query(resourceType: Entry.self, path: "/timesheets/\(timesheet.id!)/entries")
markst commented 7 years ago

It seems that there are alternatives such as:

var query = Query(resourceType: Entry.self)
query.whereProperty("timesheet-id", equalTo: self.timesheet.id)

or

var query = Query(resourceType: Timesheet.self)
query.include("entries") // Sideload relationships

Which both produce URL's which can't be paginated.

markst commented 7 years ago

SaveOperation can update the relationships using the Resource linkage data. However it would be good for this to be possible for fetching linkage data too.

Or perhaps being able to get that URL as: router.urlForRelationship(timesheet.relationships["entries"], ofResource: timesheet)

...

wvteijlingen commented 7 years ago

Does Query(resourceType: Entry.self, resourceCollection: self.timesheet.entries) work for you? The query is then initialised with the related link from the JSON:API response. This should point to the entries of that timesheet as long as your back-end includes it in the timesheet response.

PS: router.urlForRelationship returns the self URL of the relationship, which is not what you need.

markst commented 7 years ago

Thanks @wvteijlingen. This has worked for instances when the relationship includes the appropriate links.

However there are instances when we have the ids and type of a relationship defined & we'd like to resolve a resources relationship.

 },
 "relationships":{  
    "skills":{  
       "data":[  
          {  
             "id":"59451a73-1e14-464d-a4a0-7136ec2bf644",
             "type":"skills"
          },
          {  
             "id":"4c43fb46-a5d1-471a-ad87-a4289bccc939",
             "type":"skills"
          },
          {  
             "id":"a7e91fb3-8113-434b-a8e4-4d1f1e169992",
             "type":"skills"
          },

If I expose the ResourceIdentifier type & id I can generate of required ids & fetch:

var skillIds: [String]
skillIds = selectedJobCategories.reduce([], { (results, jobCategory) -> [String] in
    var results = results

    results.append(contentsOf: jobCategory.skills!.linkage!.reduce([], { (results, resourceIdentifier) -> [String] in
        var results = results
        results.append(resourceIdentifier.id)
        return results
    }))

    return results
})
SpineSingleton.find(skillIds, ofType: Skill.self).onSuccess { [weak self] in

It would be sweet if this were available as a query & resolve the relationships on the fetching resource...?

markst commented 7 years ago

This is infact a better alternative for us:

var query = Query(resourceType: Skill.self)
selectedJobCategories.forEach { (jobCategory) in
    query.whereRelationship("jobCategory", isOrContains: jobCategory)
}
SpineSingleton.find(query).onSuccess { [weak self] in
ryancarlson commented 7 years ago

I hit a similar issue this morning, but to solve my case I just need access to the ResourceIdentifier struct's id property. Does anyone know why that is made to be private?