From 2e354b435c45df45af750ea3dd41e84e55e33e4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sascha=20K=C3=BChl?= Date: Fri, 5 Dec 2025 15:38:35 +0100 Subject: [PATCH] progress on combining foreign_node_completer and relation_completer --- demo/author.hpp | 2 +- demo/book.hpp | 2 +- demo/main.cpp | 4 +- demo/sandbox.cpp | 6 +- demo/work/jobs/IdListPayload.hpp | 2 +- .../matador/object/foreign_node_completer.hpp | 46 ++++++++---- .../matador/object/many_to_many_relation.hpp | 6 +- include/matador/object/relation_completer.hpp | 72 +++++++++---------- include/matador/object/repository.hpp | 12 +++- include/matador/object/repository_node.hpp | 1 + include/matador/orm/session.hpp | 6 +- .../sql/internal/object_result_binder.hpp | 12 ++-- .../matador/sql/object_parameter_binder.hpp | 6 +- include/matador/sql/object_pk_binder.hpp | 6 +- include/matador/utils/foreign_attributes.hpp | 5 +- source/core/object/foreign_node_completer.cpp | 11 --- source/core/object/repository.cpp | 8 +++ test/backends/SchemaTest.cpp | 26 ++++++- test/core/object/SchemaTest.cpp | 66 ++++++++++++----- test/models/order_details.hpp | 4 +- 20 files changed, 195 insertions(+), 108 deletions(-) diff --git a/demo/author.hpp b/demo/author.hpp index 0e255df..6726938 100644 --- a/demo/author.hpp +++ b/demo/author.hpp @@ -27,7 +27,7 @@ struct author { field::attribute( op, "date_of_birth", date_of_birth, 31 ); field::attribute( op, "year_of_birth", year_of_birth ); field::attribute( op, "distinguished", distinguished ); - field::has_many( op, "books", books, "author_id", matador::utils::default_foreign_attributes ); + field::has_many( op, "books", books, "author_id", matador::utils::CascadeNoneFetchLazy ); } }; } diff --git a/demo/book.hpp b/demo/book.hpp index d8ae0fc..932d0b5 100644 --- a/demo/book.hpp +++ b/demo/book.hpp @@ -18,7 +18,7 @@ struct book { namespace field = matador::access; field::primary_key( op, "id", id ); field::attribute( op, "title", title, 511 ); - field::belongs_to( op, "author_id", book_author, matador::utils::default_foreign_attributes ); + field::belongs_to( op, "author_id", book_author, matador::utils::CascadeNoneFetchLazy ); field::attribute( op, "published_in", published_in ); } }; diff --git a/demo/main.cpp b/demo/main.cpp index a47a8ab..d80af68 100644 --- a/demo/main.cpp +++ b/demo/main.cpp @@ -49,7 +49,7 @@ struct book { namespace field = matador::access; field::primary_key( op, "id", id ); field::attribute( op, "title", title, 511 ); - field::has_one( op, "author_id", book_author, matador::utils::default_foreign_attributes ); + field::has_one( op, "author_id", book_author, matador::utils::CascadeNoneFetchLazy ); field::attribute( op, "published_in", published_in ); } }; @@ -89,7 +89,7 @@ struct job { void process( Operator& op ) { namespace field = matador::access; field::primary_key( op, "id", id ); - field::belongs_to( op, "payload", data, matador::utils::default_foreign_attributes ); + field::belongs_to( op, "payload", data, matador::utils::CascadeNoneFetchLazy ); field::attribute( op, "type", type, 511 ); field::attribute( op, "description", description, 511 ); field::attribute( op, "state", state ); diff --git a/demo/sandbox.cpp b/demo/sandbox.cpp index 60a2204..d86f015 100644 --- a/demo/sandbox.cpp +++ b/demo/sandbox.cpp @@ -82,7 +82,7 @@ struct profile { field::primary_key( op, "id", id ); field::attribute( op, "first_name", first_name, 255 ); field::attribute( op, "last_name", last_name, 255 ); - field::belongs_to( op, "user_id", user, matador::utils::default_foreign_attributes ); + field::belongs_to( op, "user_id", user, matador::utils::CascadeNoneFetchLazy ); } }; struct user { @@ -95,7 +95,7 @@ struct user { namespace field = matador::access; field::primary_key( op, "id", id ); field::attribute( op, "username", username, 255 ); - field::has_one(op, "profile_id", profile, matador::utils::default_foreign_attributes ); + field::has_one(op, "profile_id", profile, matador::utils::CascadeNoneFetchLazy ); } }; @@ -119,7 +119,7 @@ struct person_repo { void process(Operator &op) { namespace field = matador::access; field::primary_key( op, "id", id ); - field::has_many( op, "person_list", person_list, "person_id", matador::utils::default_foreign_attributes ); + field::has_many( op, "person_list", person_list, "person_id", matador::utils::CascadeNoneFetchLazy ); } }; } diff --git a/demo/work/jobs/IdListPayload.hpp b/demo/work/jobs/IdListPayload.hpp index 7e07072..acc3e7a 100644 --- a/demo/work/jobs/IdListPayload.hpp +++ b/demo/work/jobs/IdListPayload.hpp @@ -14,7 +14,7 @@ struct IdListPayload : Payload { void process( Operator& op ) { namespace field = matador::access; field::process( op, *matador::base_class( this ) ); - field::has_many( op, "payload_ids", payload_ids, "payload_id", matador::utils::default_foreign_attributes ); + field::has_many( op, "payload_ids", payload_ids, "payload_id", matador::utils::CascadeNoneFetchLazy ); } }; } diff --git a/include/matador/object/foreign_node_completer.hpp b/include/matador/object/foreign_node_completer.hpp index 6babbad..3ad1cc6 100644 --- a/include/matador/object/foreign_node_completer.hpp +++ b/include/matador/object/foreign_node_completer.hpp @@ -19,6 +19,7 @@ namespace matador::object { * that all foreign nodes needed by the given node * relations are attached in schema. */ +template < typename NodeType > class foreign_node_completer final { private: using node_ptr = std::shared_ptr; @@ -27,9 +28,9 @@ public: template static void complete(const std::shared_ptr &node) { internal::shadow_repository shadow(node->repo_); - foreign_node_completer completer(shadow); + foreign_node_completer completer(shadow); - completer.complete_node(node); + completer.complete_node(node); } template @@ -56,13 +57,14 @@ public: void on_has_many_to_many(const char *id, CollectionType &collection, const utils::foreign_attributes &attr); private: - explicit foreign_node_completer(internal::shadow_repository &shadow); + explicit foreign_node_completer(internal::shadow_repository &shadow) + : repo_(shadow) + , log_(logger::create_logger("node_completer")) {} - template void complete_node(const std::shared_ptr &node) { nodes_.push(node); - Type obj; + NodeType obj; access::process(*this, obj); nodes_.pop(); } @@ -77,6 +79,21 @@ private: complete(result.value()); } } + + template + void attach_relation_node(const std::string &name) { + if (repo_.contains(typeid(Type))) { + return; + } + const auto node = repository_node::make_node(repo_.repo(), ""); + if (auto result = repo_.attach_node(node)) { + complete(result.value()); + } + } +private: + template< typename Type > + friend class foreign_node_completer; + private: std::stack nodes_; internal::shadow_repository &repo_; @@ -85,29 +102,34 @@ private: }; +template < typename NodeType > template -void foreign_node_completer::on_belongs_to( const char* /*id*/, ForeignPointerType&, const utils::foreign_attributes& ) { +void foreign_node_completer::on_belongs_to( const char* /*id*/, ForeignPointerType&, const utils::foreign_attributes& ) { attach_node(); } +template < typename NodeType > template -void foreign_node_completer::on_has_one( const char*, ForeignPointerType&, const utils::foreign_attributes& ) { +void foreign_node_completer::on_has_one( const char*, ForeignPointerType&, const utils::foreign_attributes& ) { attach_node(); } +template < typename NodeType > template -void foreign_node_completer::on_has_many( const char* /*id*/, CollectionType&, const char* /*join_column*/, const utils::foreign_attributes& /*attr*/, std::enable_if_t::value>* ) { +void foreign_node_completer::on_has_many( const char* /*id*/, CollectionType&, const char* /*join_column*/, const utils::foreign_attributes& /*attr*/, std::enable_if_t::value>* ) { attach_node(); } +template < typename NodeType > template -void foreign_node_completer::on_has_many_to_many( const char* /*id*/, CollectionType& /*collection*/, const char* /*join_column*/, const char* /*inverse_join_column*/, const utils::foreign_attributes& /*attr*/ ) { - attach_node(/*id*/); +void foreign_node_completer::on_has_many_to_many( const char* id, CollectionType& /*collection*/, const char* /*join_column*/, const char* /*inverse_join_column*/, const utils::foreign_attributes& /*attr*/ ) { + attach_relation_node(id); } +template < typename NodeType > template -void foreign_node_completer::on_has_many_to_many( const char* /*id*/, CollectionType& /*collection*/, const utils::foreign_attributes& /*attr*/ ) { - attach_node(/*id*/); +void foreign_node_completer::on_has_many_to_many( const char* id, CollectionType& /*collection*/, const utils::foreign_attributes& /*attr*/ ) { + attach_relation_node(id); } } diff --git a/include/matador/object/many_to_many_relation.hpp b/include/matador/object/many_to_many_relation.hpp index 96ad29d..1b22211 100644 --- a/include/matador/object/many_to_many_relation.hpp +++ b/include/matador/object/many_to_many_relation.hpp @@ -19,8 +19,8 @@ public: template void process(Operator &op) { namespace field = matador::access; - field::belongs_to(op, local_name_.c_str(), local_, utils::default_foreign_attributes); - field::belongs_to(op, remote_name_.c_str(), remote_, utils::default_foreign_attributes); + field::belongs_to(op, local_name_.c_str(), local_, utils::CascadeNoneFetchLazy); + field::belongs_to(op, remote_name_.c_str(), remote_, utils::CascadeNoneFetchLazy); } object_ptr local() const { return local_; } @@ -44,7 +44,7 @@ public: template void process(Operator &op) { namespace field = matador::access; - field::belongs_to(op, local_name_.c_str(), local_, utils::default_foreign_attributes); + field::belongs_to(op, local_name_.c_str(), local_, utils::CascadeNoneFetchLazy); field::attribute(op, type_name_.c_str(), value_); } diff --git a/include/matador/object/relation_completer.hpp b/include/matador/object/relation_completer.hpp index e871359..38aedac 100644 --- a/include/matador/object/relation_completer.hpp +++ b/include/matador/object/relation_completer.hpp @@ -165,14 +165,14 @@ void relation_completer::on_has_many(const char *id, CollectionType &, using value_type = typename CollectionType::value_type::value_type; using relation_value_type = many_to_many_relation; - // Check if the object_ptr type is already inserted in the schema (by id) - auto result = schema_.find_node(typeid(value_type)); - if (!result) { - // Todo: throw internal error or attach node - return; - } - const auto foreign_node = result.value(); - // has foreign node corresponding join column (join_column) + // Check if the object_ptr type is already inserted in the schema (by id) + auto result = schema_.find_node(typeid(value_type)); + if (!result) { + // Todo: throw internal error or attach node + return; + } + const auto foreign_node = result.value(); + // has foreign node corresponding join column (join_column) if (const auto it = foreign_node->info_->find_relation_endpoint(typeid(Type)); it != foreign_node->info().endpoint_end()) { // corresponding belongs_to is available and was called (has_many <-> belongs_to) @@ -257,35 +257,35 @@ void relation_completer::on_has_many_to_many(const char *id, const auto foreign_node = result.value(); result = schema_.find_node(id); - if (!result) { - // Relation not found. - auto creator = [join_column, inverse_join_column] { - return std::make_unique(join_column, inverse_join_column); - }; - - result = repository_node::make_relation_node(schema_.repo(), id, std::move(creator)); - if (!result) { - // Todo: throw internal error - return; - } - - auto& node = result.value(); - const auto local_endpoint = std::make_shared(id, relation_type::HAS_MANY, node); - const auto join_endpoint = std::make_shared(join_column, relation_type::BELONGS_TO, nodes_.top()); - const auto inverse_join_endpoint = std::make_shared(inverse_join_column, relation_type::BELONGS_TO, foreign_node); - const auto foreign_endpoint = std::make_shared(id, relation_type::HAS_MANY, node); - // register relation endpoint in local node - nodes_.top()->info_->register_relation_endpoint(typeid(typename CollectionType::value_type::value_type), local_endpoint); - // register relation endpoint in foreign node - foreign_node->info_->register_relation_endpoint(nodes_.top()->type_index(), foreign_endpoint); - // register endpoints in relation node - node->info_->register_relation_endpoint(nodes_.top()->type_index(), join_endpoint); - node->info_->register_relation_endpoint(typeid(typename CollectionType::value_type::value_type), inverse_join_endpoint); - - // link endpoints - link_relation_endpoints(local_endpoint, join_endpoint); - link_relation_endpoints(foreign_endpoint, inverse_join_endpoint); + if (result) { + return; } + // Relation not found. + auto creator = [join_column, inverse_join_column] { + return std::make_unique(join_column, inverse_join_column); + }; + + result = repository_node::make_relation_node(schema_.repo(), id, std::move(creator)); + if (!result) { + // Todo: throw internal error + return; + } + + auto& node = result.value(); + const auto local_endpoint = std::make_shared(id, relation_type::HAS_MANY, node); + const auto join_endpoint = std::make_shared(join_column, relation_type::BELONGS_TO, nodes_.top()); + const auto inverse_join_endpoint = std::make_shared(inverse_join_column, relation_type::BELONGS_TO, foreign_node); + const auto foreign_endpoint = std::make_shared(id, relation_type::HAS_MANY, node); + // register relation endpoint in local node + nodes_.top()->info_->register_relation_endpoint(typeid(typename CollectionType::value_type::value_type), local_endpoint); + // register relation endpoint in foreign node + foreign_node->info_->register_relation_endpoint(nodes_.top()->type_index(), foreign_endpoint); + // register endpoints in relation node + node->info_->register_relation_endpoint(nodes_.top()->type_index(), join_endpoint); + node->info_->register_relation_endpoint(typeid(typename CollectionType::value_type::value_type), inverse_join_endpoint); + // link endpoints + link_relation_endpoints(local_endpoint, join_endpoint); + link_relation_endpoints(foreign_endpoint, inverse_join_endpoint); } template diff --git a/include/matador/object/repository.hpp b/include/matador/object/repository.hpp index 12462d2..f20e4d9 100644 --- a/include/matador/object/repository.hpp +++ b/include/matador/object/repository.hpp @@ -45,7 +45,7 @@ public: if (auto result = attach_node(node, parent); !result) { return utils::failure(result.err()); } - foreign_node_completer::complete(node); + foreign_node_completer::template complete(node); relation_completer::complete(node); } else if (!has_node(name)) { it->second->update_name(name); @@ -177,10 +177,19 @@ private: static void insert_node(const node_ptr &parent, const node_ptr &child); void remove_node(const node_ptr &node); + bool expecting_relation_node(const std::string &name) const; + template + void expect_relation_node(const std::string &name) { + expected_relation_nodes_[name] = std::type_index(typeid(NodeType)); + } + void remove_expected_relation_node(const std::string &name); + private: friend class internal::shadow_repository; friend class repository_node; friend class attribute_generator; + template < typename NodeType > + friend class foreign_node_completer; std::string name_; std::shared_ptr root_; @@ -190,6 +199,7 @@ private: logger::logger log_; std::unordered_map missing_references_; + std::unordered_map expected_relation_nodes_; }; } diff --git a/include/matador/object/repository_node.hpp b/include/matador/object/repository_node.hpp index 2df49dd..a669558 100644 --- a/include/matador/object/repository_node.hpp +++ b/include/matador/object/repository_node.hpp @@ -96,6 +96,7 @@ private: friend class repository; template friend class relation_completer; + template < typename NodeType > friend class foreign_node_completer; friend class const_repository_node_iterator; diff --git a/include/matador/orm/session.hpp b/include/matador/orm/session.hpp index d3e0f7e..6e5f503 100644 --- a/include/matador/orm/session.hpp +++ b/include/matador/orm/session.hpp @@ -212,15 +212,15 @@ public: template < class Type > static void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {} template < class Pointer > - static void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/ = utils::default_foreign_attributes) {} + static void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/ = utils::CascadeNoneFetchLazy) {} template < class Pointer > - static void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/ = utils::default_foreign_attributes) {} + static void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/ = utils::CascadeNoneFetchLazy) {} template static void on_has_many(const char * /*id*/, ContainerType &/*c*/, const char * /*join_column*/, - const utils::foreign_attributes &/*attr*/ = utils::default_foreign_attributes) {} + const utils::foreign_attributes &/*attr*/ = utils::CascadeNoneFetchLazy) {} template static void on_has_many_to_many(const char * /*id*/, ContainerType &/*c*/, diff --git a/include/matador/sql/internal/object_result_binder.hpp b/include/matador/sql/internal/object_result_binder.hpp index ce5613a..32a463c 100644 --- a/include/matador/sql/internal/object_result_binder.hpp +++ b/include/matador/sql/internal/object_result_binder.hpp @@ -34,15 +34,15 @@ public: template < class Type > static void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {} template < class Pointer > - static void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/ = utils::default_foreign_attributes) {} + static void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/ = utils::CascadeNoneFetchLazy) {} template < class Pointer > - static void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/ = utils::default_foreign_attributes) {} + static void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/ = utils::CascadeNoneFetchLazy) {} template static void on_has_many(const char * /*id*/, ContainerType &/*c*/, const char * /*join_column*/, - const utils::foreign_attributes &/*attr*/ = utils::default_foreign_attributes) {} + const utils::foreign_attributes &/*attr*/ = utils::CascadeNoneFetchLazy) {} template static void on_has_many_to_many(const char * /*id*/, ContainerType &/*c*/, @@ -88,18 +88,18 @@ public: void on_attribute(const char *id, utils::value &val, const utils::field_attributes &attr = utils::null_attributes); template class Pointer> - void on_belongs_to(const char *id, Pointer &x, const utils::foreign_attributes &/*attr*/ = utils::default_foreign_attributes) { + void on_belongs_to(const char *id, Pointer &x, const utils::foreign_attributes &/*attr*/ = utils::CascadeNoneFetchLazy) { fk_result_binder_.bind(*x, id, index_++, *binder_); } template class Pointer> - void on_has_one(const char *id, Pointer &x, const utils::foreign_attributes &/*attr*/ = utils::default_foreign_attributes) { + void on_has_one(const char *id, Pointer &x, const utils::foreign_attributes &/*attr*/ = utils::CascadeNoneFetchLazy) { fk_result_binder_.bind(*x, id, index_++, *binder_); } template static void on_has_many(const char * /*id*/, ContainerType &/*c*/, const char * /*join_column*/, - const utils::foreign_attributes &/*attr*/ = utils::default_foreign_attributes) {} + const utils::foreign_attributes &/*attr*/ = utils::CascadeNoneFetchLazy) {} template static void on_has_many_to_many(const char * /*id*/, ContainerType &/*c*/, diff --git a/include/matador/sql/object_parameter_binder.hpp b/include/matador/sql/object_parameter_binder.hpp index 554ac6a..0c1c3db 100644 --- a/include/matador/sql/object_parameter_binder.hpp +++ b/include/matador/sql/object_parameter_binder.hpp @@ -29,18 +29,18 @@ public: } template class Pointer> - void on_belongs_to(const char * /*id*/, Pointer &x, const utils::foreign_attributes &/*attr*/ = utils::default_foreign_attributes) { + void on_belongs_to(const char * /*id*/, Pointer &x, const utils::foreign_attributes &/*attr*/ = utils::CascadeNoneFetchLazy) { pk_binder_.bind(*x, index_++, *binder_); } template class Pointer> - void on_has_one(const char * /*id*/, Pointer &x, const utils::foreign_attributes &/*attr*/ = utils::default_foreign_attributes) { + void on_has_one(const char * /*id*/, Pointer &x, const utils::foreign_attributes &/*attr*/ = utils::CascadeNoneFetchLazy) { pk_binder_.bind(*x, index_++, *binder_); } template static void on_has_many(const char * /*id*/, ContainerType &/*c*/, const char * /*join_column*/, - const utils::foreign_attributes &/*attr*/ = utils::default_foreign_attributes) {} + const utils::foreign_attributes &/*attr*/ = utils::CascadeNoneFetchLazy) {} template static void on_has_many_to_many(const char * /*id*/, ContainerType &/*c*/, diff --git a/include/matador/sql/object_pk_binder.hpp b/include/matador/sql/object_pk_binder.hpp index 17a9cd2..32e4f62 100644 --- a/include/matador/sql/object_pk_binder.hpp +++ b/include/matador/sql/object_pk_binder.hpp @@ -26,15 +26,15 @@ public: template < class Type > static void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {} template < class Pointer > - static void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/ = utils::default_foreign_attributes) {} + static void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/ = utils::CascadeNoneFetchLazy) {} template < class Pointer > - static void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/ = utils::default_foreign_attributes) {} + static void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/ = utils::CascadeNoneFetchLazy) {} template static void on_has_many(const char * /*id*/, ContainerType &/*c*/, const char * /*join_column*/, - const utils::foreign_attributes &/*attr*/ = utils::default_foreign_attributes) {} + const utils::foreign_attributes &/*attr*/ = utils::CascadeNoneFetchLazy) {} template static void on_has_many_to_many(const char * /*id*/, ContainerType &/*c*/, diff --git a/include/matador/utils/foreign_attributes.hpp b/include/matador/utils/foreign_attributes.hpp index b03a6f7..ff72402 100644 --- a/include/matador/utils/foreign_attributes.hpp +++ b/include/matador/utils/foreign_attributes.hpp @@ -4,6 +4,7 @@ #include "matador/utils/fetch_type.hpp" #include "matador/utils/cascade_type.hpp" +// ReSharper disable CppNonExplicitConvertingConstructor namespace matador::utils { class foreign_attributes @@ -27,7 +28,9 @@ private: fetch_type fetch_{fetch_type::LAZY}; }; -const foreign_attributes default_foreign_attributes {}; +const foreign_attributes CascadeNoneFetchLazy {}; +const foreign_attributes CascadeAllFetchLazy {cascade_type::ALL, fetch_type::LAZY}; +const foreign_attributes CascadeAllFetchEager {cascade_type::ALL, fetch_type::EAGER}; } diff --git a/source/core/object/foreign_node_completer.cpp b/source/core/object/foreign_node_completer.cpp index 9c08e37..e69de29 100644 --- a/source/core/object/foreign_node_completer.cpp +++ b/source/core/object/foreign_node_completer.cpp @@ -1,11 +0,0 @@ -#include "matador/object/foreign_node_completer.hpp" - -#include "matador/object/repository.hpp" - -#include "matador/logger/logger.hpp" - -namespace matador::object { -foreign_node_completer::foreign_node_completer(internal::shadow_repository &shadow) -: repo_(shadow) -, log_(logger::create_logger("node_completer")) {} -} \ No newline at end of file diff --git a/source/core/object/repository.cpp b/source/core/object/repository.cpp index 7c3fdb8..5a78b3b 100644 --- a/source/core/object/repository.cpp +++ b/source/core/object/repository.cpp @@ -201,4 +201,12 @@ bool repository::has_node(const std::type_index &index) const { bool repository::has_node(const node_ptr& node) const { return nodes_by_name_.count(node->name()) > 0 || nodes_by_type_.count(node->type_index()) > 0; } + +bool repository::expecting_relation_node( const std::string& name ) const { + return expected_relation_nodes_.count(name) > 0; +} + +void repository::remove_expected_relation_node( const std::string& name ) { + expected_relation_nodes_.erase(name); +} } // namespace matador::object diff --git a/test/backends/SchemaTest.cpp b/test/backends/SchemaTest.cpp index 5f2deae..81ba5a2 100644 --- a/test/backends/SchemaTest.cpp +++ b/test/backends/SchemaTest.cpp @@ -8,11 +8,12 @@ #include "matador/orm/schema.hpp" #include "../models/department.hpp" +#include "../models/recipe.hpp" using namespace matador; using namespace matador::test; -TEST_CASE_METHOD(SchemaFixture, "Test schema one-two-many", "[schema]") { +TEST_CASE_METHOD(SchemaFixture, "Test schema one-two-many", "[schema][one-to-many]") { using namespace matador::test; orm::schema repo(pool/*, "NoopSchema"*/); @@ -34,3 +35,26 @@ TEST_CASE_METHOD(SchemaFixture, "Test schema one-two-many", "[schema]") { REQUIRE(exists_result.is_ok()); REQUIRE(!exists_result.value()); } + +TEST_CASE_METHOD(SchemaFixture, "Test schema many-to-many", "[schema][many-to-many]") { + using namespace matador::test; + orm::schema repo(pool/*, "NoopSchema"*/); + + auto result = repo.attach("recipes") + .and_then( [&repo] { return repo.attach("ingredients"); } ); + REQUIRE(result); + + result = repo.create(); + REQUIRE(result); + + auto exists_result = repo.table_exists("recipes"); + REQUIRE(exists_result.is_ok()); + REQUIRE(exists_result.value()); + + result = repo.drop(); + REQUIRE(result); + + exists_result = repo.table_exists("recipes"); + REQUIRE(exists_result.is_ok()); + REQUIRE(!exists_result.value()); +} \ No newline at end of file diff --git a/test/core/object/SchemaTest.cpp b/test/core/object/SchemaTest.cpp index 070e5bc..e748078 100644 --- a/test/core/object/SchemaTest.cpp +++ b/test/core/object/SchemaTest.cpp @@ -3,6 +3,7 @@ #include "matador/object/repository.hpp" #include "../../models/department.hpp" +#include "../../models/recipe.hpp" struct node {}; @@ -30,50 +31,51 @@ struct names { }; TEST_CASE("Test empty prototype tree", "[schema_node][empty]") { - const object::repository tree; + const object::repository repo; - REQUIRE( tree.empty() ); + REQUIRE( repo.empty() ); } TEST_CASE("Test add type to prototype tree", "[schema_node][add]") { - object::repository tree; + object::repository repo; - REQUIRE( tree.empty() ); + REQUIRE( repo.empty() ); - auto res = tree.attach("person"); + auto res = repo.attach("person"); REQUIRE( res.is_ok() ); - res = tree.attach("student"); + res = repo.attach("student"); REQUIRE( res.is_ok() ); - res = tree.attach("teacher"); + res = repo.attach("teacher"); REQUIRE( res.is_ok() ); - REQUIRE( !tree.empty() ); - REQUIRE( tree.size() == 3 ); + REQUIRE( !repo.empty() ); + REQUIRE( repo.size() == 3 ); } TEST_CASE("Test next and previous of schema node", "[schema_node][next][previous]") { - object::repository tree; + object::repository repo; - REQUIRE( tree.empty() ); + REQUIRE( repo.empty() ); - auto res = tree.attach("person"); + auto res = repo.attach("person"); REQUIRE( res.is_ok() ); - REQUIRE( tree.size() == 1 ); + REQUIRE( repo.size() == 1 ); - auto it = tree.begin(); + auto it = repo.begin(); REQUIRE( it->name() == "person" ); REQUIRE( (--it)->name() == "person" ); - REQUIRE( ++it == tree.end() ); + REQUIRE( ++it == repo.end() ); } TEST_CASE("Test automatic creating of a relation table with foreign key", "[schema][relation_table][foreign_key]") { - object::repository tree; + object::repository repo; - REQUIRE( tree.empty() ); + REQUIRE( repo.empty() ); - auto res = tree.attach("department"); + auto res = repo.attach("department"); REQUIRE( res.is_ok() ); + REQUIRE(repo.size() == 2); } TEST_CASE("Test automatic creating of a relation table with values", "[schema][relation_table][values]") { @@ -83,4 +85,32 @@ TEST_CASE("Test automatic creating of a relation table with values", "[schema][r auto res = repo.attach("names"); REQUIRE( res.is_ok() ); +} + +TEST_CASE("Test one to many", "[relation][one-to-many]") { + object::repository repo; + + REQUIRE( repo.empty() ); + + auto res = repo.attach("departments") + .and_then( [&repo] { return repo.attach("employees"); } ); + REQUIRE( res.is_ok() ); + REQUIRE(repo.size() == 2); + REQUIRE(repo.contains("departments")); + REQUIRE(repo.contains("employees")); +} + +TEST_CASE("Test many to many relation", "[relation][many-to-many]") { + object::repository repo; + + REQUIRE(repo.empty()); + + auto result = repo.attach("recipes") + .and_then( [&repo] { return repo.attach("ingredients"); } ); + REQUIRE(result); + + REQUIRE(repo.size() == 3); + REQUIRE(repo.contains("ingredients")); + REQUIRE(repo.contains("recipes")); + REQUIRE(repo.contains("recipe_ingredients")); } \ No newline at end of file diff --git a/test/models/order_details.hpp b/test/models/order_details.hpp index ebc93a8..98da120 100644 --- a/test/models/order_details.hpp +++ b/test/models/order_details.hpp @@ -21,8 +21,8 @@ struct order_details void process(Operator &op) { namespace field = matador::access; field::primary_key(op, "order_details_id", order_details_id); - field::belongs_to(op, "order_id", order_, utils::default_foreign_attributes); - field::has_one(op, "product_id", product_, utils::default_foreign_attributes); + field::belongs_to(op, "order_id", order_, utils::CascadeNoneFetchLazy); + field::has_one(op, "product_id", product_, utils::CascadeNoneFetchLazy); } };