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 }