QEloquent 1.1.0
Qt most flexible ORM.
Loading...
Searching...
No Matches
relation_impl.h
1#ifndef QELOQUENT_RELATION_IMPL_H
2#define QELOQUENT_RELATION_IMPL_H
3
4#include <QEloquent/relation.h>
5#include <QEloquent/namingconvention.h>
6#include <QEloquent/query.h>
7
8namespace QEloquent {
9
10template<typename RelatedModel>
11class BasicRelationData : public RelationData
12{
13public:
14 BasicRelationData() { relatedObject = MetaObject::from<RelatedModel>(); }
15 virtual ~BasicRelationData() = default;
16
18 bool exists() override final { return related.count() > 0; }
19
20 void *relatedList() const override final
21 { return &const_cast<BasicRelationData<RelatedModel> *>(this)->related; }
22
23 QList<DataMap> serialize() const override final {
24 QList<DataMap> maps;
25
26 std::transform(related.begin(), related.end(), std::back_inserter(maps), [](const Serializable &model) {
27 const QList<DataMap> maps = model.serialize();
28 // Model always return a single map
29 return maps.first();
30 });
31
32 return maps;
33 }
34
35 QList<RelatedModel> related;
36};
37
38template<typename RelatedModel>
39class HasRelationData : public BasicRelationData<RelatedModel>
40{
41public:
42 virtual ~HasRelationData() = default;
43
44 void init(NamingConvention *) override
45 {
46 if (foreignKey.isEmpty())
47 foreignKey = this->primaryObject.foreignProperty().fieldName();
48
49 if (localKey.isEmpty())
50 localKey = this->primaryObject.primaryProperty().fieldName();
51 }
52
53 bool get() override
54 {
55 Query q;
56 q.where(foreignKey, this->parentField(localKey));
57 if (!this->multiple()) q.limit(1);
58
59 auto result = RelatedModel::find(q);
60 if (result) {
61 this->related = result.value();
62 this->conserve(q);
63 return true;
64 } else {
65 this->conserve(q, result.error());
66 return false;
67 }
68 }
69
70 QString foreignKey;
71 QString localKey;
72};
73
74template<typename RelatedModel>
75class HasOneRelationData final : public HasRelationData<RelatedModel>
76{
77public:
78 HasOneRelationData *clone() const override { return new HasOneRelationData(*this); }
79 bool multiple() const override { return false; }
80};
81
82template<typename RelatedModel>
83class HasManyRelationData final : public HasRelationData<RelatedModel>
84{
85public:
86 HasManyRelationData *clone() const override { return new HasManyRelationData(*this); }
87 bool multiple() const override { return true; }
88};
89
90template<typename RelatedModel, typename ThroughModel>
91class HasManyThroughRelationData final : public BasicRelationData<RelatedModel>
92{
93public:
94 void init(NamingConvention *) override
95 {
96 const MetaObject through = MetaObject::from<ThroughModel>();
97
98 if (localKey.isEmpty())
99 localKey = this->primaryObject.primaryProperty().fieldName();
100
101 if (throughForeignKey.isEmpty()) {
102 RelatedModel m;
103 throughForeignKey = m.metaObject().foreignProperty().fieldName();
104 }
105
106 if (throughLocalKey.isEmpty()) {
107 throughLocalKey = through.primaryProperty().fieldName();
108 }
109
110 if (foreignKey.isEmpty()) {
111 foreignKey = through.foreignProperty().fieldName();
112 }
113 }
114
115 bool get() override
116 {
117 // SELECT related.* FROM related
118 // INNER JOIN through ON related.through_foreign_key = through.through_local_key
119 // WHERE through.foreign_key = parent.local_key
120
121 const MetaObject through = MetaObject::from<ThroughModel>();
122
123 Query q;
124 q.join(through.tableName(),
125 this->relatedObject.tableName() + "." + throughForeignKey,
126 "=",
127 through.tableName() + "." + throughLocalKey);
128
129 q.where(through.tableName() + "." + foreignKey, this->parentField(localKey));
130
131 auto result = RelatedModel::find(q);
132 if (result) {
133 this->related = result.value();
134 this->conserve(q);
135 return true;
136 } else {
137 this->conserve(q, result.error());
138 return false;
139 }
140 }
141
142 bool multiple() const override { return true; }
143
144 HasManyThroughRelationData *clone() const override { return new HasManyThroughRelationData(*this); }
145
146 QString foreignKey;
147 QString localKey;
148 QString throughForeignKey;
149 QString throughLocalKey;
150};
151
152template<typename RelatedModel>
153class BelongsToRelationData final : public BasicRelationData<RelatedModel>
154{
155public:
156 void init(NamingConvention *) override
157 {
158 if (foreignKey.isEmpty())
159 foreignKey = this->relatedObject.foreignProperty().fieldName();
160
161 if (ownerKey.isEmpty())
162 ownerKey = this->relatedObject.primaryProperty().fieldName();
163 }
164
165 bool get() override
166 {
167 Query q;
168 q.where(ownerKey, this->parentField(foreignKey));
169
170 auto result = RelatedModel::find(q);
171 if (result) {
172 this->related = { result.value() };
173 return true;
174 }
175 return false;
176 }
177
178 bool multiple() const override { return false; }
179
180 BelongsToRelationData *clone() const override { return new BelongsToRelationData(*this); }
181
182 QString foreignKey;
183 QString ownerKey;
184};
185
186template<typename RelatedModel>
187class BelongsToManyRelationData final : public BasicRelationData<RelatedModel>
188{
189public:
190 void init(NamingConvention *convention) override
191 {
192 if (table.isEmpty()) {
193 QStringList tables = { this->primaryObject.tableName(), this->relatedObject.tableName() };
194 tables.sort();
195 table = convention->pivotTableName(tables.first(), tables.last());
196 }
197
198 if (foreignPivotKey.isEmpty())
199 foreignPivotKey = this->primaryObject.foreignProperty().fieldName();
200
201 if (relatedPivotKey.isEmpty())
202 relatedPivotKey = this->relatedObject.foreignProperty().fieldName();
203
204 if (parentKey.isEmpty())
205 parentKey = this->primaryObject.primaryProperty().fieldName();
206
207 if (relatedKey.isEmpty())
208 relatedKey = this->relatedObject.primaryProperty().fieldName();
209 }
210
211 bool get() override
212 {
213 // SELECT related.* FROM related
214 // INNER JOIN pivot ON related.related_key = pivot.related_pivot_key
215 // WHERE pivot.foreign_pivot_key = parent.parent_key
216
217 Query q;
218 q.join(table,
219 this->relatedObject.tableName() + "." + relatedKey,
220 "=",
221 table + "." + relatedPivotKey);
222
223 q.where(table + "." + foreignPivotKey, this->parentField(parentKey));
224
225 QString s = q.toString(this->parent->connection());
226
227 auto result = RelatedModel::find(q);
228 if (result) {
229 this->related = result.value();
230 this->conserve(q);
231 return true;
232 } else {
233 this->conserve(q, result.error());
234 return false;
235 }
236 }
237
238 bool multiple() const override { return true; }
239
240 BelongsToManyRelationData *clone() const override { return new BelongsToManyRelationData(*this); }
241
242 QString table;
243 QString foreignPivotKey;
244 QString relatedPivotKey;
245 QString parentKey;
246 QString relatedKey;
247};
248
249template<typename RelatedModel, typename ThroughModel>
250class BelongsToManyThroughRelationData final : public BasicRelationData<RelatedModel>
251{
252public:
253 void init(NamingConvention *convention) override
254 {
255 const MetaObject through = MetaObject::from<ThroughModel>();
256
257 if (table.isEmpty()) {
258 table = convention->pivotTableName(through.tableName(), this->relatedObject.tableName());
259 }
260
261 if (foreignPivotKey.isEmpty())
262 foreignPivotKey = through.foreignProperty().fieldName();
263
264 if (relatedPivotKey.isEmpty())
265 relatedPivotKey = this->relatedObject.foreignProperty().fieldName();
266
267 if (parentKey.isEmpty())
268 parentKey = through.foreignProperty().fieldName();
269
270 if (relatedKey.isEmpty())
271 relatedKey = this->relatedObject.primaryProperty().fieldName();
272 }
273
274 bool get() override
275 {
276 // SELECT related.* FROM related
277 // INNER JOIN pivot ON related.related_key = pivot.related_pivot_key
278 // WHERE pivot.foreign_pivot_key = parent.parent_key
279
280 Query q;
281 q.join(table,
282 this->relatedObject.tableName() + "." + relatedKey,
283 "=",
284 table + "." + relatedPivotKey);
285
286 q.where(table + "." + foreignPivotKey, this->parentField(parentKey));
287
288 auto result = RelatedModel::find(q);
289 if (result) {
290 this->related = result.value();
291 this->conserve(q);
292 return true;
293 } else {
294 this->conserve(q, result.error());
295 return false;
296 }
297 }
298
299 bool multiple() const override { return true; }
300
301 BelongsToManyThroughRelationData *clone() const override { return new BelongsToManyThroughRelationData(*this); }
302
303 QString table;
304 QString foreignPivotKey;
305 QString relatedPivotKey;
306 QString parentKey;
307 QString relatedKey;
308};
309
310} // namespace QEloquent
311
312#endif // QELOQUENT_RELATION_IMPL_H