1 module dorm.types.relations; 2 3 import dorm.declarative.conversion; 4 import dorm.model; 5 import dorm.types.patches; 6 7 version(none) static struct ManyToManyField(alias idOrModel) 8 { 9 alias T = ModelFromIdOrModel!idOrModel; 10 alias primaryKeyAlias = IdAliasFromIdOrModel!idOrModel; 11 enum primaryKeyField = IdFieldFromIdOrModel!idOrModel; 12 alias PrimaryKeyType = typeof(primaryKeyAlias); 13 14 bool toClear; 15 PrimaryKeyType[] toAdd; 16 PrimaryKeyType[] toRemove; 17 18 private T[] cached; 19 private bool resolved; 20 21 T[] populated() 22 { 23 assert(resolved, "ManyToManyField reference is not populated! Call " 24 ~ "`db.populate!(Model.manyToManyFieldName)(modelInstance)` or query " 25 ~ "data with the recursion flag set!"); 26 return cached; 27 } 28 29 void setCachedPopulated(T[] populated) 30 { 31 cached = populated; 32 resolved = true; 33 } 34 35 void add(T other) 36 { 37 auto refField = __traits(child, other, primaryKeyAlias); 38 toRemove = toRemove.remove!(refField); 39 toAdd ~= refField; 40 } 41 42 void add(PrimaryKeyType primaryKey) 43 { 44 toRemove = toRemove.remove!(primaryKey); 45 toAdd ~= primaryKey; 46 } 47 48 void add(Range)(Range range) 49 if (!is(Range == T) 50 && !is(Range == PrimaryKeyType)) 51 { 52 foreach (item; range) 53 add(item); 54 } 55 56 void remove(T other) 57 { 58 auto refField = __traits(child, other, primaryKeyAlias); 59 toAdd = toAdd.remove!(refField); 60 toRemove ~= refField; 61 } 62 63 void add(PrimaryKeyType primaryKey) 64 { 65 toRemove = toRemove.remove!(primaryKey); 66 toAdd ~= primaryKey; 67 } 68 69 void remove(Range)(Range range) 70 if (!is(Range == T) 71 && !is(Range == PrimaryKeyType)) 72 { 73 foreach (item; range) 74 remove(item); 75 } 76 77 void clear() 78 { 79 toAdd.length = 0; 80 toRemove.length = 0; 81 toClear = true; 82 } 83 } 84 85 static template ModelRef(alias idOrPatch) 86 { 87 alias primaryKeyAlias = IdAliasFromIdOrPatch!idOrPatch; 88 alias TPatch = PatchFromIdOrPatch!idOrPatch; 89 alias T = ModelFromSomePatch!TPatch; 90 alias ModelRef = ModelRefImpl!(primaryKeyAlias, T, TPatch); 91 } 92 93 static struct ModelRefImpl(alias id, _TModel, _TSelect) 94 { 95 alias TModel = _TModel; 96 alias TSelect = _TSelect; 97 alias primaryKeyAlias = id; 98 enum primaryKeyField = DormField!(_TModel, __traits(identifier, id)); 99 alias PrimaryKeyType = typeof(primaryKeyAlias); 100 101 PrimaryKeyType foreignKey; 102 103 private TSelect cached; 104 private bool resolved; 105 106 TSelect populated() 107 { 108 assert(resolved, "ModelRef reference is not populated! Call " 109 ~ "`db.populate!(Model.referenceFieldName)(modelInstance)` or query " 110 ~ "data with the recursion flag set!"); 111 return cached; 112 } 113 114 auto opAssign(TSelect value) 115 { 116 resolved = true; 117 cached = value; 118 foreignKey = __traits(child, value, primaryKeyAlias); 119 return value; 120 } 121 } 122 123 // TODO: need to figure out how to make BackRefs 124 version (none) 125 static struct BackRef(alias foreignField) 126 { 127 static assert(is(__traits(parent, foreignField) : Model), 128 "Invalid foreign key field `" ~ foreignField.stringof 129 ~ "`! Change to `BackRef!(OtherModel.foreignKeyReferencingThis)`"); 130 131 alias T = __traits(parent, foreignField); 132 133 private T[] cached; 134 private bool resolved; 135 136 T[] populated() 137 { 138 assert(resolved, "BackRef value is not populated! Call " 139 ~ "`db.populate!(Model.otherFieldReferencingThis)(modelInstance)` or query " 140 ~ "data with the recursion flag set!"); 141 return cached; 142 } 143 }