1#ifndef QELOQUENT_RELATION_IMPL_H
2#define QELOQUENT_RELATION_IMPL_H
4#include <QEloquent/relation.h>
5#include <QEloquent/namingconvention.h>
6#include <QEloquent/query.h>
10template<
typename RelatedModel>
11class BasicRelationData :
public RelationData
14 BasicRelationData() { relatedObject = MetaObject::from<RelatedModel>(); }
15 virtual ~BasicRelationData() =
default;
18 bool exists() override final {
return related.count() > 0; }
20 void *relatedList() const override final
21 {
return &
const_cast<BasicRelationData<RelatedModel> *
>(
this)->related; }
23 QList<DataMap> serialize() const override final {
26 std::transform(related.begin(), related.end(), std::back_inserter(maps), [](
const Serializable &model) {
27 const QList<DataMap> maps = model.serialize();
35 QList<RelatedModel> related;
38template<
typename RelatedModel>
39class HasRelationData :
public BasicRelationData<RelatedModel>
42 virtual ~HasRelationData() =
default;
44 void init(NamingConvention *)
override
46 if (foreignKey.isEmpty())
47 foreignKey = this->primaryObject.foreignProperty().fieldName();
49 if (localKey.isEmpty())
50 localKey = this->primaryObject.primaryProperty().fieldName();
56 q.where(foreignKey, this->parentField(localKey));
57 if (!this->multiple()) q.limit(1);
59 auto result = RelatedModel::find(q);
61 this->related = result.value();
65 this->conserve(q, result.error());
74template<
typename RelatedModel>
75class HasOneRelationData final :
public HasRelationData<RelatedModel>
78 HasOneRelationData *clone()
const override {
return new HasOneRelationData(*
this); }
79 bool multiple()
const override {
return false; }
82template<
typename RelatedModel>
83class HasManyRelationData final :
public HasRelationData<RelatedModel>
86 HasManyRelationData *clone()
const override {
return new HasManyRelationData(*
this); }
87 bool multiple()
const override {
return true; }
90template<
typename RelatedModel,
typename ThroughModel>
91class HasManyThroughRelationData final :
public BasicRelationData<RelatedModel>
94 void init(NamingConvention *)
override
96 const MetaObject through = MetaObject::from<ThroughModel>();
98 if (localKey.isEmpty())
99 localKey = this->primaryObject.primaryProperty().fieldName();
101 if (throughForeignKey.isEmpty()) {
103 throughForeignKey = m.metaObject().foreignProperty().fieldName();
106 if (throughLocalKey.isEmpty()) {
107 throughLocalKey = through.primaryProperty().fieldName();
110 if (foreignKey.isEmpty()) {
111 foreignKey = through.foreignProperty().fieldName();
121 const MetaObject through = MetaObject::from<ThroughModel>();
124 q.join(through.tableName(),
125 this->relatedObject.tableName() +
"." + throughForeignKey,
127 through.tableName() +
"." + throughLocalKey);
129 q.where(through.tableName() +
"." + foreignKey, this->parentField(localKey));
131 auto result = RelatedModel::find(q);
133 this->related = result.value();
137 this->conserve(q, result.error());
142 bool multiple()
const override {
return true; }
144 HasManyThroughRelationData *clone()
const override {
return new HasManyThroughRelationData(*
this); }
148 QString throughForeignKey;
149 QString throughLocalKey;
152template<
typename RelatedModel>
153class BelongsToRelationData final :
public BasicRelationData<RelatedModel>
156 void init(NamingConvention *)
override
158 if (foreignKey.isEmpty())
159 foreignKey = this->relatedObject.foreignProperty().fieldName();
161 if (ownerKey.isEmpty())
162 ownerKey = this->relatedObject.primaryProperty().fieldName();
168 q.where(ownerKey, this->parentField(foreignKey));
170 auto result = RelatedModel::find(q);
172 this->related = { result.value() };
178 bool multiple()
const override {
return false; }
180 BelongsToRelationData *clone()
const override {
return new BelongsToRelationData(*
this); }
186template<
typename RelatedModel>
187class BelongsToManyRelationData final :
public BasicRelationData<RelatedModel>
190 void init(NamingConvention *convention)
override
192 if (table.isEmpty()) {
193 QStringList tables = { this->primaryObject.tableName(), this->relatedObject.tableName() };
195 table = convention->pivotTableName(tables.first(), tables.last());
198 if (foreignPivotKey.isEmpty())
199 foreignPivotKey = this->primaryObject.foreignProperty().fieldName();
201 if (relatedPivotKey.isEmpty())
202 relatedPivotKey = this->relatedObject.foreignProperty().fieldName();
204 if (parentKey.isEmpty())
205 parentKey = this->primaryObject.primaryProperty().fieldName();
207 if (relatedKey.isEmpty())
208 relatedKey = this->relatedObject.primaryProperty().fieldName();
219 this->relatedObject.tableName() +
"." + relatedKey,
221 table +
"." + relatedPivotKey);
223 q.where(table +
"." + foreignPivotKey, this->parentField(parentKey));
225 QString s = q.toString(this->parent->connection());
227 auto result = RelatedModel::find(q);
229 this->related = result.value();
233 this->conserve(q, result.error());
238 bool multiple()
const override {
return true; }
240 BelongsToManyRelationData *clone()
const override {
return new BelongsToManyRelationData(*
this); }
243 QString foreignPivotKey;
244 QString relatedPivotKey;
249template<
typename RelatedModel,
typename ThroughModel>
250class BelongsToManyThroughRelationData final :
public BasicRelationData<RelatedModel>
253 void init(NamingConvention *convention)
override
255 const MetaObject through = MetaObject::from<ThroughModel>();
257 if (table.isEmpty()) {
258 table = convention->pivotTableName(through.tableName(), this->relatedObject.tableName());
261 if (foreignPivotKey.isEmpty())
262 foreignPivotKey = through.foreignProperty().fieldName();
264 if (relatedPivotKey.isEmpty())
265 relatedPivotKey = this->relatedObject.foreignProperty().fieldName();
267 if (parentKey.isEmpty())
268 parentKey = through.foreignProperty().fieldName();
270 if (relatedKey.isEmpty())
271 relatedKey = this->relatedObject.primaryProperty().fieldName();
282 this->relatedObject.tableName() +
"." + relatedKey,
284 table +
"." + relatedPivotKey);
286 q.where(table +
"." + foreignPivotKey, this->parentField(parentKey));
288 auto result = RelatedModel::find(q);
290 this->related = result.value();
294 this->conserve(q, result.error());
299 bool multiple()
const override {
return true; }
301 BelongsToManyThroughRelationData *clone()
const override {
return new BelongsToManyThroughRelationData(*
this); }
304 QString foreignPivotKey;
305 QString relatedPivotKey;