farmOS / field-kit

A modular, offline-first companion app to farmOS.
https://farmOS.org
GNU General Public License v3.0
62 stars 39 forks source link

Provide `link()` method to checkout and revise related entities. #505

Closed jgaehring closed 1 year ago

jgaehring commented 2 years ago

In keeping with farmOS/farmOS.js#62 (and dependent upon it), this will probably be the best way to get quantities, files and assets related to a particular log.

jgaehring commented 1 year ago

Despite having implemented the corresponding include feature in farmOS/farmOS.js#75, I decided to go a very different route in Field Kit, adding a link() function, which essentially checks out a single entity (one-to-one) or collection of entities (one-to-many) that is reactively "linked" to the original entity's corresponding relationship field. So I've gone ahead and changed the title of this issue accordingly.

To take an example straight from my current fork & branch of the Tasks.vue component, given you have a log, assigned below as current:

https://github.com/farmOS/field-kit/blob/5bcd847f15e9233196de58ce937835a6c0b6b9a2/packages/field-module-tasks/src/components/Tasks.vue#L56-L59

You can then checkout a collection of all the quantities related to that log on the quantity field:

https://github.com/farmOS/field-kit/blob/5bcd847f15e9233196de58ce937835a6c0b6b9a2/packages/field-module-tasks/src/components/Tasks.vue#L72

To add a new quantity to the log, you just call revise() on the original log (in the below example, update() is essentially just a wrapper around that), appending a resource identifier with a null id to the quantity field:

https://github.com/farmOS/field-kit/blob/5bcd847f15e9233196de58ce937835a6c0b6b9a2/packages/field-module-tasks/src/components/Tasks.vue#L73-L78

Because they're reactively linked, the quantities collection will automatically get a reference to the same quantity, with the same type and id and default values for all other fields, appended to the same position of the collection as the log's quantity field.

Then if you want to update an individual quantity itself, you just use the revise() function, once again, but on the quantity reference:

https://github.com/farmOS/field-kit/blob/5bcd847f15e9233196de58ce937835a6c0b6b9a2/packages/field-module-tasks/src/components/Tasks.vue#L79-L85

Finally, and this was the tricky part, to save all the changes for both the log and any added or modified quantities related to it, you just have to commit the log itself:

https://github.com/farmOS/field-kit/blob/5bcd847f15e9233196de58ce937835a6c0b6b9a2/packages/field-module-tasks/src/components/Tasks.vue#L99

Essentially, the log reference keeps a running list of dependencies for each of its fields, and so once the time comes to commit any changes, it checks its dependencies for any outstanding changes, commits those first, and then commits the log. This was pretty tricky, especially because Drupal's revision history module requires a whole bunch of extra Drupal metadata, a requirement that honestly makes no sense to me, but :shrug: ...

https://github.com/farmOS/field-kit/blob/5bcd847f15e9233196de58ce937835a6c0b6b9a2/packages/field-kit/src/entities/index.js#L453-L471

So I'm really glad I could abstract that nasty bit of boilerplate away, and each and every field module won't have to negotiate that exchange on their own

The best part about this, at least I hope :crossed_fingers:, is that it's a pattern that should work well for most relationships between entities. Images and files will still need a bit of their own added logic, which will be the entire concern for the next release, alpha.9 (Observations Module), but hopefully it should make it much easier to actually link the file entity itself to the relevant log(s).

jgaehring commented 1 year ago

Resolved by fc2337f.