Okay, I looked into fluent a little bit and regarding the projectedValue
it's easy: The property wrappers simply return self
for that, so e.g. $gender
in your case returns a value type OptionalField<Gender>
, which is a typealias for OptionalFieldProperty<User, Gender>
, I believe...
This does not look right to me, as Gender
is a model itself, you probably want to have a relation between those. OptionalField
is supposed to hold actual values from the database's table, not data that is actually rows from another table. Relations in fluent are expressed by a group of property wrappers, as I see it: Parent<To>
, OptionalParent<To>
, OptionalChild<To>
, Children<To>
, and Siblings<To, Through>
. See here and here.
On that note, it also strikes me as odd that User
has two (optional) parents, could it be you're misunderstanding the property wrapper? @OptionalParent(key: "status") var status: UserStatus?
does not mean that a User
is the parent of UserStatus
, it's the other way around. But you don't want the UserStatus
to be the parent of the User
, right? At least the definition of UserStatus
has the User
defined as its parent, but you can't really have both be parents...
You most likely want to keep UserStatus
to have the @OptionalParent
wrapper (maybe even not as optional? Does it even make sense to have a UserStatus
without a User
?). User
then needs to have a property wrapped in @OptionalChild(for:)
.
This is also a great example of the difference between properties and their wrappers, as @OptionalChild(for:)
expects not a Model
conforming type as for
parameter, but the @<Optional>Parent
property wrapper itself. That makes sense, because the child isn't just interested in what type its parent has, but also which relation the parent uses to refer to it. Fluent expresses this, apparently, with the wrapper's and not just the property's key path like this: @OptionalChild(for: \.$owner) var status: UserStatus?
.
(Side note: the \.$owner
is a short form of \UserStatus.$owner
which is a way to get a key path, in this case of type KeyPath<UserStatus, OptionalParentProperty<UserStatus, User>
... I know this gets complex, but property wrappers do a lot of work...)
This is becoming pretty long, I hope I'm not over-explaining things here, but I am a) not really familiar with Fluent myself, and b) have the hunch that you may still be missing the necessary understanding of the difference between a property and its wrapper.
As I see it from my glance of the Fluent documentation, the provided property wrappers fall into various categories that express basically the structure of your object graph in relation to how the data is expressed in the database (i.e. the scheme).
Wrappers like @Field
denote simple data entries, basically just holding the "table column name" in e.g. an SQL database. @ID
is a little special as it provides the primary key, but that's in the end also just a field.
@Parent
works differently: In your object graph (i.e. your Swift model type that becomes the "child") you want a concrete object of whatever other model type is appropriate to function as your instances' parent. That is the property itself.
However, the database cannot store that parent "inside" the child (i.e. in a column of the table that holds the other child data). Instead, it actually needs to store a reference, the identifier of the parent. The parent itself then lives in its own table.
Especially when one parent can have multiple children that's important, but even in 1-to-1 relations that's done this way (otherwise you'd just have one big table in the end).
This allows Fluent to read a child's parent by simply looking into that other table (the one holding all existing parent values) and select the one with the correct identifier (that it gets from the child itself).
This is why you have to use $...
: The property wrapper provides you with "access to the relation itself" (which is, as said, basically stored in the child) when you want to "set the parent". You're not setting the identifier on the parent, you're actually setting something on yourself ("inside your child"), at least from the database's point of view. And you set that "thing" to the parent ('s identifier).
The property (i.e. your variable without the $
) then becomes the parent "magically" (Fluent takes care of that).
The @Child
is even weirder, in some sense. You must give that property wrapper the information about the parent-relation, i.e. the relevant @Parent
property wrapper defined in the child's class.
This is because in the database nothing is actually stored in the parent's table itself. Instead, if you assign a child instance to the wrapped property in a parent instance, even though this assignment you mutate the parent, the database instead requires you to mutate the child's row: You write the parent's identifier in the according field. And that was, as described above defined via the @Parent
-wrapped property.
All in all I think you may want to refactor your data model in general. Specifically it strikes me as odd to have multiple parents, but in addition it seems fishy to change an existing instance's id
in a merge function like in your first screenshot.
While you may change an instance's parent or child, I assume doing so directly via the identifier happens mostly in an init
method, at least you have to be certain the new identifier exists.
Oh, and one last remark in case you didn't know: Instead of screenshots, you can post code snippets with by wrapping them in two ` (that's a backtick) or even as blocks with triple ```. 