ApiModel icon indicating copy to clipboard operation
ApiModel copied to clipboard

Mapping Active Record Associations

Open markst opened this issue 9 years ago • 4 comments

Our API's return object associated objects. Is it possible for multiple object types to be mapped for one endpoint?

markst avatar Mar 30 '16 02:03 markst

i.e:

{
  "jobs": [
    {
      "state": "pending",
      "id": "d4f2eb03-3510-4c17-8c95-f0a5c630899a",
      "price_cents": 1100,
      "notes": null,
      "created_at": "2016-01-06T02:40:53.393Z",
      "updated_at": "2016-01-06T02:40:53.513Z",
      "conversation_id": null,
      "total_duration": 779,
      "total_distance": 2919,
      "received_by": null,
      "signature_url": null,
      "delivered_at": null,
      "distance_travelled": null,
      "time_taken": null,
      "customer_id": "80ab6ca2-eb63-4ce7-b427-bf14a6163f23",
      "customer_rated": null,
      "fetcher_rated": null,
      "payment_status": "pending",
      "picked_up_at": null,
      "promotional_code_used": false,
      "pickup_id": "18c8edc7-e03f-4734-a9e5-e4c1da5395ed",
      "dropoff_id": "fdff59cc-4f4f-4dfd-bfb0-751fd5a1a75b",
      "offer_ids": [
        "aafc8cce-64e5-44d6-8eb6-d5ff53aa8041"
      ],
      "item_type_id": "ce25c0bc-045c-4b3a-aaad-c8cc466bc32b",
      "credit_card_id": "3ebb282d-dd90-4ccf-9b75-3e22f325a6b6"
    }
  ],
  "destinations": [
    {
      "id": "18c8edc7-e03f-4734-a9e5-e4c1da5395ed",
      "job_id": "d4f2eb03-3510-4c17-8c95-f0a5c630899a",
      "type": "Pickup",
      "created_at": "2016-01-06T02:40:53.396Z",
      "updated_at": "2016-01-06T02:40:53.396Z",
      "coordinates": {
        "lat": -37.81007400153584,
        "lon": 144.9639367684722
      },
      "address": "325 Swanston St, Melbourne",
      "suburb_name": "MELBOURNE",
      "postcode": "3000",
      "contact_id": "e6941a6d-d95f-45ee-971a-00d2724dee65",
      "suburb_id": "40c02201-b6d3-493c-99c8-7b52daff010e"
    },
],
  "suburbs": [
    {
      "id": "40c02201-b6d3-493c-99c8-7b52daff010e",
      "name": "MELBOURNE",
      "postcode": "3000",
      "display_name": "Melbourne, 3000"
    },
  ]
}

markst avatar Mar 30 '16 02:03 markst

also what is recommendation for mapping relationships?

markst avatar Mar 30 '16 02:03 markst

Currently it's not possible to return multiple objects per response in that way, however, if you were to refactor the response to look like this: (ie nested)

{
  "destination": {
    "id": "18c8edc7-e03f-4734-a9e5-e4c1da5395ed",
    "job": {
      "state": "pending",
      "id": "d4f2eb03-3510-4c17-8c95-f0a5c630899a",
      "price_cents": 1100,
      "notes": null,
      "created_at": "2016-01-06T02:40:53.393Z",
      "updated_at": "2016-01-06T02:40:53.513Z",
      "conversation_id": null,
      "total_duration": 779,
      "total_distance": 2919,
      "received_by": null,
      "signature_url": null,
      "delivered_at": null,
      "distance_travelled": null,
      "time_taken": null,
      "customer_id": "80ab6ca2-eb63-4ce7-b427-bf14a6163f23",
      "customer_rated": null,
      "fetcher_rated": null,
      "payment_status": "pending",
      "picked_up_at": null,
      "promotional_code_used": false,
      "pickup_id": "18c8edc7-e03f-4734-a9e5-e4c1da5395ed",
      "dropoff_id": "fdff59cc-4f4f-4dfd-bfb0-751fd5a1a75b",
      "offer_ids": [
        "aafc8cce-64e5-44d6-8eb6-d5ff53aa8041"
      ],
      "item_type_id": "ce25c0bc-045c-4b3a-aaad-c8cc466bc32b",
      "credit_card_id": "3ebb282d-dd90-4ccf-9b75-3e22f325a6b6"
    },
    "type": "Pickup",
    "created_at": "2016-01-06T02:40:53.396Z",
    "updated_at": "2016-01-06T02:40:53.396Z",
    "coordinates": {
      "lat": -37.81007400153584,
      "lon": 144.9639367684722
    },
    "address": "325 Swanston St, Melbourne",
    "suburb_name": "MELBOURNE",
    "postcode": "3000",
    "contact_id": "e6941a6d-d95f-45ee-971a-00d2724dee65",
    "suburb": {
      "id": "40c02201-b6d3-493c-99c8-7b52daff010e",
      "name": "MELBOURNE",
      "postcode": "3000",
      "display_name": "Melbourne, 3000"
    }
  }
}

And your model had the relations a-la Realm:

class Destination: Object, ApiModel {
    dynamic var job: Job?
    dynamic var suburb: Suburb?

    // ... boilerplate

    static func fromJSONMapping() -> JSONMapping {
        return [
            "id": ApiIdTransform(),
            "job": ModelTransform<Job>(),
            "suburb": ModelTransform<Suburb>()
            // ... More stuff
        ]
    }
}

It should get picked up by the call:

Api<Destination>.get("destinations/abcde-1234") { response in
   if let destination = response.object {
        print("Destination: \(destination.id)")
        print("Suburb: \(destination.suburb?.name)");
   }
}

erkie avatar Mar 30 '16 09:03 erkie

@erkie We have a couple of cases in which associated objects help to keep JSONs small. For example, consider this case:

{
  "cars": [
    {
      "model": "Ford",
      "user": {
        "username": "exampleuser",
        "email": "[email protected]"
      }
    },
    {
      "model": "Chevrolet",
      "user": {
        "username": "exampleuser",
        "email": "[email protected]"
      }
    },
    {
      "model": "Renault",
      "user": {
        "username": "exampleuser",
        "email": "[email protected]"
      }
    }
  ]
}

That JSON contains three times the same user, because it's shared among cars. Using associated objects, it becomes smaller by just having one occurrence of the user:

{
  "cars": [
    {
      "model": "Ford",
      "user_id": "1"
    },
    {
      "model": "Chevrolet",
      "user_id": "1"
    },
    {
      "model": "Renault",
      "user_id": "1"
    }
  ],
  "users": [
    {
      "id": "1",
      "username": "exampleuser",
      "email": "[email protected]"
    }
  ]
}

Note that this example is quite basic; if user was a bigger object, or there were other nested objects, it could produce significantly bigger JSONs.

Do you think there's a way to handle that scenario?

ezescaruli avatar Apr 01 '16 03:04 ezescaruli