diff --git a/include/matador/object/abstract_collection_resolver.hpp b/include/matador/object/abstract_joined_resolver.hpp similarity index 59% rename from include/matador/object/abstract_collection_resolver.hpp rename to include/matador/object/abstract_joined_resolver.hpp index 84d2ded..534e52d 100644 --- a/include/matador/object/abstract_collection_resolver.hpp +++ b/include/matador/object/abstract_joined_resolver.hpp @@ -1,24 +1,22 @@ #ifndef MATADOR_ABSTRACT_COLLECTION_RESOLVER_HPP #define MATADOR_ABSTRACT_COLLECTION_RESOLVER_HPP +#include "matador/object/abstract_type_resolver.hpp" + #include #include namespace matador::object { -class abstract_collection_resolver { +class abstract_joined_resolver : public abstract_type_resolver { public: - virtual ~abstract_collection_resolver() = default; - [[nodiscard]] const std::type_index& root_type() const; - [[nodiscard]] const std::type_index& type() const; [[nodiscard]] const std::string& collection_name() const; protected: - explicit abstract_collection_resolver(const std::type_index& root_type, const std::type_index& type, std::string collection_name); + explicit abstract_joined_resolver(const std::type_index& root_type, const std::type_index& type, std::string collection_name); public: const std::type_index root_type_; - const std::type_index type_; const std::string collection_name_; }; } diff --git a/include/matador/object/collection_resolver.hpp b/include/matador/object/collection_resolver.hpp index 74e4ed0..a1d564d 100644 --- a/include/matador/object/collection_resolver.hpp +++ b/include/matador/object/collection_resolver.hpp @@ -1,7 +1,7 @@ #ifndef MATADOR_COLLECTION_RESOLVER_HPP #define MATADOR_COLLECTION_RESOLVER_HPP -#include "matador/object/abstract_collection_resolver.hpp" +#include "matador/object/abstract_joined_resolver.hpp" #include #include @@ -12,10 +12,10 @@ class identifier; namespace matador::object { template -class collection_resolver : public abstract_collection_resolver { +class collection_resolver : public abstract_joined_resolver { public: collection_resolver(const std::type_index& root_type, std::string collection_name) - : abstract_collection_resolver(root_type, typeid(Type), std::move(collection_name)) {} + : abstract_joined_resolver(root_type, typeid(Type), std::move(collection_name)) {} virtual std::vector resolve(const utils::identifier& id) = 0; }; diff --git a/include/matador/object/collection_resolver_factory.hpp b/include/matador/object/collection_resolver_factory.hpp index 679323d..f7ea756 100644 --- a/include/matador/object/collection_resolver_factory.hpp +++ b/include/matador/object/collection_resolver_factory.hpp @@ -8,8 +8,8 @@ class abstract_collection_resolver_factory { public: virtual ~abstract_collection_resolver_factory() = default; - [[nodiscard]] virtual std::shared_ptr acquire_collection_resolver(const std::type_index &root_type, const std::type_index &element_type, const std::string &collection_name) const = 0; - virtual void register_collection_resolver(std::shared_ptr &&resolver) = 0; + [[nodiscard]] virtual std::shared_ptr acquire_collection_resolver(const std::type_index &root_type, const std::type_index &element_type, const std::string &collection_name) const = 0; + virtual void register_collection_resolver(std::shared_ptr &&resolver) = 0; }; class collection_resolver_factory : public abstract_collection_resolver_factory { @@ -24,5 +24,26 @@ public: return std::dynamic_pointer_cast>(res); } }; + +class abstract_joined_object_resolver_factory { +public: + virtual ~abstract_joined_object_resolver_factory() = default; + + [[nodiscard]] virtual std::shared_ptr acquire_joined_object_resolver(const std::type_index &root_type, const std::type_index &element_type, const std::string &collection_name) const = 0; + virtual void register_joined_object_resolver(std::shared_ptr &&resolver) = 0; +}; + +class joined_object_resolver_factory : public abstract_joined_object_resolver_factory { +public: + template + [[nodiscard]] std::shared_ptr> resolver(const std::type_index &root_type, const std::string &collection_name) const { + const auto res = acquire_joined_object_resolver(root_type, typeid(Type), collection_name); + if (!res) { + return std::dynamic_pointer_cast>(res); + } + + return std::dynamic_pointer_cast>(res); + } +}; } #endif //MATADOR_CONTAINER_RESOLVER_FACTORY_HPP \ No newline at end of file diff --git a/include/matador/object/foreign_node_completer.hpp b/include/matador/object/foreign_node_completer.hpp index 23ad2a9..30a7807 100644 --- a/include/matador/object/foreign_node_completer.hpp +++ b/include/matador/object/foreign_node_completer.hpp @@ -50,7 +50,7 @@ public: template void on_belongs_to(const char *id, ForeignPointerType &/*obj*/, const utils::foreign_attributes &/*attr*/); template - void on_has_one(const char * /*id*/, ForeignPointerType &/*obj*/, const utils::foreign_attributes &/*attr*/); + void on_has_one(const char * /*id*/, ForeignPointerType &/*obj*/, const char *join_column, const utils::foreign_attributes &/*attr*/); template void on_has_many(const char *id, CollectionType &, const char *join_column, @@ -134,7 +134,7 @@ void foreign_node_completer::on_belongs_to(const char * template typename ...Observers> template -void foreign_node_completer::on_has_one(const char * /*id*/, ForeignPointerType &, const utils::foreign_attributes &) { +void foreign_node_completer::on_has_one(const char * /*id*/, ForeignPointerType &, const char * /*join_column*/, const utils::foreign_attributes &) { attach_node(); } diff --git a/include/matador/object/join_columns_collector.hpp b/include/matador/object/join_columns_collector.hpp index fef0f13..6319382 100644 --- a/include/matador/object/join_columns_collector.hpp +++ b/include/matador/object/join_columns_collector.hpp @@ -34,7 +34,7 @@ public: template static void on_belongs_to(const char * /*id*/, Pointer &/*obj*/, const utils::foreign_attributes &/*attr*/) {} template - static void on_has_one(const char * /*id*/, Pointer &/*obj*/, const utils::foreign_attributes &/*attr*/) {} + static void on_has_one(const char * /*id*/, Pointer &/*obj*/, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {} template static void on_has_many(ContainerType &, const char */*join_column*/, const utils::foreign_attributes &/*attr*/) {} template diff --git a/include/matador/object/object_generator.hpp b/include/matador/object/object_generator.hpp index eba305e..c45062c 100644 --- a/include/matador/object/object_generator.hpp +++ b/include/matador/object/object_generator.hpp @@ -35,7 +35,7 @@ public: template static void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {} template - static void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {} + static void on_has_one(const char * /*id*/, Pointer &/*x*/, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {} template static void on_has_many(const char * /*id*/, ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {} template @@ -92,7 +92,7 @@ public: create_fk_constraint(id); } template - static void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {} + static void on_has_one(const char * /*id*/, Pointer &/*x*/, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {} template void on_foreign_key(const char *id, Pointer &/*x*/) { diff --git a/include/matador/object/object_resolver.hpp b/include/matador/object/object_resolver.hpp index 11487c1..e9dc52d 100644 --- a/include/matador/object/object_resolver.hpp +++ b/include/matador/object/object_resolver.hpp @@ -2,6 +2,7 @@ #define MATADOR_OBJECT_LOADER_HPP #include "matador/object/abstract_type_resolver.hpp" +#include "matador/object/abstract_joined_resolver.hpp" #include @@ -17,5 +18,12 @@ public: virtual std::shared_ptr resolve(const utils::identifier& id) = 0; }; +template +class joined_object_resolver : public abstract_joined_resolver { +public: + joined_object_resolver(const std::type_index& root_type, const std::string& join_column) : abstract_joined_resolver(root_type, typeid(Type), join_column) {} + + virtual std::shared_ptr resolve(const utils::identifier& id) = 0; +}; } #endif //MATADOR_OBJECT_LOADER_HPP \ No newline at end of file diff --git a/include/matador/object/pk_field_locator.hpp b/include/matador/object/pk_field_locator.hpp index 8cdb218..ad09ef6 100644 --- a/include/matador/object/pk_field_locator.hpp +++ b/include/matador/object/pk_field_locator.hpp @@ -119,7 +119,7 @@ struct pk_field_locator { template static void on_belongs_to(const char *, Pointer &, const utils::foreign_attributes &) {} template - static void on_has_one(const char *, Pointer &, const utils::foreign_attributes &) {} + static void on_has_one(const char *, Pointer &, const char * /*join_column*/, const utils::foreign_attributes &) {} template static void on_has_many(const char *, Container &, const char *, const utils::foreign_attributes &) {} template diff --git a/include/matador/object/primary_key_resolver.hpp b/include/matador/object/primary_key_resolver.hpp index 5667639..426d246 100644 --- a/include/matador/object/primary_key_resolver.hpp +++ b/include/matador/object/primary_key_resolver.hpp @@ -58,7 +58,7 @@ public: template static void on_belongs_to(const char * /*id*/, Pointer &/*val*/, const utils::foreign_attributes &/*attr*/) {} template - static void on_has_one(const char * /*id*/, Pointer &/*val*/, const utils::foreign_attributes &/*attr*/) {} + static void on_has_one(const char * /*id*/, Pointer &/*val*/, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {} template static void on_has_many(const char * /*id*/, ContainerType &/*col*/, const char *, const utils::foreign_attributes &/*attr*/) {} template diff --git a/include/matador/object/relation_completer.hpp b/include/matador/object/relation_completer.hpp index 0cb66b9..97cb91d 100644 --- a/include/matador/object/relation_completer.hpp +++ b/include/matador/object/relation_completer.hpp @@ -43,7 +43,7 @@ public: found_ = requested_join_column_ == id; } template - static void on_has_one(const char * /*id*/, ForeignPointerType &/*obj*/, const utils::foreign_attributes &/*attr*/) {} + static void on_has_one(const char * /*id*/, ForeignPointerType &/*obj*/, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {} template static void on_has_many(const char * /*id*/, CollectionType &, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {} @@ -117,7 +117,7 @@ public: template void on_belongs_to(const char *id, ForeignPointerType &obj, const utils::foreign_attributes &attr); template - void on_has_one(const char * /*id*/, ForeignPointerType &/*obj*/, const utils::foreign_attributes &/*attr*/); + void on_has_one(const char * /*id*/, ForeignPointerType &/*obj*/, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/); template void on_has_many(const char *id, CollectionType &, const char *join_column, const utils::foreign_attributes &attr, std::enable_if_t::value> * = nullptr); @@ -280,6 +280,7 @@ template typename... Observers> template void relation_completer::on_has_one(const char *id, ForeignPointerType &/*obj*/, + const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) { using value_type = typename ForeignPointerType::value_type; const auto foreign_node = find_node(typeid(value_type)); diff --git a/include/matador/query/basic_schema.hpp b/include/matador/query/basic_schema.hpp index 69c00e0..6c04a91 100644 --- a/include/matador/query/basic_schema.hpp +++ b/include/matador/query/basic_schema.hpp @@ -56,7 +56,7 @@ public: template static void on_belongs_to(const char * /*id*/, P &, const utils::foreign_attributes & ) {} template - static void on_has_one(const char * /*id*/, P &, const utils::foreign_attributes & ) {} + static void on_has_one(const char * /*id*/, P &, const char * /*join_column*/, const utils::foreign_attributes & ) {} template static void on_has_many(const char * /*id*/, C &, const char * /*join_column*/, const utils::foreign_attributes & ) {} template @@ -103,6 +103,7 @@ public: [[nodiscard]] const std::unordered_map>& resolver_producers() const; [[nodiscard]] const std::unordered_map, object::collection_composite_key_hash>& collection_resolver_producers() const; + [[nodiscard]] const std::unordered_map, object::collection_composite_key_hash>& joined_object_resolver_producers() const; protected: template @@ -113,6 +114,7 @@ protected: std::unordered_map schema_nodes_; std::unordered_map> resolver_producers_; std::unordered_map, object::collection_composite_key_hash> collection_resolver_producers_; + std::unordered_map, object::collection_composite_key_hash> joined_object_resolver_producers_; }; } #endif //MATADOR_BASIC_SCHEMA_HPP \ No newline at end of file diff --git a/include/matador/query/dependency_collector.hpp b/include/matador/query/dependency_collector.hpp index a7ae15e..80bde8d 100644 --- a/include/matador/query/dependency_collector.hpp +++ b/include/matador/query/dependency_collector.hpp @@ -89,16 +89,16 @@ struct dependency_collector { } template - static void on_has_one(const char*, Pointer&, const auto&) {} + static void on_has_one(const char*, Pointer&, const char * /*join_column*/, const utils::foreign_attributes&) {} template - static void on_has_many(const char*, Container&, const char*, const auto&) {} + static void on_has_many(const char*, Container&, const char*, const utils::foreign_attributes&) {} template - static void on_has_many_to_many(const char*, Container&, const char*, const char*, const auto&) {} + static void on_has_many_to_many(const char*, Container&, const char*, const char*, const utils::foreign_attributes&) {} template - static void on_has_many_to_many(const char*, Container&, const auto&) {} + static void on_has_many_to_many(const char*, Container&, const utils::foreign_attributes&) {} }; struct flush_node { diff --git a/include/matador/query/fk_value_extractor.hpp b/include/matador/query/fk_value_extractor.hpp index 56e0b64..701d511 100644 --- a/include/matador/query/fk_value_extractor.hpp +++ b/include/matador/query/fk_value_extractor.hpp @@ -37,7 +37,7 @@ public: template static void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {} template - static void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {} + static void on_has_one(const char * /*id*/, Pointer &/*x*/, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {} template 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 diff --git a/include/matador/query/generator.hpp b/include/matador/query/generator.hpp index 8160ae2..bcbd2ea 100644 --- a/include/matador/query/generator.hpp +++ b/include/matador/query/generator.hpp @@ -81,7 +81,7 @@ public: on_foreign_key(id, x, attr); } template - void on_has_one(const char *id, Pointer &x, const utils::foreign_attributes &attr) { + void on_has_one(const char *id, Pointer &x, const char * /*join_column*/, const utils::foreign_attributes &attr) { on_foreign_key(id, x, attr); } @@ -189,7 +189,7 @@ public: result_.emplace_back(utils::_); } template class Pointer> - void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) { + void on_has_one(const char * /*id*/, Pointer &/*x*/, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) { result_.emplace_back(utils::_); } template @@ -249,7 +249,7 @@ public: push_back(id, fk_value_extractor_.extract(*x)); } template class Pointer> - void on_has_one(const char *id, Pointer &x, const utils::foreign_attributes &/*attr*/) { + void on_has_one(const char *id, Pointer &x, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) { push_back(id, fk_value_extractor_.extract(*x)); } template diff --git a/include/matador/query/insert_query_builder.hpp b/include/matador/query/insert_query_builder.hpp index b24f192..9990581 100644 --- a/include/matador/query/insert_query_builder.hpp +++ b/include/matador/query/insert_query_builder.hpp @@ -43,7 +43,7 @@ public: obj = ptr_; } template - static void on_has_one(const char * /*id*/, Pointer &/*obj*/, const utils::foreign_attributes &/*attr*/) {} + static void on_has_one(const char * /*id*/, Pointer &/*obj*/, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {} template static void on_has_many(const char * /*id*/, CollectionType &/*con*/, const char *, const utils::foreign_attributes &/*attr*/, std::enable_if_t::value> * = nullptr) {} template @@ -100,7 +100,7 @@ public: } template - void on_has_one(const char * /*id*/, Pointer &obj, const utils::foreign_attributes &attr) { + void on_has_one(const char * /*id*/, Pointer &obj, const char * /*join_column*/, const utils::foreign_attributes &attr) { on_foreign_object(obj, attr); } template diff --git a/include/matador/query/key_value_generator.hpp b/include/matador/query/key_value_generator.hpp index b40624f..658be5c 100644 --- a/include/matador/query/key_value_generator.hpp +++ b/include/matador/query/key_value_generator.hpp @@ -44,7 +44,7 @@ public: result_.emplace_back(id, fk_value_extractor_.extract(*x)); } template class Pointer> - void on_has_one(const char *id, Pointer &x, const utils::foreign_attributes &/*attr*/) { + void on_has_one(const char *id, Pointer &x, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) { result_.emplace_back(id, fk_value_extractor_.extract(*x)); } template diff --git a/include/matador/query/schema.hpp b/include/matador/query/schema.hpp index 385ef18..f899e49 100644 --- a/include/matador/query/schema.hpp +++ b/include/matador/query/schema.hpp @@ -41,6 +41,33 @@ private: std::string pk_name_; }; +template +class query_joined_object_resolver_producer : public sql::joined_object_resolver_producer { +public: + query_joined_object_resolver_producer() = default; + query_joined_object_resolver_producer(basic_schema& repo, const table& tab, std::string pk_name, const std::type_index& root_type, std::string join_column) + : joined_object_resolver_producer(root_type, typeid(Type), join_column) + , repo_(repo) + , table_(tab) + , pk_name_(std::move(pk_name)) {} + + std::shared_ptr produce(sql::statement&& stmt) override { + return std::make_shared>(std::move(stmt)); + } + + utils::result build_query(const sql::dialect& d) override { + const auto *pk_column = table_[pk_name_]; + const auto *join_column = table_[collection_name()]; + + return utils::ok(sql::query_context{}); + } + +private: + basic_schema& repo_; + const table& table_; + std::string pk_name_; +}; + template class query_collection_resolver_producer : public sql::collection_resolver_producer { public: @@ -64,7 +91,7 @@ public: return utils::ok(stmt); } - std::shared_ptr produce(sql::statement&& stmt, const sql::resolver_service& rs) override { + std::shared_ptr produce(sql::statement&& stmt, const sql::resolver_service& rs) override { const auto object_resolver = rs.object_resolver(); return std::make_shared>(std::move(stmt), root_type(), collection_name(), object_resolver); @@ -99,7 +126,7 @@ public: return utils::ok(stmt); } - std::shared_ptr produce(sql::statement&& stmt, const sql::resolver_service& /*rs*/) override { + std::shared_ptr produce(sql::statement&& stmt, const sql::resolver_service& /*rs*/) override { return std::make_shared>(std::move(stmt), root_type(), collection_name()/*, object_resolver*/); } @@ -126,7 +153,7 @@ public: template static void on_belongs_to(const char * /*id*/, Pointer & /*x*/, const utils::foreign_attributes &/*attr*/) {} template - void on_has_one(const char * /*id*/, Pointer & /*x*/, const utils::foreign_attributes &/*attr*/) { + void on_has_one(const char * /*id*/, Pointer & /*x*/, const char *join_column, const utils::foreign_attributes &/*attr*/) { const auto it = schema_.find(typeid(typename Pointer::value_type)); if (it == schema_.end()) { throw query_builder_exception{error_code::UnknownType, "Unknown type"}; @@ -135,8 +162,12 @@ public: throw query_builder_exception{error_code::MissingPrimaryKey, "Missing primary key"}; } - auto producer = std::make_unique>(schema_, it->second.table(), it->second.node().info().primary_key_attribute()->name()); - schema_.resolver_producers_[typeid(typename Pointer::value_type)] = std::move(producer); + auto producer = std::make_unique>( + schema_, + it->second.table(), + join_column); + const object::collection_composite_key key{root_type_, typeid(Pointer), join_column}; + schema_.joined_object_resolver_producers_[key] = std::move(producer); } template diff --git a/include/matador/query/select_query_builder.hpp b/include/matador/query/select_query_builder.hpp index cc411c4..ef33011 100644 --- a/include/matador/query/select_query_builder.hpp +++ b/include/matador/query/select_query_builder.hpp @@ -117,7 +117,7 @@ public: } template - void on_has_one(const char *id, Pointer &obj, const utils::foreign_attributes &attr) { + void on_has_one(const char *id, Pointer &obj, const char * /*join_column*/, const utils::foreign_attributes &attr) { on_foreign_object(id, obj, attr, false); } diff --git a/include/matador/query/session.hpp b/include/matador/query/session.hpp index 5f8a36c..00196aa 100644 --- a/include/matador/query/session.hpp +++ b/include/matador/query/session.hpp @@ -144,7 +144,7 @@ public: template static void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {} template - static void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {} + static void on_has_one(const char * /*id*/, Pointer &/*x*/, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {} template static void on_has_many(const char * /*id*/, diff --git a/include/matador/query/value_extractor.hpp b/include/matador/query/value_extractor.hpp index b8436fb..e1b661f 100644 --- a/include/matador/query/value_extractor.hpp +++ b/include/matador/query/value_extractor.hpp @@ -44,8 +44,7 @@ public: values_.emplace_back(fk_value_extractor_.extract(*x)); } template class Pointer> - void on_has_one(const char * /*id*/, Pointer &x, const utils::foreign_attributes &/*attr*/) - { + void on_has_one(const char * /*id*/, Pointer &x, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) { values_.emplace_back(fk_value_extractor_.extract(*x)); } template diff --git a/include/matador/sql/generator.hpp b/include/matador/sql/generator.hpp index 15a61ec..63bf47d 100644 --- a/include/matador/sql/generator.hpp +++ b/include/matador/sql/generator.hpp @@ -68,7 +68,7 @@ public: on_foreign_key(id, x, attr); } template - void on_has_one(const char *id, Pointer &x, const utils::foreign_attributes &attr) { + void on_has_one(const char *id, Pointer &x, const char * /*join_column*/, const utils::foreign_attributes &attr) { on_foreign_key(id, x, attr); } diff --git a/include/matador/sql/internal/collection_resolver_producer.hpp b/include/matador/sql/internal/collection_resolver_producer.hpp index 3bfb0f6..4c911cc 100644 --- a/include/matador/sql/internal/collection_resolver_producer.hpp +++ b/include/matador/sql/internal/collection_resolver_producer.hpp @@ -1,7 +1,7 @@ #ifndef MATADOR_COLLECTION_RESOLVER_PRODUCER_HPP #define MATADOR_COLLECTION_RESOLVER_PRODUCER_HPP -#include "matador/object/abstract_collection_resolver.hpp" +#include "matador/object/abstract_joined_resolver.hpp" #include "matador/sql/query_context.hpp" @@ -18,7 +18,7 @@ class collection_resolver_producer { public: virtual ~collection_resolver_producer() = default; virtual utils::result build_query(const dialect& d) = 0; - virtual std::shared_ptr produce(statement&& stmt, const resolver_service& rs) = 0; + virtual std::shared_ptr produce(statement&& stmt, const resolver_service& rs) = 0; [[nodiscard]] const std::type_index& root_type() const; [[nodiscard]] const std::type_index& type() const; diff --git a/include/matador/sql/internal/identifier_reader.hpp b/include/matador/sql/internal/identifier_reader.hpp index 7a7ef9f..6625467 100644 --- a/include/matador/sql/internal/identifier_reader.hpp +++ b/include/matador/sql/internal/identifier_reader.hpp @@ -36,7 +36,7 @@ public: template < class Pointer > 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*/) {} + static void on_has_one(const char * /*id*/, Pointer &/*x*/, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {} template static void on_has_many(const char * /*id*/, ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {} template diff --git a/include/matador/sql/internal/object_resolver_producer.hpp b/include/matador/sql/internal/object_resolver_producer.hpp index 7c49a2b..5094c1e 100644 --- a/include/matador/sql/internal/object_resolver_producer.hpp +++ b/include/matador/sql/internal/object_resolver_producer.hpp @@ -27,5 +27,24 @@ protected: private: std::type_index type_; }; + +class joined_object_resolver_producer { +public: + virtual ~joined_object_resolver_producer() = default; + virtual utils::result build_query(const dialect& d) = 0; + virtual std::shared_ptr produce(statement&& stmt) = 0; + + [[nodiscard]] const std::type_index& root_type() const; + [[nodiscard]] const std::type_index& type() const; + [[nodiscard]] const std::string& collection_name() const; + +protected: + explicit joined_object_resolver_producer(const std::type_index &root_type, const std::type_index &type, std::string collection_name); + +private: + std::type_index root_type_; + std::type_index type_; + std::string collection_name_; +}; } #endif //MATADOR_RESOLVER_PRODUCER_HPP \ No newline at end of file diff --git a/include/matador/sql/internal/object_result_binder.hpp b/include/matador/sql/internal/object_result_binder.hpp index 9ebcea1..72fb3c5 100644 --- a/include/matador/sql/internal/object_result_binder.hpp +++ b/include/matador/sql/internal/object_result_binder.hpp @@ -38,7 +38,7 @@ public: template < class Pointer > 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*/) {} + static void on_has_one(const char * /*id*/, Pointer &/*x*/, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {} template static void on_has_many(const char * /*id*/, @@ -96,7 +96,7 @@ public: 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*/) { + void on_has_one(const char *id, Pointer &x, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) { fk_result_binder_.bind(*x, id, index_++, *binder_); } template diff --git a/include/matador/sql/internal/pk_reader.hpp b/include/matador/sql/internal/pk_reader.hpp index e0eaaac..d60cfe6 100644 --- a/include/matador/sql/internal/pk_reader.hpp +++ b/include/matador/sql/internal/pk_reader.hpp @@ -32,7 +32,7 @@ public: ++column_index_; } template - static void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {} + static void on_has_one(const char * /*id*/, Pointer &/*x*/, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {} template static void on_has_many(const char * /*id*/, ContainerType &, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {} template diff --git a/include/matador/sql/internal/query_result_impl.hpp b/include/matador/sql/internal/query_result_impl.hpp index 538cab6..5dbf53a 100644 --- a/include/matador/sql/internal/query_result_impl.hpp +++ b/include/matador/sql/internal/query_result_impl.hpp @@ -63,15 +63,9 @@ public: void on_attribute(const char *id, utils::value &val, const utils::field_attributes &attr); template - void on_belongs_to(const char * /*id*/, Pointer &x, const utils::foreign_attributes &attr) { - on_foreign_key(x, attr); - } - - template - void on_has_one(const char * /*id*/, Pointer &x, const utils::foreign_attributes &attr) { - on_foreign_key(x, attr); - } - + void on_belongs_to(const char * /*id*/, Pointer &x, const utils::foreign_attributes &attr); + template + void on_has_one(const char * /*id*/, object::object_ptr &x, const char * /*join_column*/, const utils::foreign_attributes &attr); template void on_has_many(const char * /*id*/, CollectionType &cont, const char *join_column, const utils::foreign_attributes &attr, std::enable_if_t::value> * = nullptr); template @@ -103,23 +97,6 @@ private: return resolver.discover(obj); } - template - void on_foreign_key(Pointer &x, const utils::foreign_attributes &attr) { - const auto resolver = resolver_->object_resolver(); - if (attr.fetch() == utils::fetch_type::Lazy) { - typename Pointer::value_type obj; - auto pk = id_reader_.read(obj, column_index_++); - x = Pointer(std::make_shared>(resolver, pk)); - } else { - auto obj = std::make_shared(); - const auto ti = std::type_index(typeid(*x)); - type_stack_.push(ti); - access::process(*this, *obj); - type_stack_.pop(); - x = Pointer(std::make_shared>(resolver, obj)); - } - } - protected: size_t column_index_ = 0; std::vector prototype_; @@ -134,6 +111,38 @@ protected: std::unordered_set initialized_collections_; }; +template +void query_result_impl::on_belongs_to(const char*, Pointer& x, const utils::foreign_attributes& attr) { + const auto resolver = resolver_->object_resolver(); + if (attr.fetch() == utils::fetch_type::Lazy) { + typename Pointer::value_type obj; + auto pk = id_reader_.read(obj, column_index_++); + x = Pointer(std::make_shared>(resolver, pk)); + } else { + auto obj = std::make_shared(); + const auto ti = std::type_index(typeid(*x)); + type_stack_.push(ti); + access::process(*this, *obj); + type_stack_.pop(); + x = Pointer(std::make_shared>(resolver, obj)); + } +} + +template +void query_result_impl::on_has_one(const char*, object::object_ptr& x, const char *join_column, const utils::foreign_attributes& attr) { + const auto resolver = resolver_->collection_resolver(result_type_, join_column); + if (attr.fetch() == utils::fetch_type::Lazy) { + x = object::object_ptr(std::make_shared>(resolver, id_reader_.read(x, column_index_++))); + } else { + auto obj = std::make_shared(); + const auto ti = std::type_index(typeid(*x)); + type_stack_.push(ti); + access::process(*this, *obj); + type_stack_.pop(); + x = object::object_ptr(std::make_shared>(resolver, obj)); + } +} + template void query_result_impl::on_has_many(const char *, CollectionType &cont, const char *join_column, const utils::foreign_attributes &attr, std::enable_if_t::value> *) { using value_type = typename CollectionType::value_type::value_type; diff --git a/include/matador/sql/internal/query_result_pk_resolver.hpp b/include/matador/sql/internal/query_result_pk_resolver.hpp index a954c2e..0a1a2c1 100644 --- a/include/matador/sql/internal/query_result_pk_resolver.hpp +++ b/include/matador/sql/internal/query_result_pk_resolver.hpp @@ -16,7 +16,8 @@ namespace matador::sql::internal { class query_result_pk_resolver final { public: - explicit query_result_pk_resolver(query_result_reader &reader) : reader_(reader) {} + explicit query_result_pk_resolver(query_result_reader &reader) + : reader_(reader) {} template utils::identifier discover(Type &obj) { @@ -38,16 +39,22 @@ public: utils::data_type_traits::read_value(reader_, id, column_index_++, value, attr.size()); pk_ = value; } - void on_revision(const char * /*id*/, uint64_t &/*rev*/) { ++column_index_; } + void on_revision(const char * /*id*/, uint64_t &/*rev*/) { + ++column_index_; + } template < class Type > void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/) { ++column_index_; } void on_attribute(const char *id, const utils::value &x, const utils::field_attributes &attr); template < class Pointer > - void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &attr) { on_foreign_key(attr.fetch() ); } + void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &attr) { + on_foreign_key(attr.fetch() ); + } template < class Pointer > - void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &attr) { on_foreign_key(attr.fetch() ); } + void on_has_one(const char * /*id*/, Pointer &/*x*/, const char * /*join_column*/, const utils::foreign_attributes &attr) { + on_foreign_key(attr.fetch() ); + } template static void on_has_many(const char * /*id*/, ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {} diff --git a/include/matador/sql/object_parameter_binder.hpp b/include/matador/sql/object_parameter_binder.hpp index 9ef2707..30416f4 100644 --- a/include/matador/sql/object_parameter_binder.hpp +++ b/include/matador/sql/object_parameter_binder.hpp @@ -40,6 +40,7 @@ public: template class Pointer> static void on_has_one(const char * /*id*/, Pointer &/*x*/, + const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {} template static void on_has_many(const char * /*id*/, diff --git a/include/matador/sql/object_pk_binder.hpp b/include/matador/sql/object_pk_binder.hpp index e2659a1..4957a69 100644 --- a/include/matador/sql/object_pk_binder.hpp +++ b/include/matador/sql/object_pk_binder.hpp @@ -30,7 +30,7 @@ public: template < class Pointer > 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*/) {} + static void on_has_one(const char * /*id*/, Pointer &/*x*/, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {} template static void on_has_many(const char * /*id*/, diff --git a/include/matador/sql/producer_resolver_factory.hpp b/include/matador/sql/producer_resolver_factory.hpp index 6fc2cce..fa0a09a 100644 --- a/include/matador/sql/producer_resolver_factory.hpp +++ b/include/matador/sql/producer_resolver_factory.hpp @@ -3,7 +3,7 @@ #include -#include "matador/object/abstract_collection_resolver.hpp" +#include "matador/object/abstract_joined_resolver.hpp" #include "matador/object/object_resolver_factory.hpp" #include "matador/object/collection_resolver_factory.hpp" #include "matador/object/collection_utils.hpp" @@ -22,13 +22,24 @@ private: class producer_collection_resolver_factory : public object::collection_resolver_factory { public: - [[nodiscard]] std::shared_ptr acquire_collection_resolver(const std::type_index& root_type, + [[nodiscard]] std::shared_ptr acquire_collection_resolver(const std::type_index& root_type, const std::type_index& element_type, const std::string& collection_name) const override; - void register_collection_resolver(std::shared_ptr&& resolver) override; + void register_collection_resolver(std::shared_ptr&& resolver) override; private: - std::unordered_map, object::collection_composite_key_hash> resolvers_; + std::unordered_map, object::collection_composite_key_hash> resolvers_; +}; + +class producer_joined_object_resolver_factory : public object::joined_object_resolver_factory { +public: + [[nodiscard]] std::shared_ptr acquire_joined_object_resolver(const std::type_index& root_type, + const std::type_index& element_type, + const std::string& collection_name) const override; + void register_joined_object_resolver(std::shared_ptr&& resolver) override; + +private: + std::unordered_map, object::collection_composite_key_hash> resolvers_; }; } #endif //MATADOR_RESOLVER_FACTORY_HPP \ No newline at end of file diff --git a/include/matador/sql/resolver_service.hpp b/include/matador/sql/resolver_service.hpp index d38872d..0987830 100644 --- a/include/matador/sql/resolver_service.hpp +++ b/include/matador/sql/resolver_service.hpp @@ -16,12 +16,19 @@ public: return collection_resolver_factory_.resolver(root_type, collection_name); } + template + std::shared_ptr> joined_object_resolver(const std::type_index &root_type, const std::string &join_column) const { + return joined_object_resolver_factory_.resolver(root_type, join_column); + } + void register_object_resolver(std::shared_ptr &&resolver); - void register_collection_resolver(std::shared_ptr&& resolver); + void register_collection_resolver(std::shared_ptr&& resolver); + void register_joined_object_resolver(std::shared_ptr&& resolver); private: sql::producer_resolver_factory object_resolver_factory_; sql::producer_collection_resolver_factory collection_resolver_factory_; + sql::producer_resolver_factory joined_object_resolver_factory_; }; } #endif // MATADOR_RESOLVER_SERVICE_HPP diff --git a/include/matador/utils/access.hpp b/include/matador/utils/access.hpp index c4403dd..479ed39 100644 --- a/include/matador/utils/access.hpp +++ b/include/matador/utils/access.hpp @@ -53,13 +53,13 @@ void attribute(Operator &op, const char *id, std::optional &value, const u } template -void has_one(Operator &op, const char *id, Type &value, const utils::foreign_attributes &attr = utils::CascadeNoneFetchLazy) { - op.on_has_one(id, value, attr); +void belongs_to(Operator &op, const char *id, Type &value, const utils::foreign_attributes &attr = utils::CascadeNoneFetchLazy) { + op.on_belongs_to(id, value, attr); } template -void belongs_to(Operator &op, const char *id, Type &value, const utils::foreign_attributes &attr = utils::CascadeNoneFetchLazy) { - op.on_belongs_to(id, value, attr); +void has_one(Operator &op, const char *id, Type &value, const char *join_column, const utils::foreign_attributes &attr = utils::CascadeNoneFetchLazy) { + op.on_has_one(id, value, join_column, attr); } template class ContainerType> diff --git a/include/matador/utils/primary_key_accessor.hpp b/include/matador/utils/primary_key_accessor.hpp index 5d4a42a..218d21e 100644 --- a/include/matador/utils/primary_key_accessor.hpp +++ b/include/matador/utils/primary_key_accessor.hpp @@ -38,7 +38,7 @@ public: template static void on_belongs_to(const char * /*id*/, P &, const foreign_attributes & ) {} template - static void on_has_one(const char * /*id*/, P &, const foreign_attributes & ) {} + static void on_has_one(const char * /*id*/, P &, const char * /*join_column*/, const foreign_attributes & ) {} template static void on_has_many(const char * /*id*/, C &, const char * /*join_column*/, const foreign_attributes & ) {} template @@ -65,7 +65,7 @@ struct pk_unset_checker { template static void on_belongs_to(const char * /*id*/, P &, const foreign_attributes & ) {} template - static void on_has_one(const char * /*id*/, P &, const foreign_attributes & ) {} + static void on_has_one(const char * /*id*/, P &, const char * /*join_column*/, const foreign_attributes & ) {} template static void on_has_many(const char * /*id*/, C &, const char * /*join_column*/, const foreign_attributes & ) {} template @@ -89,7 +89,7 @@ struct primary_key_getter { template static void on_belongs_to(const char * /*id*/, P &, const foreign_attributes & ) {} template - static void on_has_one(const char * /*id*/, P &, const foreign_attributes & ) {} + static void on_has_one(const char * /*id*/, P &, const char * /*join_column*/, const foreign_attributes & ) {} template static void on_has_many(const char * /*id*/, C &, const char * /*join_column*/, const foreign_attributes & ) {} template diff --git a/source/core/CMakeLists.txt b/source/core/CMakeLists.txt index b3603d3..3aff2c1 100644 --- a/source/core/CMakeLists.txt +++ b/source/core/CMakeLists.txt @@ -13,7 +13,7 @@ add_library(matador-core STATIC ../../include/matador/net/reactor.hpp ../../include/matador/net/select_fd_sets.hpp ../../include/matador/net/socket_interrupter.hpp - ../../include/matador/object/abstract_collection_resolver.hpp + ../../include/matador/object/abstract_joined_resolver.hpp ../../include/matador/object/abstract_type_resolver.hpp ../../include/matador/object/abstract_type_resolver_factory.hpp ../../include/matador/object/attribute.hpp @@ -92,7 +92,7 @@ add_library(matador-core STATIC logger/log_manager.cpp logger/logger.cpp logger/rotating_file_sink.cpp - object/abstract_collection_resolver.cpp + object/abstract_joined_resolver.cpp object/attribute.cpp object/basic_object_info.cpp object/basic_repository.cpp diff --git a/source/core/object/abstract_collection_resolver.cpp b/source/core/object/abstract_collection_resolver.cpp deleted file mode 100644 index 8f5c9a6..0000000 --- a/source/core/object/abstract_collection_resolver.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "matador/object/abstract_collection_resolver.hpp" - -namespace matador::object { - -const std::type_index& abstract_collection_resolver::root_type() const { - return root_type_; -} - -const std::type_index& abstract_collection_resolver::type() const { - return type_; -} - -const std::string& abstract_collection_resolver::collection_name() const { - return collection_name_; -} - -abstract_collection_resolver::abstract_collection_resolver(const std::type_index& root_type, const std::type_index& type, std::string collection_name) -: root_type_(root_type) -, type_(type) -, collection_name_(std::move(collection_name)) {} - - -} // namespace matador::object \ No newline at end of file diff --git a/source/core/object/abstract_joined_resolver.cpp b/source/core/object/abstract_joined_resolver.cpp new file mode 100644 index 0000000..c9f9b56 --- /dev/null +++ b/source/core/object/abstract_joined_resolver.cpp @@ -0,0 +1,16 @@ +#include "matador/object/abstract_collection_resolver.hpp" + +namespace matador::object { +const std::type_index& abstract_joined_resolver::root_type() const { + return root_type_; +} + +const std::string& abstract_joined_resolver::collection_name() const { + return collection_name_; +} + +abstract_joined_resolver::abstract_joined_resolver(const std::type_index& root_type, const std::type_index& type, std::string collection_name) +: abstract_type_resolver(type) +, root_type_(root_type) +, collection_name_(std::move(collection_name)) {} +} // namespace matador::object \ No newline at end of file diff --git a/source/orm/query/basic_schema.cpp b/source/orm/query/basic_schema.cpp index caac447..0a29b43 100644 --- a/source/orm/query/basic_schema.cpp +++ b/source/orm/query/basic_schema.cpp @@ -145,6 +145,10 @@ const std::unordered_map, object::collection_composite_key_hash>& basic_schema::joined_object_resolver_producers() const { + return joined_object_resolver_producers_; +} + namespace detail { utils::result create_tables(const object::repository &repo, const sql::connection &conn) { for (const auto &node: repo) { diff --git a/source/orm/query/session.cpp b/source/orm/query/session.cpp index c71820e..bdc2b2b 100644 --- a/source/orm/query/session.cpp +++ b/source/orm/query/session.cpp @@ -93,6 +93,23 @@ session::session(session_context&& ctx, const basic_schema &scm) } } + for (const auto &pair : schema_.joined_object_resolver_producers()) { + auto res = pair.second->build_query(dialect_).and_then([this](sql::query_context&& query_ctx) -> result { + query_ctx.resolver = resolver_service_; + return cache_.acquire(query_ctx); + }).and_then([&pair, this](sql::statement&& stmt) -> result { + resolver_service_->register_collection_resolver(pair.second->produce(std::move(stmt), *resolver_service_)); + + return ok(); + }).or_else([](const auto &err) { + return failure(err); + }); + + if (!res) { + throw std::runtime_error(res.err().message()); + } + } + for (const auto &pair : schema_.collection_resolver_producers()) { auto res = pair.second->build_query(dialect_).and_then([this](sql::query_context&& query_ctx) -> result { query_ctx.resolver = resolver_service_; diff --git a/source/orm/sql/internal/object_resolver_producer.cpp b/source/orm/sql/internal/object_resolver_producer.cpp index 48bd4c4..fd7fd43 100644 --- a/source/orm/sql/internal/object_resolver_producer.cpp +++ b/source/orm/sql/internal/object_resolver_producer.cpp @@ -8,3 +8,20 @@ const std::type_index & object_resolver_producer::type() const { object_resolver_producer::object_resolver_producer(const std::type_index &type) : type_(type) {} } + +const std::type_index& matador::sql::joined_object_resolver_producer::root_type() const { + return root_type_; +} + +const std::type_index& matador::sql::joined_object_resolver_producer::type() const { + return type_; +} + +const std::string& matador::sql::joined_object_resolver_producer::collection_name() const { + return collection_name_; +} + +matador::sql::joined_object_resolver_producer::joined_object_resolver_producer(const std::type_index& root_type, const std::type_index& type, std::string collection_name) +: root_type_(root_type) +, type_(type) +, collection_name_(std::move(collection_name)) {} diff --git a/source/orm/sql/producer_resolver_factory.cpp b/source/orm/sql/producer_resolver_factory.cpp index 139fa8d..aff72db 100644 --- a/source/orm/sql/producer_resolver_factory.cpp +++ b/source/orm/sql/producer_resolver_factory.cpp @@ -11,7 +11,7 @@ std::shared_ptr producer_resolver_factory::acqui void producer_resolver_factory::register_object_resolver(std::shared_ptr &&resolver) { resolvers_[resolver->type()] = std::move(resolver); } -std::shared_ptr +std::shared_ptr producer_collection_resolver_factory::acquire_collection_resolver(const std::type_index& root_type, const std::type_index& element_type, const std::string& collection_name) const { @@ -21,7 +21,22 @@ producer_collection_resolver_factory::acquire_collection_resolver(const std::typ } return nullptr; } -void producer_collection_resolver_factory::register_collection_resolver(std::shared_ptr&& resolver) { +void producer_collection_resolver_factory::register_collection_resolver(std::shared_ptr&& resolver) { + const object::collection_composite_key key{resolver->root_type(), resolver->type(), resolver->collection_name()}; + resolvers_[key] = std::move(resolver); +} + +std::shared_ptr producer_joined_object_resolver_factory::acquire_joined_object_resolver(const std::type_index& root_type, + const std::type_index& element_type, + const std::string& collection_name) const { + const object::collection_composite_key key{root_type, element_type, collection_name}; + if (const auto it = resolvers_.find(key); it != resolvers_.end()) { + return it->second; + } + return nullptr; +} + +void producer_joined_object_resolver_factory::register_joined_object_resolver(std::shared_ptr&& resolver) { const object::collection_composite_key key{resolver->root_type(), resolver->type(), resolver->collection_name()}; resolvers_[key] = std::move(resolver); } diff --git a/source/orm/sql/resolver_service.cpp b/source/orm/sql/resolver_service.cpp index 1681201..b3db96f 100644 --- a/source/orm/sql/resolver_service.cpp +++ b/source/orm/sql/resolver_service.cpp @@ -4,7 +4,12 @@ namespace matador::sql { void resolver_service::register_object_resolver(std::shared_ptr&& resolver) { object_resolver_factory_.register_object_resolver(std::move(resolver)); } -void resolver_service::register_collection_resolver(std::shared_ptr&& resolver) { + +void resolver_service::register_collection_resolver(std::shared_ptr&& resolver) { collection_resolver_factory_.register_collection_resolver(std::move(resolver)); } + +void resolver_service::register_joined_object_resolver(std::shared_ptr&& resolver) { + joined_object_resolver_factory_.register_object_resolver(std::move(resolver)); +} } // namespace matador::query \ No newline at end of file diff --git a/test/backends/SessionInsertHasOne.cpp b/test/backends/SessionInsertHasOne.cpp index 1ebc7a1..1ab5ba6 100644 --- a/test/backends/SessionInsertHasOne.cpp +++ b/test/backends/SessionInsertHasOne.cpp @@ -29,5 +29,7 @@ TEST_CASE_METHOD(SessionFixture, "Test insert object with has_one relation", "[s REQUIRE(us.is_persistent()); REQUIRE(u.is_persistent()); - REQUIRE(u->session->session_token == "session1"); + const auto user_result = ses.find(u->id); + REQUIRE(user_result.is_ok()); + REQUIRE(user_result.value()->session->session_token == "session1"); } \ No newline at end of file diff --git a/test/models/user.hpp b/test/models/user.hpp index 52a9b88..923c473 100644 --- a/test/models/user.hpp +++ b/test/models/user.hpp @@ -31,7 +31,7 @@ struct user_pk_generator { field::primary_key(op, "id", id, PkAttribute); field::attribute(op, "name", name, UniqueVarChar255); field::attribute(op, "password", password, UniqueVarChar255); - field::has_one(op, "session", session, CascadeAllFetchLazy); + field::has_one(op, "session", session, "user_id", CascadeAllFetchLazy); } };