AUI Framework  master
Cross-platform base for C++ UI apps
Loading...
Searching...
No Matches
ASqlModel.h
    1/*
    2 * AUI Framework - Declarative UI toolkit for modern C++20
    3 * Copyright (C) 2020-2025 Alex2772 and Contributors
    4 *
    5 * SPDX-License-Identifier: MPL-2.0
    6 *
    7 * This Source Code Form is subject to the terms of the Mozilla Public
    8 * License, v. 2.0. If a copy of the MPL was not distributed with this
    9 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
   10 */
   11
   12#pragma once
   13
   14#include "AModelMeta.h"
   15#include <AUI/Data/ASqlBuilder.h>
   16
   17#include <utility>
   18
   25template<typename Model>
   30    class NoSuchRowException: public AException {};
   31
   35    class TooManyRowsException: public AException {};
   36
   37
   38    typedef AModelMeta<Model> Meta;
   39    id_t id = 0;
   40
   47    void save() {
   48        if (id == 0) {
   49            id = table(Meta::getSqlTable()).insertORM((Model&)*this);
   50        } else {
   51            table(Meta::getSqlTable()).updateORM((Model&)*this);
   52        }
   53    }
   54
   58    void remove() {
   59        AUI_ASSERT(id != 0);
   60        table(Meta::getSqlTable()).removeORM((Model&)*this);
   61    }
   62
   63    class IncompleteSelectRequest {
   64        friend struct ASqlModel<Model>;
   65    private:
   66        AString mSql;
   67        AString mWhereExpr;
   68        AVector<AVariant> mWhereParams;
   69
   70        IncompleteSelectRequest(AString sql, const ASqlBuilder::WhereStatement::WhereExpr& expression):
   71            mSql(std::move(sql)) {
   72            where(expression);
   73        }
   74
   75    public:
   76        IncompleteSelectRequest(const IncompleteSelectRequest&) = delete;
   77        ~IncompleteSelectRequest() = default;
   78
   79        IncompleteSelectRequest& where(const ASqlBuilder::WhereStatement::WhereExpr& w) {
   80            auto[exprString, whereParams] = ASqlBuilder::WhereStatement::WhereExpr::unpack(w);
   81            if (!exprString.empty()) {
   82                if (mWhereExpr.empty()) {
   83                    mWhereExpr = "WHERE " + exprString;
   84                    mWhereParams = std::move(whereParams);
   85                } else {
   86                    mWhereExpr += "AND " + exprString;
   87                    mWhereParams.insertAll(whereParams);
   88                }
   89            }
   90            return *this;
   91        }
   92
   97        AVector<Model> get() {
   98            auto idField = AField<ASqlModel<Model>>::make(&ASqlModel<Model>::id);
   99            AVector<Model> result;
  100            result.reserve(0x100);
  101
  102            mSql += " ";
  103            mSql += mWhereExpr;
  104            auto dbResult = Autumn::get<ASqlDatabase>()->query(mSql, mWhereParams);
  105
  106            AVector<size_t> sqlColumnToModelFieldIndexMapping;
  107            AVector<_<AField<Model>>> fields;
  108            fields << AModelMeta<Model>::getFields().valueVector();
  109
  110            for (auto& row : dbResult) {
  111                Model m;
  112                idField->set(m, row->getValue(0));
  113                for (size_t columnIndex = 1; columnIndex < dbResult->getColumns().size(); ++columnIndex) {
  114                    fields[columnIndex - 1]->set(m, row->getValue(columnIndex));
  115                }
  116                result << std::move(m);
  117            }
  118
  119            return result;
  120        }
  121
  128        inline Model first() {
  129            auto result = get();
  130            if (result.size() == 0)
  131                throw NoSuchRowException();
  132            if (result.size() != 1)
  133                throw TooManyRowsException();
  134            return result.first();
  135        }
  136    };
  137    static _<IncompleteSelectRequest> where(const ASqlBuilder::WhereStatement::WhereExpr& expression) {
  138        AStringVector columnNames;
  139        columnNames << "id";
  140        columnNames << Meta::getFields().keyVector();
  141        return aui::ptr::manage(new IncompleteSelectRequest("SELECT " + (columnNames.empty() ? '*'
  142            : columnNames.join(',')) + " FROM " + AModelMeta<Model>::getSqlTable(), expression));
  143    }
  144
  151    static Model byId(id_t id) {
  152        AStringVector columns;
  153        columns << "id";
  154        columns << Meta::getFields().keyVector();
  155        auto result = table(Meta::getSqlTable()).select(columns).where(col("id") == id)
  156                .template as<Model>();
  157
  158        if (result.empty())
  159            throw NoSuchRowException();
  160        AUI_ASSERT(result.size() == 1);
  161        return result.first();
  162    }
  163
  164    static _<IncompleteSelectRequest> all() {
  165        AStringVector columnNames;
  166        columnNames << "id";
  167        columnNames << Meta::getFields().keyVector();
  168        return aui::ptr::manage(new IncompleteSelectRequest("SELECT " + (columnNames.empty() ? '*'
  169            : columnNames.join(',')) + " FROM " + AModelMeta<Model>::getSqlTable(), {}));
  170    }
  171
  172
  179    template<typename ... Args>
  180    static Model make(Args&&... args) {
  181        Model m = {0, std::forward<Args>(args)...};
  182        m.toJson();
  183        return m;
  184    }
  185
  186
  194        AString tableName = AModelMeta<Model>::getSqlTable();
  195        if (tableName.endsWith("s")) {
  196            tableName = tableName.substr(0, tableName.length() - 1);
  197        }
  198        tableName += "_id";
  199        return tableName;
  200    }
  201
  202protected:    /* ORM RELATIONSHIP */
  203
  217    template<typename Other>
  219        const AString columnName = getIdColumnNameInOtherTables();
  220        return Other::where(col(columnName) == id);
  221    }
  222
  223    template<typename Other>
  224    Other belongsTo(id_t desiredId) {
  225        return Other::where(col("id") == desiredId)->first();
  226    }
  227};
  228
Definition ASqlBuilder.h:79
AVector< Model > get()
Get query result in ORM.
Definition ASqlModel.h:97
Model first()
Do query and get first row in ORM.
Definition ASqlModel.h:128
Thrown when a single row is expected to be received, but the database did not return any rows.
Definition ASqlModel.h:30
Thrown when one row is expected to be received, but the database returned more than one row.
Definition ASqlModel.h:35
An AVector with string-related functions.
Definition AStringVector.h:22
Represents a Unicode character string.
Definition AString.h:38
A std::vector with AUI extensions.
Definition AVector.h:39
An std::weak_ptr with AUI extensions.
Definition SharedPtrTypes.h:179
#define AUI_ASSERT(condition)
Asserts that the passed condition evaluates to true.
Definition Assert.h:55
Defines model metadata (list of fields, name of appropriate sql table, etc...)
Definition AModelMeta.h:52
Defines a model that can be stored in an SQL database. Implements queries for this type to the databa...
Definition ASqlModel.h:26
void remove()
Removes row from the table by ID.
Definition ASqlModel.h:58
void save()
Saves this model in DB. If id = 0 then a new row will be created in the table, and the id of the crea...
Definition ASqlModel.h:47
static Model byId(id_t id)
Get a row from the table by ID.
Definition ASqlModel.h:151
_< typename Other::IncompleteSelectRequest > hasMany()
Implementation of one-to-many relation between ORM structures. Used with belongsTo.
Definition ASqlModel.h:218
static Model make(Args &&... args)
Creates a model and saves it to the database.
Definition ASqlModel.h:180
static AString getIdColumnNameInOtherTables()
Definition ASqlModel.h:193
static _< T > manage(T *raw)
Delegates memory management of the raw pointer T* raw to the shared pointer, which is returned.
Definition SharedPtrTypes.h:424