Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
dsogari-libcpporm-dev / usr / local / include / cpporm / relationship.h
Size: Mime:
#pragma once

#include <cpporm/util/macros.h>

CPPORM_BEGIN_NAMESPACE

// Forward declarations
namespace db
{
class Session;
class Criteria;
}
class Entity;
class PropertyMap;

/*
 * Helper classes
 */
namespace detail
{
template <typename T>
struct function_traits /** @cond */ : public function_traits<decltype(&T::operator())>
/** @endcond */
{ /** @cond */
};

template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType (ClassType::*)(Args...) const>
{ /** @endcond */
    enum
    {
        arity = sizeof...(Args)
    };

    typedef ReturnType result_type;

    template <std::size_t i>
    struct arg
    {
        typedef typename std::tuple_element<i, std::tuple<Args...>>::type type;
    };
};
}

/*!
 * \brief Abstract class to represent database relationships
 *
 * A relationship has a name, a set of properties, a list of names of foreign keys and a list of
 * names of referenced columns (one for each foreign key). Relationships should derive from
 * ToOneRelationship or ToManyRelationship, not from this class.
 *
 * \see ToOneRelationship
 * \see ToManyRelationship
 */
class CPPORM_EXPORT Relationship
{
public:
    /*!
     * \brief Destructor
     */
    virtual ~Relationship();

    /*!
     * \brief Construct a new relationship
     * \param[in] parent The parent entity
     */
    Relationship(Entity &parent);

    Relationship(const Relationship &other) = delete;

    /*!
     * \brief Copy from another relationship
     * \param[in] other The other relationship
     */
    Relationship &operator=(const Relationship &other);

    /*!
     * \brief Get relationship name
     * \return The relationship name
     */
    virtual const std::string &GetName() const = 0;

    /*!
     * \brief Get relationship properties
     * \return The relationship properties
     */
    virtual const PropertyMap &GetProperties() const = 0;

    /*!
     * \brief Get relationship foreign key names
     * \return The list of foreign key names
     */
    virtual const std::vector<std::string> &GetForeignKeyNames() const = 0;

    /*!
     * \brief Get relationship referenced names
     * \return The list of referenced names
     */
    virtual const std::vector<std::string> &GetReferencedNames() const = 0;

protected:
    /*!
     * \brief Befriend class Entity
     */
    friend class Entity;

    /*!
     * \brief Befriend class ToOneRelationship
     */
    friend class ToOneRelationship;

    /*!
     * \brief Befriend class ToManyRelationship
     */
    friend class ToManyRelationship;

    /*!
     * \brief Get relationship reciprocal name
     * \return The relationship reciprocal name
     */
    virtual const std::string &GetReciprocalName() const = 0;

    /*!
     * \brief Get prototype of the related type
     * \return The prototype of the related type
     */
    virtual Entity &GetPrototype() const = 0;

    /*!
     * \brief Unrelate
     */
    virtual void Unrelate();

    /*!
     * \brief Invalidate relationship
     */
    virtual void Invalidate();

    /*!
     * \brief Dissolve relationship
     */
    virtual void Dissolve();

    /*!
     * \brief Get database session
     * \return The database session
     */
    db::Session *GetSession() const;

    /*!
     * \brief Remove parent entity
     */
    void RemoveParent() const;

    /*!
     * \brief Set cached only
     * \param[in] criteria The search criteria
     * \param[in] value The value of the flag
     */
    void SetCachedOnly(db::Criteria &criteria, bool value) const;

    /*!
     * \brief The parent entity
     */
    Entity &mParent;
};

/*!
 * \brief Class that represents "to-one" database relationships
 *
 * "To-one" means that an entity relates to another (and only one) entity in the same database. It
 * can happen that the foreign key represented by the relationship allows NULL, in which case we say
 * that the relationship has cardinality (or multiplicity) 0..1. Otherwise it has cardinality 1
 * exactly. By design, every to-one relationship has a counter-part: the "to-many" relationship.
 * This reciprocal relationship may be omitted if required, by just removing the code declaring it.
 *
 * An example might clarify how it works:
 *
 * > Suppose a table named **Car** relates to a table named **Tire** by means of a foreign key in
 * > the Tire table, linking to an entry in the Car table. The column names could be *id* for the
 * > Car identifier and *car_id* for the foreign key in the Tire table, the latter allowing NULL
 * > values (to cover the case wherepon a tire is not yet part of a car).
 * > In this situation, the mapped C++ class Tire will have a to-one relationship pointing to Car,
 * > while the Car class will have a to-many relationship pointing to Tire. The relationship which
 * > relates Tire to Car has cardinality 0..1, while its reciprocal has cardinality 1..*.
 * > This means that a tire may be part of only one car (or none) and a car may be composed of
 * > several tires. That is why the to-many relationship is implemented as a list of pointers that
 * > can be loaded on demand and iterated using a special *for* loop.
 *
 * \see ToManyRelationship
 */
class CPPORM_EXPORT ToOneRelationship : public Relationship, private std::shared_ptr<Entity>
{
    using Relationship::Relationship;

public:
    /*!
     * \brief Check whether the related entity is not NULL
     */
    explicit operator bool();

    /*!
     * \brief Check whether two relationships point to the same entity
     * \param[in] other The other relationship
     * \return True if the pointers are equal; false otherwise
     */
    bool operator==(const ToOneRelationship &other);

    /*!
     * \brief Check whether two relationships point to different entities
     * \param[in] other The other relationship
     * \return True if the pointers are different; false otherwise
     */
    bool operator!=(const ToOneRelationship &other);

    /*!
     * \brief Check whether the related entity is the same as that pointed to by `pointer`
     * \param[in] pointer The pointer
     * \return True if the pointers are equal; false otherwise
     */
    template <typename T>
    bool operator==(const std::shared_ptr<T> &pointer)
    {
        Load();
        return get() == pointer.get();
    }

    /*!
     * \brief Check whether the related entity is different from that pointed to by `pointer`
     * \param[in] pointer The pointer
     * \return True if the pointers are different; false otherwise
     */
    template <typename T>
    bool operator!=(const std::shared_ptr<T> &pointer)
    {
        Load();
        return get() != pointer.get();
    }

protected:
    /*!
     * \brief Get the related entity
     * \return The result of dereferencing the stored pointer
     */
    template <typename T>
    T *Get()
    {
        Load();
        assert(dynamic_cast<T *>(get()));
        return static_cast<T *>(get());
    }

    /*!
     * \brief Invalidate relationship
     */
    void Invalidate() override;

    /*!
     * \brief Unrelate
     */
    void Unrelate() override;

    /*!
     * \brief Load related entity
     */
    virtual void Load();

private:
    /*!
     * \brief Befriend class Entity
     */
    friend class Entity;

    /*!
     * \brief Set foreign key null
     */
    void SetForeignKeyNull();

    /*!
     * \brief Check whether the foreign is NULL
     * \return True if the foreign key is null; false otherwise
     */
    bool IsForeignKeyNull() const;

    /*!
     * \brief Check whether the foreign key was updated
     * \return True if the foreign key was updated; false otherwise
     */
    bool WasForeignKeyUpdated() const;

    /*!
     * \brief The invalidated flag
     */
    bool mInvalidated = true;
};

/*!
 * \brief Class that represents "to-many" database relationships
 *
 * \see ToOneRelationship
 */
class CPPORM_EXPORT ToManyRelationship : public Relationship,
                                         private std::vector<std::shared_ptr<Entity>>
{
    using Relationship::Relationship;

public:
    /*!
     * \brief Loop through each related entity and execute custom procedure
     * \param[in] function The function to execute
     * \param[in] args The function arguments
     * \return True if all related entities were processed successfully; false otherwise
     */
    template <typename F, typename... Args>
    bool ForEach(F &&function, Args &&... args)
    {
        typedef typename detail::function_traits<typename std::decay<F>::type>::template arg<0>
            FirstArg;
        typedef typename std::decay<typename FirstArg::type>::type T;
        Load(false);
        for (auto &pointer : *this)
        {
            assert(dynamic_cast<T *>(pointer.get()));
            if (!std::forward<F>(function)(static_cast<T &>(*pointer), std::forward<Args>(args)...))
            {
                clear();
                return false;
            }
        }
        clear();
        return true;
    }

protected:
    /*!
     * \brief Dissolve relationship
     */
    void Dissolve() override;

    /*!
     * \brief Load related entities
     * \param[in] cachedOnly A flag to indicate whether to search only already cached entities
     */
    virtual void Load(bool cachedOnly);
};

/*!
 * \example cpporm/relationship.cpp
 *
 * This is an example of how to use the various Relationship classes.
 */

CPPORM_END_NAMESPACE