HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
UT_ORMManyToManyField.h
Go to the documentation of this file.
1 /*
2  * PROPRIETARY INFORMATION. This software is proprietary to
3  * Side Effects Software Inc., and is not to be reproduced,
4  * transmitted, or disclosed in any way without written permission.
5  *
6  * NAME: UT_ORMManyToManyField.h
7  *
8  * COMMENTS:
9  *
10  * The UT_ORMManyToManyField is a powerful field that lets models define
11  * a M2M relationship between each other. The ORM system will generate the
12  * corresponding through table for the relationship. Note that objects are
13  * loaded on an as needed bases so theres no performance cost to defining
14  * these objects on models.
15  *
16  * See UT_SQLORM for more information on the ORM system.
17  *
18  * Example:
19  *
20  * The below example highlights how easy it is to work with M2M
21  * relationships through the ORM system as all the complex work is
22  * auto generated and the developer can focus on the actual task at
23  * hand.
24  *
25  * class User : public UT_ORMModel<User>
26  * {
27  * public:
28  * class Meta : public UT_ORMModelMeta
29  * {
30  * void doBuild() override
31  * {
32  * // Since the m2m isnt an actual column on the db the field
33  * // name isnt as important. It mainly lets you search via field
34  * // name. Using UT_ORMManyToManyField makes that not necessary
35  * // as it does that for you.
36  * //
37  * // The "user" is the related name. This is the name that is used
38  * // when User meta info is added to Group meta info. This allows
39  * // for searches like Group.filter({`user__in`}) to return all
40  * // groups with users in the provided list.
41  * addField("m2m_group", &User::myGroups, "user");
42  * }
43  * };
44  *
45  * UT_ORMManyToManyField<User, Group> myGroups;
46  * };
47  *
48  * int main()
49  * {
50  * UT_Array<Group> groups = user.all(ec);
51  * for (auto&& group : groups)
52  * {
53  * // ....
54  * }
55  * }
56  */
57 
58 #ifndef __UT_ORMMANYTOMANYFIELD_H__
59 #define __UT_ORMMANYTOMANYFIELD_H__
60 
61 #include "UT_API.h"
62 
63 #include "UT_Array.h"
64 #include "UT_Function.h"
65 #include "UT_ORMField.h"
66 #include "UT_ORMMigrationBuilder.h"
67 #include "UT_ORMModelMeta.h"
68 #include "UT_ORMOperation.h"
69 #include "UT_SQLORM.h"
70 #include "UT_UniquePtr.h"
71 #include "UT_ORMError.h"
72 
74 {
75 public:
76  UT_ORMManyToManyManager() = default;
78 
79  explicit inline operator bool() const { return isValid(); }
80 
81  inline bool isValid() const
82  {
83  return myThrough != nullptr;
84  }
85 
86  template <typename LObjT, typename RObjT>
87  void add(LObjT& obj, const RObjT& new_list_obj, UT_ErrorCode& ec)
88  {
89  UT_Array<RObjT> objs;
90  objs.emplace_back(new_list_obj);
91  add(obj, objs, ec);
92  }
93 
94  template <typename LObjT, typename RObjT>
95  void add(LObjT& obj, const UT_Array<RObjT>& new_list_obj, UT_ErrorCode& ec)
96  {
97  if (isValid())
98  {
99  const UT_ORMFieldColumn* source_obj_field
100  = myThrough->findForeignRelatedField(mySourceFieldName);
101  const UT_ORMFieldColumn* target_obj_field
102  = myThrough->findForeignRelatedField(myTargetFieldName);
103  if (source_obj_field != nullptr && target_obj_field != nullptr)
104  {
105  const UT_ORMModelMeta& meta = obj.meta();
106  if (UT_AutoSqlTransaction tran(
107  meta.db(), [ec]() { return !ec; },
108  /*savepoint=*/false);
109  tran)
110  {
111  for (auto&& new_item : new_list_obj)
112  {
113  UT_ORMQuerySet qset(*myThrough);
116  mySourceFieldName, source_obj_field,
117  &obj),
119  myTargetFieldName, target_obj_field,
120  &new_item)});
121  ec = result.getError();
122 
123  if (ec)
124  break;
125  }
126  }
127  else
128  {
129  ec = tran.entryFailure();
130  }
131  }
132  }
133  else
134  {
136  }
137  }
138 
139  template <typename LObjT, typename RObjT>
140  void remove(LObjT& obj, const RObjT& obj_to_remove, UT_ErrorCode& ec)
141  {
142  if (isValid())
143  {
144  const UT_ORMFieldColumn* target_obj_field
145  = myThrough->findForeignRelatedField(myTargetFieldName);
146 
147  if (target_obj_field != nullptr)
148  {
149  using r_pk_obj_t = typename RObjT::Meta::primary_key_t;
150  r_pk_obj_t value;
151  target_obj_field->adapter()->getValue(obj_to_remove, value, ec);
152  if (ec)
153  {
154  return;
155  }
156 
158  values.emplace_back(value);
159  removeIds(obj, values, ec);
160  }
161  }
162  else
163  {
165  }
166  }
167 
168  template <typename LObjT, typename RObjValueT>
169  void removeIds(
170  LObjT& obj,
172  UT_ErrorCode& ec)
173  {
174  if (isValid())
175  {
176  const UT_ORMFieldColumn* source_obj_field
177  = myThrough->findForeignRelatedField(mySourceFieldName);
178 
179  UT_StringHolder remove_in_str;
180  remove_in_str.format("{}__in", myTargetFieldName);
181 
182  UT_ORMQuerySet qset(*myThrough);
184  mySourceFieldName, source_obj_field, &obj),
185  UT::orm::FilterArg(remove_in_str, values)})
186  .remove();
187  }
188  else
189  {
191  }
192  }
193 
194  template <typename LObjT, typename RObjT>
195  void set(
196  LObjT& obj,
197  const UT_Array<RObjT>& new_items,
198  bool do_clear,
199  UT_ErrorCode& ec)
200  {
201  if (isValid())
202  {
203  const UT_ORMModelMeta& meta = obj.meta();
204  if (UT_AutoSqlTransaction tran(
205  meta.db(), [&ec]() { return !ec; },
206  /*savepoint=*/false);
207  tran)
208  {
209  if (do_clear)
210  {
211  clear(obj, ec);
212  if (ec)
213  return;
214  add(obj, new_items, ec);
215  }
216  else
217  {
218  const UT_ORMFieldColumn* source_obj_field
219  = myThrough->findForeignRelatedField(
220  mySourceFieldName);
221  const UT_ORMFieldColumn* target_obj_field
222  = myThrough->findForeignRelatedField(
223  myTargetFieldName);
224  if (source_obj_field != nullptr && target_obj_field)
225  {
226  using r_pk_field_t =
227  typename RObjT::Meta::primary_key_t;
228  UT_ORMQuerySet old_qset(*myThrough);
230  = old_qset
232  mySourceFieldName,
233  source_obj_field, &obj)})
234  .values({myTargetFieldName})
235  .get();
236 
237  UT_Array<r_pk_field_t> old_ids;
238  if (result.getError())
239  {
240  ec = result.getError();
241  return;
242  }
243 
244  for (auto&& value : result)
245  {
246  r_pk_field_t fk_v
247  = value.stmt().get<r_pk_field_t>(0);
248  old_ids.emplace_back(fk_v);
249  }
250 
251  UT_Array<RObjT> add_items;
252  for (auto&& new_item : new_items)
253  {
254  r_pk_field_t fk_v;
255  target_obj_field->adapter()->getValue(
256  new_item, fk_v, ec);
257  if (ec)
258  {
259  return;
260  }
261 
262  bool found = false;
263  for (exint i = old_ids.size(); i-- > 0;)
264  {
265  if (old_ids[i] == fk_v)
266  {
267  found = true;
268  old_ids.removeIndex(i);
269  break;
270  }
271  }
272 
273  if (!found)
274  {
275  add_items.emplace_back(new_item);
276  }
277  }
278 
279  removeIds(obj, old_ids, ec);
280  add(obj, add_items, ec);
281  }
282  }
283  }
284  else
285  {
286  ec = tran.entryFailure();
287  }
288  }
289  else
290  {
292  }
293  }
294 
295  template <typename LObjT>
296  void clear(LObjT& obj, UT_ErrorCode& ec)
297  {
298  if (isValid())
299  {
300  const UT_ORMFieldColumn* source_obj_field
301  = myThrough->findForeignRelatedField(mySourceFieldName);
302  if (source_obj_field != nullptr)
303  {
304  UT_ORMQuerySet qset(*myThrough);
306  = qset.filter({UT::orm::FilterArg(
307  mySourceFieldName,
308  source_obj_field, &obj)})
309  .remove();
310 
311  ec = result.getError();
312  }
313  }
314  else
315  {
317  }
318  }
319 
320  template <typename LObjT, typename RObjT>
321  UT_Array<RObjT> all(LObjT& obj, UT_ErrorCode& ec, const UT_ORMModelMeta& rmeta) const
322  {
323  UT_ORMQuerySet qset = all<LObjT, RObjT>(obj, rmeta);
325  ec = result.getError();
326 
327  UT_Array<RObjT> items;
328  if (ec)
329  return items;
330 
331  for (auto&& value : result)
332  {
333  RObjT obj;
334  if (value.load(obj, ec))
335  items.emplace_back(obj);
336  else
337  return items;
338  }
339  return items;
340  }
341 
342  template <typename LObjT, typename RObjT>
343  UT_ORMQuerySet all(LObjT& obj, const UT_ORMModelMeta& rmeta) const
344  {
345  UT_ORMQuerySet qset(rmeta);
346  if (isValid())
347  {
348  const UT_ORMFieldColumn* source_obj_field
349  = myThrough->findForeignRelatedField(mySourceFieldName);
350 
351  if (source_obj_field != nullptr)
352  {
353  UT_StringHolder filter_name;
354  filter_name.format(
355  "{}__{}", myQueryFieldName, source_obj_field->name());
356  UT_ORMQuerySet qset(rmeta);
357  return qset.filter({UT::orm::FilterArg(
358  filter_name, source_obj_field, &obj)});
359  }
360  else
361  {
363  }
364  }
365  else
366  {
367  qset.error(
369  }
370  return qset;
371  }
372 
373  static UT_ORMManyToManyManager lookupM2MManager(
374  const UT_ORMModelMeta& meta,
375  const UT_ORMModelMeta& other_meta,
376  const UT_StringHolder& field_name);
377 
378 private:
380  UT_StringHolder myQueryFieldName;
381  UT_StringHolder mySourceFieldName;
382  UT_StringHolder myTargetFieldName;
383 };
384 
385 template <typename Owner, typename OtherModel>
387 {
388 public:
390  Owner& owner,
392  const UT_ORMModelMeta& rmeta = OtherModel::metaInfo())
393  : myOwner(owner), myFieldName(field_name), myRightMeta(rmeta)
394  {
395  }
396 
397  void add(const OtherModel& rmodel, UT_ErrorCode& ec);
399  UT_ORMQuerySet all() const;
400  void remove(const OtherModel& model, UT_ErrorCode& ec);
401  void clear(UT_ErrorCode& ec);
402  void addBulk(const UT_Array<OtherModel>& rmodels, UT_ErrorCode& ec);
403  void set(
404  const UT_Array<OtherModel>& rmodels,
405  UT_ErrorCode& ec,
406  bool do_clear = false);
407 
408  void setOwner(Owner& owner) { myOwner = owner; }
409 
410 private:
411  UT_ORMManyToManyManager& manager()
412  {
413  // Make sure we lazy load the manager. Theres no need to find the
414  // manager if its never actually used.
415  if (!myHasLookedForManager)
416  {
417  myHasLookedForManager = true;
419  myOwner.get().meta(), myRightMeta, myFieldName);
420  }
421  return myManyManager;
422  }
423  const UT_ORMManyToManyManager& manager() const
424  {
425  return SYSconst_cast(this)->manager();
426  }
427 
428  bool myHasLookedForManager = false;
429  UT_ORMManyToManyManager myManyManager;
430  UT_StringHolder myFieldName;
431  std::reference_wrapper<Owner> myOwner;
432  const UT_ORMModelMeta& myRightMeta;
433 };
434 
435 template <typename Owner, typename Model>
436 void
438 {
439  return manager().template add<Owner, Model>(myOwner, rmodel, ec);
440 }
441 
442 template <typename Owner, typename Model>
443 void
445  const UT_Array<Model>& rmodels,
446  UT_ErrorCode& ec)
447 {
448  return manager().template add<Owner, Model>(myOwner, rmodels, ec);
449 }
450 
451 template <typename Owner, typename Model>
454 {
455  return manager().template all<Owner, Model>(myOwner, ec, myRightMeta);
456 }
457 
458 template <typename Owner, typename Model>
461 {
462  return manager().template all<Owner, Model>(myOwner, myRightMeta);
463 }
464 
465 template <typename Owner, typename Model>
466 void
468  const Model& model,
469  UT_ErrorCode& ec)
470 {
471  return manager().template remove<Owner, Model>(myOwner, model, ec);
472 }
473 
474 template <typename Owner, typename Model>
475 void
477 {
478  return manager().template clear<Owner>(myOwner, ec);
479 }
480 
481 template <typename Owner, typename OtherModel>
482 void
484  const UT_Array<OtherModel>& items,
485  UT_ErrorCode& ec,
486  bool do_clear)
487 {
488  return manager().template set<Owner, OtherModel>(
489  myOwner, items, do_clear, ec);
490 }
491 
492 #endif // __UT_ORMMANYTOMANYFIELD_H__
void getValue(const Cls &obj, MemberT &member_value, UT_ErrorCode &ec)
Definition: UT_ORMField.h:85
const UT_StringHolder & name() const
Definition: UT_ORMColumn.h:76
void clear(UT_ErrorCode &ec)
UT_ORMQuerySetResult error(UT_ErrorCode ec)
void set(LObjT &obj, const UT_Array< RObjT > &new_items, bool do_clear, UT_ErrorCode &ec)
GLsizei const GLfloat * value
Definition: glcorearb.h:824
SYS_FORCE_INLINE T * SYSconst_cast(const T *foo)
Definition: SYS_Types.h:136
int64 exint
Definition: SYS_Types.h:125
void removeIds(LObjT &obj, const UT_Array< RObjValueT > &values, UT_ErrorCode &ec)
exint removeIndex(exint index)
Definition: UT_Array.h:375
void remove(const OtherModel &model, UT_ErrorCode &ec)
static UT_ORMManyToManyManager lookupM2MManager(const UT_ORMModelMeta &meta, const UT_ORMModelMeta &other_meta, const UT_StringHolder &field_name)
#define UT_API
Definition: UT_API.h:14
UT_ORMManyToManyField(Owner &owner, const UT_StringHolder &field_name=UT_StringHolder::theEmptyString, const UT_ORMModelMeta &rmeta=OtherModel::metaInfo())
**But if you need a result
Definition: thread.h:622
UT_ORMQuerySet filter(const std::initializer_list< UT::orm::FilterArg > &args)
exint size() const
Definition: UT_Array.h:653
OutGridT const XformOp bool bool
UT_ORMQuerySet all() const
UT_IORMFieldAdapter * adapter()
Definition: UT_ORMField.h:134
exint emplace_back(S &&...s)
Definition: UT_ArrayImpl.h:769
std::shared_ptr< T > UT_SharedPtr
Wrapper around std::shared_ptr.
Definition: UT_SharedPtr.h:36
static const UT_StringHolder theEmptyString
UT_ORMQuerySet all(LObjT &obj, const UT_ORMModelMeta &rmeta) const
void addBulk(const UT_Array< OtherModel > &rmodels, UT_ErrorCode &ec)
const UT_ErrorCode & getError() const
void set(const UT_Array< OtherModel > &rmodels, UT_ErrorCode &ec, bool do_clear=false)
size_t format(const char *fmt, const Args &...args)
Format a string using the same formatting codes as UTformat.
std::error_code UT_ErrorCode
Definition: UT_ErrorCode.h:20
auto UTmakeErrorCode(EnumT e) -> decltype(make_error_code(e))
Make a UT_ErrorCode based on the provided namespaced enum class.
Definition: UT_ErrorCode.h:29
void add(LObjT &obj, const UT_Array< RObjT > &new_list_obj, UT_ErrorCode &ec)
UT_ORMQuerySetResult get()
void add(const OtherModel &rmodel, UT_ErrorCode &ec)
GLenum GLsizei GLsizei GLint * values
Definition: glcorearb.h:1602
UT_ORMQuerySetResult create(const std::initializer_list< UT::orm::FilterArg > &args, bool ignore_conflicts=false, bool no_return=false)
void add(LObjT &obj, const RObjT &new_list_obj, UT_ErrorCode &ec)
void clear(LObjT &obj, UT_ErrorCode &ec)
void setOwner(Owner &owner)
ImageBuf OIIO_API add(Image_or_Const A, Image_or_Const B, ROI roi={}, int nthreads=0)
UT_Array< RObjT > all(LObjT &obj, UT_ErrorCode &ec, const UT_ORMModelMeta &rmeta) const
UT_SqlDatabase & db()