From f070c08ff8721d10cfdd726856005b58a401c888 Mon Sep 17 00:00:00 2001 From: vabenil Date: Fri, 4 Nov 2022 16:40:44 +0200 Subject: [PATCH 1/2] Add one_to_many template --- database_generation.d | 166 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) diff --git a/database_generation.d b/database_generation.d index b6f92f0..5ba8c61 100644 --- a/database_generation.d +++ b/database_generation.d @@ -731,3 +731,169 @@ template where(conditions...) { return this_; } } + +// Basically a wrapper for a ResultSet +struct TabResultSet(T) +{ + this(ResultSet result) + { + this.result = result; + } + + bool empty() @property + { + return this.result.empty; + } + + T front() @property + { + T row; + row.populateFromDbRow(this.result.front); + return row; + } + + void popFront() + { + this.result.popFront(); + } + + size_t length() @property + { + return this.result.length; + } + + private ResultSet result; +} + +// ditto +TabResultSet!T to_table_rows(T)(ResultSet res) +{ + return TabResultSet!T(res); +} + +private template FieldReference(alias field_) +{ + alias Table = __traits(parent, field_); + alias field = field_; +} + +private template isFieldRefInAttributes(Attributes...) +{ + static if (Attributes.length == 0) { + static immutable bool isFieldRefInAttributes = false; + } + else { + alias attr = Attributes[0]; + static if (is(attr == ForeignKey!(field, s), alias field, string s)) { + static immutable bool isFieldRefInAttributes = true; + } + else { + static immutable bool fieldRefInAttributes = + isFieldRefInAttributes!(Attributes[1..$]); + } + } +} + +private template getFieldRefInAttributes(Attributes...) +{ + alias attr = Attributes[0]; + static if (is(attr == ForeignKey!(field, s), alias field, string s)) { + alias getFieldRefInAttributes = FieldReference!(field); + } + else { + alias fieldRefInAttributes = + getFieldRefInAttributes!(RT, Attributes[1..$]); + } +} + +private alias getRefToField(alias fk_field) = + getFieldRefInAttributes!(__traits(getAttributes, fk_field)); + +unittest +{ + struct Role { int id; } + + struct User + { + int id; + @ForeignKey!(Role.id, "") int role_id; + } + + alias FieldRef = getRefToField!(User.role_id); + assert(is(FieldRef.Table == Role)); + assert(__traits(isSame, FieldRef.field, Role.id)); +} + +string toFieldName(T)(string s, bool isPlural = false) +{ + int cnt = isPlural ? 2 : 1; + if (s is null) + return plural(cnt, beautify(tableNameFor!T(), '_', true)); + return s; +} + +/++ + generates get functions for a one-to-many relationship + ```d + Struct Role { int id; } + struct User { + @ForeignKey!(Role.id, "") int role_id; + } + + mixin(one_to_many!(User.role_id), "role", "users"); + /* + this will generate the following functions + Role get_role(User role, Database db) {...} + TabResultSet!User get_users(Role row, Database db) {...} + */ + ``` + if t2 or t1 are set as null they will be infered from either + the `DBName` attribute or from the name of the the Table ++/ +template one_to_many(alias fk_field, string t2 = null, string t1 = null) +{ + private { + alias T1 = __traits(parent, fk_field); + + static assert( + isFieldRefInAttributes!(__traits(getAttributes, fk_field)), + T1.stringof ~ "." ~ fk_field.stringof ~ " does't have a ForeignKey"); + + alias FieldRef = getRefToField!(fk_field); + alias T2 = FieldRef.Table; + alias ref_field = FieldRef.field; + + immutable string t2_name = toFieldName!T2(t2); + immutable string t1_name = toFieldName!T1(t1, true); + } + + static immutable string one_to_many = + T2.stringof~` get_`~t2_name~`(`~T1.stringof~` row, Database db) + { + import std.exception; + + enforce(db !is null, "Database must not be null"); + auto fk_id = row.`~fk_field.stringof~`; + + auto res = db.query( + "select * from `~tableNameFor!T2()~`" ~ + " where `~ref_field.stringof~` = ?", fk_id + ).to_table_rows!`~T2.stringof~`; + + return res.front(); + } + TabResultSet!`~T1.stringof~` get_`~t1_name~`(`~T2.stringof~` row, Database db) + { + import std.exception; + + enforce(db !is null, "Database must not be null"); + auto id = row.`~ref_field.stringof~`; + + auto res = db.query( + "select * from `~tableNameFor!T1()~`"~ + " where `~fk_field.stringof~` = ?", id + ).to_table_rows!`~T1.stringof~`; + + return res; + }`; +} From d252b39a6e818beac9e04f8aefe48e3b34798a8d Mon Sep 17 00:00:00 2001 From: vabenil Date: Sat, 5 Nov 2022 14:17:47 +0200 Subject: [PATCH 2/2] Improve one_to_many example --- database_generation.d | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/database_generation.d b/database_generation.d index 5ba8c61..1cb86fb 100644 --- a/database_generation.d +++ b/database_generation.d @@ -833,19 +833,24 @@ string toFieldName(T)(string s, bool isPlural = false) } /++ - generates get functions for a one-to-many relationship + generates get functions for a one-to-many relationship with the form + `T2 get_(T1 row, Database db)` and + `TabResultSet!T1 get_(T2 row, Database db)` + Example: ```d Struct Role { int id; } struct User { @ForeignKey!(Role.id, "") int role_id; } - mixin(one_to_many!(User.role_id), "role", "users"); - /* - this will generate the following functions - Role get_role(User role, Database db) {...} - TabResultSet!User get_users(Role row, Database db) {...} - */ + mixin(one_to_many!(User.role_id, "role", "users")); + void main() + { + Database db = ... + User user = db.find!User(1); + Role role = user.get_role(db); + auto users = role.get_users(db); + } ``` if t2 or t1 are set as null they will be infered from either the `DBName` attribute or from the name of the the Table