ores.cpp.repository.repository_header

Table of Contents

Repository declarations with custom-member paste points. repository profile. Database access layer using Qt SQL parameterised queries, tracking `valid_from`/`valid_to`.

See the Template variable reference for the complete list of available variables and their semantics.

Template

The full template source. Edit here and re-tangle with compass build --direct tangle_codegen_templates to regenerate library/templates/cpp_domain_type_repository.hpp.mustache.

{{! GENERATED FILE — tangled from projects/ores.codegen/library/templates/ores.cpp.repository.repository_header.org. Edit the org source. }}
{{{cpp_license}}}
{{#domain_entity}}
#ifndef ORES_{{component_core_upper}}_REPOSITORY_{{entity_singular_upper}}_REPOSITORY_HPP
#define ORES_{{component_core_upper}}_REPOSITORY_{{entity_singular_upper}}_REPOSITORY_HPP

#include <chrono>
#include <cstdint>
#include <optional>
#include <string>
#include <vector>
#include <sqlgen/postgres.hpp>
#include "ores.logging/make_logger.hpp"
#include "ores.database/domain/context.hpp"
#include "ores.{{component_include}}/domain/{{entity_singular}}.hpp"
#include "ores.{{component_core}}/export.hpp"
{{#has_parent_id}}
#include "ores.utility/domain/hierarchy.hpp"
#include <boost/uuid/uuid.hpp>
{{/has_parent_id}}

namespace ores::{{component}}::repository {

/**
 * @brief Reads and writes {{entity_plural_words}} to data storage.
 */
class ORES_{{component_core_upper}}_EXPORT {{entity_singular}}_repository {
private:
    inline static std::string_view logger_name =
        "ores.{{component}}.repository.{{entity_singular}}_repository";

    [[nodiscard]] static auto& lg() {
        using namespace ores::logging;
        static auto instance = make_logger(logger_name);
        return instance;
    }

public:
    using context = ores::database::context;

    /**
     * @brief Returns the SQL created by sqlgen to construct the table.
     */
    std::string sql();

    /**
     * @brief Writes {{entity_plural_words}} to database.
     */
    /**@{*/
    void write(context ctx, const domain::{{entity_singular}}& v);
    void write(context ctx, const std::vector<domain::{{entity_singular}}>& v);
    /**@}*/

    /**
     * @brief Reads latest {{entity_plural_words}}, possibly filtered by primary key.
     */
    /**@{*/
    std::vector<domain::{{entity_singular}}> read_latest(context ctx);
    std::vector<domain::{{entity_singular}}>
    read_latest(context ctx, {{{primary_key.params}}});
{{#has_batch_read}}
    std::vector<domain::{{entity_singular}}>
    read_latest(context ctx, {{{primary_key.batch_params}}});
{{/has_batch_read}}
    /**@}*/
{{#service_find_by_code}}
{{#parent_column}}

    /**
     * @brief Reads latest {{entity_plural_words}} filtered by {{parent_column}} and {{column}}.
     */
    std::vector<domain::{{entity_singular}}>
    read_latest_by_code(context ctx, const std::string& {{parent_column}}, const std::string& {{column}});
{{/parent_column}}
{{^parent_column}}

    /**
     * @brief Reads latest {{entity_plural_words}} filtered by {{column}}.
     */
    std::vector<domain::{{entity_singular}}>
    read_latest_by_code(context ctx, const std::string& {{column}});
{{/parent_column}}
{{/service_find_by_code}}
{{#has_as_of_lookup}}
    /**
     * @brief Reads {{entity_plural_words}} as they stood at a specific
     * timepoint — valid_from <= as_of < valid_to — possibly filtered by
     * {{primary_key.column}}. Distinct from read_at_version (a specific
     * version number) and from a parent/child *_as_of query (a validity
     * window overlap): this resolves what this entity's own row meant at
     * a single instant in time.
     */
    /**@{*/
    std::vector<domain::{{entity_singular}}> read_at_timepoint(context ctx, const std::string& as_of);
    std::vector<domain::{{entity_singular}}>
    read_at_timepoint(context ctx, const std::string& as_of, const std::string& {{primary_key.column}});
    /**@}*/
{{/has_as_of_lookup}}

    /**
     * @brief Reads all {{entity_plural_words}}, possibly filtered by primary key.
     */
    std::vector<domain::{{entity_singular}}>
    read_all(context ctx, {{{primary_key.params}}});

{{#has_audit_columns}}
    /**
     * @brief Reads a single {{entity_singular_words}} as it stood at a specific
     * version — the version's own [valid_from, valid_to) window is returned
     * verbatim, so the caller can compose child entities "as of" the same
     * window. See the "Temporal composite entity versioning" architecture
     * doc.
     * @param ctx Repository context with database connection
     * @param version The version to fetch
     */
    std::optional<domain::{{entity_singular}}>
    read_at_version(context ctx, {{{primary_key.params}}}, std::uint32_t version);
{{/has_audit_columns}}

{{#foreign_keys}}
{{#list_by}}
    /**
     * @brief Reads latest {{entity_plural_words}} filtered by {{column}}, with pagination.
     * @param ctx Repository context with database connection
     * @param {{column}} The {{column}} to filter by
     * @param offset Number of records to skip
     * @param limit Maximum number of records to return
     */
    std::vector<domain::{{entity_singular}}>
    read_latest_by_{{column}}(context ctx, const std::string& {{column}},
                             std::uint32_t offset, std::uint32_t limit);

    /**
     * @brief Gets the total count of active {{entity_plural_words}} filtered by {{column}}.
     */
    std::uint32_t get_total_{{entity_singular_short}}_count_by_{{column}}(context ctx,
                                                                         const std::string& {{column}});
{{/list_by}}

{{#list_by_as_of}}
    /**
     * @brief Reads {{entity_plural_words}} filtered by {{column}} that were live at
     * any point during [valid_from_bound, valid_to_bound) — i.e. the set of
     * {{entity_plural_words}} that compose a parent entity's state as of one of
     * its own historical versions. See the "Temporal composite entity
     * versioning" architecture doc.
     * @param ctx Repository context with database connection
     * @param {{column}} The {{column}} to filter by
     * @param valid_from_bound The parent version's own valid_from
     * @param valid_to_bound The parent version's own valid_to
     */
    std::vector<domain::{{entity_singular}}>
    read_by_{{column}}_as_of(context ctx, const std::string& {{column}},
                            std::chrono::system_clock::time_point valid_from_bound,
                            std::chrono::system_clock::time_point valid_to_bound);
{{/list_by_as_of}}
{{/foreign_keys}}
    /**
     * @brief Reads latest {{entity_plural_words}} with pagination support.
     * @param ctx Repository context with database connection
     * @param offset Number of records to skip
     * @param limit Maximum number of records to return
     */
    std::vector<domain::{{entity_singular}}>
    read_latest(context ctx, std::uint32_t offset, std::uint32_t limit);

    /**
     * @brief Gets the total count of active {{entity_plural_words}}.
     * @param ctx Repository context with database connection
     * @return Total number of active {{entity_plural_words}}
     */
    std::uint32_t get_total_{{entity_singular_short}}_count(context ctx);

    /**
     * @brief Deletes a {{entity_singular_words}} by closing its temporal validity.
     */
    void remove(context ctx, {{{primary_key.params}}});

    /**
     * @brief Deletes {{entity_plural_words}} by closing their temporal validity.
     */
    void remove(context ctx, {{{primary_key.batch_params}}});
{{#has_parent_id}}

    /**
     * @brief Reads the {{entity_singular}} hierarchy as a flat set of {id,
     * parent_id, name} rows, via {{sql_name_base}}_hierarchy_fn.
     *
     * @param ctx Repository context with database connection (tenant is
     * derived from ctx.tenant_id()).
     * @param root_id The {{entity_singular}} to start from.
     * @param from_root If true, first walks up to the ultimate ancestor and
     * returns the whole tree the given node belongs to, instead of just its
     * subtree.
     * @return Flat hierarchy rows, ready for ores::utility::domain::build_tree.
     */
    std::vector<ores::utility::domain::hierarchy_flat_row>
    get_hierarchy(context ctx, const boost::uuids::uuid& root_id, bool from_root);
{{/has_parent_id}}

<<paste:DCA78C69-E508-48D9-9972-A9B8094D91FB>>
};

}

#endif
{{/domain_entity}}
{{#junction}}
#ifndef ORES_{{component_core_upper}}_REPOSITORY_{{name_singular_upper}}_REPOSITORY_HPP
#define ORES_{{component_core_upper}}_REPOSITORY_{{name_singular_upper}}_REPOSITORY_HPP

#include <string>
#include <vector>
#include <sqlgen/postgres.hpp>
{{#has_uuid_left_or_right}}
#include <boost/uuid/uuid.hpp>
{{/has_uuid_left_or_right}}
#include "ores.logging/make_logger.hpp"
#include "ores.database/domain/context.hpp"
#include "ores.{{component_include}}/domain/{{name_singular}}.hpp"
#include "ores.{{component_core}}/export.hpp"

namespace ores::{{component}}::repository {

/**
{{#read_only}}
 * @brief Reads {{name_words}} from data storage. Read-only: this
 * junction's rows are managed via SQL provisioning, not application
 * writes.
{{/read_only}}
{{^read_only}}
 * @brief Reads and writes {{name_words}} to data storage.
{{/read_only}}
 */
class ORES_{{component_core_upper}}_EXPORT {{name_singular}}_repository {
private:
    inline static std::string_view logger_name =
        "ores.{{component}}.repository.{{name_singular}}_repository";

    [[nodiscard]] static auto& lg() {
        using namespace ores::logging;
        static auto instance = make_logger(logger_name);
        return instance;
    }

public:
    using context = ores::database::context;

    explicit {{name_singular}}_repository(context ctx);

    std::string sql();

{{^read_only}}
    void write(const domain::{{name_singular}}& {{name_singular_short}});
    void write(const std::vector<domain::{{name_singular}}>& {{name_short}});

{{/read_only}}
    std::vector<domain::{{name_singular}}> read_latest();
    std::vector<domain::{{name_singular}}>
{{#left.is_uuid}}
    read_latest_by_{{left.column_short}}(const boost::uuids::uuid& {{left.column}});
{{/left.is_uuid}}
{{^left.is_uuid}}
    read_latest_by_{{left.column_short}}(const std::string& {{left.column}});
{{/left.is_uuid}}
{{#left.list_by}}
    /**
     * @brief Reads latest {{name_words}} filtered by {{left.column}}, with pagination.
     */
    std::vector<domain::{{name_singular}}>
{{#left.is_uuid}}
    read_latest_by_{{left.column_short}}(const boost::uuids::uuid& {{left.column}},
                                        std::uint32_t offset, std::uint32_t limit);
{{/left.is_uuid}}
{{^left.is_uuid}}
    read_latest_by_{{left.column_short}}(const std::string& {{left.column}},
                                        std::uint32_t offset, std::uint32_t limit);
{{/left.is_uuid}}

    /**
     * @brief Gets the total count of active {{name_words}} filtered by {{left.column}}.
     */
    std::uint32_t get_total_{{name_singular_short}}_count_by_{{left.column_short}}(
{{#left.is_uuid}}
        const boost::uuids::uuid& {{left.column}});
{{/left.is_uuid}}
{{^left.is_uuid}}
        const std::string& {{left.column}});
{{/left.is_uuid}}
{{/left.list_by}}
    std::vector<domain::{{name_singular}}>
{{#right.is_uuid}}
    read_latest_by_{{right.column_short}}(const boost::uuids::uuid& {{right.column}});
{{/right.is_uuid}}
{{^right.is_uuid}}
    read_latest_by_{{right.column_short}}(const std::string& {{right.column}});
{{/right.is_uuid}}
{{#right.list_by}}
    /**
     * @brief Reads latest {{name_words}} filtered by {{right.column}}, with pagination.
     */
    std::vector<domain::{{name_singular}}>
{{#right.is_uuid}}
    read_latest_by_{{right.column_short}}(const boost::uuids::uuid& {{right.column}},
                                         std::uint32_t offset, std::uint32_t limit);
{{/right.is_uuid}}
{{^right.is_uuid}}
    read_latest_by_{{right.column_short}}(const std::string& {{right.column}},
                                         std::uint32_t offset, std::uint32_t limit);
{{/right.is_uuid}}

    /**
     * @brief Gets the total count of active {{name_words}} filtered by {{right.column}}.
     */
    std::uint32_t get_total_{{name_singular_short}}_count_by_{{right.column_short}}(
{{#right.is_uuid}}
        const boost::uuids::uuid& {{right.column}});
{{/right.is_uuid}}
{{^right.is_uuid}}
        const std::string& {{right.column}});
{{/right.is_uuid}}
{{/right.list_by}}

{{^read_only}}
{{#left.is_uuid}}
{{#right.is_uuid}}
    void remove(const boost::uuids::uuid& {{left.column}},
                const boost::uuids::uuid& {{right.column}});
    void remove_by_{{left.column_short}}(const boost::uuids::uuid& {{left.column}});
{{/right.is_uuid}}
{{^right.is_uuid}}
    void remove(const boost::uuids::uuid& {{left.column}},
                const std::string& {{right.column}});
    void remove_by_{{left.column_short}}(const boost::uuids::uuid& {{left.column}});
{{/right.is_uuid}}
{{/left.is_uuid}}
{{^left.is_uuid}}
{{#right.is_uuid}}
    void remove(const std::string& {{left.column}},
                const boost::uuids::uuid& {{right.column}});
    void remove_by_{{left.column_short}}(const std::string& {{left.column}});
{{/right.is_uuid}}
{{^right.is_uuid}}
    void remove(const std::string& {{left.column}},
                const std::string& {{right.column}});
    void remove_by_{{left.column_short}}(const std::string& {{left.column}});
{{/right.is_uuid}}
{{/left.is_uuid}}
{{/read_only}}

private:
    context ctx_;
};

}

#endif
{{/junction}}

See also

Emacs 29.3 (Org mode 9.6.15)