modelQueryProps.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. const { isObject } = require('../utils/objectUtils');
  2. const { defineNonEnumerableProperty } = require('./modelUtils');
  3. const { isKnexRaw, isKnexQueryBuilder } = require('../utils/knexUtils');
  4. const QUERY_PROPS_PROPERTY = '$$queryProps';
  5. // Removes query properties from `json` and stores them into a hidden property
  6. // inside `model` so that they can be later merged back to `json`.
  7. function splitQueryProps(model, json) {
  8. const keys = Object.keys(json);
  9. if (hasQueryProps(json, keys)) {
  10. const queryProps = {};
  11. const modelProps = {};
  12. for (let i = 0, l = keys.length; i < l; ++i) {
  13. const key = keys[i];
  14. const value = json[key];
  15. if (isQueryProp(value)) {
  16. queryProps[key] = value;
  17. } else {
  18. modelProps[key] = value;
  19. }
  20. }
  21. defineNonEnumerableProperty(model, QUERY_PROPS_PROPERTY, queryProps);
  22. return modelProps;
  23. } else {
  24. return json;
  25. }
  26. }
  27. function hasQueryProps(json, keys) {
  28. for (let i = 0, l = keys.length; i < l; ++i) {
  29. if (isQueryProp(json[keys[i]])) {
  30. return true;
  31. }
  32. }
  33. return false;
  34. }
  35. function isQueryProp(value) {
  36. if (!isObject(value)) {
  37. return false;
  38. }
  39. return (
  40. isKnexQueryBuilder(value) ||
  41. isKnexRaw(value) ||
  42. value.isObjectionQueryBuilderBase ||
  43. typeof value.toKnexRaw === 'function'
  44. );
  45. }
  46. // Merges and converts `model`'s query properties into `json`.
  47. function mergeQueryProps(model, json, knex) {
  48. json = convertExistingQueryProps(json, knex);
  49. json = convertAndMergeHiddenQueryProps(model, json, knex);
  50. return json;
  51. }
  52. // Converts the query properties in `json` to knex raw instances.
  53. // `json` may have query properties even though we removed them.
  54. // For example they may have been added in lifecycle hooks.
  55. function convertExistingQueryProps(json, knex) {
  56. const keys = Object.keys(json);
  57. for (let i = 0, l = keys.length; i < l; ++i) {
  58. const key = keys[i];
  59. const value = json[key];
  60. if (isQueryProp(value)) {
  61. json[key] = queryPropToKnexRaw(value, knex);
  62. }
  63. }
  64. return json;
  65. }
  66. // Converts and merges the query props that were split from the model
  67. // and stored into QUERY_PROPS_PROPERTY.
  68. function convertAndMergeHiddenQueryProps(model, json, knex) {
  69. const queryProps = model[QUERY_PROPS_PROPERTY];
  70. if (!queryProps) {
  71. // The model has no query properties.
  72. return json;
  73. }
  74. const modelClass = model.constructor;
  75. const keys = Object.keys(queryProps);
  76. for (let i = 0, l = keys.length; i < l; ++i) {
  77. const key = keys[i];
  78. const queryProp = queryPropToKnexRaw(queryProps[key], knex);
  79. json[modelClass.propertyNameToColumnName(key)] = queryProp;
  80. }
  81. return json;
  82. }
  83. // Converts a query property into a knex `raw` instance.
  84. function queryPropToKnexRaw(queryProp, knex) {
  85. if (!queryProp) {
  86. return queryProp;
  87. }
  88. if (queryProp.isObjectionQueryBuilderBase) {
  89. return buildObjectionQueryBuilder(queryProp);
  90. } else if (isKnexRawConvertable(queryProp)) {
  91. return buildKnexRawConvertable(queryProp, knex);
  92. } else {
  93. return queryProp;
  94. }
  95. }
  96. function buildObjectionQueryBuilder(builder) {
  97. return builder.build();
  98. }
  99. function buildKnexRawConvertable(converable, knex) {
  100. if (!knex) {
  101. throw new Error(
  102. 'toDatabaseJson called without a knex instance for a model with query properties'
  103. );
  104. }
  105. return converable.toKnexRaw(knex);
  106. }
  107. function isKnexRawConvertable(queryProp) {
  108. return typeof queryProp.toKnexRaw === 'function';
  109. }
  110. module.exports = {
  111. splitQueryProps,
  112. mergeQueryProps
  113. };