has many to many collection resolver progress

This commit is contained in:
Sascha Kühl 2026-02-03 07:26:37 +01:00
parent b2d5db2702
commit 73abd61eba
2 changed files with 94 additions and 6 deletions

View File

@ -69,10 +69,6 @@ public:
static void on_belongs_to(const char * /*id*/, Pointer & /*x*/, const utils::foreign_attributes &/*attr*/) {}
template<class Pointer>
static void on_has_one(const char * /*id*/, Pointer & /*x*/, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType>
static void on_has_many_to_many(const char *, ContainerType &, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType>
static void on_has_many_to_many(const char *, ContainerType &, const utils::foreign_attributes &/*attr*/) {}
template<class CollectionType>
void on_has_many(const char * /*id*/, CollectionType &/*cont*/, const char *join_column, const utils::foreign_attributes &/*attr*/, std::enable_if_t<object::is_object_ptr<typename CollectionType::value_type>::value> * = nullptr) {
@ -99,6 +95,20 @@ public:
}
template<class CollectionType>
void on_has_many_to_many(const char *, CollectionType &, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes &/*attr*/) {
const auto it = schema_.find(typeid(typename CollectionType::value_type::value_type));
if (it == schema_.end()) {
throw query_builder_exception{query_build_error::UnknownType};
}
if (!it->second.node().info().has_primary_key()) {
throw query_builder_exception{query_build_error::MissingPrimaryKey};
}
}
template<class ContainerType>
static void on_has_many_to_many(const char *, ContainerType &, const utils::foreign_attributes &/*attr*/) {}
private:
basic_schema& schema_;
const std::type_index root_type_;

View File

@ -740,8 +740,6 @@ TEST_CASE_METHOD(QueryFixture, "Test load entity with lazy belongs to relation",
}
TEST_CASE_METHOD(QueryFixture, "Test load entity with eager belongs to relation", "[query][belongs_to][eager]") {
// auto result = repo.attach<author>("authors")
// .and_then( [this] { return repo.attach<book>("books"); } )
auto result = repo.attach<book>("books")
.and_then( [this] { return repo.attach<author>("authors"); })
.and_then([this] { return repo.create(db); });
@ -817,3 +815,83 @@ TEST_CASE_METHOD(QueryFixture, "Test load entity with eager belongs to relation"
++index;
}
}
TEST_CASE_METHOD(QueryFixture, "Test load entity with eager has many to many relation", "[query][has_many_to_many][eager]") {
auto result = repo.attach<ingredient>("ingredients")
.and_then( [this] { return repo.attach<recipe>("recipes"); } )
.and_then([this] {return repo.create(db); });
REQUIRE(db.exists(RECIPE.table_name()));
REQUIRE(db.exists(INGREDIENT.table_name()));
REQUIRE(db.exists(RECIPE_INGREDIENT.table_name()));
std::vector<ingredient> ingredients {
{1, "Apple"},
{2, "Strawberry"},
{3, "Pineapple"},
{4, "Sugar"},
{5, "Flour"},
{6, "Butter"},
{7, "Beans"}
};
for (const auto &i: ingredients) {
auto res = query::insert()
.into(INGREDIENT, {INGREDIENT.id, INGREDIENT.name})
.values(i)
.execute(db);
REQUIRE(res.is_ok());
REQUIRE(*res == 1);
}
std::vector<recipe> recipes{
{7, "Apple Crumble"},
{8, "Beans Chili"},
{9, "Fruit Salad"}
};
for (const auto &r: recipes) {
auto res = query::insert()
.into(RECIPE, {RECIPE.id, RECIPE.name})
.values(r)
.execute(db);
REQUIRE(res.is_ok());
REQUIRE(*res == 1);
}
std::vector<std::pair<int, int>> recipe_ingredients {
{ 7, 1 },
{ 7, 4 },
{ 7, 5 },
{ 8, 6 },
{ 8, 7 },
{ 9, 1 },
{ 9, 2 },
{ 9, 3 }
};
for (const auto & [recipe_id, ingredient_id]: recipe_ingredients) {
auto res = query::insert()
.into(RECIPE_INGREDIENT, {RECIPE_INGREDIENT.recipe_id, RECIPE_INGREDIENT.ingredient_id})
.values({recipe_id, ingredient_id})
.execute(db);
REQUIRE(res.is_ok());
REQUIRE(*res == 1);
}
const auto r = RECIPE.as("r");
const auto ri= RECIPE_INGREDIENT.as("ri");
const auto i = INGREDIENT.as("i");
auto ingredients_result = query::select({r.id, r.name, ri.ingredient_id, i.name})
.from(r)
.join_left(ri).on(r.id == ri.recipe_id)
.join_left(i).on(ri.ingredient_id == i.id)
.order_by({i.id, r.id}).asc()
.fetch_all<ingredient>(db);
REQUIRE(ingredients_result.is_ok());
for (const auto &ing : *ingredients_result) {
REQUIRE(!ing->recipes.empty());
}
}