5#include "../DataBinder/Core.hpp"
6#include "../DataBinder/SqlNullValue.hpp"
7#include "../SqlStatement.hpp"
8#include "BelongsTo.hpp"
13#include <reflection-cpp/reflection.hpp>
65template <
typename OtherRecord, auto TheInverseSelector = AutoDetectRelation>
69 "The second template argument of HasMany must be a foreign key column name (a SqlRealName) "
70 "or std::nullopt to resolve the relationship automatically.");
86 using iterator = ReferencedRecordList::iterator;
111 template <
typename Callable>
125 return _records ? &*_records :
nullptr;
140 [[nodiscard]] OtherRecord
const&
At(std::size_t index)
const;
143 [[nodiscard]] OtherRecord&
At(std::size_t index);
149 [[nodiscard]] OtherRecord
const&
operator[](std::size_t index)
const;
178 std::function<RelationResult<size_t>()>
count {};
180 std::function<RelationResult<ReferencedRecordList>()>
all {};
187 return std::weak_ordering::equivalent;
205 mutable std::optional<ReferencedRecordList> _records;
206 mutable std::optional<size_t> _count;
207 mutable std::optional<RelationError> _loadError;
212 template <
typename T>
213 struct IsHasManyType: std::false_type
217 template <
typename OtherRecord, auto InverseSelector>
218 struct IsHasManyType<HasMany<OtherRecord, InverseSelector>>: std::true_type
225constexpr bool IsHasMany = detail::IsHasManyType<std::remove_cvref_t<T>>::value;
227template <
typename OtherRecord, auto InverseSelector>
230 _loader = std::move(loader);
233template <
typename OtherRecord, auto InverseSelector>
239 return std::unexpected { *_loadError };
246 auto loaded = _loader.all();
250 _loadError = loaded.error();
251 return std::unexpected { loaded.error() };
253 _records = std::move(*loaded);
257template <
typename OtherRecord, auto InverseSelector>
260 InverseSelector>::LoadOrThrow()
const
262 if (
auto const loaded = Load(); !loaded)
263 throw SqlRequireLoadedError(Reflection::TypeNameOf<std::remove_cvref_t<
decltype(*
this)>>, loaded.error());
267template <
typename OtherRecord, auto InverseSelector>
272 _records = { std::move(records) };
277template <
typename OtherRecord, auto InverseSelector>
281 return Load().transform([
this] {
return std::reference_wrapper<ReferencedRecordList> { *_records }; });
284template <
typename OtherRecord, auto InverseSelector>
288 return Load().transform([
this] {
return std::reference_wrapper<ReferencedRecordList const> { *_records }; });
291template <
typename OtherRecord, auto InverseSelector>
292template <
typename Callable>
295 if (!_records && !_loadError && _loader.each)
297 auto streamed = _loader.each(callable);
299 _loadError = streamed.error();
304 for (
auto const& record: records)
309template <
typename OtherRecord, auto InverseSelector>
313 return _records->size();
317 return std::unexpected { *_loadError };
321 auto counted = _loader.count();
325 _loadError = counted.error();
329template <
typename OtherRecord, auto InverseSelector>
332 return Count().transform([](std::size_t count) {
return count == 0; });
335template <
typename OtherRecord, auto InverseSelector>
338 return *LoadOrThrow().at(index);
341template <
typename OtherRecord, auto InverseSelector>
344 return *LoadOrThrow().at(index);
347template <
typename OtherRecord, auto InverseSelector>
350 return *LoadOrThrow()[index];
353template <
typename OtherRecord, auto InverseSelector>
356 return *LoadOrThrow()[index];
359template <
typename OtherRecord, auto InverseSelector>
361 InverseSelector>::begin()
363 return LoadOrThrow().
begin();
366template <
typename OtherRecord, auto InverseSelector>
369 return LoadOrThrow().end();
372template <
typename OtherRecord, auto InverseSelector>
374 InverseSelector>::begin()
const
376 return std::as_const(LoadOrThrow()).
begin();
379template <
typename OtherRecord, auto InverseSelector>
381 InverseSelector>::end()
const
383 return std::as_const(LoadOrThrow()).
end();
This HasMany<OtherRecord> represents a simple one-to-many relationship between two records.
OtherRecord const & At(std::size_t index) const
Retrieves the record at the given index, loading the records on first access.
OtherRecord & operator[](std::size_t index)
Retrieves the record at the given index, loading the records on first access.
ReferencedRecordList::iterator iterator
Iterator type for the list of records.
constexpr std::weak_ordering operator<=>(HasMany const &other) const noexcept=default
Three-way comparison operator.
ReferencedRecordList::const_iterator const_iterator
Const iterator type for the list of records.
ReferencedRecordList * LoadedRecords() noexcept
Returns the already-loaded records, or nullptr when the relation is not loaded.
OtherRecord & At(std::size_t index)
Retrieves the record at the given index, loading the records on first access.
OtherRecord ReferencedRecord
The record type of the "many" side of the relationship.
constexpr bool operator!=(HasMany const &other) const noexcept=default
Inequality comparison operator.
constexpr bool operator==(HasMany const &other) const noexcept=default
Equality comparison operator.
RelationResult< std::reference_wrapper< ReferencedRecordList const > > All() const
Retrieves the records, loading them on first access.
OtherRecord const & operator[](std::size_t index) const
Retrieves the record at the given index, loading the records on first access.
const_iterator begin() const
OtherRecord value_type
Record type of the "many" side of the relationship.
void SetAutoLoader(Loader loader) noexcept
Used internally to configure on-demand loading of the records.
RelationResult< void > Each(Callable const &callable)
Calls callable for each record, without holding them all in memory when not loaded yet.
static constexpr auto InverseSelector
Singles out the foreign key of OtherRecord that backs this relationship.
RelationResult< std::reference_wrapper< ReferencedRecordList > > All()
Retrieves the records, loading them on first access.
std::vector< std::shared_ptr< OtherRecord > > ReferencedRecordList
The list of records on the "many" side of the relationship.
const_iterator end() const
RelationResult< bool > IsEmpty() const
RelationResult< std::size_t > Count() const
ReferencedRecordList & Emplace(ReferencedRecordList &&records) noexcept
Emplaces the given list of records.
Constrains what may be used to single out one of several foreign keys into the same table.
@ Count
Number of enumerators; not an operation itself.
std::expected< T, RelationError > RelationResult
The outcome of a relation access: the requested value, or why it is unavailable.
Carries the deferred loads, installed by the DataMapper.
std::weak_ordering operator<=>(Loader const &) const noexcept
Loaders carry no comparable state of their own, so any two are considered equivalent.
std::function< RelationResult< void >(std::function< void(ReferencedRecord const &)>)> each
Streams the records to a callback.
std::function< RelationResult< ReferencedRecordList >()> all
Loads all records.
std::function< RelationResult< size_t >()> count
Counts the records without loading them.