Allow pointers as one relations values.

* Allow to define pointers to receive one relations values: this solves self-referencing issues (e.g. UserInfo.user in example model).
This commit is contained in:
Madeorsk 2024-11-26 14:02:41 +01:00
parent 3501c48eaf
commit 9567e57278
Signed by: Madeorsk
GPG key ID: 677E51CA765BB79F
2 changed files with 54 additions and 9 deletions

View file

@ -150,12 +150,27 @@ pub fn ResultMapper(comptime Model: type, comptime TableShape: type, comptime Me
if (inlineRelations) |_inlineRelations| {
// If there are loaded inline relations, map them to the result.
inline for (_inlineRelations) |relation| {
// Set the read inline relation value.
@field(model.*, relation.field) = (
// Read the inline related value.
const relatedValue = (
if (@field(rawModel, relation.field)) |relationVal|
try relation.repositoryConfiguration().fromSql(relationVal)
else null
);
if (pointedType(@TypeOf(@field(model.*, relation.field)))) |childType| {
if (relatedValue) |val| {
// Allocate pointer value.
@field(model.*, relation.field) = try mapperArena.allocator().create(childType);
// Set pointer value.
@field(model.*, relation.field).?.* = val;
} else {
// Set NULL value.
@field(model.*, relation.field) = null;
}
} else {
// Set simple value.
@field(model.*, relation.field) = relatedValue;
}
}
}
@ -230,3 +245,16 @@ pub fn ResultMapper(comptime Model: type, comptime TableShape: type, comptime Me
}
};
}
/// Get pointed type of the given type.
/// Return NULL if the type is not a pointer.
fn pointedType(@"type": type) ?type {
return switch (@typeInfo(@"type")) {
.Pointer => |ptr| ptr.child,
.Optional => |opt| switch (@typeInfo(opt.child)) {
.Pointer => |ptr| ptr.child,
else => null,
},
else => null,
};
}

View file

@ -110,8 +110,7 @@ pub const UserInfo = struct {
user_id: i32,
birthdate: i64,
//TODO is there a way to solve the "struct 'example.User' depends on itself" error when adding this relation?
// user: ?User = null,
user: ?*User = null,
};
pub const UserInfoRepository = zrm.Repository(UserInfo, UserInfo.Table, .{
.table = "example_users_info",
@ -123,11 +122,11 @@ pub const UserInfoRepository = zrm.Repository(UserInfo, UserInfo.Table, .{
.toSql = zrm.helpers.TableModel(UserInfo, UserInfo.Table).copyModelToTable,
});
pub const UserInfoRelations = UserInfoRepository.relations.define(.{
// .user = UserInfoRepository.relations.one(UserRepository, .{
// .direct = .{
// .foreignKey = "user_id",
// },
// }),
.user = UserInfoRepository.relations.one(UserRepository, .{
.direct = .{
.foreignKey = "user_id",
},
}),
});
pub const Message = struct {
@ -295,6 +294,24 @@ test "user has info" {
try std.testing.expectEqual(1, secondResult.models.len);
try std.testing.expect(secondResult.models[0].info == null);
var thirdQuery = UserInfoRepository.QueryWith(
// Retrieve info of users.
&[_]zrm.relations.Relation{UserInfoRelations.user}
).init(std.testing.allocator, poolConnector.connector(), .{});
try thirdQuery.whereKey(2);
defer thirdQuery.deinit();
var thirdResult = try thirdQuery.get(std.testing.allocator);
defer thirdResult.deinit();
try std.testing.expectEqual(1, thirdResult.models.len);
try std.testing.expectEqual(876348000000000, thirdResult.models[0].birthdate);
try std.testing.expect(thirdResult.models[0].user != null);
try std.testing.expectEqual(2, thirdResult.models[0].user.?.id);
try std.testing.expectEqualStrings("madeorsk", thirdResult.models[0].user.?.name);
}