9#include <reflection-cpp/reflection.hpp>
65template <
typename ReferencedRecordT,
72 "The selector template arguments of HasManyThrough must be foreign key column names "
73 "(a SqlRealName) or std::nullopt to resolve the relationship automatically.");
75 static_assert(!IsThrough<ReferencedRecordT>,
76 "The referenced record of HasManyThrough must not be wrapped in Through<>, "
77 "only the join record is.");
98 using iterator = ReferencedRecordList::iterator;
112 return Load().transform([
this] {
return std::reference_wrapper<ReferencedRecordList const> { *_records }; });
118 return Load().transform([
this] {
return std::reference_wrapper<ReferencedRecordList> { *_records }; });
124 _records = { std::move(records) };
125 _count = _records->size();
135 return _records->size();
139 return std::unexpected { *_loadError };
143 auto counted = _loader.
count();
147 _loadError = counted.error();
154 return Count().transform([](std::size_t count) {
return count == 0; });
164 return *LoadOrThrow().at(index);
170 return *LoadOrThrow().at(index);
179 return *LoadOrThrow()[index];
185 return *LoadOrThrow()[index];
192 return LoadOrThrow().begin();
199 return LoadOrThrow().end();
206 return std::as_const(LoadOrThrow()).begin();
213 return std::as_const(LoadOrThrow()).end();
223 std::function<RelationResult<size_t>()>
count;
225 std::function<RelationResult<ReferencedRecordList>()>
all;
233 _loader = std::move(loader);
240 _count = std::nullopt;
241 _records = std::nullopt;
252 template <
typename Callable>
255 if (!_records && !_loadError && _loader.
each)
257 auto streamed = _loader.
each(callable);
259 _loadError = streamed.error();
264 for (
auto const& record: records)
279 return std::unexpected { *_loadError };
283 auto loaded = _loader.
all();
287 _loadError = loaded.error();
288 return std::unexpected { loaded.error() };
290 _records = std::move(*loaded);
297 if (
auto const loaded = Load(); !loaded)
298 throw SqlRequireLoadedError(Reflection::TypeNameOf<std::remove_cvref_t<
decltype(*
this)>>, loaded.error());
304 mutable std::optional<size_t> _count;
305 mutable std::optional<ReferencedRecordList> _records;
306 mutable std::optional<RelationError> _loadError;
311 template <
typename T>
312 struct IsHasManyThroughType: std::false_type
316 template <
typename ReferencedRecordT,
typename ThroughSpec, auto OwnerSelector, auto ReferencedSelector>
317 struct IsHasManyThroughType<HasManyThrough<ReferencedRecordT, ThroughSpec, OwnerSelector, ReferencedSelector>>:
325constexpr bool IsHasManyThrough = detail::IsHasManyThroughType<std::remove_cvref_t<T>>::value;
This API represents a many-to-many relationship between two records through a third record.
void SetAutoLoader(Loader loader) noexcept
Used internally to configure on-demand loading of the records.
ThroughRecordOf< ThroughSpec > ThroughRecord
The record type of the "through" side of the relationship.
ReferencedRecord & At(std::size_t index)
Retrieves the record at the given index, loading the records on first access.
const_iterator begin() const
ReferencedRecord const & operator[](std::size_t index) const
Retrieves the record at the given index, loading the records on first access.
static constexpr auto OwnerSelector
Singles out the join record's foreign key pointing at the record owning this relationship.
RelationResult< void > Reload()
Loads the records from the database again, forgetting what was loaded or known before.
const_iterator end() const
ReferencedRecordList::const_iterator const_iterator
Const iterator type for the list of records.
RelationResult< std::reference_wrapper< ReferencedRecordList > > All()
Retrieves the records, loading them on first access.
std::vector< std::shared_ptr< ReferencedRecord > > ReferencedRecordList
The list of records on the "many" side of the relationship.
ReferencedRecordT ReferencedRecord
The record type of the "many" side of the relationship.
std::weak_ordering operator<=>(HasManyThrough const &other) const noexcept=default
Default three-way comparison operator.
RelationResult< void > Each(Callable const &callable)
Calls callable for each record, without holding them all in memory when not loaded yet.
ReferencedRecordList & Emplace(ReferencedRecordList &&records) noexcept
Emplaces the given list of records into this relationship.
static constexpr auto ReferencedSelector
Singles out the join record's foreign key pointing at ReferencedRecord.
ReferencedRecord value_type
Value type for range-based iteration.
ReferencedRecord const & At(std::size_t index) const
Retrieves the record at the given index, loading the records on first access.
RelationResult< bool > IsEmpty() const
ReferencedRecord & operator[](std::size_t index)
Retrieves the record at the given index, loading the records on first access.
RelationResult< std::reference_wrapper< ReferencedRecordList const > > All() const
Retrieves the records, loading them on first access.
RelationResult< std::size_t > Count() const
ReferencedRecordList::iterator iterator
Iterator type for the list of records.
Constrains what may be used to single out one of several foreign keys into the same table.
detail::ThroughRecordOfHelper< ThroughSpec >::type ThroughRecordOf
Resolves the join record of a through-relationship from its template argument.
std::expected< T, RelationError > RelationResult
The outcome of a relation access: the requested value, or why it is unavailable.
constexpr std::nullopt_t AutoDetectRelation
Selector value meaning "resolve the relationship automatically; the match must be unique".
Carries the deferred loads, installed by the DataMapper.
std::function< RelationResult< void >(std::function< void(ReferencedRecord const &)>)> each
Streams the records to a callback.
std::function< RelationResult< size_t >()> count
Counts the records without loading them.
std::function< RelationResult< ReferencedRecordList >()> all
Loads all records.