Skip to main content

TinyORM: Casting

Introduction

Attribute casting allows you to transform TinyORM attribute values when you retrieve them on model instances. For example, you may want to convert a datetime string that is stored in your database to the QDateTime instance when it is accessed via your TinyORM model. Or, you may want to convert a tinyint number that is stored in the database to the bool when you access it on the TinyORM model.

Attribute Casting

Attribute casting provides functionality that allows converting model attributes to the appropriate QVariant metatype when it is accessed via your TinyORM model. The core of this functionality is a model's u_casts data member that provides a convenient method of converting attributes' QVariant internal types to the defined cast types.

The u_casts data member should be the std::unordered_map<QString, Orm::CastItem> where the key is the name of the attribute being cast and the value is the type you wish to cast the column to. The supported cast types are:

  • CastType::QString
  • CastType::Boolean / CastType::Bool
  • CastType::Integer / CastType::Int
  • CastType::UInteger / CastType::UInt
  • CastType::LongLong
  • CastType::ULongLong
  • CastType::Short
  • CastType::UShort
  • CastType::QDate
  • CastType::QDateTime
  • CastType::Timestamp
  • CastType::Real
  • CastType::Float
  • CastType::Double
  • CastType::Decimal:<precision>
  • CastType::QByteArray
info

The primary key name defined by the u_primaryKey model's data member is automatically cast to the CastType::ULongLong for all database drivers if the u_incrementing is set to true (its default value).

To demonstrate attribute casting, let's cast the is_admin attribute, which is stored in our database as an integer (0 or 1) to a QVariant(bool) value:

#pragma once

#include <orm/tiny/model.hpp>

using Orm::Tiny::CastItem;
using Orm::Tiny::CastType;
using Orm::Tiny::Model;

class User final : public Model<User>
{
friend Model;
using Model::Model;

/*! The attributes that should be cast. */
std::unordered_map<QString, CastItem> u_casts {
{"is_admin", CastType::Boolean},
};
};

After defining the cast, the is_admin attribute will always be cast to a QVariant(bool) when you access it, even if the underlying value is stored in the database as an integer:

using Orm::Utils::Helpers;

auto isAdmin = User::find(1)->getAttribute("is_admin");

// Proof of the QVariant type
Q_ASSERT(Helpers::qVariantTypeId(isAdmin) == QMetaType::Bool);

if (isAdmin.value<bool>()) {
//
}

If you need to add a new, temporary cast at runtime, you may use the mergeCasts method. These cast definitions will be added to any of the casts already defined on the model:

user->mergeCasts({
{"is_paid", CastType::Boolean},
{"income", {CastType::Decimal, 2}},
});
caution

You should never define a cast (or an attribute) that has the same name as a relationship.

info

Attributes that are null will also be cast so that the QVariant's internal type will have the correct type.

Date Casting

By default, TinyORM will cast the created_at and updated_at columns to instances of QDateTime. You may cast additional date attributes by defining additional date casts within your model's u_casts data member unordered map. Typically, dates should be cast using the CastType::QDateTime, CastType::QDate, or CastType::Timestamp cast types.

When a database column is of the date type, you may set the corresponding model attribute value to a UNIX timestamp, date string (Y-m-d), date-time string, or a QDateTime instance. The date's value will be correctly converted and stored in your database.
The same is true for the datetime or timestamp database column types, you can set the corresponding model attribute value to a UNIX timestamp, date-time string, or a QDateTime instance.

To specify the format that should be used when actually storing a model's dates within your database, you should define a u_dateFormat data member on your model:

/*! The storage format of the model's date columns. */
inline static QString u_dateFormat {QLatin1Char('U')};

This format can be any format that the QDateTime's fromString or toString methods accept or the special U format that represents the UNIX timestamp (this U format is TinyORM specific and isn't supported by QDateTime).

Query Time Casting

Sometimes you may need to apply casts while executing a query, such as when selecting a raw value from a table. For example, consider the following query:

using Models::Post;
using Models::User;

auto users = User::select("users.*")
->addSelect(
Post::selectRaw("MAX(created_at)")
->whereColumnEq("user_id", "users.id")
.toBase(),
"last_posted_at"
).get();

The last_posted_at attribute on the results of this query will be a simple string. It would be wonderful if we could apply a CastType::QDateTime cast to this attribute when executing the query. Thankfully, we may accomplish this using the withCasts or withCast methods:

auto users = User::select("users.*")
->addSelect(Post::selectRaw("MAX(created_at)")
->whereColumnEq("user_id", "users.id")
.toBase(),
"last_posted_at")
.withCast({"last_posted_at", CastType::QDateTime})
.get();