Lightweight 0.20260921.0
Loading...
Searching...
No Matches
BelongsTo.hpp
1// SPDX-License-Identifier: Apache-2.0
2
3#pragma once
4
5#include "../DataBinder/Core.hpp"
6#include "../DataBinder/SqlNullValue.hpp"
7#include "../SqlColumnTypeDefinitions.hpp"
8#include "../Utils.hpp"
9#include "Error.hpp"
10#include "Field.hpp"
11
12namespace Lightweight
13{
14class SqlStatement;
15}
16
17#include <compare>
18#include <optional>
19#include <string_view>
20#include <type_traits>
21
22namespace Lightweight
23{
24
25/// @brief Helper function to use with std::optional<std::reference_wrapper<T>>
26/// like this .transform(Unwrap).value_or({})
27auto inline Unwrap = [](auto v) {
28 return v.get();
29};
30
31/// @brief Represents the many-to-one side of a foreign-key relationship.
32///
33/// This is the record that *owns* the foreign-key column: many records holding a `BelongsTo` may
34/// reference the same target record. For a one-to-one relation through a join table, see
35/// `HasOneThrough`; for the inverse (one-to-many) side, see `HasMany`.
36///
37/// The `TheReferencedField` parameter is the field in the other record that references the current record,
38/// in the form of `&OtherRecord::Field`.
39/// Other Field must be a primary key.
40///
41/// @tparam TheReferencedField The field in the other record that references the current record.
42/// @tparam ColumnNameOverrideString If not an empty string, this value will be used as the column name in the database.
43///
44/// @ingroup DataMapper
45///
46/// @code
47/// struct User {
48/// Field<SqlGuid, PrimaryKey::AutoAssign> id;
49/// Field<SqlAnsiString<30>> name;
50/// };
51/// struct Email {
52/// Field<SqlGuid, PrimaryKey::AutoAssign> id;
53/// Field<SqlAnsiString<40>> address;
54/// BelongsTo<&User::id> user;
55/// // Also possible to customize the column name
56/// BelongsTo<&User::id, SqlRealName<"the_user_id">, SqlNullable::Null> maybe_user;
57/// };
58/// @endcode
59template <auto TheReferencedField, auto ColumnNameOverrideString = std::nullopt, SqlNullable Nullable = SqlNullable::NotNull>
61{
62 public:
63 /// The field in the other record that references the current record.
64 static constexpr auto ReferencedField = TheReferencedField;
65
66 /// If not an empty string, this value will be used as the column name in the database.
67 static constexpr std::string_view ColumnNameOverride = []() consteval {
68 if constexpr (!std::same_as<decltype(ColumnNameOverrideString), std::nullopt_t>)
69 return std::string_view { ColumnNameOverrideString };
70 else
71 return std::string_view {};
72 }();
73
74#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
75 /// Represents the record type of the other field.
76 using ReferencedRecord = MemberClassType<TheReferencedField>;
77
78 /// Represents the base column type of the foreign key, matching the primary key of the other record.
79 using BaseType = typename[:std::meta::type_of(ReferencedField):] ::ValueType;
80
81 static_assert(std::remove_cvref_t<decltype(std::declval<ReferencedRecord>().[:ReferencedField:])>::IsPrimaryKey,
82 "The referenced field must be a primary key.");
83#else
84 /// Represents the record type of the other field.
85 using ReferencedRecord = MemberClassType<decltype(TheReferencedField)>;
86
87 static_assert(std::remove_cvref_t<decltype(std::declval<ReferencedRecord>().*ReferencedField)>::IsPrimaryKey,
88 "The referenced field must be a primary key.");
89
90 /// Represents the base column type of the foreign key, matching the primary key of the other record.
91 using BaseType = std::remove_cvref_t<decltype(std::declval<ReferencedRecord>().*ReferencedField)>::ValueType;
92#endif
93
94 /// Represents the value type of the foreign key,
95 /// which can be either an optional or a non-optional type of the referenced field,
96 using ValueType = std::conditional_t<Nullable == SqlNullable::Null, std::optional<BaseType>, BaseType>;
97
98 /// Indicates whether this relationship is optional (nullable).
99 static constexpr auto IsOptional = Nullable == SqlNullable::Null;
100 /// Indicates whether this relationship is mandatory (non-nullable).
101 static constexpr auto IsMandatory = !IsOptional;
102 /// Indicates that a BelongsTo field is never a primary key.
103 static constexpr auto IsPrimaryKey = false;
104 /// Indicates that a BelongsTo field is never an auto-increment primary key.
105 static constexpr auto IsAutoIncrementPrimaryKey = false;
106
107 /// Constructs a new BelongsTo with the given value(s) forwarded to the underlying value type.
108 template <typename... S>
109 requires std::constructible_from<ValueType, S...>
110 constexpr BelongsTo(S&&... value) noexcept:
111 _referencedFieldValue(std::forward<S>(value)...)
112 {
113 }
114
115 /// Constructs a new BelongsTo from the given referenced record, copying its primary key.
116 constexpr BelongsTo(ReferencedRecord const& other) noexcept:
117#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
118 _referencedFieldValue { (other.[:ReferencedField:]).Value() },
119#else
120 _referencedFieldValue { (other.*ReferencedField).Value() },
121#endif
122 _loaded { true },
123 _record { std::make_unique<ReferencedRecord>(other) }
124 {
125 }
126
127 /// Copy constructor.
128 constexpr BelongsTo(BelongsTo const& other) noexcept:
129 _referencedFieldValue(other._referencedFieldValue),
130 _loader(std::move(other._loader)),
131 _loaded(other._loaded),
132 _modified(other._modified),
133 _record(other._record ? std::make_unique<ReferencedRecord>(*other._record) : nullptr)
134 {
135 }
136
137 /// Move constructor.
138 constexpr BelongsTo(BelongsTo&& other) noexcept:
139 _referencedFieldValue(std::move(other._referencedFieldValue)),
140 _loader(std::move(other._loader)),
141 _loaded(other._loaded),
142 _modified(other._modified),
143 _record(std::move(other._record))
144 {
145 }
146
147 /// Assigns NULL to the relationship, clearing the loaded record.
148 BelongsTo& operator=(SqlNullType /*nullValue*/) noexcept
149 {
150 if (!_referencedFieldValue)
151 return *this;
152 _loaded = false;
153 _record.reset();
154 _referencedFieldValue = {};
155 _modified = true;
156 return *this;
157 }
158
159 /// Assigns a bare foreign-key value, updating the foreign key and marking the field modified.
160 ///
161 /// This is the sibling of Field<T>::operator=(S&&). Without it, `record.fk = someKeyValue`
162 /// would bind to the converting constructor plus move-assignment, and the temporary's
163 /// default-constructed `_modified` (false) would be copied over this field — leaving the
164 /// change invisible to DataMapper::Update(), which gates its SET clause on IsModified().
165 ///
166 /// Any previously loaded record is dropped, since it no longer corresponds to the new key.
167 ///
168 /// @param value The referenced record's primary-key value to point at.
169 /// @return Reference to this field.
170 template <typename S>
171 requires(std::constructible_from<ValueType, S> && !std::same_as<std::remove_cvref_t<S>, BelongsTo>
172 && !std::same_as<std::remove_cvref_t<S>, ReferencedRecord>
173 && !std::same_as<std::remove_cvref_t<S>, SqlNullType>)
174 constexpr BelongsTo& operator=(S&& value) noexcept
175 {
176 _referencedFieldValue = ValueType { std::forward<S>(value) };
177 _loaded = false;
178 _record.reset();
179 _modified = true;
180 return *this;
181 }
182
183 /// Assigns a referenced record, updating the foreign key and loaded state.
185 {
186#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
187 if (_referencedFieldValue == (other.[:ReferencedField:]).Value())
188#else
189 if (_referencedFieldValue == (other.*ReferencedField).Value())
190#endif
191 return *this;
192 _loaded = true;
193 _record = std::make_unique<ReferencedRecord>(other);
194#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
195 _referencedFieldValue = (other.[:ReferencedField:]).Value();
196#else
197 _referencedFieldValue = (other.*ReferencedField).Value();
198#endif
199 _modified = true;
200 return *this;
201 }
202
203 /// Copy assignment operator.
205 {
206 if (this == &other)
207 return *this;
208
209 _referencedFieldValue = other._referencedFieldValue;
210 _loader = std::move(other._loader);
211 _loaded = other._loaded;
212 _modified = other._modified;
213 _record = other._record ? std::make_unique<ReferencedRecord>(*other._record) : nullptr;
214
215 return *this;
216 }
217
218 /// Move assignment operator.
219 BelongsTo& operator=(BelongsTo&& other) noexcept
220 {
221 if (this == &other)
222 return *this;
223 _referencedFieldValue = std::move(other._referencedFieldValue);
224 _loader = std::move(other._loader);
225 _loaded = other._loaded;
226 _modified = other._modified;
227 _record = std::move(other._record);
228 other._loaded = false;
229 return *this;
230 }
231
232 ~BelongsTo() noexcept = default;
233
234 /// Marks the field as modified or unmodified.
235 LIGHTWEIGHT_FORCE_INLINE constexpr void SetModified(bool value) noexcept
236 {
237 _modified = value;
238 }
239
240 /// Checks if the field is modified.
241 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr bool IsModified() const noexcept
242 {
243 return _modified;
244 }
245
246 /// Retrieves the reference to the value of the field.
247 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr ValueType const& Value() const noexcept
248 {
249 return _referencedFieldValue;
250 }
251
252 /// Retrieves the mutable reference to the value of the field.
253 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr ValueType& MutableValue() noexcept
254 {
255 return _referencedFieldValue;
256 }
257
258 // NOLINTBEGIN(cppcoreguidelines-missing-std-forward)
259
260 /// Retrieves a record from the relationship. When the record is not optional
261 template <typename Self>
262 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr ReferencedRecord const& Record(this Self&& self)
263 requires(IsMandatory)
264 {
265 self.RequireLoaded();
266 return *self._record;
267 }
268
269 /// Retrieves a record from the relationship. When the record is optional
270 /// we return object similar to std::optional<ReferencedRecord&>
271 template <typename Self>
272 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr decltype(auto) Record(this Self&& self)
273 requires(IsOptional)
274 {
275 self.RequireLoaded();
276 return [&]() -> std::optional<std::reference_wrapper<ReferencedRecord>> {
277 if (self._record)
278 return *self._record;
279 return std::nullopt;
280 }();
281 // .transform([](auto v) { return v.get(); });
282 // requires at least clang-20
283 }
284
285 /// Retrieves the record from the relationship.
286 /// Only available when the relationship is mandatory.
287 template <typename Self>
288 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr ReferencedRecord& operator*(this Self&& self) noexcept
289 requires(IsMandatory)
290 {
291 self.RequireLoaded();
292 return *self._record;
293 }
294
295 /// Retrieves the record from the relationship.
296 /// Only available when the relationship is mandatory.
297 template <typename Self>
298 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr ReferencedRecord* operator->(this Self&& self)
299 requires(IsMandatory)
300 {
301 self.RequireLoaded();
302 return self._record.get();
303 }
304
305 // NOLINTEND(cppcoreguidelines-missing-std-forward)
306
307 /// Checks if the field value is NULL.
308 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr bool operator!() const noexcept
309 {
310 return !_referencedFieldValue;
311 }
312
313 /// Checks if the field value is not NULL.
314 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr explicit operator bool() const noexcept
315 {
316 return static_cast<bool>(_referencedFieldValue);
317 }
318
319 /// @brief Returns the already-loaded referenced record, or `nullptr` when none is loaded.
320 ///
321 /// Unlike `Record()`, this never runs the on-demand loader: it reports what is present right
322 /// now. That is what lets the batched relation loading walk one level deeper (`With<A, B>()`)
323 /// without turning the walk itself into the N+1 it exists to remove.
324 ///
325 /// @return Pointer to the loaded record, or `nullptr` if the relation is unloaded or NULL.
326 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr ReferencedRecord* LoadedRecord() noexcept
327 {
328 return _record.get();
329 }
330
331 /// @copydoc LoadedRecord()
332 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr ReferencedRecord const* LoadedRecord() const noexcept
333 {
334 return _record.get();
335 }
336
337 /// Emplaces a record into the relationship. This will mark the relationship as loaded.
338 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr ReferencedRecord& EmplaceRecord()
339 {
340 _loaded = true;
341 _record = std::make_unique<ReferencedRecord>();
342 return *_record;
343 }
344
345 /// Adopts an eagerly fetched referenced record, marking the relationship loaded.
346 ///
347 /// This is the sink for a record that was already read from the database, as opposed to
348 /// `operator=(ReferencedRecord&)`, which *establishes* the relationship and therefore rewrites
349 /// the foreign key and sets the modified flag. Here the foreign key is already whatever the row
350 /// said it was, so it is deliberately left untouched and the field stays unmodified - adopting a
351 /// fetched value is not a pending change to be written back.
352 ///
353 /// An empty @p record leaves the relationship unloaded rather than clearing the foreign key: a
354 /// mandatory relationship whose target row is missing is a data-integrity problem to be
355 /// surfaced by the accessor, not silently turned into NULL.
356 ///
357 /// @param record The fetched record, or `std::nullopt` if the referenced row was absent.
358 LIGHTWEIGHT_FORCE_INLINE void AdoptFetchedRecord(std::optional<ReferencedRecord> record)
359 {
360 if (!record.has_value())
361 return;
362
363 _record = std::make_unique<ReferencedRecord>(std::move(record).value());
364 _loaded = true;
365 }
366
367 /// Binds the foreign key value to the given output column index on the statement.
368 template <typename Stmt>
369 LIGHTWEIGHT_FORCE_INLINE void BindOutputColumn(SQLSMALLINT outputIndex, Stmt& stmt)
370 {
371 stmt.BindOutputColumn(outputIndex, &_referencedFieldValue);
372 }
373
374 /// Three-way comparison operator.
375 std::weak_ordering operator<=>(BelongsTo const& other) const noexcept
376 {
377 return _referencedFieldValue <=> other.Value();
378 }
379
380 /// Three-way comparison operator with a Field.
381 template <detail::FieldElementType T, PrimaryKey IsPrimaryKeyValue = PrimaryKey::No>
382 std::weak_ordering operator<=>(Field<T, IsPrimaryKeyValue> const& other) const noexcept
383 {
384 return _referencedFieldValue <=> other.Value();
385 }
386
387 /// Equality comparison operator.
388 bool operator==(BelongsTo const& other) const noexcept
389 {
390 return (_referencedFieldValue <=> other.Value()) == std::weak_ordering::equivalent;
391 }
392
393 /// Inequality comparison operator.
394 bool operator!=(BelongsTo const& other) const noexcept
395 {
396 return (_referencedFieldValue <=> other.Value()) != std::weak_ordering::equivalent;
397 }
398
399 /// Equality comparison operator with a Field.
400 template <detail::FieldElementType T, PrimaryKey IsPrimaryKeyValue = PrimaryKey::No>
401 bool operator==(Field<T, IsPrimaryKeyValue> const& other) const noexcept
402 {
403 return (_referencedFieldValue <=> other.Value()) == std::weak_ordering::equivalent;
404 }
405
406 /// Inequality comparison operator with a Field.
407 template <detail::FieldElementType T, PrimaryKey IsPrimaryKeyValue = PrimaryKey::No>
408 bool operator!=(Field<T, IsPrimaryKeyValue> const& other) const noexcept
409 {
410 return (_referencedFieldValue <=> other.Value()) != std::weak_ordering::equivalent;
411 }
412
413 struct Loader
414 {
415 std::function<std::optional<ReferencedRecord>()> loadReference {};
416 };
417
418 /// Used internally to configure on-demand loading of the record.
419 void SetAutoLoader(Loader loader) noexcept
420 {
421 _loader = std::move(loader);
422 }
423
424 private:
425 void RequireLoaded() const
426 {
427 if (_loaded)
428 return;
429
430 if (_loader.loadReference)
431 {
432 auto value = _loader.loadReference();
433 if (value)
434 {
435 _record = std::make_unique<ReferencedRecord>(std::move(value.value()));
436 _loaded = true;
437 }
438 }
439
440 if constexpr (IsMandatory)
441 if (!_loaded)
442 throw SqlRequireLoadedError(Reflection::TypeNameOf<std::remove_cvref_t<decltype(*this)>>);
443 }
444
445 ValueType _referencedFieldValue {};
446 Loader _loader {};
447 mutable bool _loaded = false;
448 bool _modified = false;
449 mutable std::unique_ptr<ReferencedRecord> _record {};
450};
451
452template <auto ReferencedField, auto ColumnNameOverrideString, SqlNullable Nullable>
453std::ostream& operator<<(std::ostream& os, BelongsTo<ReferencedField, ColumnNameOverrideString, Nullable> const& belongsTo)
454{
455 return os << belongsTo.Value();
456}
457
458namespace detail
459{
460 template <typename T>
461 struct IsBelongsToType: std::false_type
462 {
463 };
464
465 template <auto ReferencedField, auto ColumnNameOverrideString, SqlNullable Nullable>
466 struct IsBelongsToType<BelongsTo<ReferencedField, ColumnNameOverrideString, Nullable>>: std::true_type
467 {
468 };
469
470} // namespace detail
471
472template <typename T>
473constexpr bool IsBelongsTo = detail::IsBelongsToType<std::remove_cvref_t<T>>::value;
474
475template <typename T>
476concept is_belongs_to = IsBelongsTo<T>;
477
478template <typename T>
479constexpr bool IsOptionalBelongsTo = false;
480
481template <is_belongs_to T>
482constexpr bool IsOptionalBelongsTo<T> = T::IsOptional;
483
484template <auto ReferencedField, auto ColumnNameOverrideString, SqlNullable Nullable>
485struct SqlDataBinder<BelongsTo<ReferencedField, ColumnNameOverrideString, Nullable>>
486{
487 using SelfType = BelongsTo<ReferencedField, ColumnNameOverrideString, Nullable>;
488 using InnerType = SelfType::ValueType;
489
490 static constexpr auto ColumnType = SqlDataBinder<InnerType>::ColumnType;
491
492 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN InputParameter(SQLHSTMT stmt,
493 SQLUSMALLINT column,
494 SelfType const& value,
495 SqlDataBinderCallback& cb)
496 {
497 return SqlDataBinder<InnerType>::InputParameter(stmt, column, value.Value(), cb);
498 }
499
500 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN
501 OutputColumn(SQLHSTMT stmt, SQLUSMALLINT column, SelfType* result, SQLLEN* indicator, SqlDataBinderCallback& cb)
502 {
503 auto const sqlReturn = SqlDataBinder<InnerType>::OutputColumn(stmt, column, &result->MutableValue(), indicator, cb);
504 cb.PlanPostProcessOutputColumn([result]() { result->SetModified(true); });
505 return sqlReturn;
506 }
507
508 /// @throws Whatever `SqlDataBinder<InnerType>::GetColumn` throws — this forwards to an arbitrary
509 /// binder and cannot promise more than the one it wraps.
510 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN
511 GetColumn(SQLHSTMT stmt, SQLUSMALLINT column, SelfType* result, SQLLEN* indicator, SqlDataBinderCallback const& cb)
512 {
513 auto const sqlReturn = SqlDataBinder<InnerType>::GetColumn(stmt, column, &result->MutableValue(), indicator, cb);
514 if (SQL_SUCCEEDED(sqlReturn))
515 result->SetModified(true);
516 return sqlReturn;
517 }
518};
519
520} // namespace Lightweight
Represents the many-to-one side of a foreign-key relationship.
Definition BelongsTo.hpp:61
BelongsTo & operator=(BelongsTo &&other) noexcept
Move assignment operator.
LIGHTWEIGHT_FORCE_INLINE void AdoptFetchedRecord(std::optional< ReferencedRecord > record)
static constexpr auto IsAutoIncrementPrimaryKey
Indicates that a BelongsTo field is never an auto-increment primary key.
bool operator==(BelongsTo const &other) const noexcept
Equality comparison operator.
std::weak_ordering operator<=>(Field< T, IsPrimaryKeyValue > const &other) const noexcept
Three-way comparison operator with a Field.
bool operator==(Field< T, IsPrimaryKeyValue > const &other) const noexcept
Equality comparison operator with a Field.
std::weak_ordering operator<=>(BelongsTo const &other) const noexcept
Three-way comparison operator.
bool operator!=(BelongsTo const &other) const noexcept
Inequality comparison operator.
BelongsTo & operator=(SqlNullType) noexcept
Assigns NULL to the relationship, clearing the loaded record.
std::remove_cvref_t< decltype(std::declval< ReferencedRecord >().*ReferencedField)>::ValueType BaseType
Represents the base column type of the foreign key, matching the primary key of the other record.
Definition BelongsTo.hpp:91
LIGHTWEIGHT_FORCE_INLINE constexpr ReferencedRecord & EmplaceRecord()
Emplaces a record into the relationship. This will mark the relationship as loaded.
constexpr BelongsTo(S &&... value) noexcept
Constructs a new BelongsTo with the given value(s) forwarded to the underlying value type.
LIGHTWEIGHT_FORCE_INLINE constexpr ReferencedRecord & operator*(this Self &&self) noexcept
static constexpr auto ReferencedField
The field in the other record that references the current record.
Definition BelongsTo.hpp:64
static constexpr auto IsMandatory
Indicates whether this relationship is mandatory (non-nullable).
LIGHTWEIGHT_FORCE_INLINE constexpr bool operator!() const noexcept
Checks if the field value is NULL.
static constexpr auto IsPrimaryKey
Indicates that a BelongsTo field is never a primary key.
LIGHTWEIGHT_FORCE_INLINE constexpr ReferencedRecord * LoadedRecord() noexcept
Returns the already-loaded referenced record, or nullptr when none is loaded.
BelongsTo & operator=(BelongsTo const &other)
Copy assignment operator.
LIGHTWEIGHT_FORCE_INLINE void BindOutputColumn(SQLSMALLINT outputIndex, Stmt &stmt)
Binds the foreign key value to the given output column index on the statement.
LIGHTWEIGHT_FORCE_INLINE constexpr bool IsModified() const noexcept
Checks if the field is modified.
LIGHTWEIGHT_FORCE_INLINE constexpr ReferencedRecord * operator->(this Self &&self)
constexpr BelongsTo(BelongsTo &&other) noexcept
Move constructor.
LIGHTWEIGHT_FORCE_INLINE constexpr ValueType & MutableValue() noexcept
Retrieves the mutable reference to the value of the field.
BelongsTo & operator=(ReferencedRecord &other)
Assigns a referenced record, updating the foreign key and loaded state.
LIGHTWEIGHT_FORCE_INLINE constexpr ReferencedRecord const * LoadedRecord() const noexcept
Returns the already-loaded referenced record, or nullptr when none is loaded.
LIGHTWEIGHT_FORCE_INLINE constexpr ValueType const & Value() const noexcept
Retrieves the reference to the value of the field.
constexpr BelongsTo(BelongsTo const &other) noexcept
Copy constructor.
LIGHTWEIGHT_FORCE_INLINE constexpr void SetModified(bool value) noexcept
Marks the field as modified or unmodified.
std::conditional_t< Nullable==SqlNullable::Null, std::optional< BaseType >, BaseType > ValueType
Definition BelongsTo.hpp:96
LIGHTWEIGHT_FORCE_INLINE constexpr decltype(auto) Record(this Self &&self)
void SetAutoLoader(Loader loader) noexcept
Used internally to configure on-demand loading of the record.
constexpr BelongsTo(ReferencedRecord const &other) noexcept
Constructs a new BelongsTo from the given referenced record, copying its primary key.
MemberClassType< decltype(TheReferencedField)> ReferencedRecord
Represents the record type of the other field.
Definition BelongsTo.hpp:85
LIGHTWEIGHT_FORCE_INLINE constexpr ReferencedRecord const & Record(this Self &&self)
Retrieves a record from the relationship. When the record is not optional.
static constexpr auto IsOptional
Indicates whether this relationship is optional (nullable).
Definition BelongsTo.hpp:99
static constexpr std::string_view ColumnNameOverride
If not an empty string, this value will be used as the column name in the database.
Definition BelongsTo.hpp:67
bool operator!=(Field< T, IsPrimaryKeyValue > const &other) const noexcept
Inequality comparison operator with a Field.
Represents a single column in a table.
Definition Field.hpp:84