Nicholas, I upvoted your answer. Here's what I've gone with so far. Let me know if you think I'm on the right path.
# URI's Explained for base "rest_api" (controller)
# collections resource
/collections/{id}
# contacts resource
/contacts/{id}
# collections contacts subresource
/collections/{collection_id}/contacts/{id}
Routed to
==> /contacts/id/{id}/collection_id/{collection_id}
In Restful-terms it helps a lot in not thinking of SQL and joins but more into collections, sub-collections and traversal.
Some examples:
# getting contact 3 who is in collection 1
# or simply checking whether contact 3 is in that collection (200 vs. 404)
GET /collections/1/contacts/3 ==> /contacts/id/3/collection_id/1
# getting contact 3 who is also in collection 3
GET /collections/3/contacts/3 ==> /contacts/id/3/collection_id/3
# adding contact 3 also to collection 2 (RELATIONSHIP)
PUT /collections/2/contacts/3 ==> /contacts/id/3/collection_id/2
# getting all collections of contact 3
GET /contacts/3/collections
# remove contact 3 from collection 1 (RELATIONSHIP)
DELETE /collections/1/contacts/3
# collection has a new contact, who is not yet added
POST /contacts
# from payload you get back the contacts insert id (44), now place contact in collection 1 (RELATIONSHIP)
PUT /collections/1/contacts/44
As you see I'm not using POST for placing contacts to collections but PUT, which handles the n:n relationship of contacts to collections.
PHP CodeIgniter Routes used are as follows. I'm really not sure if this is needed, but allows for the pretty URI's mentions above.
// Collections
$route['rest_api/collections/(:num)'] = "rest_api/collections/id/$1";
// Collection Contacts Subresource
$route['rest_api/collections/(:num)/contacts'] = "rest_api/contacts/collection_id/$1";
$route['rest_api/collections/(:num)/contacts/(:num)'] = "rest_api/contacts/id/$2/collection_id/$1";
Some of the rest_api controller methods get a little long, but it seems to work. I have to get the URI request params and the query string parmas, if used. Phil's CodeIgniter REST API had to be tweeked a bit to make sure to get the URI params when doing a PUT or DELETE request, as the values only existed in the $this->get() method; strange.