Skip to content

Commit

Permalink
added Model::increment()/decrement()
Browse files Browse the repository at this point in the history
 - added also tests
 - added default template parameter value std::size_t
 - new method HasAttributes::syncOriginalAttributes()
  • Loading branch information
silverqx committed Jul 11, 2022
1 parent 95b1473 commit 6f752c3
Show file tree
Hide file tree
Showing 7 changed files with 255 additions and 19 deletions.
4 changes: 2 additions & 2 deletions include/orm/query/querybuilder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -434,12 +434,12 @@ namespace Orm::Query
Builder &forPage(int page, int perPage = 30);

/*! Increment a column's value by a given amount. */
template<typename T> requires std::is_arithmetic_v<T>
template<typename T = std::size_t> requires std::is_arithmetic_v<T>
std::tuple<int, QSqlQuery>
increment(const QString &column, T amount = 1,
const QVector<UpdateItem> &extra = {});
/*! Decrement a column's value by a given amount. */
template<typename T> requires std::is_arithmetic_v<T>
template<typename T = std::size_t> requires std::is_arithmetic_v<T>
std::tuple<int, QSqlQuery>
decrement(const QString &column, T amount = 1,
const QVector<UpdateItem> &extra = {});
Expand Down
35 changes: 33 additions & 2 deletions include/orm/tiny/concerns/hasattributes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,13 @@ namespace Orm::Tiny::Concerns
/*! Sync the changed attributes. */
Derived &syncChanges();

/*! Determine if the new and old values for a given key are equivalent. */
/*! Sync a single original attribute with its current value. */
inline Derived &syncOriginalAttribute(const QString &attribute);
/*! Sync multiple original attribute with their current values. */
Derived &syncOriginalAttributes(const QStringList &attributes);

/*! Determine if the new and old values for a given key are equivalent
(used by the getDirty()). */
bool originalIsEquivalent(const QString &key) const;

/*! Determine if the given attribute is a date. */
Expand Down Expand Up @@ -357,7 +363,7 @@ namespace Orm::Tiny::Concerns
return m_attributes.at(m_attributesHash.at(key)).value;
}

// NOTE api different silverqx
// NOTE api different, doesn't support key = {} silverqx
template<typename Derived, AllRelationsConcept ...AllRelations>
QVariant
HasAttributes<Derived, AllRelations...>::getOriginal(
Expand Down Expand Up @@ -771,6 +777,31 @@ namespace Orm::Tiny::Concerns
return model();
}

template<typename Derived, AllRelationsConcept ...AllRelations>
Derived &HasAttributes<Derived, AllRelations...>::syncOriginalAttribute(
const QString &attribute)
{
return syncOriginalAttributes({attribute});
}

template<typename Derived, AllRelationsConcept ...AllRelations>
Derived &HasAttributes<Derived, AllRelations...>::syncOriginalAttributes(
const QStringList &attributes)
{
const auto &modelAttributes = getAttributes();
const auto &modelAttributesHash = getAttributesHash();

for (const auto &attribute : attributes) {
const auto attributeIndex = m_originalHash.at(attribute);
Q_ASSERT(attributeIndex >= 0 && attributeIndex < m_original.size());

m_original[attributeIndex].value =
modelAttributes.at(modelAttributesHash.at(attribute)).value;
}

return model();
}

template<typename Derived, AllRelationsConcept ...AllRelations>
bool
HasAttributes<Derived, AllRelations...>::originalIsEquivalent(
Expand Down
114 changes: 114 additions & 0 deletions include/orm/tiny/model.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,17 @@ namespace Orm::Tiny
/*! Update records in the database. */
bool update(const QVector<AttributeItem> &attributes, SaveOptions options = {});

/*! Increment a column's value by a given amount. */
template<typename T = std::size_t> requires std::is_arithmetic_v<T>
inline std::tuple<int, QSqlQuery>
increment(const QString &column, T amount = 1,
const QVector<AttributeItem> &extra = {}, bool all = false);
/*! Decrement a column's value by a given amount. */
template<typename T = std::size_t> requires std::is_arithmetic_v<T>
inline std::tuple<int, QSqlQuery>
decrement(const QString &column, T amount = 1,
const QVector<AttributeItem> &extra = {}, bool all = false);

/*! Delete the model from the database. */
bool remove();
/*! Delete the model from the database (alias). */
Expand Down Expand Up @@ -281,6 +292,25 @@ namespace Orm::Tiny
inline static const QString &UPDATED_AT = Constants::UPDATED_AT; // NOLINT(cppcoreguidelines-interfaces-global-init)

private:
/* Operations on a model instance */
/*! Method to call in the incrementOrDecrement(). */
enum struct IncrementOrDecrement
{
INCREMENT,
DECREMENT,
};
/*! Call the increment() method. */
constexpr static auto Increment = IncrementOrDecrement::INCREMENT;
/*! Call the decrement() method. */
constexpr static auto Decrement = IncrementOrDecrement::DECREMENT;

/*! Run the increment or decrement method on the model. */
template<typename T> requires std::is_arithmetic_v<T>
std::tuple<int, QSqlQuery>
incrementOrDecrement(
const QString &column, T amount, const QVector<AttributeItem> &extra,
IncrementOrDecrement method, bool all);

/* HasAttributes */
/*! Throw InvalidArgumentError if attributes passed to the constructor contain
some value, which will cause access of some data member in a derived
Expand Down Expand Up @@ -503,6 +533,27 @@ namespace Orm::Tiny
return fill(attributes).save(options);
}

// NOTE api different, I have added the 'all' bool param. to avoid updating all rows by mistake silverqx
template<typename Derived, AllRelationsConcept ...AllRelations>
template<typename T> requires std::is_arithmetic_v<T>
std::tuple<int, QSqlQuery>
Model<Derived, AllRelations...>::increment(
const QString &column, const T amount, const QVector<AttributeItem> &extra,
const bool all)
{
return incrementOrDecrement(column, amount, extra, Increment, all);
}

template<typename Derived, AllRelationsConcept ...AllRelations>
template<typename T> requires std::is_arithmetic_v<T>
std::tuple<int, QSqlQuery>
Model<Derived, AllRelations...>::decrement(
const QString &column, const T amount, const QVector<AttributeItem> &extra,
const bool all)
{
return incrementOrDecrement(column, amount, extra, Decrement, all);
}

template<typename Derived, AllRelationsConcept ...AllRelations>
bool Model<Derived, AllRelations...>::remove()
{
Expand Down Expand Up @@ -1122,6 +1173,69 @@ namespace Orm::Tiny

/* private */

/* Operations on a model instance */

template<typename Derived, AllRelationsConcept ...AllRelations>
template<typename T> requires std::is_arithmetic_v<T>
std::tuple<int, QSqlQuery>
Model<Derived, AllRelations...>::incrementOrDecrement(
const QString &column, const T amount, const QVector<AttributeItem> &extra,
const IncrementOrDecrement method, const bool all)
{
// Ownership of a unique_ptr()
auto builder = newQueryWithoutRelationships();

// Increment/Decrement all rows in the database, this is damn dangerous 🙃
if (!exists) {
// This makes it much safer
if (!all)
return {-1, QSqlQuery()};

const auto extraConverted = AttributeUtils::convertVectorToUpdateItem(extra);

if (method == Increment)
return builder->increment(column, amount, extraConverted);
if (method == Decrement)
return builder->decrement(column, amount, extraConverted);

Q_UNREACHABLE();
}

// Prefill an amount and extra attributes on the current model
{
auto attributeReference = this->operator[](column);
attributeReference = attributeReference->template value<T>() + amount;

forceFill(extra);
}

// Execute the increment/decrement query on the database for the current model
std::tuple<int, QSqlQuery> result;
{
auto &model = this->model().setKeysForSaveQuery(*builder);
const auto extraConverted = AttributeUtils::convertVectorToUpdateItem(extra);

if (method == Increment)
result = model.increment(column, amount, extraConverted);
else if (method == Decrement)
result = model.decrement(column, amount, extraConverted);
else
Q_UNREACHABLE();
}

// Synchronize changes manually
this->syncChanges();

// Update originals so that they are not dirty
QStringList updatedAttributes {column};
std::ranges::transform(extra, std::back_inserter(updatedAttributes),
[](const auto &attribute) { return attribute.key; });

this->syncOriginalAttributes(updatedAttributes);

return result;
}

/* HasAttributes */

template<typename Derived, AllRelationsConcept ...AllRelations>
Expand Down
2 changes: 0 additions & 2 deletions include/orm/tiny/modelproxies.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -544,8 +544,6 @@ namespace Relations
static std::unique_ptr<TinyBuilder<Derived>>
forPage(int page, int perPage = 30);

// TODO next fuckin increment, finish later 👿 silverqx

/* Pessimistic Locking */
/*! Lock the selected rows in the table for updating. */
static std::unique_ptr<TinyBuilder<Derived>>
Expand Down
4 changes: 2 additions & 2 deletions include/orm/tiny/relations/relationproxies.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -513,12 +513,12 @@ namespace Tiny::Relations
const Relation<Model, Related> &forPage(int page, int perPage = 30) const;

/*! Increment a column's value by a given amount. */
template<typename T> requires std::is_arithmetic_v<T>
template<typename T = std::size_t> requires std::is_arithmetic_v<T>
std::tuple<int, QSqlQuery>
increment(const QString &column, T amount = 1,
const QVector<UpdateItem> &extra = {}) const;
/*! Decrement a column's value by a given amount. */
template<typename T> requires std::is_arithmetic_v<T>
template<typename T = std::size_t> requires std::is_arithmetic_v<T>
std::tuple<int, QSqlQuery>
decrement(const QString &column, T amount = 1,
const QVector<UpdateItem> &extra = {}) const;
Expand Down
16 changes: 6 additions & 10 deletions include/orm/tiny/tinybuilderproxies.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -398,12 +398,12 @@ namespace Tiny
TinyBuilder<Model> &forPage(int page, int perPage = 30);

/*! Increment a column's value by a given amount. */
template<typename T> requires std::is_arithmetic_v<T>
template<typename T = std::size_t> requires std::is_arithmetic_v<T>
std::tuple<int, QSqlQuery>
increment(const QString &column, T amount = 1,
const QVector<UpdateItem> &extra = {}) const;
/*! Decrement a column's value by a given amount. */
template<typename T> requires std::is_arithmetic_v<T>
template<typename T = std::size_t> requires std::is_arithmetic_v<T>
std::tuple<int, QSqlQuery>
decrement(const QString &column, T amount = 1,
const QVector<UpdateItem> &extra = {}) const;
Expand Down Expand Up @@ -1349,22 +1349,18 @@ namespace Tiny
template<typename T> requires std::is_arithmetic_v<T>
std::tuple<int, QSqlQuery>
BuilderProxies<Model>::increment(
const QString &column, const T amount,
const QVector<UpdateItem> &extra) const
const QString &column, const T amount, const QVector<UpdateItem> &extra) const
{
return toBase().template increment<T>(column, amount,
builder().addUpdatedAtColumn(extra));
return toBase().increment(column, amount, builder().addUpdatedAtColumn(extra));
}

template<typename Model>
template<typename T> requires std::is_arithmetic_v<T>
std::tuple<int, QSqlQuery>
BuilderProxies<Model>::decrement(
const QString &column, const T amount,
const QVector<UpdateItem> &extra) const
const QString &column, const T amount, const QVector<UpdateItem> &extra) const
{
return toBase().template decrement<T>(column, amount,
builder().addUpdatedAtColumn(extra));
return toBase().decrement(column, amount, builder().addUpdatedAtColumn(extra));
}

template<typename Model>
Expand Down
Loading

0 comments on commit 6f752c3

Please sign in to comment.