diff --git a/404.html b/404.html index 40bb9dace..e5f1b74f1 100644 --- a/404.html +++ b/404.html @@ -14,7 +14,7 @@ - + diff --git a/assets/js/0ab078a9.5134d01b.js b/assets/js/0ab078a9.2fa2fa93.js similarity index 80% rename from assets/js/0ab078a9.5134d01b.js rename to assets/js/0ab078a9.2fa2fa93.js index 68c21d35a..9090d1ee3 100644 --- a/assets/js/0ab078a9.5134d01b.js +++ b/assets/js/0ab078a9.2fa2fa93.js @@ -1 +1 @@ -"use strict";(self.webpackChunktinyorm_org=self.webpackChunktinyorm_org||[]).push([[395],{1503:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>O,contentTitle:()=>_,default:()=>I,frontMatter:()=>T,metadata:()=>v,toc:()=>R});var s=i(4848),d=i(8453),r=i(8774),l=i(6684),c=i(2364),t=i(9365),o=i(1470),a=i(5556),h=i.n(a),x=i(6694);function j(e){let{groupId:n}=e;return(0,s.jsx)("span",{children:(0,x.OZ)(n)})}j.propTypes={groupId:h().string.isRequired};const u=j;var p=i(4164),m=i(6362);const b={rootFolderInput:"rootFolderInput_ottS",input:"input_OR7e",application:"application_fjej"};function f(e){let{groupId:n,label:i}=e;const{rootFolder:d,setRootFolder:r}=(0,m.A)(),l=(0,x.T3)(n),c=l?"application":"root",t=l?"\nThis folder name is common for all shells (eg. pwsh, bash, ...)":"";return(0,s.jsx)("form",{name:"tinyorm-root-folder-form",className:(0,p.A)(b.rootFolderInput,b[n],n),onSubmit:e=>{e.preventDefault(),e.stopPropagation()},children:(0,s.jsx)("input",{name:"tinyorm-root-folder-input",className:(0,p.A)(b.input,b[n],n),placeholder:`Enter ${c} folder...`,title:`This ${c} folder will be used in all ${i} examples at tinyorm.org${t}`,onChange:e=>{r(n,e.target.value)},value:d[n]??(0,x.bw)(n)})})}f.propTypes={groupId:h().string.isRequired,label:h().string.isRequired};const g=f;var y=i(7324);const T={sidebar_position:0,sidebar_label:"TinyORM",hide_table_of_contents:!0,description:"How to compile the TinyORM C++ library on Windows and Linux.",keywords:["c++ orm","building","tinyorm"]},_="Building: TinyORM",v={id:"building/tinyorm",title:"Building: TinyORM",description:"How to compile the TinyORM C++ library on Windows and Linux.",source:"@site/docs/building/tinyorm.mdx",sourceDirName:"building",slug:"/building/tinyorm",permalink:"/building/tinyorm",draft:!1,unlisted:!1,tags:[],version:"current",sidebarPosition:0,frontMatter:{sidebar_position:0,sidebar_label:"TinyORM",hide_table_of_contents:!0,description:"How to compile the TinyORM C++ library on Windows and Linux.",keywords:["c++ orm","building","tinyorm"]},sidebar:"tinyormSidebar",previous:{title:"Getting Started",permalink:"/tinydrivers/getting-started"},next:{title:"Hello world",permalink:"/building/hello-world"}},O={},R=[{value:"Introduction",id:"introduction",level:2},{value:"Common Prerequisites",id:"common-prerequisites",level:4},{value:"Windows Prerequisites",id:"windows-prerequisites",level:4},{value:"Build environment scripts",id:"build-environment-scripts",level:5},{value:"Allow symbolic links unprivileged",id:"allow-symbolic-links-unprivileged",level:5},{value:"Folders structure",id:"folders-structure",level:2},{value:"Getting started",id:"getting-started",level:2},{value:"vcpkg",id:"vcpkg",level:2},{value:"Set up vcpkg environment",id:"set-up-vcpkg-environment",level:4},{value:"C preprocessor macros",id:"c-preprocessor-macros",level:2},{value:"Building with CMake",id:"building-with-cmake",level:2},{value:"Configure & Build (cmake)",id:"configure-and-build-cmake",level:3},{value:"CMake STRICT_MODE option",id:"cmake-strict_mode-option",level:5},{value:"Build TinyORM",id:"build-tinyorm",level:4},{value:"CMake build options",id:"cmake-build-options",level:3},{value:"Consume TinyOrm library (cmake)",id:"consume-tinyorm-library-cmake",level:3},{value:"Building with qmake",id:"building-with-qmake",level:2},{value:"Install dependencies",id:"install-dependencies",level:3},{value:"Configure & Build (qmake)",id:"configure-and-build-qmake",level:3},{value:"Open QtCreator IDE",id:"open-qtcreator-ide",level:4},{value:"Configure TinyORM",id:"configure-tinyorm",level:4},{value:"Auto-configuration and tiny_dotenv",id:"auto-configuration-and-tiny_dotenv",level:5},{value:"Manual configuration (conf.pri)",id:"manual-configuration-confpri",level:5},{value:"Opening TinyORM.pro (main project file)",id:"opening-tinyormpro-main-project-file",level:5},{value:"Build TinyORM",id:"build-tinyorm-1",level:4},{value:"qmake build options",id:"qmake-build-options",level:3},{value:"Consume TinyOrm library (qmake)",id:"consume-tinyorm-library-qmake",level:3},{value:"Requirements",id:"requirements",level:4},{value:"QMAKEFEATURES",id:"qmakefeatures",level:5},{value:"Variables affecting TinyOrm.pri",id:"variables-affecting-tinyormpri",level:5},{value:"Manual configuration examples",id:"manual-configuration-examples",level:5},{value:"Auto-configuration internals",id:"auto-configuration-internals",level:3},{value:"Environment files",id:"environment-files",level:4},{value:"Partial guessing of the TINYORM_BUILD_TREE",id:"partial-guessing-of-the-tinyorm_build_tree",level:4},{value:"Manual configuration internals",id:"manual-configuration-internals",level:3},{value:"Ccache support",id:"ccache-support",level:2}];function M(e){const n={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",h3:"h3",h4:"h4",h5:"h5",li:"li",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,d.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(n.h1,{id:"building-tinyorm",children:"Building: TinyORM"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.a,{href:"#introduction",children:"Introduction"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#common-prerequisites",children:"Common Prerequisites"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#windows-prerequisites",children:"Windows Prerequisites"})}),"\n"]}),"\n"]}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#folders-structure",children:"Folders structure"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#getting-started",children:"Getting started"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#vcpkg",children:"vcpkg"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#c-preprocessor-macros",children:"C preprocessor macros"})}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.a,{href:"#building-with-cmake",children:"Building with CMake"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#configure-and-build-cmake",children:"Configure & Build"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#cmake-build-options",children:"CMake build options"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#consume-tinyorm-library-cmake",children:"Consume TinyOrm library"})}),"\n"]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.a,{href:"#building-with-qmake",children:"Building with qmake"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#install-dependencies",children:"Install dependencies"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#configure-and-build-qmake",children:"Configure & Build"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#qmake-build-options",children:"qmake build options"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#consume-tinyorm-library-qmake",children:"Consume TinyOrm library"})}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.a,{href:"#auto-configuration-internals",children:"Auto-configuration internals"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#environment-files",children:"Environment files"})}),"\n"]}),"\n"]}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#manual-configuration-internals",children:"Manual configuration internals"})}),"\n"]}),"\n"]}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#ccache-support",children:"Ccache support"})}),"\n"]}),"\n",(0,s.jsx)(n.h2,{id:"introduction",children:"Introduction"}),"\n",(0,s.jsxs)(n.p,{children:["The build systems supported out of the box are ",(0,s.jsx)(n.code,{children:"CMake"})," and ",(0,s.jsx)(n.code,{children:"qmake"}),"."]}),"\n",(0,s.jsx)(n.admonition,{type:"info",children:(0,s.jsxs)(n.p,{children:["All examples below assume that ",(0,s.jsx)(n.code,{children:"pwsh"})," runs on ",(0,s.jsx)(n.code,{children:"Windows"})," and ",(0,s.jsx)(n.code,{children:"bash"})," runs on ",(0,s.jsx)(n.code,{children:"Linux"}),"."]})}),"\n",(0,s.jsx)(n.h4,{id:"common-prerequisites",children:"Common Prerequisites"}),"\n",(0,s.jsxs)(n.p,{children:["Install the required ",(0,s.jsx)(n.a,{href:"/dependencies",children:"dependencies"})," before starting."]}),"\n",(0,s.jsx)(n.admonition,{type:"warning",children:(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"QSqlDatabase"})," depends on ",(0,s.jsx)(n.code,{children:"QCoreApplication"})," from ",(0,s.jsx)(n.code,{children:"Qt v6.5.3"})," so you must create the ",(0,s.jsx)(n.code,{children:"QCoreApplication"})," instance before you will call anything from the ",(0,s.jsx)(n.code,{children:"TinyORM"})," library. \ud83e\udee4 The change was made ",(0,s.jsx)(n.a,{href:"https://github.com/qt/qtbase/commit/8d2bdc9cd5482eace12ba7e45304857bd24db0e6#diff-1d355c25c0b0eddec2be48253407780c4dc510d986739aec61e1ec892ccaf86e",children:"here"}),"."]})}),"\n",(0,s.jsx)(n.h4,{id:"windows-prerequisites",children:"Windows Prerequisites"}),"\n",(0,s.jsx)(n.h5,{id:"build-environment-scripts",children:"Build environment scripts"}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"Visual Studio"})," does not provide ",(0,s.jsx)(n.code,{children:"vcvars"})," scripts for ",(0,s.jsx)(n.code,{children:"pwsh"}),", you can use ",(0,s.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/tree/main/tools/vcvars64.ps1",children:(0,s.jsx)(n.code,{children:"vcvars64.ps1"})})," provided by ",(0,s.jsx)(n.code,{children:"TinyORM"})," in the ",(0,s.jsx)(n.code,{children:"tools/"})," folder. Place them on the ",(0,s.jsx)(n.code,{children:"$env:Path"})," user/system path and they will be available system-wide."]}),"\n",(0,s.jsxs)(n.p,{children:["The same is true for the ",(0,s.jsx)(n.code,{children:"Qt Framework"}),", it doesn't provide ",(0,s.jsx)(n.code,{children:"qtenv"})," scripts for ",(0,s.jsx)(n.code,{children:"pwsh"})," too. You can create your own script, place it on the ",(0,s.jsx)(n.code,{children:"$env:Path"})," user/system path and it will be available system-wide."]}),"\n",(0,s.jsxs)(n.p,{children:["Here is one simple example for ",(0,s.jsx)(n.code,{children:"pwsh"}),"."]}),"\n",(0,s.jsxs)(o.A,{groupId:y.vf,children:[(0,s.jsx)(t.A,{value:"qtenv6.ps1",label:"qtenv6.ps1",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-powershell",children:"#!/usr/bin/env pwsh\n\nSet-StrictMode -Version 3.0\n\nWrite-Host 'Setting up environment for Qt 6.7.2 usage...' -ForegroundColor Magenta\nWrite-Host\n\n$Script:QtRoot = $env:TINY_QT_ROOT ?? 'C:\\Qt'\n\n$env:Path = \"$Script:QtRoot\\6.7.2\\msvc2019_64\\bin;\" + $env:Path\n\n. vcvars64.ps1\n"})})}),(0,s.jsx)(t.A,{value:"qtenv5.ps1",label:"qtenv5.ps1",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-powershell",children:"#!/usr/bin/env pwsh\n\nSet-StrictMode -Version 3.0\n\nWrite-Host 'Setting up environment for Qt 5.15.2 usage...' -ForegroundColor Magenta\nWrite-Host\n\n$Script:QtRoot = $env:TINY_QT_ROOT ?? 'C:\\Qt'\n\n$env:Path = \"$Script:QtRoot\\5.15.2\\msvc2019_64\\bin;\" + $env:Path\n\n. vcvars64.ps1\n"})})})]}),"\n",(0,s.jsxs)(n.p,{children:["And here for ",(0,s.jsx)(n.code,{children:"Linux"}),"."]}),"\n",(0,s.jsxs)(o.A,{groupId:y.vf,children:[(0,s.jsx)(t.A,{value:"qtenv6",label:"qtenv6",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:'#!/usr/bin/env sh\n\necho \'Setting up environment for Qt 6.7.2 usage...\'\n\nQtRoot="${TINY_QT_ROOT:-/opt/Qt}"\n\nexport PATH="$QtRoot/6.7.2/gcc_64/bin"${PATH:+:}$PATH\nexport LD_LIBRARY_PATH="$QtRoot/6.7.2/gcc_64/lib"${LD_LIBRARY_PATH:+:}$LD_LIBRARY_PATH\n'})})}),(0,s.jsx)(t.A,{value:"qtenv5",label:"qtenv5",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:'#!/usr/bin/env sh\n\necho \'Setting up environment for Qt 5.15.2 usage...\'\n\nQtRoot="${TINY_QT_ROOT:-/opt/Qt}"\n\nexport PATH="$QtRoot/5.15.2/gcc_64/bin"${PATH:+:}$PATH\nexport LD_LIBRARY_PATH="$QtRoot/5.15.2/gcc_64/lib"${LD_LIBRARY_PATH:+:}$LD_LIBRARY_PATH\n'})})})]}),"\n",(0,s.jsx)(n.admonition,{type:"info",children:(0,s.jsxs)(n.p,{children:["These scripts consider the ",(0,s.jsx)(n.code,{children:"TINY_QT_ROOT"})," environment variable that should point to the ",(0,s.jsx)(n.code,{children:"Qt"})," installation folder, you can define this environment variable globally in your OS."]})}),"\n",(0,s.jsx)(n.admonition,{type:"info",children:(0,s.jsxs)(n.p,{children:["You can't execute these ",(0,s.jsx)(n.code,{children:"qtenvX"})," scripts, they have to be sourced like ",(0,s.jsx)(n.code,{children:"source qtenvX"})," or ",(0,s.jsx)(n.code,{children:". qtenvX"}),"."]})}),"\n",(0,s.jsx)(n.h5,{id:"allow-symbolic-links-unprivileged",children:"Allow symbolic links unprivileged"}),"\n",(0,s.jsxs)(n.p,{children:["Open ",(0,s.jsx)(n.code,{children:"Local Security Policy"}),", go to ",(0,s.jsx)(n.code,{children:"Local Policies - User Rights Assignment"}),", open ",(0,s.jsx)(n.code,{children:"Create symbolic links"})," and add your user account or user group, restart when it doesn't apply immediately."]}),"\n",(0,s.jsx)(n.h2,{id:"folders-structure",children:"Folders structure"}),"\n",(0,s.jsxs)(n.p,{children:["All ",(0,s.jsx)(n.code,{children:"tinyorm.org"})," examples are based on the following folders structure. The ",(0,s.jsx)(n.code,{children:"tom"})," folder will contain a ",(0,s.jsx)(n.a,{href:"/building/migrations",children:"migrations console application"}),"."]}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["You can set the root and application folder paths in the form below and they will be used across the whole ",(0,s.jsx)(n.a,{href:"http://www.tinyorm.org",children:"www.tinyorm.org"})," website. \ud83e\udd73 The pwsh shell is supposed to use on Windows and the bash shell on Linux, but it is not a requirement."]})}),"\n",(0,s.jsxs)(o.A,{groupId:y.vf,children:[(0,s.jsxs)(t.A,{value:y.b,label:y.ux,className:"tiny-tree",children:[(0,s.jsx)("div",{className:"tiny-root-folder-info-wrapper",children:(0,s.jsxs)(n.p,{children:[(0,s.jsx)("span",{className:"tiny-root-folder-info-prefix",children:"Current pwsh path"}),"\xa0",(0,s.jsx)(u,{groupId:y.b})]})}),(0,s.jsx)(g,{groupId:y.b,label:y.ux}),(0,s.jsx)(g,{groupId:y.pW,label:y.kl}),(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-text",children:"\n\n\n\u251c\u2500\u2500\n\u2502 \u251c\u2500\u2500 HelloWorld/\n\u2502 | \u251c\u2500\u2500 HelloWorld/\n\u2502 | \u251c\u2500\u2500 HelloWorld-builds-cmake/\n\u2502 | | \u2514\u2500\u2500 build-debug/\n\u2502 | \u2514\u2500\u2500 HelloWorld-builds-qmake/\n\u2502 | \u2514\u2500\u2500 build-debug/\n\u2502 \u251c\u2500\u2500 TinyORM/\n\u2502 | \u251c\u2500\u2500 TinyORM/\n\u2502 | \u251c\u2500\u2500 TinyORM-builds-cmake/\n\u2502 | \u2502 \u251c\u2500\u2500 build-gcc-debug/\n\u2502 | \u2502 \u251c\u2500\u2500 build-gcc-release/\n\u2502 | \u2502 \u2514\u2500\u2500 build-clang-debug/\n\u2502 | \u2514\u2500\u2500 TinyORM-builds-qmake/\n\u2502 | \u251c\u2500\u2500 build-debug/\n\u2502 | \u251c\u2500\u2500 build-TinyORM-Desktop_Qt_5_15_2_MSVC2019_64bit-Debug/\n\u2502 | \u2514\u2500\u2500 build-TinyORM-Desktop_Qt_5_15_2_MSYS2_UCRT64_64bit-Release/\n\u2502 \u2514\u2500\u2500 tom/\n\u2502 \u251c\u2500\u2500 tom/\n\u2502 \u2502 \u2514\u2500\u2500 database/\n\u2502 \u2502 \u251c\u2500\u2500 migrations/\n\u2502 \u2502 \u251c\u2500\u2500 seeders/\n\u2502 \u2502 \u251c\u2500\u2500 migrations.pri\n\u2502 \u2502 \u2514\u2500\u2500 seeders.pri\n\u2502 \u251c\u2500\u2500 tom-builds-cmake/\n\u2502 \u2502 \u2514\u2500\u2500 build-TinyORM-Desktop_Qt_6_7_2_MSVC2019_64bit-Debug/\n\u2502 \u2514\u2500\u2500 tom-builds-qmake/\n\u2502 \u251c\u2500\u2500 build-TinyORM-Desktop_Qt_5_15_3_MSYS2_UCRT64_64bit-Release/\n\u2502 \u2514\u2500\u2500 build-TinyORM-Desktop_Qt_6_7_2_MSVC2019_64bit-Debug/\n\u251c\u2500\u2500 tmp/\n\u2514\u2500\u2500 vcpkg/\n"})})]}),(0,s.jsxs)(t.A,{value:y.xj,label:y.gg,className:"tiny-tree",children:[(0,s.jsx)("div",{className:"tiny-root-folder-info-wrapper",children:(0,s.jsxs)(n.p,{children:[(0,s.jsx)("span",{className:"tiny-root-folder-info-prefix",children:"Current bash path"}),"\xa0",(0,s.jsx)(u,{groupId:y.xj})]})}),(0,s.jsx)(g,{groupId:y.xj,label:y.gg}),(0,s.jsx)(g,{groupId:y.pW,label:y.pW}),(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-text",children:"\n\n\n\u251c\u2500\u2500\n\u2502 \u251c\u2500\u2500 HelloWorld/\n\u2502 | \u251c\u2500\u2500 HelloWorld/\n\u2502 | \u251c\u2500\u2500 HelloWorld-builds-cmake/\n\u2502 | | \u2514\u2500\u2500 build-debug/\n\u2502 | \u2514\u2500\u2500 HelloWorld-builds-qmake/\n\u2502 | \u2514\u2500\u2500 build-debug/\n\u2502 \u251c\u2500\u2500 TinyORM/\n\u2502 | \u251c\u2500\u2500 TinyORM/\n\u2502 | \u251c\u2500\u2500 TinyORM-builds-cmake/\n\u2502 | \u2502 \u251c\u2500\u2500 build-gcc-debug/\n\u2502 | \u2502 \u251c\u2500\u2500 build-gcc-release/\n\u2502 | \u2502 \u2514\u2500\u2500 build-clang-debug/\n\u2502 | \u2514\u2500\u2500 TinyORM-builds-qmake/\n\u2502 | \u251c\u2500\u2500 build-debug/\n\u2502 | \u251c\u2500\u2500 build-TinyORM-Desktop_Qt_5_15_2_GCC_64bit-Debug/\n\u2502 | \u2514\u2500\u2500 build-TinyORM-Desktop_Qt_5_15_2_clang13_64bit_ccache-Release/\n\u2502 \u2514\u2500\u2500 tom/\n\u2502 \u251c\u2500\u2500 tom/\n\u2502 \u2502 \u2514\u2500\u2500 database/\n\u2502 \u2502 \u251c\u2500\u2500 migrations/\n\u2502 \u2502 \u251c\u2500\u2500 seeders/\n\u2502 \u2502 \u251c\u2500\u2500 migrations.pri\n\u2502 \u2502 \u2514\u2500\u2500 seeders.pri\n\u2502 \u251c\u2500\u2500 tom-builds-cmake/\n\u2502 \u2502 \u2514\u2500\u2500 build-TinyORM-Desktop_Qt_6_7_2_clang14_64bit_ccache-Debug/\n\u2502 \u2514\u2500\u2500 tom-builds-qmake/\n\u2502 \u251c\u2500\u2500 build-TinyORM-Desktop_Qt_6_7_2_GCC_64bit-Debug/\n\u2502 \u2514\u2500\u2500 build-TinyORM-Desktop_Qt_6_7_2_clang14_64bit_ccache-Release/\n\u251c\u2500\u2500 tmp/\n\u2514\u2500\u2500 vcpkg/\n"})})]})]}),"\n",(0,s.jsx)(n.admonition,{type:"danger",children:(0,s.jsxs)(n.p,{children:["Avoid paths with spaces with the ",(0,s.jsx)(n.code,{children:"qmake"})," build system, it will not compile."]})}),"\n",(0,s.jsx)(r.A,{id:"qtcreator-default-build-directory"}),"\n",(0,s.jsxs)(n.admonition,{type:"tip",children:[(0,s.jsxs)(n.p,{children:["You can force the ",(0,s.jsx)(n.code,{children:"QtCreator"})," to generate a build folders structure as is described above."]}),(0,s.jsxs)(n.p,{children:["To generate the required folders structure set the ",(0,s.jsx)(n.code,{children:"Settings"})," - ",(0,s.jsx)(n.code,{children:"Build & Run"})," - ",(0,s.jsx)(n.code,{children:"Default Build Properties"})," - ",(0,s.jsx)(n.code,{children:"Default build directory"})," to:",(0,s.jsx)("br",{}),"\n",(0,s.jsx)(n.code,{children:'../%{Project:Name}-builds-%{BuildSystem:Name}/%{JS: Util.asciify("build-%{Project:Name}-%{Kit:FileSystemName}-%{BuildConfig:Name}")}'})]})]}),"\n",(0,s.jsx)(n.h2,{id:"getting-started",children:"Getting started"}),"\n",(0,s.jsxs)(n.p,{children:["Prepare compilation environment, we need to put the Qt Framework and Visual Studio MSVC compiler on the path on Windows. The compiler is already on the path on Linux and you can export ",(0,s.jsx)(n.code,{children:"PATH"})," and ",(0,s.jsx)(n.code,{children:"LD_LIBRARY_PATH"})," for Qt Framework, or use our ",(0,s.jsx)(n.code,{children:"qtenvX"})," scripts described above."]}),"\n",(0,s.jsxs)(o.A,{groupId:y.vf,children:[(0,s.jsx)(t.A,{value:y.b,label:y.ux,children:(0,s.jsx)(c.A,{className:"language-powershell",children:`mkdir ${(0,x.Sn)(y.b)}\ncd ${(0,x.Sn)(y.b)}\n$env:Path = 'C:\\Qt\\6.7.2\\msvc2019_64\\bin;' + $env:Path\nvcvars64.ps1`})}),(0,s.jsx)(t.A,{value:y.xj,label:y.gg,children:(0,s.jsx)(c.A,{className:"language-bash",children:`mkdir -p ${(0,x.Sn)(y.xj)}\ncd ${(0,x.Sn)(y.xj)}\nexport PATH=/opt/Qt/6.7.2/gcc_64/bin\${PATH:+:}$PATH\nexport LD_LIBRARY_PATH=/opt/Qt/6.7.2/gcc_64/lib\${LD_LIBRARY_PATH:+:}$LD_LIBRARY_PATH`})})]}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["You can also use the ",(0,s.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/tools/Add-FolderOnPath.ps1",children:(0,s.jsx)(n.code,{children:"tools/Add-FolderOnPath.ps1"})})," pwsh script to fastly prepend a path or ",(0,s.jsx)("abbr",{title:"Current working directory",children:"pwd"})," on the system ",(0,s.jsx)(n.code,{children:"PATH"}),"."]})}),"\n",(0,s.jsx)(n.h2,{id:"vcpkg",children:"vcpkg"}),"\n",(0,s.jsxs)(n.p,{children:["Installing the ",(0,s.jsx)(n.code,{children:"vcpkg"})," is highly recommended, it simplifies installation of the ",(0,s.jsx)(n.code,{children:"range-v3"})," and ",(0,s.jsx)(n.code,{children:"tabulate"})," dependencies."]}),"\n",(0,s.jsxs)(o.A,{groupId:y.vf,children:[(0,s.jsx)(t.A,{value:y.b,label:y.ux,children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-powershell",children:"git clone git@github.com:microsoft/vcpkg.git\ncd vcpkg\n.\\bootstrap-vcpkg.bat\n"})})}),(0,s.jsx)(t.A,{value:y.xj,label:y.gg,children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"git clone git@github.com:microsoft/vcpkg.git\ncd vcpkg\n./bootstrap-vcpkg.sh\n"})})})]}),"\n",(0,s.jsxs)(n.p,{children:["Add ",(0,s.jsx)(n.code,{children:"vcpkg"})," on the system path, add the following to the ",(0,s.jsx)(n.code,{children:".bashrc"})," or ",(0,s.jsx)(n.code,{children:".zshrc"})," on Linux."]}),"\n",(0,s.jsx)(c.A,{className:"language-bash",children:`export PATH=${(0,x.Sn)(y.xj)}/vcpkg\${PATH:+:}$PATH`}),"\n",(0,s.jsxs)(n.p,{children:["On Windows, open the ",(0,s.jsx)(n.code,{children:"Environment variables"})," dialog and add ",(0,s.jsx)(n.code,{children:"vcpkg"})," on the user ",(0,s.jsx)(n.code,{children:"PATH"}),"."]}),"\n",(0,s.jsx)(n.p,{children:"Or you can export it for the current session only."}),"\n",(0,s.jsxs)(o.A,{groupId:y.vf,children:[(0,s.jsx)(t.A,{value:y.b,label:y.ux,children:(0,s.jsx)(c.A,{className:"language-powershell",children:`$env:Path = "${(0,x.Sn)(y.b,!1)}\\vcpkg;" + $env:Path`})}),(0,s.jsx)(t.A,{value:y.xj,label:y.gg,children:(0,s.jsx)(c.A,{className:"language-bash",children:`export PATH=${(0,x.Sn)(y.xj)}/vcpkg\${PATH:+:}$PATH`})})]}),"\n",(0,s.jsxs)(n.h4,{id:"set-up-vcpkg-environment",children:["Set up ",(0,s.jsx)(n.code,{children:"vcpkg"})," environment"]}),"\n",(0,s.jsxs)(n.p,{children:["To export ",(0,s.jsx)(n.code,{children:"vcpkg"})," environment variables globally, add it to the ",(0,s.jsx)(n.code,{children:".bashrc"})," or ",(0,s.jsx)(n.code,{children:".zshrc"})," on Linux, and you can use the ",(0,s.jsx)(n.code,{children:"Environment variables"})," dialog on Windows."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",metastring:"title='Linux'",children:'export VCPKG_DEFAULT_TRIPLET=x64-linux\n#export VCPKG_DEFAULT_HOST_TRIPLET=x64-linux\nexport VCPKG_MAX_CONCURRENCY=11\nexport VCPKG_OVERLAY_PORTS="$HOME/.local/share/vcpkg/ports"\nexport VCPKG_OVERLAY_TRIPLETS="$HOME/.local/share/vcpkg/triplets"\nexport VCPKG_ROOT="$HOME/Code/c/vcpkg"\n'})}),"\n",(0,s.jsx)(n.admonition,{type:"info",children:(0,s.jsxs)(n.p,{children:["It is recommended to define these variables globally because the ",(0,s.jsx)(n.code,{children:"CMake"})," and ",(0,s.jsx)(n.code,{children:"qmake"})," build system are able to detect the ",(0,s.jsx)(n.code,{children:"vcpkg"})," installation from them so you don't have to configure them manually to detect the ",(0,s.jsx)(n.code,{children:"vcpkg"})," installation."]})}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["On Windows, it's always better to create these types of variables as user variables instead of system variables in the ",(0,s.jsx)(n.code,{children:"Environment variables"})," dialog."]})}),"\n",(0,s.jsx)(n.h2,{id:"c-preprocessor-macros",children:"C preprocessor macros"}),"\n",(0,s.jsxs)(n.p,{children:["The following table summarizes all the C preprocessor macros defined in the ",(0,s.jsx)(n.code,{children:"TinyORM"})," library. These C macros are configured by ",(0,s.jsx)(n.code,{children:"CMake"})," or ",(0,s.jsx)(n.code,{children:"qmake"})," build systems. They are not sorted alphabetically, but they are sorted by how significant they are."]}),"\n",(0,s.jsxs)(n.p,{children:["In the ",(0,s.jsx)(n.code,{children:"CMake"})," build system, all the C macros are auto-detected / auto-configured or controlled by ",(0,s.jsx)(n.a,{href:"#cmake-build-options",children:(0,s.jsx)(n.code,{children:"CMake build options"})}),", so you don't have to care too much about them."]}),"\n",(0,s.jsxs)(n.p,{children:["In the ",(0,s.jsx)(n.code,{children:"qmake"})," build is important whether you are building ",(0,s.jsx)(n.code,{children:"TinyORM"})," library or you are building your application and linking against ",(0,s.jsx)(n.code,{children:"TinyORM"})," library. When you are building the ",(0,s.jsx)(n.code,{children:"TinyORM"})," library, all the C macros are auto-detected / auto-configured or controlled by ",(0,s.jsx)(n.a,{href:"#qmake-build-options",children:(0,s.jsx)(n.code,{children:"qmake build options"})}),", so you don't have to care too much about them."]}),"\n",(0,s.jsxs)(n.p,{children:["But a special situation is when you are building your application / library and you are linking against ",(0,s.jsx)(n.code,{children:"TinyORM"})," library. In this particular case, you must configure all these C macros manually! For this reason, the ",(0,s.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/qmake/TinyOrm.pri",children:(0,s.jsx)(n.code,{children:"TinyOrm.pri"})})," has been created, so that's not a big deal either. Little more info ",(0,s.jsx)(n.a,{href:"#consume-tinyorm-library-qmake",children:"here"}),"."]}),"\n",(0,s.jsx)("div",{id:"apitable-c-macros",children:(0,s.jsx)(l.A,{children:(0,s.jsxs)(n.table,{children:[(0,s.jsx)(n.thead,{children:(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.th,{children:"C Macro Name"}),(0,s.jsx)(n.th,{children:"Description"})]})}),(0,s.jsxs)(n.tbody,{children:[(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINYORM_LINKING_SHARED"})}),(0,s.jsxs)(n.td,{children:[(0,s.jsx)("u",{children:(0,s.jsx)(n.strong,{children:"Must"})})," be defined when you are linking against ",(0,s.jsx)(n.code,{children:"TinyORM"})," shared build (",(0,s.jsx)(n.code,{children:"dll"})," library), exported classes and functions will be tagged with ",(0,s.jsx)(n.code,{children:"__declspec(dllimport)"})," on ",(0,s.jsx)(n.code,{children:"msvc"})," and ",(0,s.jsx)(n.code,{children:'visibility("default")'})," on ",(0,s.jsx)(n.code,{children:"GCC >= 4"}),"."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINYORM_BUILDING_SHARED"})}),(0,s.jsxs)(n.td,{children:["Defined when ",(0,s.jsx)(n.code,{children:"TinyORM"})," is built as a ",(0,s.jsx)(n.code,{children:"dll"})," library (shared build)."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINYORM_DEBUG"})}),(0,s.jsx)(n.td,{children:"Defined in the debug build."})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINYORM_NO_DEBUG"})}),(0,s.jsx)(n.td,{children:"Defined in the release build."})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINYORM_DEBUG_SQL"})}),(0,s.jsx)(n.td,{children:"Defined in the debug build."})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINYORM_NO_DEBUG_SQL"})}),(0,s.jsx)(n.td,{children:"Defined in the release build."})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINYORM_MYSQL_PING"})}),(0,s.jsxs)(n.td,{children:["Enable ",(0,s.jsx)(n.code,{children:"Orm::MySqlConnection::pingDatabase()"})," method.",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Defined when ",(0,s.jsx)(n.a,{href:"#mysql_ping",children:(0,s.jsx)(n.code,{children:"mysql_ping"})})," ",(0,s.jsx)("small",{children:"(qmake)"})," / ",(0,s.jsx)(n.a,{href:"#MYSQL_PING",children:(0,s.jsx)(n.code,{children:"MYSQL_PING"})})," ",(0,s.jsx)("small",{children:"(cmake)"})," configuration ",(0,s.jsx)(n.code,{children:"build option"})," is enabled."]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINYORM_DISABLE_ORM"})}),(0,s.jsxs)(n.td,{children:["Controls the compilation of all ",(0,s.jsx)(n.code,{children:"ORM-related"})," source code, when this macro is ",(0,s.jsx)(n.code,{children:"defined"}),", then only the ",(0,s.jsx)(n.code,{children:"query builder"})," without ",(0,s.jsx)(n.code,{children:"ORM"})," is compiled. Also excludes ",(0,s.jsx)(n.code,{children:"ORM-related"})," unit tests.",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Defined when ",(0,s.jsx)(n.a,{href:"#disable_orm",children:(0,s.jsx)(n.code,{children:"disable_orm"})})," ",(0,s.jsx)("small",{children:"(qmake)"})," / ",(0,s.jsx)(n.a,{href:"#ORM",children:(0,s.jsx)(n.code,{children:"ORM"})})," ",(0,s.jsx)("small",{children:"(cmake)"})," configuration ",(0,s.jsx)(n.code,{children:"build option"})," is enabled ",(0,s.jsx)("small",{children:"(qmake)"})," / disabled ",(0,s.jsx)("small",{children:"(cmake)"}),"."]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINYORM_EXTERN_CONSTANTS"})}),(0,s.jsxs)(n.td,{children:["Defined when extern constants are used. Extern constants are enabled by default for shared builds and disabled for static builds.",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Described at ",(0,s.jsx)(n.a,{href:"#extern_constants",children:(0,s.jsx)(n.code,{children:"qmake"})})," / ",(0,s.jsx)(n.a,{href:"#INLINE_CONSTANTS",children:(0,s.jsx)(n.code,{children:"CMake"})})," how it works."]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINYORM_INLINE_CONSTANTS"})}),(0,s.jsxs)(n.td,{children:["Defined when global inline constants are used.",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Defined when ",(0,s.jsx)(n.a,{href:"#inline_constants",children:(0,s.jsx)(n.code,{children:"inline_constants"})})," ",(0,s.jsx)("small",{children:"(qmake)"})," / ",(0,s.jsx)(n.a,{href:"#INLINE_CONSTANTS",children:(0,s.jsx)(n.code,{children:"INLINE_CONSTANTS"})})," ",(0,s.jsx)("small",{children:"(cmake)"})," configuration ",(0,s.jsx)(n.code,{children:"build option"})," is enabled."]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINYORM_TESTS_CODE"})}),(0,s.jsxs)(n.td,{children:["Enable code needed by unit tests, eg. connection overriding in the ",(0,s.jsx)(n.code,{children:"Orm::Tiny::Model"}),".",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Defined when ",(0,s.jsx)(n.a,{href:"#build_tests",children:(0,s.jsx)(n.code,{children:"build_tests"})})," ",(0,s.jsx)("small",{children:"(qmake)"})," / ",(0,s.jsx)(n.a,{href:"#BUILD_TESTS",children:(0,s.jsx)(n.code,{children:"BUILD_TESTS"})})," ",(0,s.jsx)("small",{children:"(cmake)"})," configuration ",(0,s.jsx)(n.code,{children:"build option"})," is enabled."]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINYORM_DISABLE_THREAD_LOCAL"})}),(0,s.jsxs)(n.td,{children:["Remove all ",(0,s.jsx)(n.a,{href:"https://en.cppreference.com/w/c/language/storage_duration",children:(0,s.jsx)(n.code,{children:"thread_local"})})," storage duration specifiers, it disables multi-threading support.",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Defined when ",(0,s.jsx)(n.a,{href:"#disable_thread_local",children:(0,s.jsx)(n.code,{children:"disable_thread_local"})})," ",(0,s.jsx)("small",{children:"(qmake)"})," / ",(0,s.jsx)(n.a,{href:"#DISABLE_THREAD_LOCAL",children:(0,s.jsx)(n.code,{children:"DISABLE_THREAD_LOCAL"})})," ",(0,s.jsx)("small",{children:"(cmake)"})," configuration ",(0,s.jsx)(n.code,{children:"build option"})," is enabled."]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINYTOM_MIGRATIONS_DIR"})}),(0,s.jsxs)(n.td,{children:["Default migrations path for the ",(0,s.jsx)(n.code,{children:"make:migration"})," command, can be an absolute or relative path (to the ",(0,s.jsx)("abbr",{title:"Current working directory",children:"pwd"}),").",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Default value: ",(0,s.jsx)(n.code,{children:"database/migrations"})," ",(0,s.jsx)("small",{children:"(relative to the pwd)"})]}),(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Defined by ",(0,s.jsx)(n.a,{href:"#TOM_MIGRATIONS_DIR",children:(0,s.jsx)(n.code,{children:"TOM_MIGRATIONS_DIR"})})," ",(0,s.jsx)("small",{children:"(cmake)"})," configuration build option.",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["(qmake note) You can use ",(0,s.jsx)(n.code,{children:'DEFINES += TINYTOM_MIGRATIONS_DIR="\\"database/migrations\\""'})," on the command-line or set it in the ",(0,s.jsx)(n.strong,{children:"main"})," ",(0,s.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/conf.pri.example#L65-L70",children:(0,s.jsx)(n.code,{children:"conf.pri"})})," file."]})]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINYTOM_MODELS_DIR"})}),(0,s.jsxs)(n.td,{children:["Default models path for the ",(0,s.jsx)(n.code,{children:"make:model"})," command, can be an absolute or relative path (to the ",(0,s.jsx)("abbr",{title:"Current working directory",children:"pwd"}),").",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Default value: ",(0,s.jsx)(n.code,{children:"database/models"})," ",(0,s.jsx)("small",{children:"(relative to the pwd)"})]}),(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Defined by ",(0,s.jsx)(n.a,{href:"#TOM_MODELS_DIR",children:(0,s.jsx)(n.code,{children:"TOM_MODELS_DIR"})})," ",(0,s.jsx)("small",{children:"(cmake)"})," configuration build option.",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["(qmake note) You can use ",(0,s.jsx)(n.code,{children:'DEFINES += TINYTOM_MODELS_DIR="\\"database/models\\""'})," on the command-line or set it in the ",(0,s.jsx)(n.strong,{children:"main"})," ",(0,s.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/conf.pri.example#L72-L73",children:(0,s.jsx)(n.code,{children:"conf.pri"})})," file."]})]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINYTOM_SEEDERS_DIR"})}),(0,s.jsxs)(n.td,{children:["Default seeders path for the ",(0,s.jsx)(n.code,{children:"make:seeder"})," command, can be an absolute or relative path (to the ",(0,s.jsx)("abbr",{title:"Current working directory",children:"pwd"}),").",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Default value: ",(0,s.jsx)(n.code,{children:"database/seeders"})," ",(0,s.jsx)("small",{children:"(relative to the pwd)"})]}),(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Defined by ",(0,s.jsx)(n.a,{href:"#TOM_SEEDERS_DIR",children:(0,s.jsx)(n.code,{children:"TOM_SEEDERS_DIR"})})," ",(0,s.jsx)("small",{children:"(cmake)"})," configuration build option.",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["(qmake note) You can use ",(0,s.jsx)(n.code,{children:'DEFINES += TINYTOM_SEEDERS_DIR="\\"database/seeders\\""'})," on the command-line or set it in the ",(0,s.jsx)(n.strong,{children:"main"})," ",(0,s.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/conf.pri.example#L75-L76",children:(0,s.jsx)(n.code,{children:"conf.pri"})})," file."]})]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINYORM_USING_PCH"})}),(0,s.jsxs)(n.td,{children:["Defined if building with precompiled headers.",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Controlled by ",(0,s.jsx)(n.a,{href:"#qmake-precompile_header",children:(0,s.jsx)(n.code,{children:"qmake"})})," / ",(0,s.jsx)(n.a,{href:"#CMAKE_DISABLE_PRECOMPILE_HEADERS",children:(0,s.jsx)(n.code,{children:"CMake"})}),"."]})]})]})]})]})})}),"\n",(0,s.jsx)(n.h2,{id:"building-with-cmake",children:"Building with CMake"}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["If something is not clear, you can still look at GitHub Action ",(0,s.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/tree/main/.github/workflows",children:(0,s.jsx)(n.code,{children:"workflows"})})," how a building is done."]})}),"\n",(0,s.jsxs)(n.p,{children:["First, create a basic folder structure and then clone the ",(0,s.jsx)(n.code,{children:"TinyORM"})," project."]}),"\n",(0,s.jsxs)(o.A,{groupId:y.vf,children:[(0,s.jsx)(t.A,{value:y.b,label:y.ux,children:(0,s.jsx)(c.A,{className:"language-powershell",children:`cd ${(0,x.Sn)(y.b)}\nmkdir ${(0,x.np)()}/TinyORM/TinyORM-builds-cmake/build-debug\n\ncd ${(0,x.np)()}/TinyORM\ngit clone git@github.com:silverqx/TinyORM.git`})}),(0,s.jsx)(t.A,{value:y.xj,label:y.gg,children:(0,s.jsx)(c.A,{className:"language-bash",children:`cd ${(0,x.Sn)(y.xj)}\nmkdir -p ${(0,x.np)()}/TinyORM/TinyORM-builds-cmake/build-debug\n\ncd ${(0,x.np)()}/TinyORM\ngit clone git@github.com:silverqx/TinyORM.git`})})]}),"\n",(0,s.jsxs)(n.h3,{id:"configure-and-build-cmake",children:["Configure & Build ",(0,s.jsx)("small",{children:"(cmake)"})]}),"\n",(0,s.jsxs)(n.p,{children:["Now you are ready to configure the ",(0,s.jsx)(n.code,{children:"TinyORM"})," library."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"cd TinyORM-builds-cmake/build-debug\n"})}),"\n",(0,s.jsxs)(o.A,{groupId:y.vf,children:[(0,s.jsx)(t.A,{value:y.b,label:y.ux,children:(0,s.jsx)(c.A,{className:"language-powershell",children:`cmake.exe \`\n-S "${(0,x.OZ)(y.b)}/TinyORM/TinyORM" \`\n-B "${(0,x.OZ)(y.b)}/TinyORM/TinyORM-builds-cmake/build-debug" \`\n-G 'Ninja' \`\n-D CMAKE_BUILD_TYPE:STRING='Debug' \`\n-D CMAKE_TOOLCHAIN_FILE:FILEPATH="${(0,x.Sn)(y.b)}/vcpkg/scripts/buildsystems/vcpkg.cmake" \`\n-D CMAKE_CXX_SCAN_FOR_MODULES:BOOL=OFF \`\n-D CMAKE_INSTALL_PREFIX:PATH="${(0,x.Sn)(y.b)}/tmp/TinyORM" \`\n-D BUILD_TESTS:BOOL=OFF \`\n-D MATCH_EQUAL_EXPORTED_BUILDTREE:BOOL=ON \`\n-D MYSQL_PING:BOOL=OFF \`\n-D TOM:BOOL=ON \`\n-D TOM_EXAMPLE:BOOL=OFF \`\n-D VERBOSE_CONFIGURE:BOOL=ON`})}),(0,s.jsx)(t.A,{value:y.xj,label:y.gg,children:(0,s.jsx)(c.A,{className:"language-bash",children:`cmake \\\n-S "${(0,x.OZ)(y.xj)}/TinyORM/TinyORM" \\\n-B "${(0,x.OZ)(y.xj)}/TinyORM/TinyORM-builds-cmake/build-debug" \\\n-G 'Ninja' \\\n-D CMAKE_BUILD_TYPE:STRING='Debug' \\\n-D CMAKE_TOOLCHAIN_FILE:FILEPATH="${(0,x.Sn)(y.xj)}/vcpkg/scripts/buildsystems/vcpkg.cmake" \\\n-D CMAKE_CXX_SCAN_FOR_MODULES:BOOL=OFF \\\n-D CMAKE_INSTALL_PREFIX:PATH="${(0,x.Sn)(y.xj)}/tmp/TinyORM" \\\n-D VERBOSE_CONFIGURE:BOOL=ON \\\n-D BUILD_TESTS:BOOL=OFF \\\n-D MYSQL_PING:BOOL=OFF \\\n-D TOM:BOOL=ON \\\n-D TOM_EXAMPLE:BOOL=OFF \\\n-D MATCH_EQUAL_EXPORTED_BUILDTREE:BOOL=ON`})})]}),"\n",(0,s.jsxs)(n.h5,{id:"cmake-strict_mode-option",children:["CMake ",(0,s.jsx)(n.code,{children:"STRICT_MODE"})," option"]}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"STRICT_MODE"})," ",(0,s.jsx)(n.code,{children:"CMake"})," configuration option was added in ",(0,s.jsx)(n.code,{children:"TinyORM"})," ",(0,s.jsx)(n.code,{children:"v0.37.3"}),". This option was added to avoid the propagation of aggressive strict warning compiler/linker options and Qt definitions from the ",(0,s.jsx)(n.code,{children:"TinyORM"})," library to user code through the ",(0,s.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/cmake/CommonModules/TinyCommon.cmake",children:(0,s.jsx)(n.code,{children:"TinyOrm::CommonConfig"})})," interface library."]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.code,{children:"TinyORM"})," uses the strictest warning level options, virtually anything that can be enabled is enabled to produce a better code. I highly recommend enabling this option to produce better code and to follow good practices. It also helps to follow the ",(0,s.jsx)(n.code,{children:"ISOCPP"})," ",(0,s.jsx)(n.a,{href:"https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines",children:"C++ Core Guidelines"})," standards."]}),"\n",(0,s.jsxs)(n.p,{children:["If you want to enable these strict warning options in your code, you can enable the ",(0,s.jsx)(n.code,{children:"STRICT_MODE"})," ",(0,s.jsx)(n.code,{children:"CMake"})," configuration option and they will be propagated to your code. You can also enabled it globally using the ",(0,s.jsx)(n.code,{children:"TINYORM_STRICT_MODE"})," environment variable, and the value of this environment variable will be picked up during initial CMake configuration as the default value for the ",(0,s.jsx)(n.code,{children:"STRICT_MODE"})," ",(0,s.jsx)(n.code,{children:"CMake"})," configuration option."]}),"\n",(0,s.jsxs)(n.p,{children:["You can achieve the same result by manually linking against the ",(0,s.jsx)(n.code,{children:"TinyOrm::CommonConfig"})," interface library when the ",(0,s.jsx)(n.code,{children:"STRICT_MODE"})," is set to ",(0,s.jsx)(n.code,{children:"OFF"}),"."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cmake",children:"target_link_libraries( PRIVATE TinyOrm::CommonConfig)\n"})}),"\n",(0,s.jsx)(n.admonition,{type:"info",children:(0,s.jsxs)(n.p,{children:["The recommended way is to set the ",(0,s.jsx)(n.code,{children:"TINYORM_STRICT_MODE"})," environment variable to ",(0,s.jsx)(n.code,{children:"1"})," or ",(0,s.jsx)(n.code,{children:"ON"}),"."]})}),"\n",(0,s.jsx)(n.h4,{id:"build-tinyorm",children:"Build TinyORM"}),"\n",(0,s.jsx)(n.p,{children:"And build. You don't have to install it, you can use the build tree directly if you want."}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"cmake --build . --target all\ncmake --install .\n"})}),"\n",(0,s.jsx)(n.p,{children:"Or build and install in one step."}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"cmake --build . --target install\n"})}),"\n",(0,s.jsx)(n.admonition,{type:"info",children:(0,s.jsxs)(n.p,{children:["CMake multi-config generators like ",(0,s.jsx)(n.code,{children:"Ninja Multi-Config"})," or ",(0,s.jsx)(n.code,{children:"Visual Studio 16 2019"})," are also supported."]})}),"\n",(0,s.jsx)(n.h3,{id:"cmake-build-options",children:"CMake build options"}),"\n",(0,s.jsx)("div",{className:"apitable-build-options",children:(0,s.jsx)(l.A,{children:(0,s.jsxs)(n.table,{children:[(0,s.jsx)(n.thead,{children:(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.th,{children:"Option Name"}),(0,s.jsx)(n.th,{children:"Default"}),(0,s.jsx)(n.th,{children:"Description"})]})}),(0,s.jsxs)(n.tbody,{children:[(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"BUILD_DRIVERS"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Build ",(0,s.jsx)(n.a,{href:"/tinydrivers/getting-started",children:"TinyDrivers"})," SQL database drivers (core/common code; replaces QtSql module)."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"BUILD_MYSQL_DRIVER"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Build ",(0,s.jsx)(n.code,{children:"TinyDrivers"})," MySQL database driver.",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Available when: ",(0,s.jsx)(n.code,{children:"BUILD_DRIVERS"})]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"BUILD_SHARED_LIBS"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"ON"})}),(0,s.jsx)(n.td,{children:"Build as a shared/static library."})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"BUILD_TESTS"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsx)(n.td,{children:"Build TinyORM unit tests."})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"BUILD_TREE_DEPLOY"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"ON"})}),(0,s.jsxs)(n.td,{children:["Copy ",(0,s.jsx)(n.code,{children:"TinyDrivers"})," and ",(0,s.jsx)(n.code,{children:"TinyMySql"})," libraries to the root of the build tree."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"DRIVERS_TYPE"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"Shared"})}),(0,s.jsxs)(n.td,{children:["How to build and link against ",(0,s.jsx)(n.code,{children:"TinyDrivers"})," SQL database drivers.",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["The ",(0,s.jsx)(n.code,{children:"Static"})," value will be select by default when the ",(0,s.jsx)(n.code,{children:"BUILD_SHARED_LIBS"})," is ",(0,s.jsx)(n.code,{children:"OFF"}),".",(0,s.jsx)("br",{}),"Supported values: ",(0,s.jsx)(n.a,{href:"/tinydrivers/getting-started#the-shared-library-build",children:(0,s.jsx)(n.code,{children:"Shared"})}),", ",(0,s.jsx)(n.a,{href:"/tinydrivers/getting-started#the-loadable-sql-drivers-build",children:(0,s.jsx)(n.code,{children:"Loadable"})}),", and ",(0,s.jsx)(n.a,{href:"/tinydrivers/getting-started#the-static-build",children:(0,s.jsx)(n.code,{children:"Static"})}),(0,s.jsx)("br",{}),"Available when: ",(0,s.jsx)(n.code,{children:"BUILD_DRIVERS AND BUILD_SHARED_LIBS"})]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"INLINE_CONSTANTS"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Use inline constants instead of extern constants in the ",(0,s.jsx)(n.code,{children:"shared build"}),".",(0,s.jsx)("br",{}),(0,s.jsx)(n.code,{children:"OFF"})," is highly recommended for the ",(0,s.jsx)(n.code,{children:"shared build"}),";",(0,s.jsx)("br",{}),"is always ",(0,s.jsx)(n.code,{children:"ON"})," for the ",(0,s.jsx)(n.code,{children:"static build"}),".",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Available when: ",(0,s.jsx)(n.code,{children:"BUILD_SHARED_LIBS"})]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"MSVC_RUNTIME_DYNAMIC"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"ON"})}),(0,s.jsxs)(n.td,{children:["Use MSVC dynamic runtime library (",(0,s.jsx)(n.code,{children:"-MD"}),") instead of static (",(0,s.jsx)(n.code,{children:"-MT"}),"), also considers a Debug configuration (",(0,s.jsx)(n.code,{children:"-MTd"}),", ",(0,s.jsx)(n.code,{children:"-MDd"}),").",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Available when: ",(0,s.jsx)(n.code,{children:"MSVC AND NOT DEFINED CMAKE_MSVC_RUNTIME_LIBRARY"})]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"MYSQL_PING"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Enable ",(0,s.jsx)(n.code,{children:"Orm::MySqlConnection::pingDatabase()"})," method."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"ORM"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"ON"})}),(0,s.jsxs)(n.td,{children:["Controls the compilation of all ",(0,s.jsx)(n.code,{children:"ORM-related"})," source code, when this option is ",(0,s.jsx)(n.code,{children:"disabled"}),", then only the ",(0,s.jsx)(n.code,{children:"query builder"})," without ",(0,s.jsx)(n.code,{children:"ORM"})," is compiled. Also excludes ",(0,s.jsx)(n.code,{children:"ORM-related"})," unit tests."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"STRICT_MODE"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Controls propagation of strict compiler/linker options and Qt definitions using the ",(0,s.jsx)(n.code,{children:"TinyOrm::CommonConfig"})," interface library to the user code.",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["(highly recommended; can also be set with the ",(0,s.jsx)(n.code,{children:"TINYORM_STRICT_MODE"})," environment variable; described ",(0,s.jsx)(n.a,{href:"#cmake-strict_mode-option",children:"here"}),")"]}),"."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TOM"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"ON"})}),(0,s.jsxs)(n.td,{children:["Controls the compilation of all ",(0,s.jsx)(n.code,{children:"Tom-related"})," source code, when this option is ",(0,s.jsx)(n.code,{children:"disabled"}),", then it also excludes ",(0,s.jsx)(n.code,{children:"Tom-related"})," unit tests."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TOM_EXAMPLE"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Build the ",(0,s.jsx)("abbr",{title:"TinyORM Migrations",children:(0,s.jsx)(n.code,{children:"tom"})})," console application example."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TOM_MIGRATIONS_DIR"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"-"})}),(0,s.jsxs)(n.td,{children:["Default migrations path for the ",(0,s.jsx)(n.code,{children:"make:migration"})," command, can be an absolute or relative path (to the ",(0,s.jsx)("abbr",{title:"Current working directory",children:"pwd"}),").",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Default value: ",(0,s.jsx)(n.code,{children:"database/migrations"})," ",(0,s.jsx)("small",{children:"(relative to the pwd)"})]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TOM_MODELS_DIR"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"-"})}),(0,s.jsxs)(n.td,{children:["Default models path for the ",(0,s.jsx)(n.code,{children:"make:model"})," command, can be an absolute or relative path (to the ",(0,s.jsx)("abbr",{title:"Current working directory",children:"pwd"}),").",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Default value: ",(0,s.jsx)(n.code,{children:"database/models"})," ",(0,s.jsx)("small",{children:"(relative to the pwd)"})]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TOM_SEEDERS_DIR"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"-"})}),(0,s.jsxs)(n.td,{children:["Default seeders path for the ",(0,s.jsx)(n.code,{children:"make:seeder"})," command, can be an absolute or relative path (to the ",(0,s.jsx)("abbr",{title:"Current working directory",children:"pwd"}),").",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Default value: ",(0,s.jsx)(n.code,{children:"database/seeders"})," ",(0,s.jsx)("small",{children:"(relative to the pwd)"})]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"VERBOSE_CONFIGURE"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Show information about ",(0,s.jsx)(n.code,{children:"PACKAGES_FOUND"})," / ",(0,s.jsx)(n.code,{children:"PACKAGES_NOT_FOUND"})," in the CMake configure output."]})]})]})]})})}),"\n",(0,s.jsxs)(n.p,{children:["Advanced ",(0,s.jsx)(n.code,{children:"TinyORM"})," options."]}),"\n",(0,s.jsx)("div",{className:"apitable-build-options",children:(0,s.jsx)(l.A,{children:(0,s.jsxs)(n.table,{children:[(0,s.jsx)(n.thead,{children:(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.th,{children:"Option Name"}),(0,s.jsx)(n.th,{children:"Default"}),(0,s.jsx)(n.th,{children:"Description"})]})}),(0,s.jsxs)(n.tbody,{children:[(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"DISABLE_THREAD_LOCAL"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Remove all ",(0,s.jsx)(n.a,{href:"https://en.cppreference.com/w/c/language/storage_duration",children:(0,s.jsx)(n.code,{children:"thread_local"})})," storage duration specifiers, it disables multi-threading support."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)("small",{children:(0,s.jsx)(n.code,{children:"MATCH_EQUAL_EXPORTED_BUILDTREE"})})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Exported package configuration from the build tree is considered to match only when ",(0,s.jsx)(n.code,{children:"the build type"})," of application/library that is linking against the TinyORM library ",(0,s.jsx)(n.strong,{children:"is equal"}),".",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Available when:",(0,s.jsx)("br",{}),(0,s.jsx)(n.code,{children:"CMAKE_EXPORT_PACKAGE_REGISTRY AND NOT TINY_IS_MULTI_CONFIG"})]})]})]})]})]})})}),"\n",(0,s.jsxs)(n.p,{children:["Important ",(0,s.jsx)(n.code,{children:"CMake"})," options."]}),"\n",(0,s.jsx)("div",{className:"apitable-build-options",children:(0,s.jsx)(l.A,{children:(0,s.jsxs)(n.table,{children:[(0,s.jsx)(n.thead,{children:(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.th,{children:"Option Name"}),(0,s.jsx)(n.th,{children:"Default"}),(0,s.jsx)(n.th,{children:"Description"})]})}),(0,s.jsxs)(n.tbody,{children:[(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"CMAKE_DISABLE_PRECOMPILE_HEADERS"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsx)(n.td,{children:"Disable precompiled headers."})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"CMAKE_CXX_COMPILER"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"auto"})}),(0,s.jsxs)(n.td,{children:["The full path to the ",(0,s.jsx)(n.code,{children:"C++"})," compiler."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"CMAKE_CXX_COMPILER_LAUNCHER"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"-"})}),(0,s.jsxs)(n.td,{children:["Default compiler launcher to use for the ",(0,s.jsx)(n.code,{children:"C++"})," compiler.",(0,s.jsx)("br",{}),"Can be used to enable ",(0,s.jsx)(n.code,{children:"ccache"}),", eg. ",(0,s.jsx)(n.code,{children:"ccache.exe"})," on ",(0,s.jsx)(n.code,{children:"MinGW"})," or ",(0,s.jsx)(n.code,{children:"/usr/bin/ccache"})," on ",(0,s.jsx)(n.code,{children:"Linux"}),"."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"CMAKE_EXPORT_PACKAGE_REGISTRY"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Enable the ",(0,s.jsx)(n.code,{children:"export(TinyOrm)"})," command.",(0,s.jsx)("br",{}),(0,s.jsx)(n.code,{children:"TinyORM"})," doesn't set this variable by default. Its initial value is taken from the ",(0,s.jsx)(n.code,{children:"TINYORM_EXPORT_PACKAGE_REGISTRY"})," environment variable if not already defined."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)("small",{children:(0,s.jsx)(n.code,{children:"CMAKE_INTERPROCEDURAL_OPTIMIZATION"})})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Enable ",(0,s.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/Interprocedural_optimization",children:"Link time code optimization"})," (",(0,s.jsx)(n.a,{href:"https://wiki.gentoo.org/wiki/LTO",children:"LTO"}),").",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Works on all ",(0,s.jsx)(n.a,{href:"/supported-compilers",children:"Supported Compilers"}),"."]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"CMAKE_VERBOSE_MAKEFILE"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsx)(n.td,{children:"Enable verbose output from Makefile builds."})]})]})]})})}),"\n",(0,s.jsxs)(n.h3,{id:"consume-tinyorm-library-cmake",children:["Consume TinyOrm library ",(0,s.jsx)("small",{children:"(cmake)"})]}),"\n",(0,s.jsxs)(n.p,{children:["In your application or library ",(0,s.jsx)(n.code,{children:"CMakeLists.txt"})," file add following ",(0,s.jsx)(n.code,{children:"find_package()"})," call."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cmake",metastring:"title='CMakeLists.txt'",children:"find_package(TinyOrm 0.37.3 CONFIG REQUIRED)\n"})}),"\n",(0,s.jsxs)(n.p,{children:["If the ",(0,s.jsx)(n.code,{children:"TinyORM"})," build tree is not exported to the CMake's ",(0,s.jsx)(n.a,{href:"https://cmake.org/cmake/help/latest/manual/cmake-packages.7.html#user-package-registry",children:(0,s.jsx)(n.code,{children:"User Package Registry"})})," then also add the ",(0,s.jsx)(n.code,{children:"TinyORM"})," build tree or ",(0,s.jsx)(n.code,{children:"CMAKE_INSTALL_PREFIX"})," folder to the ",(0,s.jsx)(n.code,{children:"CMAKE_PREFIX_PATH"}),", so CMake can find TinyORM's package configuration file during ",(0,s.jsx)(n.code,{children:"find_package(TinyOrm)"})," call."]}),"\n",(0,s.jsxs)(o.A,{groupId:y.vf,children:[(0,s.jsx)(t.A,{value:y.b,label:`cmake (${y.b})`,children:(0,s.jsx)(c.A,{className:"language-cmake",children:`# build tree\nlist(APPEND CMAKE_PREFIX_PATH "${(0,x.nC)(y.b,(0,x.OZ)(y.b))}/TinyORM/TinyORM-builds-cmake/build-debug")\n\n# installation folder - CMAKE_INSTALL_PREFIX\nlist(APPEND CMAKE_PREFIX_PATH "${(0,x.nC)(y.b,(0,x.Sn)(y.b))}/tmp/TinyORM")`})}),(0,s.jsx)(t.A,{value:y.xj,label:`cmake (${y.xj})`,children:(0,s.jsx)(c.A,{className:"language-cmake",children:`# build tree\nlist(APPEND CMAKE_PREFIX_PATH "${(0,x.nC)(y.xj,(0,x.OZ)(y.xj))}/TinyORM/TinyORM-builds-cmake/build-debug")\n\n# installation folder - CMAKE_INSTALL_PREFIX\nlist(APPEND CMAKE_PREFIX_PATH "${(0,x.nC)(y.xj,(0,x.Sn)(y.xj))}/tmp/TinyORM")`})})]}),"\n",(0,s.jsxs)(n.p,{children:["Or as an alternative, you can set ",(0,s.jsx)(n.code,{children:"CMAKE_PREFIX_PATH"})," environment variable."]}),"\n",(0,s.jsx)(r.A,{id:"tinyorm-on-path-cmake"}),"\n",(0,s.jsxs)(n.p,{children:["As the last thing, do not forget to add ",(0,s.jsx)(n.code,{children:"TinyOrm0d.dll"})," on the path on Windows and on the ",(0,s.jsx)(n.code,{children:"LD_LIBRARY_PATH"})," on Linux, so your application can find it during execution."]}),"\n",(0,s.jsxs)(o.A,{groupId:y.vf,name:"tinyorm-on-path",children:[(0,s.jsx)(t.A,{value:y.b,label:y.ux,children:(0,s.jsx)(c.A,{className:"language-powershell",children:`$env:Path = "${(0,x.OZ)(y.b,!1)}\\TinyORM\\TinyORM-builds-cmake\\build-debug;" + $env:Path`})}),(0,s.jsx)(t.A,{value:y.xj,label:y.gg,children:(0,s.jsx)(c.A,{className:"language-bash",children:`export LD_LIBRARY_PATH=${(0,x.OZ)(y.xj)}/TinyORM/TinyORM-builds-cmake/build-debug\${PATH:+:}$PATH`})})]}),"\n",(0,s.jsxs)(n.p,{children:["Now you can try the ",(0,s.jsx)(n.a,{href:"/building/hello-world#hello-world-with-cmake",children:(0,s.jsx)(n.code,{children:"HelloWorld CMake"})})," example."]}),"\n",(0,s.jsx)(n.admonition,{type:"info",children:(0,s.jsxs)(n.p,{children:["You can also try the ",(0,s.jsx)(n.a,{href:"/building/hello-world#fetchcontent",children:(0,s.jsx)(n.code,{children:"FetchContent"})})," method to fastly link against the ",(0,s.jsx)(n.code,{children:"TinyORM"})," library."]})}),"\n",(0,s.jsx)(n.h2,{id:"building-with-qmake",children:"Building with qmake"}),"\n",(0,s.jsxs)(n.p,{children:["First, create a basic folder structure and then clone the ",(0,s.jsx)(n.code,{children:"TinyORM"})," project."]}),"\n",(0,s.jsxs)(o.A,{groupId:y.vf,children:[(0,s.jsx)(t.A,{value:y.b,label:y.ux,children:(0,s.jsx)(c.A,{className:"language-powershell",children:`cd ${(0,x.Sn)(y.b)}\nmkdir ${(0,x.np)()}/TinyORM/TinyORM-builds-qmake\n\ncd ${(0,x.np)()}/TinyORM\ngit clone git@github.com:silverqx/TinyORM.git`})}),(0,s.jsx)(t.A,{value:y.xj,label:y.gg,children:(0,s.jsx)(c.A,{className:"language-bash",children:`cd ${(0,x.Sn)(y.xj)}\nmkdir -p ${(0,x.np)()}/TinyORM/TinyORM-builds-qmake\n\ncd ${(0,x.np)()}/TinyORM\ngit clone git@github.com:silverqx/TinyORM.git`})})]}),"\n",(0,s.jsx)(n.h3,{id:"install-dependencies",children:"Install dependencies"}),"\n",(0,s.jsxs)(n.p,{children:["With the ",(0,s.jsx)(n.code,{children:"qmake"})," build system, you have to install ",(0,s.jsx)(n.code,{children:"TinyORM"})," dependencies manually. We will use the ",(0,s.jsx)(n.code,{children:"vcpkg"})," package manager."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"cd ../../vcpkg\n\nvcpkg search range-v3\nvcpkg search tabulate\nvcpkg install range-v3 tabulate\nvcpkg list\n"})}),"\n",(0,s.jsxs)(n.p,{children:["On ",(0,s.jsx)(n.code,{children:"Linux"}),", you can install the ",(0,s.jsx)(n.code,{children:"range-v3"})," library and some other ",(0,s.jsx)(n.a,{href:"/dependencies#install-dependencies",children:"dependencies"})," with the package manager."]}),"\n",(0,s.jsxs)(n.h3,{id:"configure-and-build-qmake",children:["Configure & Build ",(0,s.jsx)("small",{children:"(qmake)"})]}),"\n",(0,s.jsx)(n.h4,{id:"open-qtcreator-ide",children:"Open QtCreator IDE"}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["I recommend creating a new ",(0,s.jsx)(n.a,{href:"https://doc.qt.io/qtcreator/creator-project-managing-sessions.html",children:(0,s.jsx)(n.code,{children:"Session"})})," in the ",(0,s.jsx)(n.code,{children:"QtCreator"}),", this way you will have all the examples in one place and as a bonus, everything will be in the same place when you close and reopen ",(0,s.jsx)(n.code,{children:"QtCreator IDE"}),". You can name it ",(0,s.jsx)(n.code,{children:"tinyorm.org"})," or ",(0,s.jsx)(n.code,{children:"TinyORM examples"}),", it is up to you."]})}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["If you are using sessions, you can use a single ",(0,s.jsx)(n.code,{children:"clangd"})," instance for all projects in this session in the ",(0,s.jsx)(n.code,{children:"QtCreator IDE"}),". One significant advantage of this method is that the ",(0,s.jsx)(n.code,{children:".qtc_clangd/"})," folder will not be created in the build folder, but will be stored globally in the Roaming profile. You can enable it in the ",(0,s.jsx)(n.code,{children:"Settings"})," - ",(0,s.jsx)(n.code,{children:"C++"})," - ",(0,s.jsx)(n.code,{children:"Clangd"})," - ",(0,s.jsx)(n.code,{children:"Sessions with a single clangd instance"}),"."]})}),"\n",(0,s.jsx)(n.h4,{id:"configure-tinyorm",children:"Configure TinyORM"}),"\n",(0,s.jsxs)(n.p,{children:["Now you are ready to configure the ",(0,s.jsx)(n.code,{children:"TinyORM"})," library. There are two ways how to configure the ",(0,s.jsx)(n.code,{children:"TinyORM"})," library and it's the new ",(0,s.jsx)(n.code,{children:"Auto-configure"})," feature added in ",(0,s.jsx)(n.code,{children:"TinyORM"})," ",(0,s.jsx)(n.code,{children:"v0.34.0"})," using the ",(0,s.jsx)(n.code,{children:".env"})," files and the old way using the ",(0,s.jsx)(n.code,{children:"conf.pri"})," files."]}),"\n",(0,s.jsx)(n.h5,{id:"auto-configuration-and-tiny_dotenv",children:"Auto-configuration and tiny_dotenv"}),"\n",(0,s.jsxs)(n.p,{children:["This is the new recommended method to auto-configure TinyORM's ",(0,s.jsx)(n.code,{children:"qmake"})," build system and also the dependencies, it was added in ",(0,s.jsx)(n.code,{children:"TinyORM"})," ",(0,s.jsx)(n.code,{children:"v0.34.0"}),". You need to copy the prepared ",(0,s.jsx)(n.code,{children:".env.(win32|unix|mingw).example"})," file to the ",(0,s.jsx)(n.code,{children:".env.(win32|unix|mingw)"}),". One ",(0,s.jsx)(n.code,{children:".env"})," example file is prepared for each supported platform."]}),"\n",(0,s.jsxs)(n.p,{children:["All prepared ",(0,s.jsx)(n.code,{children:".env.(win32|unix|mingw).example"})," files are simple and clear. You can also create a common ",(0,s.jsx)(n.code,{children:".env"})," file that is included before the platform-specific ",(0,s.jsx)(n.code,{children:".env.(win32|unix|mingw)"})," files."]}),"\n",(0,s.jsxs)(o.A,{groupId:y.vf,children:[(0,s.jsx)(t.A,{value:y.b,label:y.ux,children:(0,s.jsx)(c.A,{className:"language-powershell",children:`cd ${(0,x.OZ)(y.b)}/TinyORM/TinyORM\n\ncp .env.win32.example .env.win32`})}),(0,s.jsx)(t.A,{value:y.xj,label:y.gg,children:(0,s.jsx)(c.A,{className:"language-bash",children:`cd ${(0,x.OZ)(y.xj)}/TinyORM/TinyORM\n\ncp .env.unix.example .env.unix`})})]}),"\n",(0,s.jsxs)(n.p,{children:["And that is all, if you have correctly set all ",(0,s.jsx)(n.code,{children:"qmake"})," variables in this ",(0,s.jsx)(n.code,{children:".env.(win32|unix|mingw)"})," file or you have correctly set environment variables, then the ",(0,s.jsx)(n.code,{children:"qmake"})," build system should be able to ",(0,s.jsx)(n.code,{children:"auto-detect"})," all dependencies . \ud83d\udd25"]}),"\n",(0,s.jsx)(n.admonition,{type:"info",children:(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.a,{href:"#auto-configuration-internals",children:(0,s.jsx)(n.code,{children:"Auto-configuration"})})," and ",(0,s.jsx)(n.a,{href:"#environment-files",children:(0,s.jsx)(n.code,{children:"Environment files"})})," internals are described at the end to make this section more clear."]})}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"Auto-configuration"})," feature can be turned off using the ",(0,s.jsx)(n.a,{href:"#disable_autoconf",children:(0,s.jsx)(n.code,{children:"disable_autoconf"})})," ",(0,s.jsx)(n.code,{children:"qmake"})," configuration option (eg. ",(0,s.jsx)(n.code,{children:"CONFIG*=disable_autoconf"}),")."]})}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"tiny_dotenv"})," feature can be turned off using the ",(0,s.jsx)(n.a,{href:"#disable_dotenv",children:(0,s.jsx)(n.code,{children:"disable_dotenv"})})," ",(0,s.jsx)(n.code,{children:"qmake"})," configuration option (eg. ",(0,s.jsx)(n.code,{children:"CONFIG*=disable_dotenv"}),")."]})}),"\n",(0,s.jsx)(n.h5,{id:"manual-configuration-confpri",children:"Manual configuration (conf.pri)"}),"\n",(0,s.jsxs)(n.p,{children:["This is the old method used before ",(0,s.jsx)(n.code,{children:"TinyORM"})," ",(0,s.jsx)(n.code,{children:"v0.34.0"}),". You need to copy the ",(0,s.jsx)(n.code,{children:"conf.pri.example"})," files to ",(0,s.jsx)(n.code,{children:"conf.pri"})," (there are four, one for every project or sub-project) and manually update the ",(0,s.jsx)(n.code,{children:"INCLUDEPATH"})," and ",(0,s.jsx)(n.code,{children:"LIBS"})," to configure TinyORM's ",(0,s.jsx)(n.code,{children:"qmake"})," build dependencies. This way you can override any ",(0,s.jsx)(n.code,{children:"qmake"})," build options or variables."]}),"\n",(0,s.jsxs)(n.p,{children:["To disable the ",(0,s.jsx)(n.a,{href:"#auto-configuration-internals",children:(0,s.jsx)(n.code,{children:"Auto-configuration"})})," feature you must define the ",(0,s.jsx)(n.a,{href:"#disable_autoconf",children:(0,s.jsx)(n.code,{children:"disable_autoconf"})})," ",(0,s.jsx)(n.code,{children:"qmake"})," configuration option (eg. ",(0,s.jsx)(n.code,{children:"CONFIG*=disable_autoconf"}),") because from ",(0,s.jsx)(n.code,{children:"TinyORM"})," ",(0,s.jsx)(n.code,{children:"v0.34.0"})," is the ",(0,s.jsx)(n.code,{children:"Auto-configuration"})," feature enabled by default."]}),"\n",(0,s.jsxs)(n.p,{children:["You can also remove all ",(0,s.jsx)(n.code,{children:".env"})," files or turn off the ",(0,s.jsx)(n.code,{children:"tiny_dotenv"})," feature using ",(0,s.jsx)(n.code,{children:"CONFIG*=disable_dotenv"}),". You can use them all at once if you want, ",(0,s.jsx)(n.code,{children:".env"})," and also ",(0,s.jsx)(n.code,{children:"conf.pri"})," files."]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.code,{children:"conf.pri"})," files are nicely commented on, so you can see what needs to be modified."]}),"\n",(0,s.jsxs)(o.A,{groupId:y.vf,children:[(0,s.jsx)(t.A,{value:y.b,label:y.ux,children:(0,s.jsx)(c.A,{className:"language-powershell",children:`cd ${(0,x.OZ)(y.b)}/TinyORM/TinyORM\n\ncp conf.pri.example conf.pri\ncp tests/conf.pri.example tests/conf.pri\ncp tests/testdata_tom/conf.pri.example tests/testdata_tom/conf.pri\ncp examples/tom/conf.pri.example examples/tom/conf.pri`})}),(0,s.jsx)(t.A,{value:y.xj,label:y.gg,children:(0,s.jsx)(c.A,{className:"language-bash",children:`cd ${(0,x.OZ)(y.xj)}/TinyORM/TinyORM\n\ncp conf.pri.example conf.pri\ncp tests/conf.pri.example tests/conf.pri\ncp tests/testdata_tom/conf.pri.example tests/testdata_tom/conf.pri\ncp examples/tom/conf.pri.example examples/tom/conf.pri`})})]}),"\n",(0,s.jsx)(n.admonition,{type:"info",children:(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.a,{href:"#manual-configuration-internals",children:(0,s.jsx)(n.code,{children:"Manual configuration"})})," internals are described at the end to make this section more clear."]})}),"\n",(0,s.jsx)(n.admonition,{type:"note",children:(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"Manual configuration"})," is still relevant if you have any non-standard installation of the ",(0,s.jsx)(n.code,{children:"vcpkg"})," or ",(0,s.jsx)(n.code,{children:"MySQL"})," and the ",(0,s.jsx)(n.code,{children:"Auto-configuration"})," feature fails."]})}),"\n",(0,s.jsx)(n.h5,{id:"opening-tinyormpro-main-project-file",children:"Opening TinyORM.pro (main project file)"}),"\n",(0,s.jsxs)(n.p,{children:["Now you can open the ",(0,s.jsx)(n.code,{children:"TinyORM.pro"})," project in the ",(0,s.jsx)(n.code,{children:"QtCreator IDE"}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["This will open the ",(0,s.jsx)(n.code,{children:"Configure Project"})," tab, select some kit and update build folder paths to meet our ",(0,s.jsx)(n.a,{href:"#folders-structure",children:"folders structure"})," or like you want."]}),"\n",(0,s.jsx)("img",{src:i(885).A,alt:"TinyORM - QtCreator - Configure Project",width:"760"}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["You can force the ",(0,s.jsx)(n.code,{children:"QtCreator"})," to generate a build folders structure as is described ",(0,s.jsx)(n.a,{href:"#qtcreator-default-build-directory",children:"above"}),"."]})}),"\n",(0,s.jsxs)(n.p,{children:["You are ready to configure build options, hit ",(0,s.jsx)("kbd",{children:"Ctrl"}),"+",(0,s.jsx)("kbd",{children:"5"})," to open ",(0,s.jsx)(n.code,{children:"Project Settings"})," tab and select ",(0,s.jsx)(n.code,{children:"Build"})," in the left sidebar to open the ",(0,s.jsx)(n.code,{children:"Build Settings"}),", it should look similar to the following picture."]}),"\n",(0,s.jsxs)(n.p,{children:["Disable ",(0,s.jsx)(n.code,{children:"QML debugging and profiling"})," and ",(0,s.jsx)(n.code,{children:"Qt Quick Compiler"}),", they are not used."]}),"\n",(0,s.jsx)("img",{src:i(7619).A,alt:"TinyORM - QtCreator - Build Settings",width:"760"}),"\n",(0,s.jsxs)(n.p,{children:["If you want to change some ",(0,s.jsx)(n.code,{children:"TinyORM"})," build options, you can pass them to the ",(0,s.jsx)(n.code,{children:"Build Steps"})," - ",(0,s.jsx)(n.code,{children:"qmake TinyORM.pro"})," - ",(0,s.jsx)(n.code,{children:"Additional arguments"})," input field. It can look like this."]}),"\n",(0,s.jsx)("img",{src:i(2721).A,alt:"TinyORM - QtCreator - Build Settings - Additional arguments",width:"660"}),"\n",(0,s.jsx)(n.h4,{id:"build-tinyorm-1",children:"Build TinyORM"}),"\n",(0,s.jsxs)(n.p,{children:["Everything is ready for build, you can press ",(0,s.jsx)("kbd",{children:"Ctrl"}),"+",(0,s.jsx)("kbd",{children:"b"})," to build the project."]}),"\n",(0,s.jsx)(n.h3,{id:"qmake-build-options",children:"qmake build options"}),"\n",(0,s.jsx)("div",{className:"apitable-build-options",children:(0,s.jsx)(l.A,{children:(0,s.jsxs)(n.table,{children:[(0,s.jsx)(n.thead,{children:(0,s.jsxs)(n.tr,{children:[(0,s.jsxs)(n.th,{children:[(0,s.jsx)(n.code,{children:"CONFIG"})," ",(0,s.jsx)("small",{children:"Option Name"})]}),(0,s.jsx)(n.th,{children:"Default"}),(0,s.jsx)(n.th,{children:"Description"})]})}),(0,s.jsxs)(n.tbody,{children:[(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"build_loadable_drivers"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Build ",(0,s.jsx)(n.a,{href:"/tinydrivers/getting-started",children:(0,s.jsx)(n.code,{children:"TinyDrivers"})})," as a shared library and SQL database drivers (eg. ",(0,s.jsx)(n.code,{children:"TinyMySql"}),") as shared libraries (",(0,s.jsx)(n.a,{href:"/tinydrivers/getting-started#the-loadable-sql-drivers-build",children:(0,s.jsx)(n.code,{children:"Loadable"})})," modules) that are loaded at runtime using ",(0,s.jsx)(n.code,{children:"LoadLibrary()"})," on Windows or ",(0,s.jsx)(n.code,{children:"dlopen()"})," on Linux."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"build_mysql_driver"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Build ",(0,s.jsx)(n.code,{children:"TinyDrivers"})," MySQL database driver.",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["It's enabled by default when ",(0,s.jsx)(n.code,{children:"build_shared_drivers"}),", ",(0,s.jsx)(n.code,{children:"build_loadable_drivers"}),", or ",(0,s.jsx)(n.code,{children:"build_static_drivers"})," is enabled.",(0,s.jsx)("br",{}),"Available when: ",(0,s.jsx)(n.code,{children:"build_shared_drivers"})," OR ",(0,s.jsx)(n.code,{children:"build_loadable_drivers"})," OR ",(0,s.jsx)(n.code,{children:"build_static_drivers"})]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"build_shared_drivers"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Build ",(0,s.jsx)(n.code,{children:"TinyDrivers"})," as a ",(0,s.jsx)(n.a,{href:"/tinydrivers/getting-started#the-shared-library-build",children:(0,s.jsx)(n.code,{children:"Shared"})})," library."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"build_static_drivers"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Build ",(0,s.jsx)(n.code,{children:"TinyDrivers"})," as a ",(0,s.jsx)(n.a,{href:"/tinydrivers/getting-started#the-static-build",children:(0,s.jsx)(n.code,{children:"Static"})})," library archive.",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["The ",(0,s.jsx)(n.code,{children:"build_static_drivers"})," ",(0,s.jsx)(n.code,{children:"qmake"})," configuration option will be select by default when the ",(0,s.jsx)(n.a,{href:"#qmake-static",children:(0,s.jsx)(n.code,{children:"CONFIG*=static"})})," is enabled."]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"build_tests"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsx)(n.td,{children:"Build TinyORM unit tests."})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"ccache"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"ON"})}),(0,s.jsxs)(n.td,{children:["Enable compiler cache. ",(0,s.jsx)(n.a,{href:"https://ccache.dev/",children:"Homepage"}),(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["It works on Windows and Unix systems. This option overrides qmake's ",(0,s.jsx)(n.code,{children:"ccache"})," option. It internally calls qmake's ",(0,s.jsx)(n.a,{href:"#qmake-ccache",children:(0,s.jsx)(n.code,{children:"ccache"})})," option on Unix and ",(0,s.jsx)(n.a,{href:"#tiny_ccache_win32",children:(0,s.jsx)(n.code,{children:"tiny_ccache_win32"})})," on Windows.",(0,s.jsx)("br",{}),"Reason: It allows using the same option on both OS-es.",(0,s.jsx)("br",{}),"See ",(0,s.jsx)(n.a,{href:"#ccache-support",children:"Ccache support"})," for more information."]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"disable_autoconf"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Disable the ",(0,s.jsx)(n.a,{href:"#auto-configuration-internals",children:(0,s.jsx)(n.code,{children:"Auto-configuration"})})," feature ",(0,s.jsxs)("small",{children:["(auto-configuration is enabled by default from ",(0,s.jsx)(n.code,{children:"TinyORM"})," ",(0,s.jsx)(n.code,{children:"v0.34.0"}),")"]}),"."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"disable_dotenv"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Disable the ",(0,s.jsx)(n.a,{href:"#environment-files",children:(0,s.jsx)(n.code,{children:"tiny_dotenv"})})," feature ",(0,s.jsxs)("small",{children:["(environment files are enabled by default from ",(0,s.jsx)(n.code,{children:"TinyORM"})," ",(0,s.jsx)(n.code,{children:"v0.34.0"}),")"]}),"."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"disable_thread_local"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Remove all ",(0,s.jsx)(n.a,{href:"https://en.cppreference.com/w/c/language/storage_duration",children:(0,s.jsx)(n.code,{children:"thread_local"})})," storage duration specifiers, it disables multi-threading support."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"disable_orm"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Controls the compilation of all ",(0,s.jsx)(n.code,{children:"ORM-related"})," source code, when this option is ",(0,s.jsx)(n.code,{children:"enabled"}),", then only the ",(0,s.jsx)(n.code,{children:"query builder"})," without ",(0,s.jsx)(n.code,{children:"ORM"})," is compiled. Also excludes ",(0,s.jsx)(n.code,{children:"ORM-related"})," unit tests."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"disable_tom"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Controls the compilation of all ",(0,s.jsx)(n.code,{children:"Tom-related"})," source code, when this option is ",(0,s.jsx)(n.code,{children:"disabled"}),", then it also excludes ",(0,s.jsx)(n.code,{children:"Tom-related"})," unit tests."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"extern_constants"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"ON"})}),(0,s.jsxs)(n.td,{children:["Use ",(0,s.jsx)(n.code,{children:"extern"})," constants instead of ",(0,s.jsx)(n.code,{children:"inline"})," constants in the ",(0,s.jsx)(n.code,{children:"shared build"}),".",(0,s.jsx)("br",{}),(0,s.jsx)(n.code,{children:"ON"})," is highly recommended for the ",(0,s.jsx)(n.code,{children:"shared build"})," ",(0,s.jsx)("small",{children:"(by default)"}),";",(0,s.jsx)("br",{}),"is always ",(0,s.jsx)(n.code,{children:"OFF"})," for the ",(0,s.jsx)(n.code,{children:"static build"}),".",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Available when: ",(0,s.jsx)("code",{children:"CONFIG(shared|dll):!inline_constants"})]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"inline_constants"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Use ",(0,s.jsx)(n.code,{children:"inline"})," constants instead of ",(0,s.jsx)(n.code,{children:"extern"})," constants in the ",(0,s.jsx)(n.code,{children:"shared build"}),".",(0,s.jsx)("br",{}),(0,s.jsx)(n.code,{children:"OFF"})," is highly recommended for the ",(0,s.jsx)(n.code,{children:"shared build"}),";",(0,s.jsx)("br",{}),"is always ",(0,s.jsx)(n.code,{children:"ON"})," for the ",(0,s.jsx)(n.code,{children:"static build"}),"."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"link_pkgconfig_off"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Link against ",(0,s.jsx)(n.code,{children:"mysqlclient"})," or ",(0,s.jsx)(n.code,{children:"libmariadb"})," with ",(0,s.jsx)(n.code,{children:"PKGCONFIG"}),".",(0,s.jsx)("br",{}),"Used only in the ",(0,s.jsx)(n.code,{children:"Unix"})," and ",(0,s.jsx)(n.code,{children:"MinGW"})," ",(0,s.jsx)(n.strong,{children:"shared"})," build ",(0,s.jsxs)("small",{children:["(exactly ",(0,s.jsx)("code",{children:"win32-g++|win32-clang-g++"}),")"]})," and when ",(0,s.jsx)(n.code,{children:"mysql_ping"})," is also defined to link against ",(0,s.jsx)(n.code,{children:"mysqlclient"})," or ",(0,s.jsx)(n.code,{children:"libmariadb"}),", ",(0,s.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/conf.pri.example#L132",children:"source code"}),".",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Available when: ",(0,s.jsx)(n.code,{children:"unix:mysql_ping"})," or ",(0,s.jsx)("code",{children:"(win32-g++|win32-clang-g++):mysql_ping:!static:!staticlib"})]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"mysql_ping"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Enable ",(0,s.jsx)(n.code,{children:"Orm::MySqlConnection::pingDatabase()"})," method."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"tiny_ccache_win32"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"ON"})}),(0,s.jsxs)(n.td,{children:["Enable compiler cache. ",(0,s.jsx)(n.a,{href:"https://ccache.dev/",children:"Homepage"}),(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["It works only on Windows systems. It works well with the MSYS2 ",(0,s.jsx)(n.code,{children:"g++"}),", ",(0,s.jsx)(n.code,{children:"clang++"}),", ",(0,s.jsx)(n.code,{children:"msvc"}),", and ",(0,s.jsx)(n.code,{children:"clang-cl"})," with ",(0,s.jsx)(n.code,{children:"msvc"}),". It replaces the ",(0,s.jsx)(n.code,{children:"-Zi"})," and ",(0,s.jsx)(n.code,{children:"-ZI"})," compiler options with the ",(0,s.jsx)(n.code,{children:"-Z7"})," for debug builds as the ",(0,s.jsx)(n.code,{children:"-Zi"})," and ",(0,s.jsx)(n.code,{children:"-ZI"})," compiler options are not supported (",(0,s.jsx)(n.a,{href:"https://github.com/ccache/ccache/issues/1040",children:"link"})," to the issue) and disables ",(0,s.jsx)(n.code,{children:"precompile_header"})," if ",(0,s.jsx)(n.code,{children:"ccache"})," ",(0,s.jsx)(n.code,{children:"<4.10"})," as they are not supported on Windows."]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"tom_example"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Build the ",(0,s.jsx)("abbr",{title:"TinyORM Migrations",children:(0,s.jsx)(n.code,{children:"tom"})})," console application example."]})]})]})]})})}),"\n",(0,s.jsxs)(n.p,{children:["Advanced ",(0,s.jsx)(n.code,{children:"TinyORM"})," options."]}),"\n",(0,s.jsx)("div",{className:"apitable-build-options",children:(0,s.jsx)(l.A,{children:(0,s.jsxs)(n.table,{children:[(0,s.jsx)(n.thead,{children:(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.th,{children:"Option Name"}),(0,s.jsx)(n.th,{children:"Default"}),(0,s.jsx)(n.th,{children:"Description"})]})}),(0,s.jsx)(n.tbody,{children:(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"ubsan"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Allows to enable ",(0,s.jsx)(n.a,{href:"https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html",children:"UBSan"})," sanitizer (Clang only)."]})]})})]})})}),"\n",(0,s.jsxs)(n.p,{children:["Important ",(0,s.jsx)(n.code,{children:"qmake"})," options."]}),"\n",(0,s.jsx)("div",{className:"apitable-build-options",children:(0,s.jsx)(l.A,{name:"qmake",children:(0,s.jsxs)(n.table,{children:[(0,s.jsx)(n.thead,{children:(0,s.jsxs)(n.tr,{children:[(0,s.jsxs)(n.th,{children:[(0,s.jsx)(n.code,{children:"CONFIG"})," ",(0,s.jsx)("small",{children:"Option Name"})]}),(0,s.jsx)(n.th,{children:"Default"}),(0,s.jsx)(n.th,{children:"Description"})]})}),(0,s.jsxs)(n.tbody,{children:[(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"ccache"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Enable compiler cache. ",(0,s.jsx)(n.a,{href:"https://ccache.dev/",children:"Homepage"}),(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["It works only on the Unix systems. It works well with ",(0,s.jsx)(n.code,{children:"g++"})," and ",(0,s.jsx)(n.code,{children:"clang++"})," and also supports precompiled headers. TinyORM overrides this qmake option with the ",(0,s.jsx)(n.a,{href:"#ccache",children:(0,s.jsx)(n.code,{children:"ccache"})})," option.",(0,s.jsx)("br",{}),"See ",(0,s.jsx)(n.a,{href:"#ccache-support",children:"Ccache support"})," for more information."]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"ltcg"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Enable ",(0,s.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/Interprocedural_optimization",children:"Link time code optimization"})," (",(0,s.jsx)(n.a,{href:"https://wiki.gentoo.org/wiki/LTO",children:"LTO"}),").",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Works on all ",(0,s.jsx)(n.a,{href:"/supported-compilers",children:"Supported Compilers"}),"."]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"precompile_header"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"-"})}),(0,s.jsxs)(n.td,{children:["Enable precompiled headers, you can disable them with:",(0,s.jsx)("br",{})," ",(0,s.jsx)(n.code,{children:"CONFIG-=precompile_header"}),".",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["The ",(0,s.jsx)(n.code,{children:"precompile_header"})," is enabled by default on ",(0,s.jsx)(n.code,{children:"msvc"}),", ",(0,s.jsx)(n.code,{children:"g++"}),", ",(0,s.jsx)(n.code,{children:"clang++"}),", ",(0,s.jsx)(n.code,{children:"clang-cl"})," on ",(0,s.jsx)(n.code,{children:"Windows"})," and disabled by default on ",(0,s.jsx)(n.code,{children:"linux"}),"."]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsxs)(n.td,{children:[(0,s.jsx)(n.code,{children:"static"}),(0,s.jsx)("br",{}),(0,s.jsx)(n.code,{children:"staticlib"})]}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Build as a ",(0,s.jsx)(n.code,{children:"static"})," library (lib only).",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["If you want to build all libraries in the ",(0,s.jsx)(n.code,{children:"TinyORM"})," project as static library archives and link against static libraries use the ",(0,s.jsx)(n.a,{href:"https://doc.qt.io/qt/qmake-variable-reference.html#config",children:(0,s.jsx)(n.code,{children:"CONFIG += static"})}),". Don't use the ",(0,s.jsx)(n.code,{children:"CONFIG += staticlib"}),".",(0,s.jsx)("br",{}),"See ",(0,s.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/NOTES.txt",children:"NOTES.txt"})," for more information (search ",(0,s.jsx)(n.code,{children:"static vs staticlib"}),")."]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"static_runtime"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Link against the ",(0,s.jsx)(n.code,{children:"shared"})," (dynamic) or ",(0,s.jsx)(n.code,{children:"static"})," run-time library.",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["The ",(0,s.jsx)(n.code,{children:"-MD"})," becomes ",(0,s.jsx)(n.code,{children:"-MT"})," and ",(0,s.jsx)(n.code,{children:"-MDd"})," becomes ",(0,s.jsx)(n.code,{children:"-MTd"}),". It works only on ",(0,s.jsx)(n.code,{children:"MSVC"})," and ",(0,s.jsx)(n.code,{children:"MinGW"})," or ",(0,s.jsx)(n.code,{children:"MSYS2"}),".",(0,s.jsx)("br",{}),"Please ",(0,s.jsx)("u",{children:"don't use"})," this option.",(0,s.jsx)("br",{}),"Available when: ",(0,s.jsx)(n.code,{children:"msvc"})," or ",(0,s.jsx)(n.code,{children:"mingw"})]})]})]})]})]})})}),"\n",(0,s.jsxs)(n.h3,{id:"consume-tinyorm-library-qmake",children:["Consume TinyOrm library ",(0,s.jsx)("small",{children:"(qmake)"})]}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/qmake/TinyOrm.pri",children:(0,s.jsx)(n.code,{children:"TinyOrm.pri"})})," file is available to simplify the integration of the ",(0,s.jsx)(n.code,{children:"TinyORM"})," library into your application. It sets up and configures the ",(0,s.jsx)(n.code,{children:"CONFIG"})," and ",(0,s.jsx)(n.code,{children:"DEFINES"})," qmake variables, adds the ",(0,s.jsx)(n.code,{children:"TinyORM"}),", ",(0,s.jsx)("abbr",{title:"TinyORM Migrations",children:(0,s.jsx)(n.code,{children:"tom"})}),", and ",(0,s.jsx)(n.code,{children:"vcpkg"})," header files on the system ",(0,s.jsx)(n.code,{children:"INCLUDEPATH"})," (cross-platform using the ",(0,s.jsx)(n.code,{children:"-isystem"})," or ",(0,s.jsx)(n.code,{children:"-imsvc"}),"), links against the TinyORM ",(0,s.jsx)(n.code,{children:"shared"})," or ",(0,s.jsx)(n.code,{children:"static"})," library using the ",(0,s.jsx)(n.code,{children:"LIBS"}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["You can use it to configure the ",(0,s.jsx)(n.code,{children:"TinyORM"})," library when you are linking against it. It does a very similar thing like the CMake's ",(0,s.jsx)(n.code,{children:"Find Modules"})," feature."]}),"\n",(0,s.jsx)(n.h4,{id:"requirements",children:"Requirements"}),"\n",(0,s.jsx)(n.p,{children:"It has a few requirements, you need to:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["specify path to the ",(0,s.jsx)(n.code,{children:"TinyORM"})," qmake features (",(0,s.jsx)(n.code,{children:".prf"})," files) using the ",(0,s.jsx)(n.code,{children:"QMAKEFEATURES"})," variable that can only be set in the ",(0,s.jsx)(n.code,{children:".qmake.conf"})," file"]}),"\n",(0,s.jsxs)(n.li,{children:["specify ",(0,s.jsx)(n.code,{children:"qmake"})," or ",(0,s.jsx)(n.code,{children:"environment"})," variables to find the ",(0,s.jsx)(n.code,{children:"vcpkg"})," installation ",(0,s.jsxs)("small",{children:["(",(0,s.jsx)(n.code,{children:"TINY_VCPKG_ROOT"})," and ",(0,s.jsx)(n.code,{children:"TINY_VCPKG_TRIPLET"}),")"]})]}),"\n",(0,s.jsxs)(n.li,{children:["specify path to the ",(0,s.jsx)(n.code,{children:"TinyORM"})," build folder ",(0,s.jsxs)("small",{children:["(",(0,s.jsx)(n.code,{children:"TINYORM_BUILD_TREE"}),")"]}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["you can specify it ",(0,s.jsx)(n.strong,{children:"manually"})]}),"\n",(0,s.jsxs)(n.li,{children:["or you can use ",(0,s.jsxs)(n.a,{href:"#partial-guessing-of-the-tinyorm_build_tree",children:["Partial guessing of the ",(0,s.jsx)(n.code,{children:"TINYORM_BUILD_TREE"})]})]}),"\n"]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["build your application with the same ",(0,s.jsx)(n.code,{children:"CONFIG"})," ",(0,s.jsx)(n.code,{children:"qmake"})," variables that were used when building the ",(0,s.jsx)(n.code,{children:"TinyORM"})," library"]}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:"Let's explain one by one."}),"\n",(0,s.jsx)(n.h5,{id:"qmakefeatures",children:(0,s.jsx)(n.code,{children:"QMAKEFEATURES"})}),"\n",(0,s.jsxs)(n.p,{children:["Create the ",(0,s.jsx)(n.code,{children:".qmake.conf"})," file in your application root folder with the following content."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-qmake",metastring:"title='.qmake.conf'",children:"# Path to the PARENT folder of the TinyORM source folder\nTINY_MAIN_DIR = $$clean_path()\n# To find .env and .env.$$QMAKE_PLATFORM files in YOUR project\nTINY_DOTENV_ROOT = $$PWD\n\n# Path to the TinyORM build folder (specified manually)\nTINYORM_BUILD_TREE = $$quote($$TINY_MAIN_DIR/TinyORM-builds-qmake/build-TinyORM-Desktop_Qt_6_7_2_MSVC2019_64bit-Debug/)\n# vcpkg - range-v3 and tabulate\nTINY_VCPKG_ROOT = $$quote(/vcpkg/)\n#TINY_VCPKG_TRIPLET = x64-windows\n\n# To find .prf files, needed by eg. CONFIG += tiny_system_headers inline/extern_constants\nQMAKEFEATURES *= $$quote($$TINY_MAIN_DIR/TinyORM/qmake/features)\n"})}),"\n",(0,s.jsxs)(n.p,{children:["You can move all ",(0,s.jsx)(n.code,{children:"qmake"})," variables that are part of the ",(0,s.jsx)(n.code,{children:"qmake"})," configuration process to the ",(0,s.jsx)(n.code,{children:".env"})," file if you want (recommended), this is possible because the ",(0,s.jsx)(n.code,{children:"TinyOrm.pri"})," enables the ",(0,s.jsx)(n.a,{href:"#environment-files",children:(0,s.jsx)(n.code,{children:"Environment files"})})," feature by default."]}),"\n",(0,s.jsxs)(n.p,{children:["You can look at the ",(0,s.jsx)(n.a,{href:"/building/hello-world#auto-configure-using-qmake_conf-and-env",children:"Auto-configure using .qmake.conf and .env"})," example for ",(0,s.jsx)(n.code,{children:"Hello world"})," project of what must stay in the ",(0,s.jsx)(n.code,{children:"qmake.conf"})," file and what can be moved to the ",(0,s.jsx)(n.code,{children:".env"})," files."]}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["You can use the ",(0,s.jsxs)(n.a,{href:"#partial-guessing-of-the-tinyorm_build_tree",children:["Partial guessing of the ",(0,s.jsx)(n.code,{children:"TINYORM_BUILD_TREE"})]})," if you don't like to specify it manually."]})}),"\n",(0,s.jsxs)(n.h5,{id:"variables-affecting-tinyormpri",children:["Variables affecting ",(0,s.jsx)(n.code,{children:"TinyOrm.pri"})]}),"\n",(0,s.jsxs)(n.p,{children:["You must define the following variables before the ",(0,s.jsx)(n.code,{children:"TinyOrm.pri"})," is included:"]}),"\n",(0,s.jsxs)(n.table,{children:[(0,s.jsx)(n.thead,{children:(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.th,{children:"Variable Name"}),(0,s.jsx)(n.th,{children:"Description"})]})}),(0,s.jsxs)(n.tbody,{children:[(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINYORM_BUILD_TREE"})}),(0,s.jsxs)(n.td,{children:["Path to the ",(0,s.jsx)(n.code,{children:"TinyORM"})," build folder."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINY_VCPKG_ROOT"})}),(0,s.jsxs)(n.td,{children:["Path to the ",(0,s.jsx)(n.code,{children:"vcpkg"})," installation folder.",(0,s.jsx)("br",{}),"If not defined, then it tries to use the ",(0,s.jsx)(n.code,{children:"VCPKG_ROOT"})," environment variable."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINY_VCPKG_TRIPLET"})}),(0,s.jsxs)(n.td,{children:["The ",(0,s.jsx)(n.code,{children:"vcpkg"})," ",(0,s.jsx)(n.code,{children:"triplet"})," to use ",(0,s.jsx)("small",{children:"(vcpkg/installed/$$TINY_VCPKG_TRIPLET/)"}),".",(0,s.jsx)("br",{}),"If not defined, then it tries to guess the ",(0,s.jsx)(n.code,{children:"vcpkg"})," ",(0,s.jsx)(n.code,{children:"triplet"})," based on the current compiler and OS (based on the ",(0,s.jsx)(n.code,{children:"QMAKESPEC"}),"), and as the last thing, it tries to use the ",(0,s.jsx)(n.code,{children:"VCPKG_DEFAULT_TRIPLET"})," environment variable."]})]})]})]}),"\n",(0,s.jsx)(n.p,{children:"These variables will be set after the configuration is done:"}),"\n",(0,s.jsxs)(n.table,{children:[(0,s.jsx)(n.thead,{children:(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.th,{children:"Variable Name"}),(0,s.jsx)(n.th,{children:"Description"})]})}),(0,s.jsxs)(n.tbody,{children:[(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINY_BUILD_SUBFOLDER"})}),(0,s.jsxs)(n.td,{children:["Folder by release type if ",(0,s.jsx)(n.code,{children:"CONFIG+=debug_and_release"})," is defined ",(0,s.jsx)("small",{children:"(/debug, /release, or an empty string)"}),"."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINY_CCACHE_BUILD"})}),(0,s.jsxs)(n.td,{children:["To correctly link ",(0,s.jsx)(n.code,{children:"ccache"})," build against a ",(0,s.jsx)(n.code,{children:"ccache"})," build ",(0,s.jsx)("small",{children:"(_ccache or an empty string)"}),"."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINY_MSVC_VERSION"})}),(0,s.jsxs)(n.td,{children:["The ",(0,s.jsx)(n.code,{children:"MSVC"})," compiler string ",(0,s.jsx)("small",{children:"(MSVC2022 or MSVC2019)"}),"."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINY_QT_VERSION_UNDERSCORED"})}),(0,s.jsxs)(n.td,{children:["Underscored ",(0,s.jsx)(n.code,{children:"Qt"})," version ",(0,s.jsx)("small",{children:"(eg. 6_7_2)"}),"."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINY_RELEASE_TYPE_CAMEL"})}),(0,s.jsxs)(n.td,{children:["Build type string ",(0,s.jsx)("small",{children:"(Debug, Profile, or Release)"}),"."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINY_VCPKG_INCLUDE"})}),(0,s.jsxs)(n.td,{children:["Path to the ",(0,s.jsx)(n.code,{children:"vcpkg"})," ",(0,s.jsx)(n.code,{children:"include"})," folder ",(0,s.jsx)("small",{children:"(vcpkg/installed//include/)"}),"."]})]})]})]}),"\n",(0,s.jsxs)(n.p,{children:["Then you simply include the ",(0,s.jsx)(n.code,{children:"TinyOrm.pri"})," in your project file."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-qmake",metastring:"title='AnyProject.pro'",children:"include($$TINY_MAIN_DIR/TinyORM/qmake/TinyOrm.pri)\n"})}),"\n",(0,s.jsxs)(n.p,{children:["And that is all, now you should be able to link against the ",(0,s.jsx)(n.code,{children:"TinyORM"})," library. \ud83d\udc4c"]}),"\n",(0,s.jsx)(n.h5,{id:"manual-configuration-examples",children:"Manual configuration examples"}),"\n",(0,s.jsxs)(n.p,{children:["Frankly, there is no reason to use the Manual configuration (define the variables described below before the ",(0,s.jsx)(n.code,{children:"TinyOrm.pri"})," inclusion), the only reason to use it is when you want more control over this process or want to define everything yourself. I'll leave this section here to show how things work."]}),"\n",(0,s.jsxs)(n.p,{children:["You will have to link against the ",(0,s.jsx)(n.code,{children:"TinyORM"})," library manually if you don't set the ",(0,s.jsx)(n.code,{children:"TINYORM_BUILD_TREE"})," ",(0,s.jsx)(n.code,{children:"qmake"})," variable before the inclusion of the ",(0,s.jsx)(n.code,{children:"TinyOrm.pri"})," file. The ",(0,s.jsx)(n.code,{children:"INCLUDEPATH"})," is auto-detected every time."]}),"\n",(0,s.jsxs)(o.A,{groupId:y.vf,children:[(0,s.jsx)(t.A,{value:y.b,label:y.ux,children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-qmake",metastring:"{9}",children:"# Link against TinyORM library\n# ---\nTINY_MAIN_DIR = $$clean_path()\n\n# Configure TinyORM library\ninclude($$TINY_MAIN_DIR/TinyORM/qmake/TinyOrm.pri)\n\n# TinyORM library path\nTINYORM_BUILD_TREE = $$quote($$TINY_MAIN_DIR/TinyORM-builds-qmake)\nLIBS += $$quote(-L$$TINYORM_BUILD_TREE/build-TinyORM-Desktop_Qt_6_7_2_MSVC2019_64bit-Debug/src$${TINY_BUILD_SUBFOLDER}/)\nLIBS += -lTinyOrm\n"})})}),(0,s.jsx)(t.A,{value:y.xj,label:y.gg,children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-qmake",metastring:"{9}",children:"# Link against TinyORM library\n# ---\nTINY_MAIN_DIR = $$clean_path()\n\n# Configure TinyORM library\ninclude($$TINY_MAIN_DIR/TinyORM/qmake/TinyOrm.pri)\n\n# TinyORM library path\nTINYORM_BUILD_TREE = $$quote($$TINY_MAIN_DIR/TinyORM-builds-qmake)\nLIBS += $$quote(-L$$TINYORM_BUILD_TREE/build-TinyORM-Desktop_Qt_6_7_2_GCC_64bit-Debug/src$${TINY_BUILD_SUBFOLDER}/)\nLIBS += -lTinyOrm\n"})})})]}),"\n",(0,s.jsxs)(n.p,{children:["The same is true for the ",(0,s.jsx)(n.code,{children:"vcpkg"})," include path. If you don't set the ",(0,s.jsx)(n.code,{children:"TINY_VCPKG_ROOT"})," or have not defined the ",(0,s.jsx)(n.code,{children:"VCPKG_ROOT"})," environment variable, then you need to set up the ",(0,s.jsx)(n.code,{children:"INCLUDEPATH"})," for the ",(0,s.jsx)(n.code,{children:"vcpkg"})," that provides the ",(0,s.jsx)(n.code,{children:"range-v3"})," and ",(0,s.jsx)(n.code,{children:"tabulate"})," header files."]}),"\n",(0,s.jsxs)(o.A,{groupId:y.vf,children:[(0,s.jsx)(t.A,{value:y.b,label:y.ux,children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-qmake",children:"# vcpkg - range-v3 and tabulate\n# ---\nINCLUDEPATH += $$quote(/vcpkg/installed/x64-windows/include/)\n"})})}),(0,s.jsx)(t.A,{value:y.xj,label:y.gg,children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-qmake",children:"# vcpkg - range-v3 and tabulate\n# ---\nQMAKE_CXXFLAGS += -isystem $$shell_quote(/vcpkg/installed/x64-linux/include/)\n"})})})]}),"\n",(0,s.jsxs)(n.p,{children:["You can also use TinyORM's ",(0,s.jsx)(n.code,{children:"qmake"})," function ",(0,s.jsx)(n.code,{children:"tiny_add_system_includepath()"})," which handles ",(0,s.jsx)(n.code,{children:"INCLUDEPATH"})," in a cross-platform way."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-qmake",children:"# vcpkg - range-v3 and tabulate\n# ---\nload(tiny_system_includepath)\ntiny_add_system_includepath(/vcpkg/installed/x64-linux/include/)\n"})}),"\n",(0,s.jsxs)(n.p,{children:["Do not forget to add ",(0,s.jsx)(n.code,{children:"TinyOrm0.dll"})," on the path on Windows and on the ",(0,s.jsx)(n.code,{children:"LD_LIBRARY_PATH"})," on Linux, so your application can find it during execution."]}),"\n",(0,s.jsxs)(o.A,{groupId:y.vf,name:"tinyorm-on-path",children:[(0,s.jsx)(t.A,{value:y.b,label:y.ux,children:(0,s.jsx)(c.A,{className:"language-powershell",children:`$env:Path = "${(0,x.OZ)(y.b,!1)}\\TinyORM\\TinyORM-builds-qmake\\build-debug;" + $env:Path`})}),(0,s.jsx)(t.A,{value:y.xj,label:y.gg,children:(0,s.jsx)(c.A,{className:"language-bash",children:`export LD_LIBRARY_PATH=${(0,x.OZ)(y.xj)}/TinyORM/TinyORM-builds-qmake/build-debug\${PATH:+:}$PATH`})})]}),"\n",(0,s.jsxs)(n.admonition,{type:"tip",children:[(0,s.jsxs)(n.p,{children:["On Linux ",(0,s.jsx)(n.code,{children:"-isystem"})," marks the directory as a system directory, it prevents warnings."]}),(0,s.jsxs)(n.p,{children:["On Windows you can use ",(0,s.jsx)(n.code,{children:"QMAKE_CXXFLAGS_WARN_ON = -external:anglebrackets -external:W0"}),", it applies a warning level 0 to the angel bracket includes; ",(0,s.jsx)(n.code,{children:"#include "}),"."]}),(0,s.jsxs)(n.p,{children:["With the ",(0,s.jsx)(n.code,{children:"Clang-cl"})," with ",(0,s.jsx)(n.code,{children:"MSVC"})," you can use ",(0,s.jsx)(n.code,{children:"-imsvc"}),"."]})]}),"\n",(0,s.jsx)(n.h3,{id:"auto-configuration-internals",children:"Auto-configuration internals"}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"qmake"})," build system does not support ",(0,s.jsx)(n.code,{children:"auto-configuration"})," of dependencies out of the box but ",(0,s.jsx)(n.code,{children:"TinyORM"})," from ",(0,s.jsx)(n.code,{children:"v0.34.0"})," added its own ",(0,s.jsx)(n.code,{children:"Auto-configuration"})," feature along with the ",(0,s.jsx)(n.code,{children:"tiny_dotenv"})," qmake feature. These new features allow us to ",(0,s.jsx)(n.code,{children:"auto-configure"})," ",(0,s.jsx)(n.code,{children:"TinyORM"})," project, and with their help, the ",(0,s.jsx)(n.code,{children:"conf.pri"})," files can be ",(0,s.jsx)("u",{children:"skipped entirely"}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["While it adds additional complexity to the ",(0,s.jsx)(n.code,{children:"qmake"})," configuration process, the benefits are significant."]}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"Auto-configuration"})," feature is designed to find the ",(0,s.jsx)(n.code,{children:"vcpkg"})," and ",(0,s.jsx)(n.code,{children:"MySQL"})," installations, and ",(0,s.jsx)(n.code,{children:"tiny_dotenv"})," to include the ",(0,s.jsx)(n.code,{children:".env"})," and ",(0,s.jsx)(n.code,{children:".env.(win32|unix|mingw)"})," files in the project's root folder. These new features can be configured using ",(0,s.jsx)(n.code,{children:"qmake"})," and ",(0,s.jsx)(n.code,{children:"environment"})," variables, and they also contain some guessing logic if these variables are not defined."]}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"Auto-configuration"})," feature can be turned off using the ",(0,s.jsx)(n.a,{href:"#disable_autoconf",children:(0,s.jsx)(n.code,{children:"disable_autoconf"})})," ",(0,s.jsx)(n.code,{children:"qmake"})," configuration option (eg. ",(0,s.jsx)(n.code,{children:"CONFIG*=disable_autoconf"}),")."]}),"\n",(0,s.jsxs)(n.p,{children:["These are ",(0,s.jsx)("u",{children:(0,s.jsx)(n.code,{children:"qmake"})})," and ",(0,s.jsx)("u",{children:(0,s.jsx)(n.code,{children:"environment"})})," variables that affect the ",(0,s.jsx)(n.code,{children:"Auto-configuration"})," feature:"]}),"\n",(0,s.jsxs)(n.table,{children:[(0,s.jsx)(n.thead,{children:(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.th,{children:"Variable Name"}),(0,s.jsx)(n.th,{children:"Description"})]})}),(0,s.jsxs)(n.tbody,{children:[(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINY_VCPKG_ROOT"})}),(0,s.jsxs)(n.td,{children:["Path to the ",(0,s.jsx)(n.code,{children:"vcpkg"})," installation folder.",(0,s.jsx)("br",{}),"If not defined, then it tries to use the ",(0,s.jsx)(n.code,{children:"VCPKG_ROOT"})," environment variable."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINY_VCPKG_TRIPLET"})}),(0,s.jsxs)(n.td,{children:["The ",(0,s.jsx)(n.code,{children:"vcpkg"})," ",(0,s.jsx)(n.code,{children:"triplet"})," to use ",(0,s.jsx)("small",{children:"(vcpkg/installed/$$TINY_VCPKG_TRIPLET/)"}),".",(0,s.jsx)("br",{}),"If not defined, then it tries to guess the ",(0,s.jsx)(n.code,{children:"vcpkg"})," ",(0,s.jsx)(n.code,{children:"triplet"})," based on the current compiler and OS (based on the ",(0,s.jsx)(n.code,{children:"QMAKESPEC"}),"), and as the last thing, it tries to use the ",(0,s.jsx)(n.code,{children:"VCPKG_DEFAULT_TRIPLET"})," environment variable."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINY_MYSQL_ROOT"})}),(0,s.jsxs)(n.td,{children:["Path to the ",(0,s.jsx)(n.code,{children:"MySQL"})," installation folder.",(0,s.jsx)("br",{}),"If not defined, then it tries to guess the ",(0,s.jsx)(n.code,{children:"MySQL"})," installation folder (",(0,s.jsx)(n.code,{children:"win32"})," only): ",(0,s.jsx)("code",{children:"$$(ProgramFiles)/MySQL/MySQL Server (8.4|8.3|8.2|8.1|8.0|5.7)/"})]})]})]})]}),"\n",(0,s.jsxs)(n.p,{children:["You can set these variables in the ",(0,s.jsx)(n.code,{children:".env"})," (recommended) or ",(0,s.jsx)(n.code,{children:"conf.pri"})," files, in the ",(0,s.jsx)(n.code,{children:".qmake.conf"})," file (or wherever you want), or as environment variables."]}),"\n",(0,s.jsxs)(n.p,{children:["These variables will be set after ",(0,s.jsx)(n.code,{children:"auto-configuration"})," is done:"]}),"\n",(0,s.jsxs)(n.table,{children:[(0,s.jsx)(n.thead,{children:(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.th,{children:"Variable Name"}),(0,s.jsx)(n.th,{children:"Description"})]})}),(0,s.jsxs)(n.tbody,{children:[(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINY_VCPKG_INCLUDE"})}),(0,s.jsxs)(n.td,{children:["Path to the ",(0,s.jsx)(n.code,{children:"vcpkg"})," ",(0,s.jsx)(n.code,{children:"include"})," folder ",(0,s.jsx)("small",{children:"(vcpkg/installed//include/)"}),"."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINY_MYSQL_INCLUDE"})}),(0,s.jsxs)(n.td,{children:["Path to the ",(0,s.jsx)(n.code,{children:"MySQL"})," ",(0,s.jsx)(n.code,{children:"include"})," folder ",(0,s.jsx)("small",{children:"(MySQL Server 8.4/include/)"}),"."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINY_MYSQL_LIB"})}),(0,s.jsxs)(n.td,{children:["Path to the ",(0,s.jsx)(n.code,{children:"MySQL"})," ",(0,s.jsx)(n.code,{children:"lib"})," folder ",(0,s.jsx)("small",{children:"(MySQL Server 8.4/lib/)"}),"."]})]})]})]}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"TINY_MYSQL_INCLUDE"})," and ",(0,s.jsx)(n.code,{children:"TINY_MYSQL_LIB"})," are only set on ",(0,s.jsx)(n.code,{children:"win32"})," platform except ",(0,s.jsx)(n.code,{children:"mingw"}),"."]}),"\n",(0,s.jsx)(n.h4,{id:"environment-files",children:"Environment files"}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"tiny_dotenv"})," feature allows us to define the ",(0,s.jsx)(n.code,{children:".env"})," and ",(0,s.jsx)(n.code,{children:".env.$$TINY_DOTENV_PLATFORM"})," files in the project's root folder. These files are loaded as early as possible so you can affect the ",(0,s.jsx)(n.code,{children:"qmake"})," configuration process. On the other hand, the ",(0,s.jsx)(n.code,{children:"conf.pri"})," files are loaded as late as possible, and they can be used to override the ",(0,s.jsx)(n.code,{children:"qmake"})," configuration."]}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:".env"})," file is included ",(0,s.jsx)("u",{children:"first"})," and is included on all platforms."]}),"\n",(0,s.jsxs)(n.p,{children:["There is only one requirement for this feature to work correctly, and that is to set the ",(0,s.jsx)(n.code,{children:"TINY_DOTENV_ROOT"})," ",(0,s.jsx)(n.code,{children:"qmake"})," variable to the project's root folder. This variable is ",(0,s.jsx)(n.strong,{children:"already"})," set in the ",(0,s.jsx)(n.code,{children:".qmake.conf"})," file for the ",(0,s.jsx)(n.code,{children:"TinyORM"})," project."]}),"\n",(0,s.jsx)(n.p,{children:"Then the following names are taken into account: .env, .env.win32, .env.unix, .env.mingw"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-qmake",metastring:"title='.qmake.conf'",children:"# To find .env and .env.$$QMAKE_PLATFORM files\nTINY_DOTENV_ROOT = $$PWD\n"})}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"tiny_dotenv"})," feature can be turned off using the ",(0,s.jsx)(n.a,{href:"#disable_dotenv",children:(0,s.jsx)(n.code,{children:"disable_dotenv"})})," ",(0,s.jsx)(n.code,{children:"qmake"})," configuration option (eg. ",(0,s.jsx)(n.code,{children:"CONFIG*=disable_dotenv"}),")."]}),"\n",(0,s.jsx)(n.admonition,{type:"warning",children:(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.code,{children:"Environment files"})," don't work in the ",(0,s.jsx)(n.code,{children:"CMake"})," builds."]})}),"\n",(0,s.jsxs)(n.h4,{id:"partial-guessing-of-the-tinyorm_build_tree",children:["Partial guessing of the ",(0,s.jsx)(n.code,{children:"TINYORM_BUILD_TREE"})]}),"\n",(0,s.jsxs)(n.p,{children:["You don't have to manually define the ",(0,s.jsx)(n.code,{children:"TINYORM_BUILD_TREE"})," in ",(0,s.jsx)(n.code,{children:".env"})," or ",(0,s.jsx)(n.code,{children:".qmake.conf"})," files. The ",(0,s.jsx)(n.code,{children:"TINYORM_BUILD_TREE"})," absolute path can be put together for you (this is happening inside the ",(0,s.jsx)(n.code,{children:"variables.pri"})," file) and ",(0,s.jsx)(n.code,{children:"TinyORM"})," build folder name can be guessed for you too."]}),"\n",(0,s.jsxs)(n.p,{children:["You must define the following variables before the ",(0,s.jsx)(n.code,{children:"TinyOrm.pri"})," will be included to make this real (set them in the ",(0,s.jsx)(n.code,{children:".qmake.conf"}),"):"]}),"\n",(0,s.jsxs)(n.table,{children:[(0,s.jsx)(n.thead,{children:(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.th,{children:"Variable Name"}),(0,s.jsx)(n.th,{children:"Description"})]})}),(0,s.jsxs)(n.tbody,{children:[(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINY_MAIN_DIR"})}),(0,s.jsxs)(n.td,{children:["Path to the ",(0,s.jsx)(n.strong,{children:"PARENT"})," folder of the ",(0,s.jsx)(n.code,{children:"TinyORM"})," source folder."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINY_BUILD_TREE"})}),(0,s.jsxs)(n.td,{children:["Path to the ",(0,s.jsx)(n.strong,{children:"current"})," build tree - ",(0,s.jsx)(n.code,{children:"TINY_BUILD_TREE = $$shadowed($$PWD)"}),"."]})]})]})]}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"TINY_MAIN_DIR"})," is required for another features anyway (so it should already be set) and all that's left is to set the ",(0,s.jsx)(n.code,{children:"TINY_BUILD_TREE"}),"."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-qmake",metastring:"title='.qmake.conf'",children:"# Path to the current build tree (used to guess the TinyORM build tree)\nTINY_BUILD_TREE = $$shadowed($$PWD)\n"})}),"\n",(0,s.jsxs)(n.p,{children:["If you will follow this pattern or logic then you can switch ",(0,s.jsx)(n.code,{children:"QtCreator Kits"})," and the ",(0,s.jsx)(n.code,{children:"TINYORM_BUILD_TREE"})," will be ",(0,s.jsx)(n.strong,{children:"auto-generated"})," correctly and will always point to the correct ",(0,s.jsx)(n.code,{children:"TinyORM"})," build tree."]}),"\n",(0,s.jsxs)(n.p,{children:["It works this way, all is happening inside the ",(0,s.jsx)(n.code,{children:"variables.pri"}),", it takes a build folder name for the ",(0,s.jsx)(n.strong,{children:"current"})," project eg. ",(0,s.jsx)(n.code,{children:"build-HelloWorld-Desktop_Qt_6_7_2_MSVC2022_64bit-Debug"}),", replaces the ",(0,s.jsx)(n.code,{children:"HelloWorld"})," with the ",(0,s.jsx)(n.code,{children:"TinyORM"})," and as we already know the ",(0,s.jsx)(n.code,{children:"TinyORM"})," build folder location we can simply concatenate these paths like ",(0,s.jsx)(n.code,{children:"$$TINY_MAIN_DIR/TinyORM-builds-qmake/build-TinyORM-Desktop_Qt_6_7_2_MSVC2022_64bit-Debug"}),"."]}),"\n",(0,s.jsx)(n.admonition,{type:"warning",children:(0,s.jsxs)(n.p,{children:["This will only work if you follow the recommended ",(0,s.jsx)(n.a,{href:"#folders-structure",children:(0,s.jsx)(n.code,{children:"Folders structure"})}),"."]})}),"\n",(0,s.jsx)(n.h3,{id:"manual-configuration-internals",children:"Manual configuration internals"}),"\n",(0,s.jsxs)(n.p,{children:["There is not much to say about the ",(0,s.jsx)(n.code,{children:"Manual configuration"})," feature. It uses ",(0,s.jsx)(n.code,{children:"conf.pri"})," files (there are four, one for every project or sub-project), and every project has prepared its own ",(0,s.jsx)(n.code,{children:"conf.pri.example"})," file for faster initial configuration."]}),"\n",(0,s.jsxs)(n.p,{children:["These ",(0,s.jsx)(n.code,{children:"conf.pri.example"})," files are nicely commented on, so you can see what needs to be modified. The ",(0,s.jsx)(n.code,{children:"conf.pri"})," files are loaded as late as possible, and they can be used to override the ",(0,s.jsx)(n.code,{children:"qmake"})," configuration."]}),"\n",(0,s.jsxs)(n.p,{children:["If the ",(0,s.jsx)(n.code,{children:"Auto-configuration"})," feature is disabled and there are no ",(0,s.jsx)(n.code,{children:"conf.pri"})," files, then the ",(0,s.jsx)(n.code,{children:"TinyORM"})," ",(0,s.jsx)(n.code,{children:"qmake"})," configuration or build will fail at 100%."]}),"\n",(0,s.jsxs)(n.p,{children:["These ",(0,s.jsx)(n.code,{children:"conf.pri"})," files are intended for configuring qmake's ",(0,s.jsx)(n.code,{children:"INCLUDEPATH"})," and ",(0,s.jsx)(n.code,{children:"LIBS"}),", ",(0,s.jsx)(n.code,{children:"CONFIG"})," or eg. ",(0,s.jsx)(n.code,{children:"QMAKE_LFLAGS"}),", or any other ",(0,s.jsx)(n.code,{children:"qmake"})," options or variables."]}),"\n",(0,s.jsx)(n.h2,{id:"ccache-support",children:"Ccache support"}),"\n",(0,s.jsxs)(n.p,{children:["The TinyORM supports the ",(0,s.jsx)(n.a,{href:"https://ccache.dev/",children:(0,s.jsx)(n.code,{children:"ccache"})})," out of the box for all ",(0,s.jsx)(n.a,{href:"/supported-compilers",children:"supported compilers"}),". For ",(0,s.jsx)(n.code,{children:"qmake"})," you can enable it using the ",(0,s.jsx)(n.a,{href:"#ccache",children:(0,s.jsx)(n.code,{children:"CONFIG+=ccache"})})," and for ",(0,s.jsx)(n.code,{children:"CMake"})," you can set the ",(0,s.jsx)(n.a,{href:"#CMAKE_CXX_COMPILER_LAUNCHER",children:(0,s.jsx)(n.code,{children:"CMAKE_CXX_COMPILER_LAUNCHER=ccache"})}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["On ",(0,s.jsx)(n.code,{children:"Linux"})," it's clear, the ",(0,s.jsx)(n.code,{children:"ccache"})," is fully supported and works also with the ",(0,s.jsx)(n.code,{children:"precompiled headers"}),". But was necessary to add some workarounds to the ",(0,s.jsx)(n.code,{children:"qmake"}),"/",(0,s.jsx)(n.code,{children:"CMake"})," build systems to make out of the box support on ",(0,s.jsx)(n.code,{children:"Windows"}),". When you enable the ",(0,s.jsx)(n.code,{children:"ccache"})," on ",(0,s.jsx)(n.code,{children:"Windows"})," then the build system replaces the ",(0,s.jsx)(n.code,{children:"-Zi"})," and ",(0,s.jsx)(n.code,{children:"-ZI"})," compiler options with the ",(0,s.jsx)(n.code,{children:"-Z7"})," (link to the ",(0,s.jsx)(n.a,{href:"https://github.com/ccache/ccache/issues/1040",children:"issue"}),") and disables ",(0,s.jsx)(n.code,{children:"precompiled headers"})," if ",(0,s.jsx)(n.code,{children:"ccache <4.10"}),"."]}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["You can install the ",(0,s.jsx)(n.code,{children:"ccache"})," using the ",(0,s.jsx)(n.code,{children:"scoop install ccache"})," command on Windows. See the ",(0,s.jsx)(n.a,{href:"/dependencies#linux-installation-ccache",children:"Dependencies"})," page for how to install ",(0,s.jsx)(n.code,{children:"ccache"})," on Linux."]})})]})}function I(e={}){const{wrapper:n}={...(0,d.R)(),...e.components};return n?(0,s.jsx)(n,{...e,children:(0,s.jsx)(M,{...e})}):M(e)}},9365:(e,n,i)=>{i.d(n,{A:()=>l});i(6540);var s=i(4164);const d={tabItem:"tabItem_Ymn6"};var r=i(4848);function l(e){let{children:n,hidden:i,className:l}=e;return(0,r.jsx)("div",{role:"tabpanel",className:(0,s.A)(d.tabItem,l),hidden:i,children:n})}},1470:(e,n,i)=>{i.d(n,{A:()=>_});var s=i(6540),d=i(4164),r=i(3104),l=i(6347),c=i(205),t=i(7485),o=i(1682),a=i(679);function h(e){return s.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,s.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function x(e){const{values:n,children:i}=e;return(0,s.useMemo)((()=>{const e=n??function(e){return h(e).map((e=>{let{props:{value:n,label:i,attributes:s,default:d}}=e;return{value:n,label:i,attributes:s,default:d}}))}(i);return function(e){const n=(0,o.X)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[n,i])}function j(e){let{value:n,tabValues:i}=e;return i.some((e=>e.value===n))}function u(e){let{queryString:n=!1,groupId:i}=e;const d=(0,l.W6)(),r=function(e){let{queryString:n=!1,groupId:i}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!i)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return i??null}({queryString:n,groupId:i});return[(0,t.aZ)(r),(0,s.useCallback)((e=>{if(!r)return;const n=new URLSearchParams(d.location.search);n.set(r,e),d.replace({...d.location,search:n.toString()})}),[r,d])]}function p(e){const{defaultValue:n,queryString:i=!1,groupId:d}=e,r=x(e),[l,t]=(0,s.useState)((()=>function(e){let{defaultValue:n,tabValues:i}=e;if(0===i.length)throw new Error("Docusaurus error: the component requires at least one children component");if(n){if(!j({value:n,tabValues:i}))throw new Error(`Docusaurus error: The has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${i.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}const s=i.find((e=>e.default))??i[0];if(!s)throw new Error("Unexpected error: 0 tabValues");return s.value}({defaultValue:n,tabValues:r}))),[o,h]=u({queryString:i,groupId:d}),[p,m]=function(e){let{groupId:n}=e;const i=function(e){return e?`docusaurus.tab.${e}`:null}(n),[d,r]=(0,a.Dv)(i);return[d,(0,s.useCallback)((e=>{i&&r.set(e)}),[i,r])]}({groupId:d}),b=(()=>{const e=o??p;return j({value:e,tabValues:r})?e:null})();(0,c.A)((()=>{b&&t(b)}),[b]);return{selectedValue:l,selectValue:(0,s.useCallback)((e=>{if(!j({value:e,tabValues:r}))throw new Error(`Can't select invalid tab value=${e}`);t(e),h(e),m(e)}),[h,m,r]),tabValues:r}}var m=i(2303);const b={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var f=i(4848);function g(e){let{className:n,block:i,selectedValue:s,selectValue:l,tabValues:c}=e;const t=[],{blockElementScrollPositionUntilNextRender:o}=(0,r.a_)(),a=e=>{const n=e.currentTarget,i=t.indexOf(n),d=c[i].value;d!==s&&(o(n),l(d))},h=e=>{let n=null;switch(e.key){case"Enter":a(e);break;case"ArrowRight":{const i=t.indexOf(e.currentTarget)+1;n=t[i]??t[0];break}case"ArrowLeft":{const i=t.indexOf(e.currentTarget)-1;n=t[i]??t[t.length-1];break}}n?.focus()};return(0,f.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,d.A)("tabs",{"tabs--block":i},n),children:c.map((e=>{let{value:n,label:i,attributes:r}=e;return(0,f.jsx)("li",{role:"tab",tabIndex:s===n?0:-1,"aria-selected":s===n,ref:e=>t.push(e),onKeyDown:h,onClick:a,...r,className:(0,d.A)("tabs__item",b.tabItem,r?.className,{"tabs__item--active":s===n}),children:i??n},n)}))})}function y(e){let{lazy:n,children:i,selectedValue:d}=e;const r=(Array.isArray(i)?i:[i]).filter(Boolean);if(n){const e=r.find((e=>e.props.value===d));return e?(0,s.cloneElement)(e,{className:"margin-top--md"}):null}return(0,f.jsx)("div",{className:"margin-top--md",children:r.map(((e,n)=>(0,s.cloneElement)(e,{key:n,hidden:e.props.value!==d})))})}function T(e){const n=p(e);return(0,f.jsxs)("div",{className:(0,d.A)("tabs-container",b.tabList),children:[(0,f.jsx)(g,{...n,...e}),(0,f.jsx)(y,{...n,...e})]})}function _(e){const n=(0,m.A)();return(0,f.jsx)(T,{...e,children:h(e.children)},String(n))}},6684:(e,n,i)=>{i.d(n,{A:()=>a});var s=i(6540),d=i(3427),r=i(6347);const l={apiTable:"apiTable_flxF"};var c=i(4848);function t(e,n){let{name:i,children:l}=e;const t=function(e){let n=e;for(;(0,s.isValidElement)(n);)[n]=s.Children.toArray(n.props.children);if("string"!=typeof n)throw new Error(`Could not extract APITable row name from JSX tree:\n${JSON.stringify(e,null,2)}`);return n}(l),o=i?`${i}-${t}`:t,a=`#${o}`,h=(0,r.W6)();return(0,d.A)().collectAnchor(o),(0,c.jsx)("tr",{id:o,tabIndex:0,ref:h.location.hash===a?n:void 0,onClick:e=>{const n=e.target;[n,n.parentElement].some((e=>"A"===e?.tagName.toUpperCase()))||h.push(a)},onKeyDown:e=>{"Enter"===e.key&&h.push(a)},children:l.props.children})}const o=s.forwardRef(t);function a(e){let{children:n,name:i}=e;if("table"!==n.type)throw new Error("Bad usage of APITable component.\nIt is probably that your Markdown table is malformed.\nMake sure to double-check you have the appropriate number of columns for each table row.");const[d,r]=s.Children.toArray(n.props.children),t=(0,s.useRef)(null);(0,s.useEffect)((()=>{t.current?.focus()}),[t]);const a=s.Children.map(r.props.children,(e=>(0,c.jsx)(o,{name:i,ref:t,children:e})));return(0,c.jsxs)("table",{className:l.apiTable,children:[d,(0,c.jsx)("tbody",{children:a})]})}},7324:(e,n,i)=>{i.d(n,{$E:()=>m,A3:()=>f,CW:()=>b,Dx:()=>a,F4:()=>x,Fi:()=>o,J_:()=>_,LQ:()=>g,Lf:()=>v,OO:()=>d,Q7:()=>y,b:()=>c,cy:()=>t,gg:()=>u,kl:()=>j,os:()=>h,pW:()=>r,ux:()=>p,vf:()=>s,xj:()=>l,xt:()=>T});const s="shell",d="database",r="application",l="bash",c="pwsh",t="zsh",o="maria",a="mysql",h="postgres",x="sqlite",j="application",u="bash",p="pwsh",m="zsh",b="MariaDB",f="MySQL",g="PostgreSQL",y="SQLite",T="tinyorm.org",_="$HOME/Code/c/",v="$env:USERPROFILE\\Code\\c\\"},6362:(e,n,i)=>{i.d(n,{A:()=>r});var s=i(6540),d=i(1838);function r(){const e=(0,s.useContext)(d.A);if(null!=e)return e;throw new Error("useRootFolderContext is used outside of Layout component.")}},6694:(e,n,i)=>{i.d(n,{OZ:()=>t,Sn:()=>l,T3:()=>a,bw:()=>o,nC:()=>h,np:()=>c});var s=i(6362),d=i(2303),r=i(7324);const l=function(e,n){return void 0===n&&(n=!0),x((0,s.A)().rootFolder[e]??o(e),e,n)},c=()=>(0,s.A)().rootFolder[r.pW]??o(r.pW),t=function(e,n){if(void 0===n&&(n=!0),null==e)throw new Error("The groupId in the applicationFolderPath() can not be empty.");const i=n||e!==r.b?"/":"\\";return x(l(e)+i+c(),e,n)};function o(e){if(null==e)throw new Error("The groupId in the folderDefaultValue() can not be empty.");if(!(0,d.A)())return"";switch(e){case r.b:return r.Lf;case r.xj:return r.J_;case r.pW:return r.xt;default:throw new Error(`No default value for '${e}' groupId in the folderDefaultValue().`)}}function a(e){return e===r.pW}function h(e,n){if(null==n||""===n)return n;const i="$ENV{$1}$2";switch(e){case r.b:return u(n).replace(/\$env:(.+?)(\/.*)/,i);case r.xj:return n.replace(/\$(.+?)(\/.*)/,i);default:throw new Error(`Unsupported shell type '${e}' in the convertToCmakeEnvVariable().`)}}function x(e,n,i){if(void 0===i&&(i=!0),null==e||""===e)return e;if(n!==r.b)return j(e);const s=j(e);return i?u(s):function(e){return null==e||""===e?e:e.replaceAll(/\/+/g,"\\")}(s)}function j(e){return null==e||""===e?e:e.replace(/[/\\]+$/,"")}function u(e){return null==e||""===e?e:e.replaceAll(/\\+(?! )/g,"/")}},2721:(e,n,i)=>{i.d(n,{A:()=>s});const s=i.p+"assets/images/qmake-additional_arguments-14d3b6b82ad6d28db5b999a462500a6a.png"},7619:(e,n,i)=>{i.d(n,{A:()=>s});const s=i.p+"assets/images/qmake-build_settings-7caa6d7c86232484b82acb24b5a3a6a7.png"},885:(e,n,i)=>{i.d(n,{A:()=>s});const s=i.p+"assets/images/qmake-configure_project-0b6821ea0523567dab9f21b3215055a3.png"}}]); \ No newline at end of file +"use strict";(self.webpackChunktinyorm_org=self.webpackChunktinyorm_org||[]).push([[395],{1503:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>O,contentTitle:()=>_,default:()=>I,frontMatter:()=>T,metadata:()=>v,toc:()=>R});var s=i(4848),d=i(8453),r=i(8774),l=i(6684),c=i(2364),t=i(9365),o=i(1470),a=i(5556),h=i.n(a),x=i(6694);function j(e){let{groupId:n}=e;return(0,s.jsx)("span",{children:(0,x.OZ)(n)})}j.propTypes={groupId:h().string.isRequired};const u=j;var p=i(4164),m=i(6362);const b={rootFolderInput:"rootFolderInput_ottS",input:"input_OR7e",application:"application_fjej"};function f(e){let{groupId:n,label:i}=e;const{rootFolder:d,setRootFolder:r}=(0,m.A)(),l=(0,x.T3)(n),c=l?"application":"root",t=l?"\nThis folder name is common for all shells (eg. pwsh, bash, ...)":"";return(0,s.jsx)("form",{name:"tinyorm-root-folder-form",className:(0,p.A)(b.rootFolderInput,b[n],n),onSubmit:e=>{e.preventDefault(),e.stopPropagation()},children:(0,s.jsx)("input",{name:"tinyorm-root-folder-input",className:(0,p.A)(b.input,b[n],n),placeholder:`Enter ${c} folder...`,title:`This ${c} folder will be used in all ${i} examples at tinyorm.org${t}`,onChange:e=>{r(n,e.target.value)},value:d[n]??(0,x.bw)(n)})})}f.propTypes={groupId:h().string.isRequired,label:h().string.isRequired};const g=f;var y=i(7324);const T={sidebar_position:0,sidebar_label:"TinyORM",hide_table_of_contents:!0,description:"How to compile the TinyORM C++ library on Windows and Linux.",keywords:["c++ orm","building","tinyorm"]},_="Building: TinyORM",v={id:"building/tinyorm",title:"Building: TinyORM",description:"How to compile the TinyORM C++ library on Windows and Linux.",source:"@site/docs/building/tinyorm.mdx",sourceDirName:"building",slug:"/building/tinyorm",permalink:"/building/tinyorm",draft:!1,unlisted:!1,tags:[],version:"current",sidebarPosition:0,frontMatter:{sidebar_position:0,sidebar_label:"TinyORM",hide_table_of_contents:!0,description:"How to compile the TinyORM C++ library on Windows and Linux.",keywords:["c++ orm","building","tinyorm"]},sidebar:"tinyormSidebar",previous:{title:"Getting Started",permalink:"/tinydrivers/getting-started"},next:{title:"Hello world",permalink:"/building/hello-world"}},O={},R=[{value:"Introduction",id:"introduction",level:2},{value:"Common Prerequisites",id:"common-prerequisites",level:4},{value:"Windows Prerequisites",id:"windows-prerequisites",level:4},{value:"Build environment scripts",id:"build-environment-scripts",level:5},{value:"Allow symbolic links unprivileged",id:"allow-symbolic-links-unprivileged",level:5},{value:"Folders structure",id:"folders-structure",level:2},{value:"Getting started",id:"getting-started",level:2},{value:"vcpkg",id:"vcpkg",level:2},{value:"Set up vcpkg environment",id:"set-up-vcpkg-environment",level:4},{value:"C preprocessor macros",id:"c-preprocessor-macros",level:2},{value:"Building with CMake",id:"building-with-cmake",level:2},{value:"Configure & Build (cmake)",id:"configure-and-build-cmake",level:3},{value:"CMake STRICT_MODE option",id:"cmake-strict_mode-option",level:5},{value:"Build TinyORM",id:"build-tinyorm",level:4},{value:"CMake build options",id:"cmake-build-options",level:3},{value:"Consume TinyOrm library (cmake)",id:"consume-tinyorm-library-cmake",level:3},{value:"Building with qmake",id:"building-with-qmake",level:2},{value:"Install dependencies",id:"install-dependencies",level:3},{value:"Configure & Build (qmake)",id:"configure-and-build-qmake",level:3},{value:"Open QtCreator IDE",id:"open-qtcreator-ide",level:4},{value:"Configure TinyORM",id:"configure-tinyorm",level:4},{value:"Auto-configuration and tiny_dotenv",id:"auto-configuration-and-tiny_dotenv",level:5},{value:"Manual configuration (conf.pri)",id:"manual-configuration-confpri",level:5},{value:"Opening TinyORM.pro (main project file)",id:"opening-tinyormpro-main-project-file",level:5},{value:"Build TinyORM",id:"build-tinyorm-1",level:4},{value:"qmake build options",id:"qmake-build-options",level:3},{value:"Consume TinyOrm library (qmake)",id:"consume-tinyorm-library-qmake",level:3},{value:"Requirements",id:"requirements",level:4},{value:"QMAKEFEATURES",id:"qmakefeatures",level:5},{value:"Variables affecting TinyOrm.pri",id:"variables-affecting-tinyormpri",level:5},{value:"Manual configuration examples",id:"manual-configuration-examples",level:5},{value:"Auto-configuration internals",id:"auto-configuration-internals",level:3},{value:"Environment files",id:"environment-files",level:4},{value:"Partial guessing of the TINYORM_BUILD_TREE",id:"partial-guessing-of-the-tinyorm_build_tree",level:4},{value:"Manual configuration internals",id:"manual-configuration-internals",level:3},{value:"Ccache support",id:"ccache-support",level:2}];function M(e){const n={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",h3:"h3",h4:"h4",h5:"h5",li:"li",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,d.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(n.h1,{id:"building-tinyorm",children:"Building: TinyORM"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.a,{href:"#introduction",children:"Introduction"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#common-prerequisites",children:"Common Prerequisites"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#windows-prerequisites",children:"Windows Prerequisites"})}),"\n"]}),"\n"]}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#folders-structure",children:"Folders structure"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#getting-started",children:"Getting started"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#vcpkg",children:"vcpkg"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#c-preprocessor-macros",children:"C preprocessor macros"})}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.a,{href:"#building-with-cmake",children:"Building with CMake"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#configure-and-build-cmake",children:"Configure & Build"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#cmake-build-options",children:"CMake build options"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#consume-tinyorm-library-cmake",children:"Consume TinyOrm library"})}),"\n"]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.a,{href:"#building-with-qmake",children:"Building with qmake"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#install-dependencies",children:"Install dependencies"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#configure-and-build-qmake",children:"Configure & Build"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#qmake-build-options",children:"qmake build options"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#consume-tinyorm-library-qmake",children:"Consume TinyOrm library"})}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.a,{href:"#auto-configuration-internals",children:"Auto-configuration internals"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#environment-files",children:"Environment files"})}),"\n"]}),"\n"]}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#manual-configuration-internals",children:"Manual configuration internals"})}),"\n"]}),"\n"]}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#ccache-support",children:"Ccache support"})}),"\n"]}),"\n",(0,s.jsx)(n.h2,{id:"introduction",children:"Introduction"}),"\n",(0,s.jsxs)(n.p,{children:["The build systems supported out of the box are ",(0,s.jsx)(n.code,{children:"CMake"})," and ",(0,s.jsx)(n.code,{children:"qmake"}),"."]}),"\n",(0,s.jsx)(n.admonition,{type:"info",children:(0,s.jsxs)(n.p,{children:["All examples below assume that ",(0,s.jsx)(n.code,{children:"pwsh"})," runs on ",(0,s.jsx)(n.code,{children:"Windows"})," and ",(0,s.jsx)(n.code,{children:"bash"})," runs on ",(0,s.jsx)(n.code,{children:"Linux"}),"."]})}),"\n",(0,s.jsx)(n.h4,{id:"common-prerequisites",children:"Common Prerequisites"}),"\n",(0,s.jsxs)(n.p,{children:["Install the required ",(0,s.jsx)(n.a,{href:"/dependencies",children:"dependencies"})," before starting."]}),"\n",(0,s.jsx)(n.admonition,{type:"warning",children:(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"QSqlDatabase"})," depends on ",(0,s.jsx)(n.code,{children:"QCoreApplication"})," from ",(0,s.jsx)(n.code,{children:"Qt v6.5.3"})," so you must create the ",(0,s.jsx)(n.code,{children:"QCoreApplication"})," instance before you will call anything from the ",(0,s.jsx)(n.code,{children:"TinyORM"})," library. \ud83e\udee4 The change was made ",(0,s.jsx)(n.a,{href:"https://github.com/qt/qtbase/commit/8d2bdc9cd5482eace12ba7e45304857bd24db0e6#diff-1d355c25c0b0eddec2be48253407780c4dc510d986739aec61e1ec892ccaf86e",children:"here"}),"."]})}),"\n",(0,s.jsx)(n.h4,{id:"windows-prerequisites",children:"Windows Prerequisites"}),"\n",(0,s.jsx)(n.h5,{id:"build-environment-scripts",children:"Build environment scripts"}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"Visual Studio"})," does not provide ",(0,s.jsx)(n.code,{children:"vcvars"})," scripts for ",(0,s.jsx)(n.code,{children:"pwsh"}),", you can use ",(0,s.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/tree/main/tools/vcvars64.ps1",children:(0,s.jsx)(n.code,{children:"vcvars64.ps1"})})," provided by ",(0,s.jsx)(n.code,{children:"TinyORM"})," in the ",(0,s.jsx)(n.code,{children:"tools/"})," folder. Place them on the ",(0,s.jsx)(n.code,{children:"$env:Path"})," user/system path and they will be available system-wide."]}),"\n",(0,s.jsxs)(n.p,{children:["The same is true for the ",(0,s.jsx)(n.code,{children:"Qt Framework"}),", it doesn't provide ",(0,s.jsx)(n.code,{children:"qtenv"})," scripts for ",(0,s.jsx)(n.code,{children:"pwsh"})," too. You can create your own script, place it on the ",(0,s.jsx)(n.code,{children:"$env:Path"})," user/system path and it will be available system-wide."]}),"\n",(0,s.jsxs)(n.p,{children:["Here is one simple example for ",(0,s.jsx)(n.code,{children:"pwsh"}),"."]}),"\n",(0,s.jsxs)(o.A,{groupId:y.vf,children:[(0,s.jsx)(t.A,{value:"qtenv6.ps1",label:"qtenv6.ps1",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-powershell",children:"#!/usr/bin/env pwsh\n\nSet-StrictMode -Version 3.0\n\nWrite-Host 'Setting up environment for Qt 6.7.2 usage...' -ForegroundColor Magenta\nWrite-Host\n\n$Script:QtRoot = $env:TINY_QT_ROOT ?? 'C:\\Qt'\n\n$env:Path = \"$Script:QtRoot\\6.7.2\\msvc2019_64\\bin;\" + $env:Path\n\n. vcvars64.ps1\n"})})}),(0,s.jsx)(t.A,{value:"qtenv5.ps1",label:"qtenv5.ps1",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-powershell",children:"#!/usr/bin/env pwsh\n\nSet-StrictMode -Version 3.0\n\nWrite-Host 'Setting up environment for Qt 5.15.2 usage...' -ForegroundColor Magenta\nWrite-Host\n\n$Script:QtRoot = $env:TINY_QT_ROOT ?? 'C:\\Qt'\n\n$env:Path = \"$Script:QtRoot\\5.15.2\\msvc2019_64\\bin;\" + $env:Path\n\n. vcvars64.ps1\n"})})})]}),"\n",(0,s.jsxs)(n.p,{children:["And here for ",(0,s.jsx)(n.code,{children:"Linux"}),"."]}),"\n",(0,s.jsxs)(o.A,{groupId:y.vf,children:[(0,s.jsx)(t.A,{value:"qtenv6",label:"qtenv6",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:'#!/usr/bin/env sh\n\necho \'Setting up environment for Qt 6.7.2 usage...\'\n\nQtRoot="${TINY_QT_ROOT:-/opt/Qt}"\n\nexport PATH="$QtRoot/6.7.2/gcc_64/bin"${PATH:+:}$PATH\nexport LD_LIBRARY_PATH="$QtRoot/6.7.2/gcc_64/lib"${LD_LIBRARY_PATH:+:}$LD_LIBRARY_PATH\n'})})}),(0,s.jsx)(t.A,{value:"qtenv5",label:"qtenv5",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:'#!/usr/bin/env sh\n\necho \'Setting up environment for Qt 5.15.2 usage...\'\n\nQtRoot="${TINY_QT_ROOT:-/opt/Qt}"\n\nexport PATH="$QtRoot/5.15.2/gcc_64/bin"${PATH:+:}$PATH\nexport LD_LIBRARY_PATH="$QtRoot/5.15.2/gcc_64/lib"${LD_LIBRARY_PATH:+:}$LD_LIBRARY_PATH\n'})})})]}),"\n",(0,s.jsx)(n.admonition,{type:"info",children:(0,s.jsxs)(n.p,{children:["These scripts consider the ",(0,s.jsx)(n.code,{children:"TINY_QT_ROOT"})," environment variable that should point to the ",(0,s.jsx)(n.code,{children:"Qt"})," installation folder, you can define this environment variable globally in your OS."]})}),"\n",(0,s.jsx)(n.admonition,{type:"info",children:(0,s.jsxs)(n.p,{children:["You can't execute these ",(0,s.jsx)(n.code,{children:"qtenvX"})," scripts, they have to be sourced like ",(0,s.jsx)(n.code,{children:"source qtenvX"})," or ",(0,s.jsx)(n.code,{children:". qtenvX"}),"."]})}),"\n",(0,s.jsx)(n.h5,{id:"allow-symbolic-links-unprivileged",children:"Allow symbolic links unprivileged"}),"\n",(0,s.jsxs)(n.p,{children:["Open ",(0,s.jsx)(n.code,{children:"Local Security Policy"}),", go to ",(0,s.jsx)(n.code,{children:"Local Policies - User Rights Assignment"}),", open ",(0,s.jsx)(n.code,{children:"Create symbolic links"})," and add your user account or user group, restart when it doesn't apply immediately."]}),"\n",(0,s.jsx)(n.h2,{id:"folders-structure",children:"Folders structure"}),"\n",(0,s.jsxs)(n.p,{children:["All ",(0,s.jsx)(n.code,{children:"tinyorm.org"})," examples are based on the following folders structure. The ",(0,s.jsx)(n.code,{children:"tom"})," folder will contain a ",(0,s.jsx)(n.a,{href:"/building/migrations",children:"migrations console application"}),"."]}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["You can set the root and application folder paths in the form below and they will be used across the whole ",(0,s.jsx)(n.a,{href:"http://www.tinyorm.org",children:"www.tinyorm.org"})," website. \ud83e\udd73 The pwsh shell is supposed to use on Windows and the bash shell on Linux, but it is not a requirement."]})}),"\n",(0,s.jsxs)(o.A,{groupId:y.vf,children:[(0,s.jsxs)(t.A,{value:y.b,label:y.ux,className:"tiny-tree",children:[(0,s.jsx)("div",{className:"tiny-root-folder-info-wrapper",children:(0,s.jsxs)(n.p,{children:[(0,s.jsx)("span",{className:"tiny-root-folder-info-prefix",children:"Current pwsh path"}),"\xa0",(0,s.jsx)(u,{groupId:y.b})]})}),(0,s.jsx)(g,{groupId:y.b,label:y.ux}),(0,s.jsx)(g,{groupId:y.pW,label:y.kl}),(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-text",children:"\n\n\n\u251c\u2500\u2500\n\u2502 \u251c\u2500\u2500 HelloWorld/\n\u2502 | \u251c\u2500\u2500 HelloWorld/\n\u2502 | \u251c\u2500\u2500 HelloWorld-builds-cmake/\n\u2502 | | \u2514\u2500\u2500 build-debug/\n\u2502 | \u2514\u2500\u2500 HelloWorld-builds-qmake/\n\u2502 | \u2514\u2500\u2500 build-debug/\n\u2502 \u251c\u2500\u2500 TinyORM/\n\u2502 | \u251c\u2500\u2500 TinyORM/\n\u2502 | \u251c\u2500\u2500 TinyORM-builds-cmake/\n\u2502 | \u2502 \u251c\u2500\u2500 build-gcc-debug/\n\u2502 | \u2502 \u251c\u2500\u2500 build-gcc-release/\n\u2502 | \u2502 \u2514\u2500\u2500 build-clang-debug/\n\u2502 | \u2514\u2500\u2500 TinyORM-builds-qmake/\n\u2502 | \u251c\u2500\u2500 build-debug/\n\u2502 | \u251c\u2500\u2500 build-TinyORM-Desktop_Qt_5_15_2_MSVC2019_64bit-Debug/\n\u2502 | \u2514\u2500\u2500 build-TinyORM-Desktop_Qt_5_15_2_MSYS2_UCRT64_64bit-Release/\n\u2502 \u2514\u2500\u2500 tom/\n\u2502 \u251c\u2500\u2500 tom/\n\u2502 \u2502 \u2514\u2500\u2500 database/\n\u2502 \u2502 \u251c\u2500\u2500 migrations/\n\u2502 \u2502 \u251c\u2500\u2500 seeders/\n\u2502 \u2502 \u251c\u2500\u2500 migrations.pri\n\u2502 \u2502 \u2514\u2500\u2500 seeders.pri\n\u2502 \u251c\u2500\u2500 tom-builds-cmake/\n\u2502 \u2502 \u2514\u2500\u2500 build-TinyORM-Desktop_Qt_6_7_2_MSVC2019_64bit-Debug/\n\u2502 \u2514\u2500\u2500 tom-builds-qmake/\n\u2502 \u251c\u2500\u2500 build-TinyORM-Desktop_Qt_5_15_3_MSYS2_UCRT64_64bit-Release/\n\u2502 \u2514\u2500\u2500 build-TinyORM-Desktop_Qt_6_7_2_MSVC2019_64bit-Debug/\n\u251c\u2500\u2500 tmp/\n\u2514\u2500\u2500 vcpkg/\n"})})]}),(0,s.jsxs)(t.A,{value:y.xj,label:y.gg,className:"tiny-tree",children:[(0,s.jsx)("div",{className:"tiny-root-folder-info-wrapper",children:(0,s.jsxs)(n.p,{children:[(0,s.jsx)("span",{className:"tiny-root-folder-info-prefix",children:"Current bash path"}),"\xa0",(0,s.jsx)(u,{groupId:y.xj})]})}),(0,s.jsx)(g,{groupId:y.xj,label:y.gg}),(0,s.jsx)(g,{groupId:y.pW,label:y.pW}),(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-text",children:"\n\n\n\u251c\u2500\u2500\n\u2502 \u251c\u2500\u2500 HelloWorld/\n\u2502 | \u251c\u2500\u2500 HelloWorld/\n\u2502 | \u251c\u2500\u2500 HelloWorld-builds-cmake/\n\u2502 | | \u2514\u2500\u2500 build-debug/\n\u2502 | \u2514\u2500\u2500 HelloWorld-builds-qmake/\n\u2502 | \u2514\u2500\u2500 build-debug/\n\u2502 \u251c\u2500\u2500 TinyORM/\n\u2502 | \u251c\u2500\u2500 TinyORM/\n\u2502 | \u251c\u2500\u2500 TinyORM-builds-cmake/\n\u2502 | \u2502 \u251c\u2500\u2500 build-gcc-debug/\n\u2502 | \u2502 \u251c\u2500\u2500 build-gcc-release/\n\u2502 | \u2502 \u2514\u2500\u2500 build-clang-debug/\n\u2502 | \u2514\u2500\u2500 TinyORM-builds-qmake/\n\u2502 | \u251c\u2500\u2500 build-debug/\n\u2502 | \u251c\u2500\u2500 build-TinyORM-Desktop_Qt_5_15_2_GCC_64bit-Debug/\n\u2502 | \u2514\u2500\u2500 build-TinyORM-Desktop_Qt_5_15_2_clang13_64bit_ccache-Release/\n\u2502 \u2514\u2500\u2500 tom/\n\u2502 \u251c\u2500\u2500 tom/\n\u2502 \u2502 \u2514\u2500\u2500 database/\n\u2502 \u2502 \u251c\u2500\u2500 migrations/\n\u2502 \u2502 \u251c\u2500\u2500 seeders/\n\u2502 \u2502 \u251c\u2500\u2500 migrations.pri\n\u2502 \u2502 \u2514\u2500\u2500 seeders.pri\n\u2502 \u251c\u2500\u2500 tom-builds-cmake/\n\u2502 \u2502 \u2514\u2500\u2500 build-TinyORM-Desktop_Qt_6_7_2_clang14_64bit_ccache-Debug/\n\u2502 \u2514\u2500\u2500 tom-builds-qmake/\n\u2502 \u251c\u2500\u2500 build-TinyORM-Desktop_Qt_6_7_2_GCC_64bit-Debug/\n\u2502 \u2514\u2500\u2500 build-TinyORM-Desktop_Qt_6_7_2_clang14_64bit_ccache-Release/\n\u251c\u2500\u2500 tmp/\n\u2514\u2500\u2500 vcpkg/\n"})})]})]}),"\n",(0,s.jsx)(n.admonition,{type:"danger",children:(0,s.jsxs)(n.p,{children:["Avoid paths with spaces with the ",(0,s.jsx)(n.code,{children:"qmake"})," build system, it will not compile."]})}),"\n",(0,s.jsx)(r.A,{id:"qtcreator-default-build-directory"}),"\n",(0,s.jsxs)(n.admonition,{type:"tip",children:[(0,s.jsxs)(n.p,{children:["You can force the ",(0,s.jsx)(n.code,{children:"QtCreator"})," to generate a build folders structure as is described above."]}),(0,s.jsxs)(n.p,{children:["To generate the required folders structure set the ",(0,s.jsx)(n.code,{children:"Settings"})," - ",(0,s.jsx)(n.code,{children:"Build & Run"})," - ",(0,s.jsx)(n.code,{children:"Default Build Properties"})," - ",(0,s.jsx)(n.code,{children:"Default build directory"})," to:",(0,s.jsx)("br",{}),"\n",(0,s.jsx)(n.code,{children:'../%{Project:Name}-builds-%{BuildSystem:Name}/%{JS: Util.asciify("build-%{Project:Name}-%{Kit:FileSystemName}-%{BuildConfig:Name}")}'})]})]}),"\n",(0,s.jsx)(n.h2,{id:"getting-started",children:"Getting started"}),"\n",(0,s.jsxs)(n.p,{children:["Prepare compilation environment, we need to put the Qt Framework and Visual Studio MSVC compiler on the path on Windows. The compiler is already on the path on Linux and you can export ",(0,s.jsx)(n.code,{children:"PATH"})," and ",(0,s.jsx)(n.code,{children:"LD_LIBRARY_PATH"})," for Qt Framework, or use our ",(0,s.jsx)(n.code,{children:"qtenvX"})," scripts described above."]}),"\n",(0,s.jsxs)(o.A,{groupId:y.vf,children:[(0,s.jsx)(t.A,{value:y.b,label:y.ux,children:(0,s.jsx)(c.A,{className:"language-powershell",children:`mkdir ${(0,x.Sn)(y.b)}\ncd ${(0,x.Sn)(y.b)}\n$env:Path = 'C:\\Qt\\6.7.2\\msvc2019_64\\bin;' + $env:Path\nvcvars64.ps1`})}),(0,s.jsx)(t.A,{value:y.xj,label:y.gg,children:(0,s.jsx)(c.A,{className:"language-bash",children:`mkdir -p ${(0,x.Sn)(y.xj)}\ncd ${(0,x.Sn)(y.xj)}\nexport PATH=/opt/Qt/6.7.2/gcc_64/bin\${PATH:+:}$PATH\nexport LD_LIBRARY_PATH=/opt/Qt/6.7.2/gcc_64/lib\${LD_LIBRARY_PATH:+:}$LD_LIBRARY_PATH`})})]}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["You can also use the ",(0,s.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/tools/Add-FolderOnPath.ps1",children:(0,s.jsx)(n.code,{children:"tools/Add-FolderOnPath.ps1"})})," pwsh script to fastly prepend a path or ",(0,s.jsx)("abbr",{title:"Current working directory",children:"pwd"})," on the system ",(0,s.jsx)(n.code,{children:"PATH"}),"."]})}),"\n",(0,s.jsx)(n.h2,{id:"vcpkg",children:"vcpkg"}),"\n",(0,s.jsxs)(n.p,{children:["Installing the ",(0,s.jsx)(n.code,{children:"vcpkg"})," is highly recommended, it simplifies installation of the ",(0,s.jsx)(n.code,{children:"range-v3"})," and ",(0,s.jsx)(n.code,{children:"tabulate"})," dependencies."]}),"\n",(0,s.jsxs)(o.A,{groupId:y.vf,children:[(0,s.jsx)(t.A,{value:y.b,label:y.ux,children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-powershell",children:"git clone git@github.com:microsoft/vcpkg.git\ncd vcpkg\n.\\bootstrap-vcpkg.bat\n"})})}),(0,s.jsx)(t.A,{value:y.xj,label:y.gg,children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"git clone git@github.com:microsoft/vcpkg.git\ncd vcpkg\n./bootstrap-vcpkg.sh\n"})})})]}),"\n",(0,s.jsxs)(n.p,{children:["Add ",(0,s.jsx)(n.code,{children:"vcpkg"})," on the system path, add the following to the ",(0,s.jsx)(n.code,{children:".bashrc"})," or ",(0,s.jsx)(n.code,{children:".zshrc"})," on Linux."]}),"\n",(0,s.jsx)(c.A,{className:"language-bash",children:`export PATH=${(0,x.Sn)(y.xj)}/vcpkg\${PATH:+:}$PATH`}),"\n",(0,s.jsxs)(n.p,{children:["On Windows, open the ",(0,s.jsx)(n.code,{children:"Environment variables"})," dialog and add ",(0,s.jsx)(n.code,{children:"vcpkg"})," on the user ",(0,s.jsx)(n.code,{children:"PATH"}),"."]}),"\n",(0,s.jsx)(n.p,{children:"Or you can export it for the current session only."}),"\n",(0,s.jsxs)(o.A,{groupId:y.vf,children:[(0,s.jsx)(t.A,{value:y.b,label:y.ux,children:(0,s.jsx)(c.A,{className:"language-powershell",children:`$env:Path = "${(0,x.Sn)(y.b,!1)}\\vcpkg;" + $env:Path`})}),(0,s.jsx)(t.A,{value:y.xj,label:y.gg,children:(0,s.jsx)(c.A,{className:"language-bash",children:`export PATH=${(0,x.Sn)(y.xj)}/vcpkg\${PATH:+:}$PATH`})})]}),"\n",(0,s.jsxs)(n.h4,{id:"set-up-vcpkg-environment",children:["Set up ",(0,s.jsx)(n.code,{children:"vcpkg"})," environment"]}),"\n",(0,s.jsxs)(n.p,{children:["To export ",(0,s.jsx)(n.code,{children:"vcpkg"})," environment variables globally, add it to the ",(0,s.jsx)(n.code,{children:".bashrc"})," or ",(0,s.jsx)(n.code,{children:".zshrc"})," on Linux, and you can use the ",(0,s.jsx)(n.code,{children:"Environment variables"})," dialog on Windows."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",metastring:"title='Linux'",children:'export VCPKG_DEFAULT_TRIPLET=x64-linux\n#export VCPKG_DEFAULT_HOST_TRIPLET=x64-linux\nexport VCPKG_MAX_CONCURRENCY=11\nexport VCPKG_OVERLAY_PORTS="$HOME/.local/share/vcpkg/ports"\nexport VCPKG_OVERLAY_TRIPLETS="$HOME/.local/share/vcpkg/triplets"\nexport VCPKG_ROOT="$HOME/Code/c/vcpkg"\n'})}),"\n",(0,s.jsx)(n.admonition,{type:"info",children:(0,s.jsxs)(n.p,{children:["It is recommended to define these variables globally because the ",(0,s.jsx)(n.code,{children:"CMake"})," and ",(0,s.jsx)(n.code,{children:"qmake"})," build system are able to detect the ",(0,s.jsx)(n.code,{children:"vcpkg"})," installation from them so you don't have to configure them manually to detect the ",(0,s.jsx)(n.code,{children:"vcpkg"})," installation."]})}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["On Windows, it's always better to create these types of variables as user variables instead of system variables in the ",(0,s.jsx)(n.code,{children:"Environment variables"})," dialog."]})}),"\n",(0,s.jsx)(n.h2,{id:"c-preprocessor-macros",children:"C preprocessor macros"}),"\n",(0,s.jsxs)(n.p,{children:["The following table summarizes all the C preprocessor macros defined in the ",(0,s.jsx)(n.code,{children:"TinyORM"})," library. These C macros are configured by ",(0,s.jsx)(n.code,{children:"CMake"})," or ",(0,s.jsx)(n.code,{children:"qmake"})," build systems. They are not sorted alphabetically, but they are sorted by how significant they are."]}),"\n",(0,s.jsxs)(n.p,{children:["In the ",(0,s.jsx)(n.code,{children:"CMake"})," build system, all the C macros are auto-detected / auto-configured or controlled by ",(0,s.jsx)(n.a,{href:"#cmake-build-options",children:(0,s.jsx)(n.code,{children:"CMake build options"})}),", so you don't have to care too much about them."]}),"\n",(0,s.jsxs)(n.p,{children:["In the ",(0,s.jsx)(n.code,{children:"qmake"})," build is important whether you are building ",(0,s.jsx)(n.code,{children:"TinyORM"})," library or you are building your application and linking against ",(0,s.jsx)(n.code,{children:"TinyORM"})," library. When you are building the ",(0,s.jsx)(n.code,{children:"TinyORM"})," library, all the C macros are auto-detected / auto-configured or controlled by ",(0,s.jsx)(n.a,{href:"#qmake-build-options",children:(0,s.jsx)(n.code,{children:"qmake build options"})}),", so you don't have to care too much about them."]}),"\n",(0,s.jsxs)(n.p,{children:["But a special situation is when you are building your application / library and you are linking against ",(0,s.jsx)(n.code,{children:"TinyORM"})," library. In this particular case, you must configure all these C macros manually! For this reason, the ",(0,s.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/qmake/TinyOrm.pri",children:(0,s.jsx)(n.code,{children:"TinyOrm.pri"})})," has been created, so that's not a big deal either. Little more info ",(0,s.jsx)(n.a,{href:"#consume-tinyorm-library-qmake",children:"here"}),"."]}),"\n",(0,s.jsx)("div",{id:"apitable-c-macros",children:(0,s.jsx)(l.A,{children:(0,s.jsxs)(n.table,{children:[(0,s.jsx)(n.thead,{children:(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.th,{children:"C Macro Name"}),(0,s.jsx)(n.th,{children:"Description"})]})}),(0,s.jsxs)(n.tbody,{children:[(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINYORM_LINKING_SHARED"})}),(0,s.jsxs)(n.td,{children:[(0,s.jsx)("u",{children:(0,s.jsx)(n.strong,{children:"Must"})})," be defined when you are linking against ",(0,s.jsx)(n.code,{children:"TinyORM"})," shared build (",(0,s.jsx)(n.code,{children:"dll"})," library), exported classes and functions will be tagged with ",(0,s.jsx)(n.code,{children:"__declspec(dllimport)"})," on ",(0,s.jsx)(n.code,{children:"msvc"})," and ",(0,s.jsx)(n.code,{children:'visibility("default")'})," on ",(0,s.jsx)(n.code,{children:"GCC >= 4"}),"."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINYORM_BUILDING_SHARED"})}),(0,s.jsxs)(n.td,{children:["Defined when ",(0,s.jsx)(n.code,{children:"TinyORM"})," is built as a ",(0,s.jsx)(n.code,{children:"dll"})," library (shared build)."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINYORM_DEBUG"})}),(0,s.jsx)(n.td,{children:"Defined in the debug build."})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINYORM_NO_DEBUG"})}),(0,s.jsx)(n.td,{children:"Defined in the release build."})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINYORM_DEBUG_SQL"})}),(0,s.jsx)(n.td,{children:"Defined in the debug build."})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINYORM_NO_DEBUG_SQL"})}),(0,s.jsx)(n.td,{children:"Defined in the release build."})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINYORM_MYSQL_PING"})}),(0,s.jsxs)(n.td,{children:["Enable ",(0,s.jsx)(n.code,{children:"Orm::MySqlConnection::pingDatabase()"})," method.",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Defined when ",(0,s.jsx)(n.a,{href:"#mysql_ping",children:(0,s.jsx)(n.code,{children:"mysql_ping"})})," ",(0,s.jsx)("small",{children:"(qmake)"})," / ",(0,s.jsx)(n.a,{href:"#MYSQL_PING",children:(0,s.jsx)(n.code,{children:"MYSQL_PING"})})," ",(0,s.jsx)("small",{children:"(cmake)"})," configuration ",(0,s.jsx)(n.code,{children:"build option"})," is enabled."]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINYORM_DISABLE_ORM"})}),(0,s.jsxs)(n.td,{children:["Controls the compilation of all ",(0,s.jsx)(n.code,{children:"ORM-related"})," source code, when this macro is ",(0,s.jsx)(n.code,{children:"defined"}),", then only the ",(0,s.jsx)(n.code,{children:"query builder"})," without ",(0,s.jsx)(n.code,{children:"ORM"})," is compiled. Also excludes ",(0,s.jsx)(n.code,{children:"ORM-related"})," unit tests.",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Defined when ",(0,s.jsx)(n.a,{href:"#disable_orm",children:(0,s.jsx)(n.code,{children:"disable_orm"})})," ",(0,s.jsx)("small",{children:"(qmake)"})," / ",(0,s.jsx)(n.a,{href:"#ORM",children:(0,s.jsx)(n.code,{children:"ORM"})})," ",(0,s.jsx)("small",{children:"(cmake)"})," configuration ",(0,s.jsx)(n.code,{children:"build option"})," is enabled ",(0,s.jsx)("small",{children:"(qmake)"})," / disabled ",(0,s.jsx)("small",{children:"(cmake)"}),"."]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINYORM_EXTERN_CONSTANTS"})}),(0,s.jsxs)(n.td,{children:["Defined when extern constants are used. Extern constants are enabled by default for shared builds and disabled for static builds.",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Described at ",(0,s.jsx)(n.a,{href:"#extern_constants",children:(0,s.jsx)(n.code,{children:"qmake"})})," / ",(0,s.jsx)(n.a,{href:"#INLINE_CONSTANTS",children:(0,s.jsx)(n.code,{children:"CMake"})})," how it works."]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINYORM_INLINE_CONSTANTS"})}),(0,s.jsxs)(n.td,{children:["Defined when global inline constants are used.",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Defined when ",(0,s.jsx)(n.a,{href:"#inline_constants",children:(0,s.jsx)(n.code,{children:"inline_constants"})})," ",(0,s.jsx)("small",{children:"(qmake)"})," / ",(0,s.jsx)(n.a,{href:"#INLINE_CONSTANTS",children:(0,s.jsx)(n.code,{children:"INLINE_CONSTANTS"})})," ",(0,s.jsx)("small",{children:"(cmake)"})," configuration ",(0,s.jsx)(n.code,{children:"build option"})," is enabled."]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINYORM_TESTS_CODE"})}),(0,s.jsxs)(n.td,{children:["Enable code needed by unit tests, eg. connection overriding in the ",(0,s.jsx)(n.code,{children:"Orm::Tiny::Model"}),".",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Defined when ",(0,s.jsx)(n.a,{href:"#build_tests",children:(0,s.jsx)(n.code,{children:"build_tests"})})," ",(0,s.jsx)("small",{children:"(qmake)"})," / ",(0,s.jsx)(n.a,{href:"#BUILD_TESTS",children:(0,s.jsx)(n.code,{children:"BUILD_TESTS"})})," ",(0,s.jsx)("small",{children:"(cmake)"})," configuration ",(0,s.jsx)(n.code,{children:"build option"})," is enabled."]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINYORM_DISABLE_THREAD_LOCAL"})}),(0,s.jsxs)(n.td,{children:["Remove all ",(0,s.jsx)(n.a,{href:"https://en.cppreference.com/w/c/language/storage_duration",children:(0,s.jsx)(n.code,{children:"thread_local"})})," storage duration specifiers, it disables multi-threading support.",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Defined when ",(0,s.jsx)(n.a,{href:"#disable_thread_local",children:(0,s.jsx)(n.code,{children:"disable_thread_local"})})," ",(0,s.jsx)("small",{children:"(qmake)"})," / ",(0,s.jsx)(n.a,{href:"#DISABLE_THREAD_LOCAL",children:(0,s.jsx)(n.code,{children:"DISABLE_THREAD_LOCAL"})})," ",(0,s.jsx)("small",{children:"(cmake)"})," configuration ",(0,s.jsx)(n.code,{children:"build option"})," is enabled."]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINYTOM_MIGRATIONS_DIR"})}),(0,s.jsxs)(n.td,{children:["Default migrations path for the ",(0,s.jsx)(n.code,{children:"make:migration"})," command, can be an absolute or relative path (to the ",(0,s.jsx)("abbr",{title:"Current working directory",children:"pwd"}),").",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Default value: ",(0,s.jsx)(n.code,{children:"database/migrations"})," ",(0,s.jsx)("small",{children:"(relative to the pwd)"})]}),(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Defined by ",(0,s.jsx)(n.a,{href:"#TOM_MIGRATIONS_DIR",children:(0,s.jsx)(n.code,{children:"TOM_MIGRATIONS_DIR"})})," ",(0,s.jsx)("small",{children:"(cmake)"})," configuration build option.",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["(qmake note) You can use ",(0,s.jsx)(n.code,{children:'DEFINES += TINYTOM_MIGRATIONS_DIR="\\"database/migrations\\""'})," on the command-line or set it in the ",(0,s.jsx)(n.strong,{children:"main"})," ",(0,s.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/conf.pri.example#L65-L70",children:(0,s.jsx)(n.code,{children:"conf.pri"})})," file."]})]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINYTOM_MODELS_DIR"})}),(0,s.jsxs)(n.td,{children:["Default models path for the ",(0,s.jsx)(n.code,{children:"make:model"})," command, can be an absolute or relative path (to the ",(0,s.jsx)("abbr",{title:"Current working directory",children:"pwd"}),").",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Default value: ",(0,s.jsx)(n.code,{children:"database/models"})," ",(0,s.jsx)("small",{children:"(relative to the pwd)"})]}),(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Defined by ",(0,s.jsx)(n.a,{href:"#TOM_MODELS_DIR",children:(0,s.jsx)(n.code,{children:"TOM_MODELS_DIR"})})," ",(0,s.jsx)("small",{children:"(cmake)"})," configuration build option.",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["(qmake note) You can use ",(0,s.jsx)(n.code,{children:'DEFINES += TINYTOM_MODELS_DIR="\\"database/models\\""'})," on the command-line or set it in the ",(0,s.jsx)(n.strong,{children:"main"})," ",(0,s.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/conf.pri.example#L72-L73",children:(0,s.jsx)(n.code,{children:"conf.pri"})})," file."]})]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINYTOM_SEEDERS_DIR"})}),(0,s.jsxs)(n.td,{children:["Default seeders path for the ",(0,s.jsx)(n.code,{children:"make:seeder"})," command, can be an absolute or relative path (to the ",(0,s.jsx)("abbr",{title:"Current working directory",children:"pwd"}),").",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Default value: ",(0,s.jsx)(n.code,{children:"database/seeders"})," ",(0,s.jsx)("small",{children:"(relative to the pwd)"})]}),(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Defined by ",(0,s.jsx)(n.a,{href:"#TOM_SEEDERS_DIR",children:(0,s.jsx)(n.code,{children:"TOM_SEEDERS_DIR"})})," ",(0,s.jsx)("small",{children:"(cmake)"})," configuration build option.",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["(qmake note) You can use ",(0,s.jsx)(n.code,{children:'DEFINES += TINYTOM_SEEDERS_DIR="\\"database/seeders\\""'})," on the command-line or set it in the ",(0,s.jsx)(n.strong,{children:"main"})," ",(0,s.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/conf.pri.example#L75-L76",children:(0,s.jsx)(n.code,{children:"conf.pri"})})," file."]})]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINYORM_USING_PCH"})}),(0,s.jsxs)(n.td,{children:["Defined if building with precompiled headers.",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Controlled by ",(0,s.jsx)(n.a,{href:"#qmake-precompile_header",children:(0,s.jsx)(n.code,{children:"qmake"})})," / ",(0,s.jsx)(n.a,{href:"#CMAKE_DISABLE_PRECOMPILE_HEADERS",children:(0,s.jsx)(n.code,{children:"CMake"})}),"."]})]})]})]})]})})}),"\n",(0,s.jsx)(n.h2,{id:"building-with-cmake",children:"Building with CMake"}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["If something is not clear, you can still look at GitHub Action ",(0,s.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/tree/main/.github/workflows",children:(0,s.jsx)(n.code,{children:"workflows"})})," how a building is done."]})}),"\n",(0,s.jsxs)(n.p,{children:["First, create a basic folder structure and then clone the ",(0,s.jsx)(n.code,{children:"TinyORM"})," project."]}),"\n",(0,s.jsxs)(o.A,{groupId:y.vf,children:[(0,s.jsx)(t.A,{value:y.b,label:y.ux,children:(0,s.jsx)(c.A,{className:"language-powershell",children:`cd ${(0,x.Sn)(y.b)}\nmkdir ${(0,x.np)()}/TinyORM/TinyORM-builds-cmake/build-debug\n\ncd ${(0,x.np)()}/TinyORM\ngit clone git@github.com:silverqx/TinyORM.git`})}),(0,s.jsx)(t.A,{value:y.xj,label:y.gg,children:(0,s.jsx)(c.A,{className:"language-bash",children:`cd ${(0,x.Sn)(y.xj)}\nmkdir -p ${(0,x.np)()}/TinyORM/TinyORM-builds-cmake/build-debug\n\ncd ${(0,x.np)()}/TinyORM\ngit clone git@github.com:silverqx/TinyORM.git`})})]}),"\n",(0,s.jsxs)(n.h3,{id:"configure-and-build-cmake",children:["Configure & Build ",(0,s.jsx)("small",{children:"(cmake)"})]}),"\n",(0,s.jsxs)(n.p,{children:["Now you are ready to configure the ",(0,s.jsx)(n.code,{children:"TinyORM"})," library."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"cd TinyORM-builds-cmake/build-debug\n"})}),"\n",(0,s.jsxs)(o.A,{groupId:y.vf,children:[(0,s.jsx)(t.A,{value:y.b,label:y.ux,children:(0,s.jsx)(c.A,{className:"language-powershell",children:`cmake.exe \`\n-S "${(0,x.OZ)(y.b)}/TinyORM/TinyORM" \`\n-B "${(0,x.OZ)(y.b)}/TinyORM/TinyORM-builds-cmake/build-debug" \`\n-G 'Ninja' \`\n-D CMAKE_BUILD_TYPE:STRING='Debug' \`\n-D CMAKE_TOOLCHAIN_FILE:FILEPATH="${(0,x.Sn)(y.b)}/vcpkg/scripts/buildsystems/vcpkg.cmake" \`\n-D CMAKE_CXX_SCAN_FOR_MODULES:BOOL=OFF \`\n-D CMAKE_INSTALL_PREFIX:PATH="${(0,x.Sn)(y.b)}/tmp/TinyORM" \`\n-D BUILD_TESTS:BOOL=OFF \`\n-D MATCH_EQUAL_EXPORTED_BUILDTREE:BOOL=ON \`\n-D MYSQL_PING:BOOL=OFF \`\n-D TOM:BOOL=ON \`\n-D TOM_EXAMPLE:BOOL=OFF \`\n-D VERBOSE_CONFIGURE:BOOL=ON`})}),(0,s.jsx)(t.A,{value:y.xj,label:y.gg,children:(0,s.jsx)(c.A,{className:"language-bash",children:`cmake \\\n-S "${(0,x.OZ)(y.xj)}/TinyORM/TinyORM" \\\n-B "${(0,x.OZ)(y.xj)}/TinyORM/TinyORM-builds-cmake/build-debug" \\\n-G 'Ninja' \\\n-D CMAKE_BUILD_TYPE:STRING='Debug' \\\n-D CMAKE_TOOLCHAIN_FILE:FILEPATH="${(0,x.Sn)(y.xj)}/vcpkg/scripts/buildsystems/vcpkg.cmake" \\\n-D CMAKE_CXX_SCAN_FOR_MODULES:BOOL=OFF \\\n-D CMAKE_INSTALL_PREFIX:PATH="${(0,x.Sn)(y.xj)}/tmp/TinyORM" \\\n-D VERBOSE_CONFIGURE:BOOL=ON \\\n-D BUILD_TESTS:BOOL=OFF \\\n-D MYSQL_PING:BOOL=OFF \\\n-D TOM:BOOL=ON \\\n-D TOM_EXAMPLE:BOOL=OFF \\\n-D MATCH_EQUAL_EXPORTED_BUILDTREE:BOOL=ON`})})]}),"\n",(0,s.jsxs)(n.h5,{id:"cmake-strict_mode-option",children:["CMake ",(0,s.jsx)(n.code,{children:"STRICT_MODE"})," option"]}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"STRICT_MODE"})," ",(0,s.jsx)(n.code,{children:"CMake"})," configuration option was added in ",(0,s.jsx)(n.code,{children:"TinyORM"})," ",(0,s.jsx)(n.code,{children:"v0.37.3"}),". This option was added to avoid the propagation of aggressive strict warning compiler/linker options and Qt definitions from the ",(0,s.jsx)(n.code,{children:"TinyORM"})," library to user code through the ",(0,s.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/cmake/CommonModules/TinyCommon.cmake",children:(0,s.jsx)(n.code,{children:"TinyOrm::CommonConfig"})})," interface library."]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.code,{children:"TinyORM"})," uses the strictest warning level options, virtually anything that can be enabled is enabled to produce a better code. I highly recommend enabling this option to produce better code and to follow good practices. It also helps to follow the ",(0,s.jsx)(n.code,{children:"ISOCPP"})," ",(0,s.jsx)(n.a,{href:"https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines",children:"C++ Core Guidelines"})," standards."]}),"\n",(0,s.jsxs)(n.p,{children:["If you want to enable these strict warning options in your code, you can enable the ",(0,s.jsx)(n.code,{children:"STRICT_MODE"})," ",(0,s.jsx)(n.code,{children:"CMake"})," configuration option and they will be propagated to your code. You can also enabled it globally using the ",(0,s.jsx)(n.code,{children:"TINYORM_STRICT_MODE"})," environment variable, and the value of this environment variable will be picked up during initial CMake configuration as the default value for the ",(0,s.jsx)(n.code,{children:"STRICT_MODE"})," ",(0,s.jsx)(n.code,{children:"CMake"})," configuration option."]}),"\n",(0,s.jsxs)(n.p,{children:["You can achieve the same result by manually linking against the ",(0,s.jsx)(n.code,{children:"TinyOrm::CommonConfig"})," interface library when the ",(0,s.jsx)(n.code,{children:"STRICT_MODE"})," is set to ",(0,s.jsx)(n.code,{children:"OFF"}),"."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cmake",children:"target_link_libraries( PRIVATE TinyOrm::CommonConfig)\n"})}),"\n",(0,s.jsx)(n.admonition,{type:"info",children:(0,s.jsxs)(n.p,{children:["The recommended way is to set the ",(0,s.jsx)(n.code,{children:"TINYORM_STRICT_MODE"})," environment variable to ",(0,s.jsx)(n.code,{children:"1"})," or ",(0,s.jsx)(n.code,{children:"ON"}),"."]})}),"\n",(0,s.jsx)(n.h4,{id:"build-tinyorm",children:"Build TinyORM"}),"\n",(0,s.jsx)(n.p,{children:"And build. You don't have to install it, you can use the build tree directly if you want."}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"cmake --build . --target all\ncmake --install .\n"})}),"\n",(0,s.jsx)(n.p,{children:"Or build and install in one step."}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"cmake --build . --target install\n"})}),"\n",(0,s.jsx)(n.admonition,{type:"info",children:(0,s.jsxs)(n.p,{children:["CMake multi-config generators like ",(0,s.jsx)(n.code,{children:"Ninja Multi-Config"})," or ",(0,s.jsx)(n.code,{children:"Visual Studio 16 2019"})," are also supported."]})}),"\n",(0,s.jsx)(n.h3,{id:"cmake-build-options",children:"CMake build options"}),"\n",(0,s.jsx)("div",{className:"apitable-build-options",children:(0,s.jsx)(l.A,{children:(0,s.jsxs)(n.table,{children:[(0,s.jsx)(n.thead,{children:(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.th,{children:"Option Name"}),(0,s.jsx)(n.th,{children:"Default"}),(0,s.jsx)(n.th,{children:"Description"})]})}),(0,s.jsxs)(n.tbody,{children:[(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"BUILD_DRIVERS"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Build ",(0,s.jsx)(n.a,{href:"/tinydrivers/getting-started",children:"TinyDrivers"})," SQL database drivers (core/common code; replaces QtSql module)."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"BUILD_MYSQL_DRIVER"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Build ",(0,s.jsx)(n.code,{children:"TinyDrivers"})," MySQL database driver.",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Available when: ",(0,s.jsx)(n.code,{children:"BUILD_DRIVERS"})]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"BUILD_SHARED_LIBS"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"ON"})}),(0,s.jsx)(n.td,{children:"Build as a shared/static library."})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"BUILD_TESTS"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsx)(n.td,{children:"Build TinyORM unit tests."})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"BUILD_TREE_DEPLOY"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"ON"})}),(0,s.jsxs)(n.td,{children:["Copy ",(0,s.jsx)(n.code,{children:"TinyDrivers"})," and ",(0,s.jsx)(n.code,{children:"TinyMySql"})," libraries to the root of the build tree."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"DRIVERS_TYPE"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"Shared"})}),(0,s.jsxs)(n.td,{children:["How to build and link against ",(0,s.jsx)(n.code,{children:"TinyDrivers"})," SQL database drivers.",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["The ",(0,s.jsx)(n.code,{children:"Static"})," value will be select by default when the ",(0,s.jsx)(n.code,{children:"BUILD_SHARED_LIBS"})," is ",(0,s.jsx)(n.code,{children:"OFF"}),".",(0,s.jsx)("br",{}),"Supported values: ",(0,s.jsx)(n.a,{href:"/tinydrivers/getting-started#the-shared-library-build",children:(0,s.jsx)(n.code,{children:"Shared"})}),", ",(0,s.jsx)(n.a,{href:"/tinydrivers/getting-started#the-loadable-sql-drivers-build",children:(0,s.jsx)(n.code,{children:"Loadable"})}),", and ",(0,s.jsx)(n.a,{href:"/tinydrivers/getting-started#the-static-build",children:(0,s.jsx)(n.code,{children:"Static"})}),(0,s.jsx)("br",{}),"Available when: ",(0,s.jsx)(n.code,{children:"BUILD_DRIVERS AND BUILD_SHARED_LIBS"})]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"INLINE_CONSTANTS"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Use inline constants instead of extern constants in the ",(0,s.jsx)(n.code,{children:"shared build"}),".",(0,s.jsx)("br",{}),(0,s.jsx)(n.code,{children:"OFF"})," is highly recommended for the ",(0,s.jsx)(n.code,{children:"shared build"}),";",(0,s.jsx)("br",{}),"is always ",(0,s.jsx)(n.code,{children:"ON"})," for the ",(0,s.jsx)(n.code,{children:"static build"}),".",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Available when: ",(0,s.jsx)(n.code,{children:"BUILD_SHARED_LIBS"})]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"MSVC_RUNTIME_DYNAMIC"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"ON"})}),(0,s.jsxs)(n.td,{children:["Use MSVC dynamic runtime library (",(0,s.jsx)(n.code,{children:"-MD"}),") instead of static (",(0,s.jsx)(n.code,{children:"-MT"}),"), also considers a Debug configuration (",(0,s.jsx)(n.code,{children:"-MTd"}),", ",(0,s.jsx)(n.code,{children:"-MDd"}),").",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Available when: ",(0,s.jsx)(n.code,{children:"MSVC AND NOT DEFINED CMAKE_MSVC_RUNTIME_LIBRARY"})]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"MYSQL_PING"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Enable ",(0,s.jsx)(n.code,{children:"Orm::MySqlConnection::pingDatabase()"})," method."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"ORM"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"ON"})}),(0,s.jsxs)(n.td,{children:["Controls the compilation of all ",(0,s.jsx)(n.code,{children:"ORM-related"})," source code, when this option is ",(0,s.jsx)(n.code,{children:"disabled"}),", then only the ",(0,s.jsx)(n.code,{children:"query builder"})," without ",(0,s.jsx)(n.code,{children:"ORM"})," is compiled. Also excludes ",(0,s.jsx)(n.code,{children:"ORM-related"})," unit tests."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"STRICT_MODE"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Controls propagation of strict compiler/linker options and Qt definitions using the ",(0,s.jsx)(n.code,{children:"TinyOrm::CommonConfig"})," interface library to the user code.",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["(highly recommended; can also be set with the ",(0,s.jsx)(n.code,{children:"TINYORM_STRICT_MODE"})," environment variable; described ",(0,s.jsx)(n.a,{href:"#cmake-strict_mode-option",children:"here"}),")"]}),"."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TOM"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"ON"})}),(0,s.jsxs)(n.td,{children:["Controls the compilation of all ",(0,s.jsx)(n.code,{children:"Tom-related"})," source code, when this option is ",(0,s.jsx)(n.code,{children:"disabled"}),", then it also excludes ",(0,s.jsx)(n.code,{children:"Tom-related"})," unit tests."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TOM_EXAMPLE"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Build the ",(0,s.jsx)("abbr",{title:"TinyORM Migrations",children:(0,s.jsx)(n.code,{children:"tom"})})," console application example."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TOM_MIGRATIONS_DIR"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"-"})}),(0,s.jsxs)(n.td,{children:["Default migrations path for the ",(0,s.jsx)(n.code,{children:"make:migration"})," command, can be an absolute or relative path (to the ",(0,s.jsx)("abbr",{title:"Current working directory",children:"pwd"}),").",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Default value: ",(0,s.jsx)(n.code,{children:"database/migrations"})," ",(0,s.jsx)("small",{children:"(relative to the pwd)"})]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TOM_MODELS_DIR"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"-"})}),(0,s.jsxs)(n.td,{children:["Default models path for the ",(0,s.jsx)(n.code,{children:"make:model"})," command, can be an absolute or relative path (to the ",(0,s.jsx)("abbr",{title:"Current working directory",children:"pwd"}),").",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Default value: ",(0,s.jsx)(n.code,{children:"database/models"})," ",(0,s.jsx)("small",{children:"(relative to the pwd)"})]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TOM_SEEDERS_DIR"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"-"})}),(0,s.jsxs)(n.td,{children:["Default seeders path for the ",(0,s.jsx)(n.code,{children:"make:seeder"})," command, can be an absolute or relative path (to the ",(0,s.jsx)("abbr",{title:"Current working directory",children:"pwd"}),").",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Default value: ",(0,s.jsx)(n.code,{children:"database/seeders"})," ",(0,s.jsx)("small",{children:"(relative to the pwd)"})]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"VERBOSE_CONFIGURE"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Show information about ",(0,s.jsx)(n.code,{children:"PACKAGES_FOUND"})," / ",(0,s.jsx)(n.code,{children:"PACKAGES_NOT_FOUND"})," in the CMake configure output."]})]})]})]})})}),"\n",(0,s.jsxs)(n.p,{children:["Advanced ",(0,s.jsx)(n.code,{children:"TinyORM"})," options."]}),"\n",(0,s.jsx)("div",{className:"apitable-build-options",children:(0,s.jsx)(l.A,{children:(0,s.jsxs)(n.table,{children:[(0,s.jsx)(n.thead,{children:(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.th,{children:"Option Name"}),(0,s.jsx)(n.th,{children:"Default"}),(0,s.jsx)(n.th,{children:"Description"})]})}),(0,s.jsxs)(n.tbody,{children:[(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"DISABLE_THREAD_LOCAL"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Remove all ",(0,s.jsx)(n.a,{href:"https://en.cppreference.com/w/c/language/storage_duration",children:(0,s.jsx)(n.code,{children:"thread_local"})})," storage duration specifiers, it disables multi-threading support."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)("small",{children:(0,s.jsx)(n.code,{children:"MATCH_EQUAL_EXPORTED_BUILDTREE"})})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Exported package configuration from the build tree is considered to match only when ",(0,s.jsx)(n.code,{children:"the build type"})," of application/library that is linking against the TinyORM library ",(0,s.jsx)(n.strong,{children:"is equal"}),".",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Available when:",(0,s.jsx)("br",{}),(0,s.jsx)(n.code,{children:"CMAKE_EXPORT_PACKAGE_REGISTRY AND NOT TINY_IS_MULTI_CONFIG"})]})]})]})]})]})})}),"\n",(0,s.jsxs)(n.p,{children:["Important ",(0,s.jsx)(n.code,{children:"CMake"})," options."]}),"\n",(0,s.jsx)("div",{className:"apitable-build-options",children:(0,s.jsx)(l.A,{children:(0,s.jsxs)(n.table,{children:[(0,s.jsx)(n.thead,{children:(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.th,{children:"Option Name"}),(0,s.jsx)(n.th,{children:"Default"}),(0,s.jsx)(n.th,{children:"Description"})]})}),(0,s.jsxs)(n.tbody,{children:[(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"CMAKE_DISABLE_PRECOMPILE_HEADERS"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsx)(n.td,{children:"Disable precompiled headers."})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"CMAKE_CXX_COMPILER"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"auto"})}),(0,s.jsxs)(n.td,{children:["The full path to the ",(0,s.jsx)(n.code,{children:"C++"})," compiler."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"CMAKE_CXX_COMPILER_LAUNCHER"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"-"})}),(0,s.jsxs)(n.td,{children:["Default compiler launcher to use for the ",(0,s.jsx)(n.code,{children:"C++"})," compiler.",(0,s.jsx)("br",{}),"Can be used to enable ",(0,s.jsx)(n.code,{children:"ccache"}),", eg. ",(0,s.jsx)(n.code,{children:"ccache.exe"})," on ",(0,s.jsx)(n.code,{children:"MinGW"})," or ",(0,s.jsx)(n.code,{children:"/usr/bin/ccache"})," on ",(0,s.jsx)(n.code,{children:"Linux"}),"."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"CMAKE_EXPORT_PACKAGE_REGISTRY"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Enable the ",(0,s.jsx)(n.code,{children:"export(TinyOrm)"})," command.",(0,s.jsx)("br",{}),(0,s.jsx)(n.code,{children:"TinyORM"})," doesn't set this variable by default. Its initial value is taken from the ",(0,s.jsx)(n.code,{children:"TINYORM_EXPORT_PACKAGE_REGISTRY"})," environment variable if not already defined."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)("small",{children:(0,s.jsx)(n.code,{children:"CMAKE_INTERPROCEDURAL_OPTIMIZATION"})})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Enable ",(0,s.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/Interprocedural_optimization",children:"Link time code optimization"})," (",(0,s.jsx)(n.a,{href:"https://wiki.gentoo.org/wiki/LTO",children:"LTO"}),").",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Works on all ",(0,s.jsx)(n.a,{href:"/supported-compilers",children:"Supported Compilers"}),"."]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"CMAKE_VERBOSE_MAKEFILE"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsx)(n.td,{children:"Enable verbose output from Makefile builds."})]})]})]})})}),"\n",(0,s.jsxs)(n.h3,{id:"consume-tinyorm-library-cmake",children:["Consume TinyOrm library ",(0,s.jsx)("small",{children:"(cmake)"})]}),"\n",(0,s.jsxs)(n.p,{children:["In your application or library ",(0,s.jsx)(n.code,{children:"CMakeLists.txt"})," file add following ",(0,s.jsx)(n.code,{children:"find_package()"})," call."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cmake",metastring:"title='CMakeLists.txt'",children:"find_package(TinyOrm 0.37.3 CONFIG REQUIRED)\n"})}),"\n",(0,s.jsxs)(n.p,{children:["If the ",(0,s.jsx)(n.code,{children:"TinyORM"})," build tree is not exported to the CMake's ",(0,s.jsx)(n.a,{href:"https://cmake.org/cmake/help/latest/manual/cmake-packages.7.html#user-package-registry",children:(0,s.jsx)(n.code,{children:"User Package Registry"})})," then also add the ",(0,s.jsx)(n.code,{children:"TinyORM"})," build tree or ",(0,s.jsx)(n.code,{children:"CMAKE_INSTALL_PREFIX"})," folder to the ",(0,s.jsx)(n.code,{children:"CMAKE_PREFIX_PATH"}),", so CMake can find TinyORM's package configuration file during ",(0,s.jsx)(n.code,{children:"find_package(TinyOrm)"})," call."]}),"\n",(0,s.jsxs)(o.A,{groupId:y.vf,children:[(0,s.jsx)(t.A,{value:y.b,label:`cmake (${y.b})`,children:(0,s.jsx)(c.A,{className:"language-cmake",children:`# build tree\nlist(APPEND CMAKE_PREFIX_PATH "${(0,x.nC)(y.b,(0,x.OZ)(y.b))}/TinyORM/TinyORM-builds-cmake/build-debug")\n\n# installation folder - CMAKE_INSTALL_PREFIX\nlist(APPEND CMAKE_PREFIX_PATH "${(0,x.nC)(y.b,(0,x.Sn)(y.b))}/tmp/TinyORM")`})}),(0,s.jsx)(t.A,{value:y.xj,label:`cmake (${y.xj})`,children:(0,s.jsx)(c.A,{className:"language-cmake",children:`# build tree\nlist(APPEND CMAKE_PREFIX_PATH "${(0,x.nC)(y.xj,(0,x.OZ)(y.xj))}/TinyORM/TinyORM-builds-cmake/build-debug")\n\n# installation folder - CMAKE_INSTALL_PREFIX\nlist(APPEND CMAKE_PREFIX_PATH "${(0,x.nC)(y.xj,(0,x.Sn)(y.xj))}/tmp/TinyORM")`})})]}),"\n",(0,s.jsxs)(n.p,{children:["Or as an alternative, you can set ",(0,s.jsx)(n.code,{children:"CMAKE_PREFIX_PATH"})," environment variable."]}),"\n",(0,s.jsx)(r.A,{id:"tinyorm-on-path-cmake"}),"\n",(0,s.jsxs)(n.p,{children:["As the last thing, do not forget to add ",(0,s.jsx)(n.code,{children:"TinyOrm0d.dll"})," on the path on Windows and on the ",(0,s.jsx)(n.code,{children:"LD_LIBRARY_PATH"})," on Linux, so your application can find it during execution."]}),"\n",(0,s.jsxs)(o.A,{groupId:y.vf,name:"tinyorm-on-path",children:[(0,s.jsx)(t.A,{value:y.b,label:y.ux,children:(0,s.jsx)(c.A,{className:"language-powershell",children:`$env:Path = "${(0,x.OZ)(y.b,!1)}\\TinyORM\\TinyORM-builds-cmake\\build-debug;" + $env:Path`})}),(0,s.jsx)(t.A,{value:y.xj,label:y.gg,children:(0,s.jsx)(c.A,{className:"language-bash",children:`export LD_LIBRARY_PATH=${(0,x.OZ)(y.xj)}/TinyORM/TinyORM-builds-cmake/build-debug\${PATH:+:}$PATH`})})]}),"\n",(0,s.jsxs)(n.p,{children:["Now you can try the ",(0,s.jsx)(n.a,{href:"/building/hello-world#hello-world-with-cmake",children:(0,s.jsx)(n.code,{children:"HelloWorld CMake"})})," example."]}),"\n",(0,s.jsx)(n.admonition,{type:"info",children:(0,s.jsxs)(n.p,{children:["You can also try the ",(0,s.jsx)(n.a,{href:"/building/hello-world#fetchcontent",children:(0,s.jsx)(n.code,{children:"FetchContent"})})," method to fastly link against the ",(0,s.jsx)(n.code,{children:"TinyORM"})," library."]})}),"\n",(0,s.jsx)(n.h2,{id:"building-with-qmake",children:"Building with qmake"}),"\n",(0,s.jsxs)(n.p,{children:["First, create a basic folder structure and then clone the ",(0,s.jsx)(n.code,{children:"TinyORM"})," project."]}),"\n",(0,s.jsxs)(o.A,{groupId:y.vf,children:[(0,s.jsx)(t.A,{value:y.b,label:y.ux,children:(0,s.jsx)(c.A,{className:"language-powershell",children:`cd ${(0,x.Sn)(y.b)}\nmkdir ${(0,x.np)()}/TinyORM/TinyORM-builds-qmake\n\ncd ${(0,x.np)()}/TinyORM\ngit clone git@github.com:silverqx/TinyORM.git`})}),(0,s.jsx)(t.A,{value:y.xj,label:y.gg,children:(0,s.jsx)(c.A,{className:"language-bash",children:`cd ${(0,x.Sn)(y.xj)}\nmkdir -p ${(0,x.np)()}/TinyORM/TinyORM-builds-qmake\n\ncd ${(0,x.np)()}/TinyORM\ngit clone git@github.com:silverqx/TinyORM.git`})})]}),"\n",(0,s.jsx)(n.h3,{id:"install-dependencies",children:"Install dependencies"}),"\n",(0,s.jsxs)(n.p,{children:["With the ",(0,s.jsx)(n.code,{children:"qmake"})," build system, you have to install ",(0,s.jsx)(n.code,{children:"TinyORM"})," dependencies manually. We will use the ",(0,s.jsx)(n.code,{children:"vcpkg"})," package manager."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"cd ../../vcpkg\n\nvcpkg search range-v3\nvcpkg search tabulate\nvcpkg install range-v3 tabulate\nvcpkg list\n"})}),"\n",(0,s.jsxs)(n.p,{children:["On ",(0,s.jsx)(n.code,{children:"Linux"}),", you can install the ",(0,s.jsx)(n.code,{children:"range-v3"})," library and some other ",(0,s.jsx)(n.a,{href:"/dependencies#install-dependencies",children:"dependencies"})," with the package manager."]}),"\n",(0,s.jsxs)(n.h3,{id:"configure-and-build-qmake",children:["Configure & Build ",(0,s.jsx)("small",{children:"(qmake)"})]}),"\n",(0,s.jsx)(n.h4,{id:"open-qtcreator-ide",children:"Open QtCreator IDE"}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["I recommend creating a new ",(0,s.jsx)(n.a,{href:"https://doc.qt.io/qtcreator/creator-project-managing-sessions.html",children:(0,s.jsx)(n.code,{children:"Session"})})," in the ",(0,s.jsx)(n.code,{children:"QtCreator"}),", this way you will have all the examples in one place and as a bonus, everything will be in the same place when you close and reopen ",(0,s.jsx)(n.code,{children:"QtCreator IDE"}),". You can name it ",(0,s.jsx)(n.code,{children:"tinyorm.org"})," or ",(0,s.jsx)(n.code,{children:"TinyORM examples"}),", it is up to you."]})}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["If you are using sessions, you can use a single ",(0,s.jsx)(n.code,{children:"clangd"})," instance for all projects in this session in the ",(0,s.jsx)(n.code,{children:"QtCreator IDE"}),". One significant advantage of this method is that the ",(0,s.jsx)(n.code,{children:".qtc_clangd/"})," folder will not be created in the build folder, but will be stored globally in the Roaming profile. You can enable it in the ",(0,s.jsx)(n.code,{children:"Settings"})," - ",(0,s.jsx)(n.code,{children:"C++"})," - ",(0,s.jsx)(n.code,{children:"Clangd"})," - ",(0,s.jsx)(n.code,{children:"Sessions with a single clangd instance"}),"."]})}),"\n",(0,s.jsx)(n.h4,{id:"configure-tinyorm",children:"Configure TinyORM"}),"\n",(0,s.jsxs)(n.p,{children:["Now you are ready to configure the ",(0,s.jsx)(n.code,{children:"TinyORM"})," library. There are two ways how to configure the ",(0,s.jsx)(n.code,{children:"TinyORM"})," library and it's the new ",(0,s.jsx)(n.code,{children:"Auto-configure"})," feature added in ",(0,s.jsx)(n.code,{children:"TinyORM"})," ",(0,s.jsx)(n.code,{children:"v0.34.0"})," using the ",(0,s.jsx)(n.code,{children:".env"})," files and the old way using the ",(0,s.jsx)(n.code,{children:"conf.pri"})," files."]}),"\n",(0,s.jsx)(n.h5,{id:"auto-configuration-and-tiny_dotenv",children:"Auto-configuration and tiny_dotenv"}),"\n",(0,s.jsxs)(n.p,{children:["This is the new recommended method to auto-configure TinyORM's ",(0,s.jsx)(n.code,{children:"qmake"})," build system and also the dependencies, it was added in ",(0,s.jsx)(n.code,{children:"TinyORM"})," ",(0,s.jsx)(n.code,{children:"v0.34.0"}),". You need to copy the prepared ",(0,s.jsx)(n.code,{children:".env.(win32|unix|mingw).example"})," file to the ",(0,s.jsx)(n.code,{children:".env.(win32|unix|mingw)"}),". One ",(0,s.jsx)(n.code,{children:".env"})," example file is prepared for each supported platform."]}),"\n",(0,s.jsxs)(n.p,{children:["All prepared ",(0,s.jsx)(n.code,{children:".env.(win32|unix|mingw).example"})," files are simple and clear. You can also create a common ",(0,s.jsx)(n.code,{children:".env"})," file that is included before the platform-specific ",(0,s.jsx)(n.code,{children:".env.(win32|unix|mingw)"})," files."]}),"\n",(0,s.jsxs)(o.A,{groupId:y.vf,children:[(0,s.jsx)(t.A,{value:y.b,label:y.ux,children:(0,s.jsx)(c.A,{className:"language-powershell",children:`cd ${(0,x.OZ)(y.b)}/TinyORM/TinyORM\n\ncp .env.win32.example .env.win32`})}),(0,s.jsx)(t.A,{value:y.xj,label:y.gg,children:(0,s.jsx)(c.A,{className:"language-bash",children:`cd ${(0,x.OZ)(y.xj)}/TinyORM/TinyORM\n\ncp .env.unix.example .env.unix`})})]}),"\n",(0,s.jsxs)(n.p,{children:["And that is all, if you have correctly set all ",(0,s.jsx)(n.code,{children:"qmake"})," variables in this ",(0,s.jsx)(n.code,{children:".env.(win32|unix|mingw)"})," file or you have correctly set environment variables, then the ",(0,s.jsx)(n.code,{children:"qmake"})," build system should be able to ",(0,s.jsx)(n.code,{children:"auto-detect"})," all dependencies . \ud83d\udd25"]}),"\n",(0,s.jsx)(n.admonition,{type:"info",children:(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.a,{href:"#auto-configuration-internals",children:(0,s.jsx)(n.code,{children:"Auto-configuration"})})," and ",(0,s.jsx)(n.a,{href:"#environment-files",children:(0,s.jsx)(n.code,{children:"Environment files"})})," internals are described at the end to make this section more clear."]})}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"Auto-configuration"})," feature can be turned off using the ",(0,s.jsx)(n.a,{href:"#disable_autoconf",children:(0,s.jsx)(n.code,{children:"disable_autoconf"})})," ",(0,s.jsx)(n.code,{children:"qmake"})," configuration option (eg. ",(0,s.jsx)(n.code,{children:"CONFIG*=disable_autoconf"}),")."]})}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"tiny_dotenv"})," feature can be turned off using the ",(0,s.jsx)(n.a,{href:"#disable_dotenv",children:(0,s.jsx)(n.code,{children:"disable_dotenv"})})," ",(0,s.jsx)(n.code,{children:"qmake"})," configuration option (eg. ",(0,s.jsx)(n.code,{children:"CONFIG*=disable_dotenv"}),")."]})}),"\n",(0,s.jsx)(n.h5,{id:"manual-configuration-confpri",children:"Manual configuration (conf.pri)"}),"\n",(0,s.jsxs)(n.p,{children:["This is the old method used before ",(0,s.jsx)(n.code,{children:"TinyORM"})," ",(0,s.jsx)(n.code,{children:"v0.34.0"}),". You need to copy the ",(0,s.jsx)(n.code,{children:"conf.pri.example"})," files to ",(0,s.jsx)(n.code,{children:"conf.pri"})," (there are four, one for every project or sub-project) and manually update the ",(0,s.jsx)(n.code,{children:"INCLUDEPATH"})," and ",(0,s.jsx)(n.code,{children:"LIBS"})," to configure TinyORM's ",(0,s.jsx)(n.code,{children:"qmake"})," build dependencies. This way you can override any ",(0,s.jsx)(n.code,{children:"qmake"})," build options or variables."]}),"\n",(0,s.jsxs)(n.p,{children:["To disable the ",(0,s.jsx)(n.a,{href:"#auto-configuration-internals",children:(0,s.jsx)(n.code,{children:"Auto-configuration"})})," feature you must define the ",(0,s.jsx)(n.a,{href:"#disable_autoconf",children:(0,s.jsx)(n.code,{children:"disable_autoconf"})})," ",(0,s.jsx)(n.code,{children:"qmake"})," configuration option (eg. ",(0,s.jsx)(n.code,{children:"CONFIG*=disable_autoconf"}),") because from ",(0,s.jsx)(n.code,{children:"TinyORM"})," ",(0,s.jsx)(n.code,{children:"v0.34.0"})," is the ",(0,s.jsx)(n.code,{children:"Auto-configuration"})," feature enabled by default."]}),"\n",(0,s.jsxs)(n.p,{children:["You can also remove all ",(0,s.jsx)(n.code,{children:".env"})," files or turn off the ",(0,s.jsx)(n.code,{children:"tiny_dotenv"})," feature using ",(0,s.jsx)(n.code,{children:"CONFIG*=disable_dotenv"}),". You can use them all at once if you want, ",(0,s.jsx)(n.code,{children:".env"})," and also ",(0,s.jsx)(n.code,{children:"conf.pri"})," files."]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.code,{children:"conf.pri"})," files are nicely commented on, so you can see what needs to be modified."]}),"\n",(0,s.jsxs)(o.A,{groupId:y.vf,children:[(0,s.jsx)(t.A,{value:y.b,label:y.ux,children:(0,s.jsx)(c.A,{className:"language-powershell",children:`cd ${(0,x.OZ)(y.b)}/TinyORM/TinyORM\n\ncp conf.pri.example conf.pri\ncp tests/conf.pri.example tests/conf.pri\ncp tests/testdata_tom/conf.pri.example tests/testdata_tom/conf.pri\ncp examples/tom/conf.pri.example examples/tom/conf.pri`})}),(0,s.jsx)(t.A,{value:y.xj,label:y.gg,children:(0,s.jsx)(c.A,{className:"language-bash",children:`cd ${(0,x.OZ)(y.xj)}/TinyORM/TinyORM\n\ncp conf.pri.example conf.pri\ncp tests/conf.pri.example tests/conf.pri\ncp tests/testdata_tom/conf.pri.example tests/testdata_tom/conf.pri\ncp examples/tom/conf.pri.example examples/tom/conf.pri`})})]}),"\n",(0,s.jsx)(n.admonition,{type:"info",children:(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.a,{href:"#manual-configuration-internals",children:(0,s.jsx)(n.code,{children:"Manual configuration"})})," internals are described at the end to make this section more clear."]})}),"\n",(0,s.jsx)(n.admonition,{type:"note",children:(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"Manual configuration"})," is still relevant if you have any non-standard installation of the ",(0,s.jsx)(n.code,{children:"vcpkg"})," or ",(0,s.jsx)(n.code,{children:"MySQL"})," and the ",(0,s.jsx)(n.code,{children:"Auto-configuration"})," feature fails."]})}),"\n",(0,s.jsx)(n.h5,{id:"opening-tinyormpro-main-project-file",children:"Opening TinyORM.pro (main project file)"}),"\n",(0,s.jsxs)(n.p,{children:["Now you can open the ",(0,s.jsx)(n.code,{children:"TinyORM.pro"})," project in the ",(0,s.jsx)(n.code,{children:"QtCreator IDE"}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["This will open the ",(0,s.jsx)(n.code,{children:"Configure Project"})," tab, select some kit and update build folder paths to meet our ",(0,s.jsx)(n.a,{href:"#folders-structure",children:"folders structure"})," or like you want."]}),"\n",(0,s.jsx)("img",{src:i(885).A,alt:"TinyORM - QtCreator - Configure Project",width:"760"}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["You can force the ",(0,s.jsx)(n.code,{children:"QtCreator"})," to generate a build folders structure as is described ",(0,s.jsx)(n.a,{href:"#qtcreator-default-build-directory",children:"above"}),"."]})}),"\n",(0,s.jsxs)(n.p,{children:["You are ready to configure build options, hit ",(0,s.jsx)("kbd",{children:"Ctrl"}),"+",(0,s.jsx)("kbd",{children:"5"})," to open ",(0,s.jsx)(n.code,{children:"Project Settings"})," tab and select ",(0,s.jsx)(n.code,{children:"Build"})," in the left sidebar to open the ",(0,s.jsx)(n.code,{children:"Build Settings"}),", it should look similar to the following picture."]}),"\n",(0,s.jsxs)(n.p,{children:["Disable ",(0,s.jsx)(n.code,{children:"QML debugging and profiling"})," and ",(0,s.jsx)(n.code,{children:"Qt Quick Compiler"}),", they are not used."]}),"\n",(0,s.jsx)("img",{src:i(7619).A,alt:"TinyORM - QtCreator - Build Settings",width:"760"}),"\n",(0,s.jsxs)(n.p,{children:["If you want to change some ",(0,s.jsx)(n.code,{children:"TinyORM"})," build options, you can pass them to the ",(0,s.jsx)(n.code,{children:"Build Steps"})," - ",(0,s.jsx)(n.code,{children:"qmake TinyORM.pro"})," - ",(0,s.jsx)(n.code,{children:"Additional arguments"})," input field. It can look like this."]}),"\n",(0,s.jsx)("img",{src:i(2721).A,alt:"TinyORM - QtCreator - Build Settings - Additional arguments",width:"660"}),"\n",(0,s.jsx)(n.h4,{id:"build-tinyorm-1",children:"Build TinyORM"}),"\n",(0,s.jsxs)(n.p,{children:["Everything is ready for build, you can press ",(0,s.jsx)("kbd",{children:"Ctrl"}),"+",(0,s.jsx)("kbd",{children:"b"})," to build the project."]}),"\n",(0,s.jsx)(n.h3,{id:"qmake-build-options",children:"qmake build options"}),"\n",(0,s.jsx)("div",{className:"apitable-build-options",children:(0,s.jsx)(l.A,{children:(0,s.jsxs)(n.table,{children:[(0,s.jsx)(n.thead,{children:(0,s.jsxs)(n.tr,{children:[(0,s.jsxs)(n.th,{children:[(0,s.jsx)(n.code,{children:"CONFIG"})," ",(0,s.jsx)("small",{children:"Option Name"})]}),(0,s.jsx)(n.th,{children:"Default"}),(0,s.jsx)(n.th,{children:"Description"})]})}),(0,s.jsxs)(n.tbody,{children:[(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"build_loadable_drivers"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Build ",(0,s.jsx)(n.a,{href:"/tinydrivers/getting-started",children:(0,s.jsx)(n.code,{children:"TinyDrivers"})})," as a shared library and SQL database drivers (eg. ",(0,s.jsx)(n.code,{children:"TinyMySql"}),") as shared libraries (",(0,s.jsx)(n.a,{href:"/tinydrivers/getting-started#the-loadable-sql-drivers-build",children:(0,s.jsx)(n.code,{children:"Loadable"})})," modules) that are loaded at runtime using ",(0,s.jsx)(n.code,{children:"LoadLibrary()"})," on Windows or ",(0,s.jsx)(n.code,{children:"dlopen()"})," on Linux."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"build_mysql_driver"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Build ",(0,s.jsx)(n.code,{children:"TinyDrivers"})," MySQL database driver.",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["It's enabled by default when ",(0,s.jsx)(n.code,{children:"build_shared_drivers"}),", ",(0,s.jsx)(n.code,{children:"build_loadable_drivers"}),", or ",(0,s.jsx)(n.code,{children:"build_static_drivers"})," is enabled.",(0,s.jsx)("br",{}),"Available when: ",(0,s.jsx)(n.code,{children:"build_shared_drivers"})," OR ",(0,s.jsx)(n.code,{children:"build_loadable_drivers"})," OR ",(0,s.jsx)(n.code,{children:"build_static_drivers"})]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"build_shared_drivers"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Build ",(0,s.jsx)(n.code,{children:"TinyDrivers"})," as a ",(0,s.jsx)(n.a,{href:"/tinydrivers/getting-started#the-shared-library-build",children:(0,s.jsx)(n.code,{children:"Shared"})})," library."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"build_static_drivers"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Build ",(0,s.jsx)(n.code,{children:"TinyDrivers"})," as a ",(0,s.jsx)(n.a,{href:"/tinydrivers/getting-started#the-static-build",children:(0,s.jsx)(n.code,{children:"Static"})})," library archive.",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["The ",(0,s.jsx)(n.code,{children:"build_static_drivers"})," ",(0,s.jsx)(n.code,{children:"qmake"})," configuration option will be select by default when the ",(0,s.jsx)(n.a,{href:"#qmake-static",children:(0,s.jsx)(n.code,{children:"CONFIG*=static"})})," is enabled."]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"build_tests"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsx)(n.td,{children:"Build TinyORM unit tests."})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"ccache"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"ON"})}),(0,s.jsxs)(n.td,{children:["Enable compiler cache. ",(0,s.jsx)(n.a,{href:"https://ccache.dev/",children:"Homepage"}),(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["It works on Windows and Unix systems. This option overrides qmake's ",(0,s.jsx)(n.code,{children:"ccache"})," option. It internally calls qmake's ",(0,s.jsx)(n.a,{href:"#qmake-ccache",children:(0,s.jsx)(n.code,{children:"ccache"})})," option on Unix and ",(0,s.jsx)(n.a,{href:"#tiny_ccache_win32",children:(0,s.jsx)(n.code,{children:"tiny_ccache_win32"})})," on Windows.",(0,s.jsx)("br",{}),"Reason: It allows using the same option on both OS-es.",(0,s.jsx)("br",{}),"See ",(0,s.jsx)(n.a,{href:"#ccache-support",children:"Ccache support"})," for more information."]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"disable_autoconf"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Disable the ",(0,s.jsx)(n.a,{href:"#auto-configuration-internals",children:(0,s.jsx)(n.code,{children:"Auto-configuration"})})," feature ",(0,s.jsxs)("small",{children:["(auto-configuration is enabled by default from ",(0,s.jsx)(n.code,{children:"TinyORM"})," ",(0,s.jsx)(n.code,{children:"v0.34.0"}),")"]}),"."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"disable_dotenv"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Disable the ",(0,s.jsx)(n.a,{href:"#environment-files",children:(0,s.jsx)(n.code,{children:"tiny_dotenv"})})," feature ",(0,s.jsxs)("small",{children:["(environment files are enabled by default from ",(0,s.jsx)(n.code,{children:"TinyORM"})," ",(0,s.jsx)(n.code,{children:"v0.34.0"}),")"]}),"."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"disable_thread_local"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Remove all ",(0,s.jsx)(n.a,{href:"https://en.cppreference.com/w/c/language/storage_duration",children:(0,s.jsx)(n.code,{children:"thread_local"})})," storage duration specifiers, it disables multi-threading support."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"disable_orm"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Controls the compilation of all ",(0,s.jsx)(n.code,{children:"ORM-related"})," source code, when this option is ",(0,s.jsx)(n.code,{children:"enabled"}),", then only the ",(0,s.jsx)(n.code,{children:"query builder"})," without ",(0,s.jsx)(n.code,{children:"ORM"})," is compiled. Also excludes ",(0,s.jsx)(n.code,{children:"ORM-related"})," unit tests."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"disable_tom"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Controls the compilation of all ",(0,s.jsx)(n.code,{children:"Tom-related"})," source code, when this option is ",(0,s.jsx)(n.code,{children:"disabled"}),", then it also excludes ",(0,s.jsx)(n.code,{children:"Tom-related"})," unit tests."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"extern_constants"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"ON"})}),(0,s.jsxs)(n.td,{children:["Use ",(0,s.jsx)(n.code,{children:"extern"})," constants instead of ",(0,s.jsx)(n.code,{children:"inline"})," constants in the ",(0,s.jsx)(n.code,{children:"shared build"}),".",(0,s.jsx)("br",{}),(0,s.jsx)(n.code,{children:"ON"})," is highly recommended for the ",(0,s.jsx)(n.code,{children:"shared build"})," ",(0,s.jsx)("small",{children:"(by default)"}),";",(0,s.jsx)("br",{}),"is always ",(0,s.jsx)(n.code,{children:"OFF"})," for the ",(0,s.jsx)(n.code,{children:"static build"}),".",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Available when: ",(0,s.jsx)("code",{children:"CONFIG(shared|dll):!inline_constants"})]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"inline_constants"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Use ",(0,s.jsx)(n.code,{children:"inline"})," constants instead of ",(0,s.jsx)(n.code,{children:"extern"})," constants in the ",(0,s.jsx)(n.code,{children:"shared build"}),".",(0,s.jsx)("br",{}),(0,s.jsx)(n.code,{children:"OFF"})," is highly recommended for the ",(0,s.jsx)(n.code,{children:"shared build"}),";",(0,s.jsx)("br",{}),"is always ",(0,s.jsx)(n.code,{children:"ON"})," for the ",(0,s.jsx)(n.code,{children:"static build"}),"."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"link_pkgconfig_off"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Link against ",(0,s.jsx)(n.code,{children:"mysqlclient"})," or ",(0,s.jsx)(n.code,{children:"libmariadb"})," with ",(0,s.jsx)(n.code,{children:"PKGCONFIG"}),".",(0,s.jsx)("br",{}),"Used only in the ",(0,s.jsx)(n.code,{children:"Unix"})," and ",(0,s.jsx)(n.code,{children:"MinGW"})," ",(0,s.jsx)(n.strong,{children:"shared"})," build ",(0,s.jsxs)("small",{children:["(exactly ",(0,s.jsx)("code",{children:"win32-g++|win32-clang-g++"}),")"]})," and when ",(0,s.jsx)(n.code,{children:"mysql_ping"})," is also defined to link against ",(0,s.jsx)(n.code,{children:"mysqlclient"})," or ",(0,s.jsx)(n.code,{children:"libmariadb"}),", ",(0,s.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/conf.pri.example#L132",children:"source code"}),".",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Available when: ",(0,s.jsx)(n.code,{children:"unix:mysql_ping"})," or ",(0,s.jsx)("code",{children:"(win32-g++|win32-clang-g++):mysql_ping:!static:!staticlib"})]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"mysql_ping"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Enable ",(0,s.jsx)(n.code,{children:"Orm::MySqlConnection::pingDatabase()"})," method."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"tiny_ccache_win32"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"ON"})}),(0,s.jsxs)(n.td,{children:["Enable compiler cache. ",(0,s.jsx)(n.a,{href:"https://ccache.dev/",children:"Homepage"}),(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["It works only on Windows systems. It works well with the MSYS2 ",(0,s.jsx)(n.code,{children:"g++"}),", ",(0,s.jsx)(n.code,{children:"clang++"}),", ",(0,s.jsx)(n.code,{children:"msvc"}),", and ",(0,s.jsx)(n.code,{children:"clang-cl"})," with ",(0,s.jsx)(n.code,{children:"msvc"}),". It replaces the ",(0,s.jsx)(n.code,{children:"-Zi"})," and ",(0,s.jsx)(n.code,{children:"-ZI"})," compiler options with the ",(0,s.jsx)(n.code,{children:"-Z7"})," for debug builds as the ",(0,s.jsx)(n.code,{children:"-Zi"})," and ",(0,s.jsx)(n.code,{children:"-ZI"})," compiler options are not supported (",(0,s.jsx)(n.a,{href:"https://github.com/ccache/ccache/issues/1040",children:"link"})," to the issue) and disables ",(0,s.jsx)(n.code,{children:"precompile_header"})," if ",(0,s.jsx)(n.code,{children:"ccache"})," ",(0,s.jsx)(n.code,{children:"<4.10"})," as they are not supported on Windows."]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"tom_example"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Build the ",(0,s.jsx)("abbr",{title:"TinyORM Migrations",children:(0,s.jsx)(n.code,{children:"tom"})})," console application example."]})]})]})]})})}),"\n",(0,s.jsxs)(n.p,{children:["Advanced ",(0,s.jsx)(n.code,{children:"TinyORM"})," options."]}),"\n",(0,s.jsx)("div",{className:"apitable-build-options",children:(0,s.jsx)(l.A,{children:(0,s.jsxs)(n.table,{children:[(0,s.jsx)(n.thead,{children:(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.th,{children:"Option Name"}),(0,s.jsx)(n.th,{children:"Default"}),(0,s.jsx)(n.th,{children:"Description"})]})}),(0,s.jsx)(n.tbody,{children:(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"ubsan"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Allows to enable ",(0,s.jsx)(n.a,{href:"https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html",children:"UBSan"})," sanitizer (Clang only)."]})]})})]})})}),"\n",(0,s.jsxs)(n.p,{children:["Important ",(0,s.jsx)(n.code,{children:"qmake"})," options."]}),"\n",(0,s.jsx)("div",{className:"apitable-build-options",children:(0,s.jsx)(l.A,{name:"qmake",children:(0,s.jsxs)(n.table,{children:[(0,s.jsx)(n.thead,{children:(0,s.jsxs)(n.tr,{children:[(0,s.jsxs)(n.th,{children:[(0,s.jsx)(n.code,{children:"CONFIG"})," ",(0,s.jsx)("small",{children:"Option Name"})]}),(0,s.jsx)(n.th,{children:"Default"}),(0,s.jsx)(n.th,{children:"Description"})]})}),(0,s.jsxs)(n.tbody,{children:[(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"ccache"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Enable compiler cache. ",(0,s.jsx)(n.a,{href:"https://ccache.dev/",children:"Homepage"}),(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["It works only on the Unix systems. It works well with ",(0,s.jsx)(n.code,{children:"g++"})," and ",(0,s.jsx)(n.code,{children:"clang++"})," and also supports precompiled headers. TinyORM overrides this qmake option with the ",(0,s.jsx)(n.a,{href:"#ccache",children:(0,s.jsx)(n.code,{children:"ccache"})})," option.",(0,s.jsx)("br",{}),"See ",(0,s.jsx)(n.a,{href:"#ccache-support",children:"Ccache support"})," for more information."]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"ltcg"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Enable ",(0,s.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/Interprocedural_optimization",children:"Link time code optimization"})," (",(0,s.jsx)(n.a,{href:"https://wiki.gentoo.org/wiki/LTO",children:"LTO"}),").",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["Works on all ",(0,s.jsx)(n.a,{href:"/supported-compilers",children:"Supported Compilers"}),"."]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"precompile_header"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"-"})}),(0,s.jsxs)(n.td,{children:["Enable precompiled headers, you can disable them with:",(0,s.jsx)("br",{})," ",(0,s.jsx)(n.code,{children:"CONFIG-=precompile_header"}),".",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["The ",(0,s.jsx)(n.code,{children:"precompile_header"})," is enabled by default on ",(0,s.jsx)(n.code,{children:"msvc"}),", ",(0,s.jsx)(n.code,{children:"g++"}),", ",(0,s.jsx)(n.code,{children:"clang++"}),", ",(0,s.jsx)(n.code,{children:"clang-cl"})," on ",(0,s.jsx)(n.code,{children:"Windows"})," and disabled by default on ",(0,s.jsx)(n.code,{children:"linux"}),"."]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsxs)(n.td,{children:[(0,s.jsx)(n.code,{children:"static"}),(0,s.jsx)("br",{}),(0,s.jsx)(n.code,{children:"staticlib"})]}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Build as a ",(0,s.jsx)(n.code,{children:"static"})," library (lib only).",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["If you want to build all libraries in the ",(0,s.jsx)(n.code,{children:"TinyORM"})," project as static library archives and link against static libraries use the ",(0,s.jsx)(n.a,{href:"https://doc.qt.io/qt/qmake-variable-reference.html#config",children:(0,s.jsx)(n.code,{children:"CONFIG += static"})}),". Don't use the ",(0,s.jsx)(n.code,{children:"CONFIG += staticlib"}),".",(0,s.jsx)("br",{}),"See ",(0,s.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/NOTES.txt",children:"NOTES.txt"})," for more information (search ",(0,s.jsx)(n.code,{children:"static vs staticlib"}),")."]})]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"static_runtime"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"OFF"})}),(0,s.jsxs)(n.td,{children:["Link against the ",(0,s.jsx)(n.code,{children:"shared"})," (dynamic) or ",(0,s.jsx)(n.code,{children:"static"})," run-time library.",(0,s.jsx)("br",{}),(0,s.jsxs)("small",{children:["The ",(0,s.jsx)(n.code,{children:"-MD"})," becomes ",(0,s.jsx)(n.code,{children:"-MT"})," and ",(0,s.jsx)(n.code,{children:"-MDd"})," becomes ",(0,s.jsx)(n.code,{children:"-MTd"}),". It works only on ",(0,s.jsx)(n.code,{children:"MSVC"})," and ",(0,s.jsx)(n.code,{children:"MinGW"})," or ",(0,s.jsx)(n.code,{children:"MSYS2"}),".",(0,s.jsx)("br",{}),"Please ",(0,s.jsx)("u",{children:"don't use"})," this option.",(0,s.jsx)("br",{}),"Available when: ",(0,s.jsx)(n.code,{children:"msvc"})," or ",(0,s.jsx)(n.code,{children:"mingw"})]})]})]})]})]})})}),"\n",(0,s.jsxs)(n.h3,{id:"consume-tinyorm-library-qmake",children:["Consume TinyOrm library ",(0,s.jsx)("small",{children:"(qmake)"})]}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/qmake/TinyOrm.pri",children:(0,s.jsx)(n.code,{children:"TinyOrm.pri"})})," file is available to simplify the integration of the ",(0,s.jsx)(n.code,{children:"TinyORM"})," library into your application. It sets up and configures the ",(0,s.jsx)(n.code,{children:"CONFIG"})," and ",(0,s.jsx)(n.code,{children:"DEFINES"})," qmake variables, adds the ",(0,s.jsx)(n.code,{children:"TinyORM"}),", ",(0,s.jsx)("abbr",{title:"TinyORM Migrations",children:(0,s.jsx)(n.code,{children:"tom"})}),", and ",(0,s.jsx)(n.code,{children:"vcpkg"})," header files on the system ",(0,s.jsx)(n.code,{children:"INCLUDEPATH"})," (cross-platform using the ",(0,s.jsx)(n.code,{children:"-isystem"})," or ",(0,s.jsx)(n.code,{children:"-imsvc"}),"), links against the TinyORM ",(0,s.jsx)(n.code,{children:"shared"})," or ",(0,s.jsx)(n.code,{children:"static"})," library using the ",(0,s.jsx)(n.code,{children:"LIBS"}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["You can use it to configure the ",(0,s.jsx)(n.code,{children:"TinyORM"})," library when you are linking against it. It does a very similar thing like the CMake's ",(0,s.jsx)(n.code,{children:"Find Modules"})," feature."]}),"\n",(0,s.jsx)(n.h4,{id:"requirements",children:"Requirements"}),"\n",(0,s.jsx)(n.p,{children:"It has a few requirements, you need to:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["specify path to the ",(0,s.jsx)(n.code,{children:"TinyORM"})," qmake features (",(0,s.jsx)(n.code,{children:".prf"})," files) using the ",(0,s.jsx)(n.code,{children:"QMAKEFEATURES"})," variable that can only be set in the ",(0,s.jsx)(n.code,{children:".qmake.conf"})," file"]}),"\n",(0,s.jsxs)(n.li,{children:["specify ",(0,s.jsx)(n.code,{children:"qmake"})," or ",(0,s.jsx)(n.code,{children:"environment"})," variables to find the ",(0,s.jsx)(n.code,{children:"vcpkg"})," installation ",(0,s.jsxs)("small",{children:["(",(0,s.jsx)(n.code,{children:"TINY_VCPKG_ROOT"})," and ",(0,s.jsx)(n.code,{children:"TINY_VCPKG_TRIPLET"}),")"]})]}),"\n",(0,s.jsxs)(n.li,{children:["specify path to the ",(0,s.jsx)(n.code,{children:"TinyORM"})," build folder ",(0,s.jsxs)("small",{children:["(",(0,s.jsx)(n.code,{children:"TINYORM_BUILD_TREE"}),")"]}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["you can specify it ",(0,s.jsx)(n.strong,{children:"manually"})]}),"\n",(0,s.jsxs)(n.li,{children:["or you can use ",(0,s.jsxs)(n.a,{href:"#partial-guessing-of-the-tinyorm_build_tree",children:["Partial guessing of the ",(0,s.jsx)(n.code,{children:"TINYORM_BUILD_TREE"})]})]}),"\n"]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["build your application with the same ",(0,s.jsx)(n.code,{children:"CONFIG"})," ",(0,s.jsx)(n.code,{children:"qmake"})," variables that were used when building the ",(0,s.jsx)(n.code,{children:"TinyORM"})," library"]}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:"Let's explain one by one."}),"\n",(0,s.jsx)(n.h5,{id:"qmakefeatures",children:(0,s.jsx)(n.code,{children:"QMAKEFEATURES"})}),"\n",(0,s.jsxs)(n.p,{children:["Create the ",(0,s.jsx)(n.code,{children:".qmake.conf"})," file in your application root folder with the following content."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-qmake",metastring:"title='.qmake.conf'",children:"# Path to the PARENT folder of the TinyORM source folder\nTINY_MAIN_DIR = $$clean_path()\n# To find .env and .env.$$QMAKE_PLATFORM files in YOUR project\nTINY_DOTENV_ROOT = $$PWD\n\n# Path to the TinyORM build folder (specified manually)\nTINYORM_BUILD_TREE = $$quote($$TINY_MAIN_DIR/TinyORM-builds-qmake/build-TinyORM-Desktop_Qt_6_7_2_MSVC2019_64bit-Debug/)\n# vcpkg - range-v3 and tabulate\nTINY_VCPKG_ROOT = $$quote(/vcpkg/)\n#TINY_VCPKG_TRIPLET = x64-windows\n\n# To find .prf files, needed by eg. CONFIG += tiny_system_headers inline/extern_constants\nQMAKEFEATURES *= $$quote($$TINY_MAIN_DIR/TinyORM/qmake/features)\n"})}),"\n",(0,s.jsxs)(n.p,{children:["You can move all ",(0,s.jsx)(n.code,{children:"qmake"})," variables that are part of the ",(0,s.jsx)(n.code,{children:"qmake"})," configuration process to the ",(0,s.jsx)(n.code,{children:".env"})," file if you want (recommended), this is possible because the ",(0,s.jsx)(n.code,{children:"TinyOrm.pri"})," enables the ",(0,s.jsx)(n.a,{href:"#environment-files",children:(0,s.jsx)(n.code,{children:"Environment files"})})," feature by default."]}),"\n",(0,s.jsxs)(n.p,{children:["You can look at the ",(0,s.jsx)(n.a,{href:"/building/hello-world#auto-configure-using-qmake_conf-and-env",children:"Auto-configure using .qmake.conf and .env"})," example for ",(0,s.jsx)(n.code,{children:"Hello world"})," project of what must stay in the ",(0,s.jsx)(n.code,{children:"qmake.conf"})," file and what can be moved to the ",(0,s.jsx)(n.code,{children:".env"})," files."]}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["You can use the ",(0,s.jsxs)(n.a,{href:"#partial-guessing-of-the-tinyorm_build_tree",children:["Partial guessing of the ",(0,s.jsx)(n.code,{children:"TINYORM_BUILD_TREE"})]})," if you don't like to specify it manually."]})}),"\n",(0,s.jsxs)(n.h5,{id:"variables-affecting-tinyormpri",children:["Variables affecting ",(0,s.jsx)(n.code,{children:"TinyOrm.pri"})]}),"\n",(0,s.jsxs)(n.p,{children:["You must define the following variables before the ",(0,s.jsx)(n.code,{children:"TinyOrm.pri"})," is included:"]}),"\n",(0,s.jsxs)(n.table,{children:[(0,s.jsx)(n.thead,{children:(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.th,{children:"Variable Name"}),(0,s.jsx)(n.th,{children:"Description"})]})}),(0,s.jsxs)(n.tbody,{children:[(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINYORM_BUILD_TREE"})}),(0,s.jsxs)(n.td,{children:["Path to the ",(0,s.jsx)(n.code,{children:"TinyORM"})," build folder."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINY_VCPKG_ROOT"})}),(0,s.jsxs)(n.td,{children:["Path to the ",(0,s.jsx)(n.code,{children:"vcpkg"})," installation folder.",(0,s.jsx)("br",{}),"If not defined, then it tries to use the ",(0,s.jsx)(n.code,{children:"VCPKG_ROOT"})," environment variable."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINY_VCPKG_TRIPLET"})}),(0,s.jsxs)(n.td,{children:["The ",(0,s.jsx)(n.code,{children:"vcpkg"})," ",(0,s.jsx)(n.code,{children:"triplet"})," to use ",(0,s.jsx)("small",{children:"(vcpkg/installed/$$TINY_VCPKG_TRIPLET/)"}),".",(0,s.jsx)("br",{}),"If not defined, then it tries to guess the ",(0,s.jsx)(n.code,{children:"vcpkg"})," ",(0,s.jsx)(n.code,{children:"triplet"})," based on the current compiler and OS (based on the ",(0,s.jsx)(n.code,{children:"QMAKESPEC"}),"), and as the last thing, it tries to use the ",(0,s.jsx)(n.code,{children:"VCPKG_DEFAULT_TRIPLET"})," environment variable."]})]})]})]}),"\n",(0,s.jsx)(n.p,{children:"These variables will be set after the configuration is done:"}),"\n",(0,s.jsxs)(n.table,{children:[(0,s.jsx)(n.thead,{children:(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.th,{children:"Variable Name"}),(0,s.jsx)(n.th,{children:"Description"})]})}),(0,s.jsxs)(n.tbody,{children:[(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINY_BUILD_SUBFOLDER"})}),(0,s.jsxs)(n.td,{children:["Folder by release type if ",(0,s.jsx)(n.code,{children:"CONFIG+=debug_and_release"})," is defined ",(0,s.jsx)("small",{children:"(/debug, /release, or an empty string)"}),"."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINY_CCACHE_BUILD"})}),(0,s.jsxs)(n.td,{children:["To correctly link ",(0,s.jsx)(n.code,{children:"ccache"})," build against a ",(0,s.jsx)(n.code,{children:"ccache"})," build ",(0,s.jsx)("small",{children:"(_ccache or an empty string)"}),"."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINY_MSVC_VERSION"})}),(0,s.jsxs)(n.td,{children:["The ",(0,s.jsx)(n.code,{children:"MSVC"})," compiler string ",(0,s.jsx)("small",{children:"(MSVC2022 or MSVC2019)"}),"."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINY_QT_VERSION_UNDERSCORED"})}),(0,s.jsxs)(n.td,{children:["Underscored ",(0,s.jsx)(n.code,{children:"Qt"})," version ",(0,s.jsx)("small",{children:"(eg. 6_7_2)"}),"."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINY_RELEASE_TYPE_CAMEL"})}),(0,s.jsxs)(n.td,{children:["Build type string ",(0,s.jsx)("small",{children:"(Debug, Profile, or Release)"}),"."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINY_VCPKG_INCLUDE"})}),(0,s.jsxs)(n.td,{children:["Path to the ",(0,s.jsx)(n.code,{children:"vcpkg"})," ",(0,s.jsx)(n.code,{children:"include"})," folder ",(0,s.jsx)("small",{children:"(vcpkg/installed//include/)"}),"."]})]})]})]}),"\n",(0,s.jsxs)(n.p,{children:["Then you simply include the ",(0,s.jsx)(n.code,{children:"TinyOrm.pri"})," in your project file."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-qmake",metastring:"title='AnyProject.pro'",children:"include($$TINY_MAIN_DIR/TinyORM/qmake/TinyOrm.pri)\n"})}),"\n",(0,s.jsxs)(n.p,{children:["And that is all, now you should be able to link against the ",(0,s.jsx)(n.code,{children:"TinyORM"})," library. \ud83d\udc4c"]}),"\n",(0,s.jsx)(n.h5,{id:"manual-configuration-examples",children:"Manual configuration examples"}),"\n",(0,s.jsxs)(n.p,{children:["Frankly, there is no reason to use the Manual configuration (define the variables described below before the ",(0,s.jsx)(n.code,{children:"TinyOrm.pri"})," inclusion), the only reason to use it is when you want more control over this process or want to define everything yourself. I'll leave this section here to show how things work."]}),"\n",(0,s.jsxs)(n.p,{children:["You will have to link against the ",(0,s.jsx)(n.code,{children:"TinyORM"})," library manually if you don't set the ",(0,s.jsx)(n.code,{children:"TINYORM_BUILD_TREE"})," ",(0,s.jsx)(n.code,{children:"qmake"})," variable before the inclusion of the ",(0,s.jsx)(n.code,{children:"TinyOrm.pri"})," file. The ",(0,s.jsx)(n.code,{children:"INCLUDEPATH"})," is auto-detected every time."]}),"\n",(0,s.jsxs)(o.A,{groupId:y.vf,children:[(0,s.jsx)(t.A,{value:y.b,label:y.ux,children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-qmake",metastring:"{9}",children:"# Link against TinyORM library\n# ---\nTINY_MAIN_DIR = $$clean_path()\n\n# Configure TinyORM library\ninclude($$TINY_MAIN_DIR/TinyORM/qmake/TinyOrm.pri)\n\n# TinyORM library path\nTINYORM_BUILD_TREE = $$quote($$TINY_MAIN_DIR/TinyORM-builds-qmake)\nLIBS += $$quote(-L$$TINYORM_BUILD_TREE/build-TinyORM-Desktop_Qt_6_7_2_MSVC2019_64bit-Debug/src$${TINY_BUILD_SUBFOLDER}/)\nLIBS += -lTinyOrm\n"})})}),(0,s.jsx)(t.A,{value:y.xj,label:y.gg,children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-qmake",metastring:"{9}",children:"# Link against TinyORM library\n# ---\nTINY_MAIN_DIR = $$clean_path()\n\n# Configure TinyORM library\ninclude($$TINY_MAIN_DIR/TinyORM/qmake/TinyOrm.pri)\n\n# TinyORM library path\nTINYORM_BUILD_TREE = $$quote($$TINY_MAIN_DIR/TinyORM-builds-qmake)\nLIBS += $$quote(-L$$TINYORM_BUILD_TREE/build-TinyORM-Desktop_Qt_6_7_2_GCC_64bit-Debug/src$${TINY_BUILD_SUBFOLDER}/)\nLIBS += -lTinyOrm\n"})})})]}),"\n",(0,s.jsxs)(n.p,{children:["The same is true for the ",(0,s.jsx)(n.code,{children:"vcpkg"})," include path. If you don't set the ",(0,s.jsx)(n.code,{children:"TINY_VCPKG_ROOT"})," or have not defined the ",(0,s.jsx)(n.code,{children:"VCPKG_ROOT"})," environment variable, then you need to set up the ",(0,s.jsx)(n.code,{children:"INCLUDEPATH"})," for the ",(0,s.jsx)(n.code,{children:"vcpkg"})," that provides the ",(0,s.jsx)(n.code,{children:"range-v3"})," and ",(0,s.jsx)(n.code,{children:"tabulate"})," header files."]}),"\n",(0,s.jsxs)(o.A,{groupId:y.vf,children:[(0,s.jsx)(t.A,{value:y.b,label:y.ux,children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-qmake",children:"# vcpkg - range-v3 and tabulate\n# ---\nINCLUDEPATH += $$quote(/vcpkg/installed/x64-windows/include/)\n"})})}),(0,s.jsx)(t.A,{value:y.xj,label:y.gg,children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-qmake",children:"# vcpkg - range-v3 and tabulate\n# ---\nQMAKE_CXXFLAGS += -isystem $$shell_quote(/vcpkg/installed/x64-linux/include/)\n"})})})]}),"\n",(0,s.jsxs)(n.p,{children:["You can also use TinyORM's ",(0,s.jsx)(n.code,{children:"qmake"})," function ",(0,s.jsx)(n.code,{children:"tiny_add_system_includepath()"})," which handles ",(0,s.jsx)(n.code,{children:"INCLUDEPATH"})," in a cross-platform way."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-qmake",children:"# vcpkg - range-v3 and tabulate\n# ---\nload(private/tiny_system_includepath)\ntiny_add_system_includepath(/vcpkg/installed/x64-linux/include/)\n"})}),"\n",(0,s.jsxs)(n.p,{children:["Do not forget to add ",(0,s.jsx)(n.code,{children:"TinyOrm0.dll"})," on the path on Windows and on the ",(0,s.jsx)(n.code,{children:"LD_LIBRARY_PATH"})," on Linux, so your application can find it during execution."]}),"\n",(0,s.jsxs)(o.A,{groupId:y.vf,name:"tinyorm-on-path",children:[(0,s.jsx)(t.A,{value:y.b,label:y.ux,children:(0,s.jsx)(c.A,{className:"language-powershell",children:`$env:Path = "${(0,x.OZ)(y.b,!1)}\\TinyORM\\TinyORM-builds-qmake\\build-debug;" + $env:Path`})}),(0,s.jsx)(t.A,{value:y.xj,label:y.gg,children:(0,s.jsx)(c.A,{className:"language-bash",children:`export LD_LIBRARY_PATH=${(0,x.OZ)(y.xj)}/TinyORM/TinyORM-builds-qmake/build-debug\${PATH:+:}$PATH`})})]}),"\n",(0,s.jsxs)(n.admonition,{type:"tip",children:[(0,s.jsxs)(n.p,{children:["On Linux ",(0,s.jsx)(n.code,{children:"-isystem"})," marks the directory as a system directory, it prevents warnings."]}),(0,s.jsxs)(n.p,{children:["On Windows you can use ",(0,s.jsx)(n.code,{children:"QMAKE_CXXFLAGS_WARN_ON = -external:anglebrackets -external:W0"}),", it applies a warning level 0 to the angel bracket includes; ",(0,s.jsx)(n.code,{children:"#include "}),"."]}),(0,s.jsxs)(n.p,{children:["With the ",(0,s.jsx)(n.code,{children:"Clang-cl"})," with ",(0,s.jsx)(n.code,{children:"MSVC"})," you can use ",(0,s.jsx)(n.code,{children:"-imsvc"}),"."]})]}),"\n",(0,s.jsx)(n.h3,{id:"auto-configuration-internals",children:"Auto-configuration internals"}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"qmake"})," build system does not support ",(0,s.jsx)(n.code,{children:"auto-configuration"})," of dependencies out of the box but ",(0,s.jsx)(n.code,{children:"TinyORM"})," from ",(0,s.jsx)(n.code,{children:"v0.34.0"})," added its own ",(0,s.jsx)(n.code,{children:"Auto-configuration"})," feature along with the ",(0,s.jsx)(n.code,{children:"tiny_dotenv"})," qmake feature. These new features allow us to ",(0,s.jsx)(n.code,{children:"auto-configure"})," ",(0,s.jsx)(n.code,{children:"TinyORM"})," project, and with their help, the ",(0,s.jsx)(n.code,{children:"conf.pri"})," files can be ",(0,s.jsx)("u",{children:"skipped entirely"}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["While it adds additional complexity to the ",(0,s.jsx)(n.code,{children:"qmake"})," configuration process, the benefits are significant."]}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"Auto-configuration"})," feature is designed to find the ",(0,s.jsx)(n.code,{children:"vcpkg"})," and ",(0,s.jsx)(n.code,{children:"MySQL"})," installations, and ",(0,s.jsx)(n.code,{children:"tiny_dotenv"})," to include the ",(0,s.jsx)(n.code,{children:".env"})," and ",(0,s.jsx)(n.code,{children:".env.(win32|unix|mingw)"})," files in the project's root folder. These new features can be configured using ",(0,s.jsx)(n.code,{children:"qmake"})," and ",(0,s.jsx)(n.code,{children:"environment"})," variables, and they also contain some guessing logic if these variables are not defined."]}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"Auto-configuration"})," feature can be turned off using the ",(0,s.jsx)(n.a,{href:"#disable_autoconf",children:(0,s.jsx)(n.code,{children:"disable_autoconf"})})," ",(0,s.jsx)(n.code,{children:"qmake"})," configuration option (eg. ",(0,s.jsx)(n.code,{children:"CONFIG*=disable_autoconf"}),")."]}),"\n",(0,s.jsxs)(n.p,{children:["These are ",(0,s.jsx)("u",{children:(0,s.jsx)(n.code,{children:"qmake"})})," and ",(0,s.jsx)("u",{children:(0,s.jsx)(n.code,{children:"environment"})})," variables that affect the ",(0,s.jsx)(n.code,{children:"Auto-configuration"})," feature:"]}),"\n",(0,s.jsxs)(n.table,{children:[(0,s.jsx)(n.thead,{children:(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.th,{children:"Variable Name"}),(0,s.jsx)(n.th,{children:"Description"})]})}),(0,s.jsxs)(n.tbody,{children:[(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINY_VCPKG_ROOT"})}),(0,s.jsxs)(n.td,{children:["Path to the ",(0,s.jsx)(n.code,{children:"vcpkg"})," installation folder.",(0,s.jsx)("br",{}),"If not defined, then it tries to use the ",(0,s.jsx)(n.code,{children:"VCPKG_ROOT"})," environment variable."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINY_VCPKG_TRIPLET"})}),(0,s.jsxs)(n.td,{children:["The ",(0,s.jsx)(n.code,{children:"vcpkg"})," ",(0,s.jsx)(n.code,{children:"triplet"})," to use ",(0,s.jsx)("small",{children:"(vcpkg/installed/$$TINY_VCPKG_TRIPLET/)"}),".",(0,s.jsx)("br",{}),"If not defined, then it tries to guess the ",(0,s.jsx)(n.code,{children:"vcpkg"})," ",(0,s.jsx)(n.code,{children:"triplet"})," based on the current compiler and OS (based on the ",(0,s.jsx)(n.code,{children:"QMAKESPEC"}),"), and as the last thing, it tries to use the ",(0,s.jsx)(n.code,{children:"VCPKG_DEFAULT_TRIPLET"})," environment variable."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINY_MYSQL_ROOT"})}),(0,s.jsxs)(n.td,{children:["Path to the ",(0,s.jsx)(n.code,{children:"MySQL"})," installation folder.",(0,s.jsx)("br",{}),"If not defined, then it tries to guess the ",(0,s.jsx)(n.code,{children:"MySQL"})," installation folder (",(0,s.jsx)(n.code,{children:"win32"})," only): ",(0,s.jsx)("code",{children:"$$(ProgramFiles)/MySQL/MySQL Server (8.4|8.3|8.2|8.1|8.0|5.7)/"})]})]})]})]}),"\n",(0,s.jsxs)(n.p,{children:["You can set these variables in the ",(0,s.jsx)(n.code,{children:".env"})," (recommended) or ",(0,s.jsx)(n.code,{children:"conf.pri"})," files, in the ",(0,s.jsx)(n.code,{children:".qmake.conf"})," file (or wherever you want), or as environment variables."]}),"\n",(0,s.jsxs)(n.p,{children:["These variables will be set after ",(0,s.jsx)(n.code,{children:"auto-configuration"})," is done:"]}),"\n",(0,s.jsxs)(n.table,{children:[(0,s.jsx)(n.thead,{children:(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.th,{children:"Variable Name"}),(0,s.jsx)(n.th,{children:"Description"})]})}),(0,s.jsxs)(n.tbody,{children:[(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINY_VCPKG_INCLUDE"})}),(0,s.jsxs)(n.td,{children:["Path to the ",(0,s.jsx)(n.code,{children:"vcpkg"})," ",(0,s.jsx)(n.code,{children:"include"})," folder ",(0,s.jsx)("small",{children:"(vcpkg/installed//include/)"}),"."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINY_MYSQL_INCLUDE"})}),(0,s.jsxs)(n.td,{children:["Path to the ",(0,s.jsx)(n.code,{children:"MySQL"})," ",(0,s.jsx)(n.code,{children:"include"})," folder ",(0,s.jsx)("small",{children:"(MySQL Server 8.4/include/)"}),"."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINY_MYSQL_LIB"})}),(0,s.jsxs)(n.td,{children:["Path to the ",(0,s.jsx)(n.code,{children:"MySQL"})," ",(0,s.jsx)(n.code,{children:"lib"})," folder ",(0,s.jsx)("small",{children:"(MySQL Server 8.4/lib/)"}),"."]})]})]})]}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"TINY_MYSQL_INCLUDE"})," and ",(0,s.jsx)(n.code,{children:"TINY_MYSQL_LIB"})," are only set on ",(0,s.jsx)(n.code,{children:"win32"})," platform except ",(0,s.jsx)(n.code,{children:"mingw"}),"."]}),"\n",(0,s.jsx)(n.h4,{id:"environment-files",children:"Environment files"}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"tiny_dotenv"})," feature allows us to define the ",(0,s.jsx)(n.code,{children:".env"})," and ",(0,s.jsx)(n.code,{children:".env.$$TINY_DOTENV_PLATFORM"})," files in the project's root folder. These files are loaded as early as possible so you can affect the ",(0,s.jsx)(n.code,{children:"qmake"})," configuration process. On the other hand, the ",(0,s.jsx)(n.code,{children:"conf.pri"})," files are loaded as late as possible, and they can be used to override the ",(0,s.jsx)(n.code,{children:"qmake"})," configuration."]}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:".env"})," file is included ",(0,s.jsx)("u",{children:"first"})," and is included on all platforms."]}),"\n",(0,s.jsxs)(n.p,{children:["There is only one requirement for this feature to work correctly, and that is to set the ",(0,s.jsx)(n.code,{children:"TINY_DOTENV_ROOT"})," ",(0,s.jsx)(n.code,{children:"qmake"})," variable to the project's root folder. This variable is ",(0,s.jsx)(n.strong,{children:"already"})," set in the ",(0,s.jsx)(n.code,{children:".qmake.conf"})," file for the ",(0,s.jsx)(n.code,{children:"TinyORM"})," project."]}),"\n",(0,s.jsx)(n.p,{children:"Then the following names are taken into account: .env, .env.win32, .env.unix, .env.mingw"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-qmake",metastring:"title='.qmake.conf'",children:"# To find .env and .env.$$QMAKE_PLATFORM files\nTINY_DOTENV_ROOT = $$PWD\n"})}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"tiny_dotenv"})," feature can be turned off using the ",(0,s.jsx)(n.a,{href:"#disable_dotenv",children:(0,s.jsx)(n.code,{children:"disable_dotenv"})})," ",(0,s.jsx)(n.code,{children:"qmake"})," configuration option (eg. ",(0,s.jsx)(n.code,{children:"CONFIG*=disable_dotenv"}),")."]}),"\n",(0,s.jsx)(n.admonition,{type:"warning",children:(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.code,{children:"Environment files"})," don't work in the ",(0,s.jsx)(n.code,{children:"CMake"})," builds."]})}),"\n",(0,s.jsxs)(n.h4,{id:"partial-guessing-of-the-tinyorm_build_tree",children:["Partial guessing of the ",(0,s.jsx)(n.code,{children:"TINYORM_BUILD_TREE"})]}),"\n",(0,s.jsxs)(n.p,{children:["You don't have to manually define the ",(0,s.jsx)(n.code,{children:"TINYORM_BUILD_TREE"})," in ",(0,s.jsx)(n.code,{children:".env"})," or ",(0,s.jsx)(n.code,{children:".qmake.conf"})," files. The ",(0,s.jsx)(n.code,{children:"TINYORM_BUILD_TREE"})," absolute path can be put together for you (this is happening inside the ",(0,s.jsx)(n.code,{children:"variables.pri"})," file) and ",(0,s.jsx)(n.code,{children:"TinyORM"})," build folder name can be guessed for you too."]}),"\n",(0,s.jsxs)(n.p,{children:["You must define the following variables before the ",(0,s.jsx)(n.code,{children:"TinyOrm.pri"})," will be included to make this real (set them in the ",(0,s.jsx)(n.code,{children:".qmake.conf"}),"):"]}),"\n",(0,s.jsxs)(n.table,{children:[(0,s.jsx)(n.thead,{children:(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.th,{children:"Variable Name"}),(0,s.jsx)(n.th,{children:"Description"})]})}),(0,s.jsxs)(n.tbody,{children:[(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINY_MAIN_DIR"})}),(0,s.jsxs)(n.td,{children:["Path to the ",(0,s.jsx)(n.strong,{children:"PARENT"})," folder of the ",(0,s.jsx)(n.code,{children:"TinyORM"})," source folder."]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"TINY_BUILD_TREE"})}),(0,s.jsxs)(n.td,{children:["Path to the ",(0,s.jsx)(n.strong,{children:"current"})," build tree - ",(0,s.jsx)(n.code,{children:"TINY_BUILD_TREE = $$shadowed($$PWD)"}),"."]})]})]})]}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"TINY_MAIN_DIR"})," is required for another features anyway (so it should already be set) and all that's left is to set the ",(0,s.jsx)(n.code,{children:"TINY_BUILD_TREE"}),"."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-qmake",metastring:"title='.qmake.conf'",children:"# Path to the current build tree (used to guess the TinyORM build tree)\nTINY_BUILD_TREE = $$shadowed($$PWD)\n"})}),"\n",(0,s.jsxs)(n.p,{children:["If you will follow this pattern or logic then you can switch ",(0,s.jsx)(n.code,{children:"QtCreator Kits"})," and the ",(0,s.jsx)(n.code,{children:"TINYORM_BUILD_TREE"})," will be ",(0,s.jsx)(n.strong,{children:"auto-generated"})," correctly and will always point to the correct ",(0,s.jsx)(n.code,{children:"TinyORM"})," build tree."]}),"\n",(0,s.jsxs)(n.p,{children:["It works this way, all is happening inside the ",(0,s.jsx)(n.code,{children:"variables.pri"}),", it takes a build folder name for the ",(0,s.jsx)(n.strong,{children:"current"})," project eg. ",(0,s.jsx)(n.code,{children:"build-HelloWorld-Desktop_Qt_6_7_2_MSVC2022_64bit-Debug"}),", replaces the ",(0,s.jsx)(n.code,{children:"HelloWorld"})," with the ",(0,s.jsx)(n.code,{children:"TinyORM"})," and as we already know the ",(0,s.jsx)(n.code,{children:"TinyORM"})," build folder location we can simply concatenate these paths like ",(0,s.jsx)(n.code,{children:"$$TINY_MAIN_DIR/TinyORM-builds-qmake/build-TinyORM-Desktop_Qt_6_7_2_MSVC2022_64bit-Debug"}),"."]}),"\n",(0,s.jsx)(n.admonition,{type:"warning",children:(0,s.jsxs)(n.p,{children:["This will only work if you follow the recommended ",(0,s.jsx)(n.a,{href:"#folders-structure",children:(0,s.jsx)(n.code,{children:"Folders structure"})}),"."]})}),"\n",(0,s.jsx)(n.h3,{id:"manual-configuration-internals",children:"Manual configuration internals"}),"\n",(0,s.jsxs)(n.p,{children:["There is not much to say about the ",(0,s.jsx)(n.code,{children:"Manual configuration"})," feature. It uses ",(0,s.jsx)(n.code,{children:"conf.pri"})," files (there are four, one for every project or sub-project), and every project has prepared its own ",(0,s.jsx)(n.code,{children:"conf.pri.example"})," file for faster initial configuration."]}),"\n",(0,s.jsxs)(n.p,{children:["These ",(0,s.jsx)(n.code,{children:"conf.pri.example"})," files are nicely commented on, so you can see what needs to be modified. The ",(0,s.jsx)(n.code,{children:"conf.pri"})," files are loaded as late as possible, and they can be used to override the ",(0,s.jsx)(n.code,{children:"qmake"})," configuration."]}),"\n",(0,s.jsxs)(n.p,{children:["If the ",(0,s.jsx)(n.code,{children:"Auto-configuration"})," feature is disabled and there are no ",(0,s.jsx)(n.code,{children:"conf.pri"})," files, then the ",(0,s.jsx)(n.code,{children:"TinyORM"})," ",(0,s.jsx)(n.code,{children:"qmake"})," configuration or build will fail at 100%."]}),"\n",(0,s.jsxs)(n.p,{children:["These ",(0,s.jsx)(n.code,{children:"conf.pri"})," files are intended for configuring qmake's ",(0,s.jsx)(n.code,{children:"INCLUDEPATH"})," and ",(0,s.jsx)(n.code,{children:"LIBS"}),", ",(0,s.jsx)(n.code,{children:"CONFIG"})," or eg. ",(0,s.jsx)(n.code,{children:"QMAKE_LFLAGS"}),", or any other ",(0,s.jsx)(n.code,{children:"qmake"})," options or variables."]}),"\n",(0,s.jsx)(n.h2,{id:"ccache-support",children:"Ccache support"}),"\n",(0,s.jsxs)(n.p,{children:["The TinyORM supports the ",(0,s.jsx)(n.a,{href:"https://ccache.dev/",children:(0,s.jsx)(n.code,{children:"ccache"})})," out of the box for all ",(0,s.jsx)(n.a,{href:"/supported-compilers",children:"supported compilers"}),". For ",(0,s.jsx)(n.code,{children:"qmake"})," you can enable it using the ",(0,s.jsx)(n.a,{href:"#ccache",children:(0,s.jsx)(n.code,{children:"CONFIG+=ccache"})})," and for ",(0,s.jsx)(n.code,{children:"CMake"})," you can set the ",(0,s.jsx)(n.a,{href:"#CMAKE_CXX_COMPILER_LAUNCHER",children:(0,s.jsx)(n.code,{children:"CMAKE_CXX_COMPILER_LAUNCHER=ccache"})}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["On ",(0,s.jsx)(n.code,{children:"Linux"})," it's clear, the ",(0,s.jsx)(n.code,{children:"ccache"})," is fully supported and works also with the ",(0,s.jsx)(n.code,{children:"precompiled headers"}),". But was necessary to add some workarounds to the ",(0,s.jsx)(n.code,{children:"qmake"}),"/",(0,s.jsx)(n.code,{children:"CMake"})," build systems to make out of the box support on ",(0,s.jsx)(n.code,{children:"Windows"}),". When you enable the ",(0,s.jsx)(n.code,{children:"ccache"})," on ",(0,s.jsx)(n.code,{children:"Windows"})," then the build system replaces the ",(0,s.jsx)(n.code,{children:"-Zi"})," and ",(0,s.jsx)(n.code,{children:"-ZI"})," compiler options with the ",(0,s.jsx)(n.code,{children:"-Z7"})," (link to the ",(0,s.jsx)(n.a,{href:"https://github.com/ccache/ccache/issues/1040",children:"issue"}),") and disables ",(0,s.jsx)(n.code,{children:"precompiled headers"})," if ",(0,s.jsx)(n.code,{children:"ccache <4.10"}),"."]}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["You can install the ",(0,s.jsx)(n.code,{children:"ccache"})," using the ",(0,s.jsx)(n.code,{children:"scoop install ccache"})," command on Windows. See the ",(0,s.jsx)(n.a,{href:"/dependencies#linux-installation-ccache",children:"Dependencies"})," page for how to install ",(0,s.jsx)(n.code,{children:"ccache"})," on Linux."]})})]})}function I(e={}){const{wrapper:n}={...(0,d.R)(),...e.components};return n?(0,s.jsx)(n,{...e,children:(0,s.jsx)(M,{...e})}):M(e)}},9365:(e,n,i)=>{i.d(n,{A:()=>l});i(6540);var s=i(4164);const d={tabItem:"tabItem_Ymn6"};var r=i(4848);function l(e){let{children:n,hidden:i,className:l}=e;return(0,r.jsx)("div",{role:"tabpanel",className:(0,s.A)(d.tabItem,l),hidden:i,children:n})}},1470:(e,n,i)=>{i.d(n,{A:()=>_});var s=i(6540),d=i(4164),r=i(3104),l=i(6347),c=i(205),t=i(7485),o=i(1682),a=i(679);function h(e){return s.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,s.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function x(e){const{values:n,children:i}=e;return(0,s.useMemo)((()=>{const e=n??function(e){return h(e).map((e=>{let{props:{value:n,label:i,attributes:s,default:d}}=e;return{value:n,label:i,attributes:s,default:d}}))}(i);return function(e){const n=(0,o.X)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[n,i])}function j(e){let{value:n,tabValues:i}=e;return i.some((e=>e.value===n))}function u(e){let{queryString:n=!1,groupId:i}=e;const d=(0,l.W6)(),r=function(e){let{queryString:n=!1,groupId:i}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!i)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return i??null}({queryString:n,groupId:i});return[(0,t.aZ)(r),(0,s.useCallback)((e=>{if(!r)return;const n=new URLSearchParams(d.location.search);n.set(r,e),d.replace({...d.location,search:n.toString()})}),[r,d])]}function p(e){const{defaultValue:n,queryString:i=!1,groupId:d}=e,r=x(e),[l,t]=(0,s.useState)((()=>function(e){let{defaultValue:n,tabValues:i}=e;if(0===i.length)throw new Error("Docusaurus error: the component requires at least one children component");if(n){if(!j({value:n,tabValues:i}))throw new Error(`Docusaurus error: The has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${i.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}const s=i.find((e=>e.default))??i[0];if(!s)throw new Error("Unexpected error: 0 tabValues");return s.value}({defaultValue:n,tabValues:r}))),[o,h]=u({queryString:i,groupId:d}),[p,m]=function(e){let{groupId:n}=e;const i=function(e){return e?`docusaurus.tab.${e}`:null}(n),[d,r]=(0,a.Dv)(i);return[d,(0,s.useCallback)((e=>{i&&r.set(e)}),[i,r])]}({groupId:d}),b=(()=>{const e=o??p;return j({value:e,tabValues:r})?e:null})();(0,c.A)((()=>{b&&t(b)}),[b]);return{selectedValue:l,selectValue:(0,s.useCallback)((e=>{if(!j({value:e,tabValues:r}))throw new Error(`Can't select invalid tab value=${e}`);t(e),h(e),m(e)}),[h,m,r]),tabValues:r}}var m=i(2303);const b={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var f=i(4848);function g(e){let{className:n,block:i,selectedValue:s,selectValue:l,tabValues:c}=e;const t=[],{blockElementScrollPositionUntilNextRender:o}=(0,r.a_)(),a=e=>{const n=e.currentTarget,i=t.indexOf(n),d=c[i].value;d!==s&&(o(n),l(d))},h=e=>{let n=null;switch(e.key){case"Enter":a(e);break;case"ArrowRight":{const i=t.indexOf(e.currentTarget)+1;n=t[i]??t[0];break}case"ArrowLeft":{const i=t.indexOf(e.currentTarget)-1;n=t[i]??t[t.length-1];break}}n?.focus()};return(0,f.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,d.A)("tabs",{"tabs--block":i},n),children:c.map((e=>{let{value:n,label:i,attributes:r}=e;return(0,f.jsx)("li",{role:"tab",tabIndex:s===n?0:-1,"aria-selected":s===n,ref:e=>t.push(e),onKeyDown:h,onClick:a,...r,className:(0,d.A)("tabs__item",b.tabItem,r?.className,{"tabs__item--active":s===n}),children:i??n},n)}))})}function y(e){let{lazy:n,children:i,selectedValue:d}=e;const r=(Array.isArray(i)?i:[i]).filter(Boolean);if(n){const e=r.find((e=>e.props.value===d));return e?(0,s.cloneElement)(e,{className:"margin-top--md"}):null}return(0,f.jsx)("div",{className:"margin-top--md",children:r.map(((e,n)=>(0,s.cloneElement)(e,{key:n,hidden:e.props.value!==d})))})}function T(e){const n=p(e);return(0,f.jsxs)("div",{className:(0,d.A)("tabs-container",b.tabList),children:[(0,f.jsx)(g,{...n,...e}),(0,f.jsx)(y,{...n,...e})]})}function _(e){const n=(0,m.A)();return(0,f.jsx)(T,{...e,children:h(e.children)},String(n))}},6684:(e,n,i)=>{i.d(n,{A:()=>a});var s=i(6540),d=i(3427),r=i(6347);const l={apiTable:"apiTable_flxF"};var c=i(4848);function t(e,n){let{name:i,children:l}=e;const t=function(e){let n=e;for(;(0,s.isValidElement)(n);)[n]=s.Children.toArray(n.props.children);if("string"!=typeof n)throw new Error(`Could not extract APITable row name from JSX tree:\n${JSON.stringify(e,null,2)}`);return n}(l),o=i?`${i}-${t}`:t,a=`#${o}`,h=(0,r.W6)();return(0,d.A)().collectAnchor(o),(0,c.jsx)("tr",{id:o,tabIndex:0,ref:h.location.hash===a?n:void 0,onClick:e=>{const n=e.target;[n,n.parentElement].some((e=>"A"===e?.tagName.toUpperCase()))||h.push(a)},onKeyDown:e=>{"Enter"===e.key&&h.push(a)},children:l.props.children})}const o=s.forwardRef(t);function a(e){let{children:n,name:i}=e;if("table"!==n.type)throw new Error("Bad usage of APITable component.\nIt is probably that your Markdown table is malformed.\nMake sure to double-check you have the appropriate number of columns for each table row.");const[d,r]=s.Children.toArray(n.props.children),t=(0,s.useRef)(null);(0,s.useEffect)((()=>{t.current?.focus()}),[t]);const a=s.Children.map(r.props.children,(e=>(0,c.jsx)(o,{name:i,ref:t,children:e})));return(0,c.jsxs)("table",{className:l.apiTable,children:[d,(0,c.jsx)("tbody",{children:a})]})}},7324:(e,n,i)=>{i.d(n,{$E:()=>m,A3:()=>f,CW:()=>b,Dx:()=>a,F4:()=>x,Fi:()=>o,J_:()=>_,LQ:()=>g,Lf:()=>v,OO:()=>d,Q7:()=>y,b:()=>c,cy:()=>t,gg:()=>u,kl:()=>j,os:()=>h,pW:()=>r,ux:()=>p,vf:()=>s,xj:()=>l,xt:()=>T});const s="shell",d="database",r="application",l="bash",c="pwsh",t="zsh",o="maria",a="mysql",h="postgres",x="sqlite",j="application",u="bash",p="pwsh",m="zsh",b="MariaDB",f="MySQL",g="PostgreSQL",y="SQLite",T="tinyorm.org",_="$HOME/Code/c/",v="$env:USERPROFILE\\Code\\c\\"},6362:(e,n,i)=>{i.d(n,{A:()=>r});var s=i(6540),d=i(1838);function r(){const e=(0,s.useContext)(d.A);if(null!=e)return e;throw new Error("useRootFolderContext is used outside of Layout component.")}},6694:(e,n,i)=>{i.d(n,{OZ:()=>t,Sn:()=>l,T3:()=>a,bw:()=>o,nC:()=>h,np:()=>c});var s=i(6362),d=i(2303),r=i(7324);const l=function(e,n){return void 0===n&&(n=!0),x((0,s.A)().rootFolder[e]??o(e),e,n)},c=()=>(0,s.A)().rootFolder[r.pW]??o(r.pW),t=function(e,n){if(void 0===n&&(n=!0),null==e)throw new Error("The groupId in the applicationFolderPath() can not be empty.");const i=n||e!==r.b?"/":"\\";return x(l(e)+i+c(),e,n)};function o(e){if(null==e)throw new Error("The groupId in the folderDefaultValue() can not be empty.");if(!(0,d.A)())return"";switch(e){case r.b:return r.Lf;case r.xj:return r.J_;case r.pW:return r.xt;default:throw new Error(`No default value for '${e}' groupId in the folderDefaultValue().`)}}function a(e){return e===r.pW}function h(e,n){if(null==n||""===n)return n;const i="$ENV{$1}$2";switch(e){case r.b:return u(n).replace(/\$env:(.+?)(\/.*)/,i);case r.xj:return n.replace(/\$(.+?)(\/.*)/,i);default:throw new Error(`Unsupported shell type '${e}' in the convertToCmakeEnvVariable().`)}}function x(e,n,i){if(void 0===i&&(i=!0),null==e||""===e)return e;if(n!==r.b)return j(e);const s=j(e);return i?u(s):function(e){return null==e||""===e?e:e.replaceAll(/\/+/g,"\\")}(s)}function j(e){return null==e||""===e?e:e.replace(/[/\\]+$/,"")}function u(e){return null==e||""===e?e:e.replaceAll(/\\+(?! )/g,"/")}},2721:(e,n,i)=>{i.d(n,{A:()=>s});const s=i.p+"assets/images/qmake-additional_arguments-14d3b6b82ad6d28db5b999a462500a6a.png"},7619:(e,n,i)=>{i.d(n,{A:()=>s});const s=i.p+"assets/images/qmake-build_settings-7caa6d7c86232484b82acb24b5a3a6a7.png"},885:(e,n,i)=>{i.d(n,{A:()=>s});const s=i.p+"assets/images/qmake-configure_project-0b6821ea0523567dab9f21b3215055a3.png"}}]); \ No newline at end of file diff --git a/assets/js/3dd307b5.0ac9dd32.js b/assets/js/3dd307b5.0ac9dd32.js deleted file mode 100644 index a397a5bd8..000000000 --- a/assets/js/3dd307b5.0ac9dd32.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunktinyorm_org=self.webpackChunktinyorm_org||[]).push([[117],{4251:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>i,contentTitle:()=>l,default:()=>h,frontMatter:()=>r,metadata:()=>d,toc:()=>c});var o=t(4848),s=t(8453);const r={sidebar_position:2,sidebar_label:"Collections",description:"The ModelsCollection is specialized container which provides a fluent, convenient wrapper for working with vector of models. Is much more powerful than vectors and expose a variety of map / reduce operations that may be chained using an intuitive interface. All TinyORM methods that return more than one model result will return instances of the ModelsCollection class.",keywords:["c++ orm","orm","collections","collection","model","tinyorm"]},l="TinyORM: Collections",d={id:"tinyorm/collections",title:"TinyORM: Collections",description:"The ModelsCollection is specialized container which provides a fluent, convenient wrapper for working with vector of models. Is much more powerful than vectors and expose a variety of map / reduce operations that may be chained using an intuitive interface. All TinyORM methods that return more than one model result will return instances of the ModelsCollection class.",source:"@site/docs/tinyorm/collections.mdx",sourceDirName:"tinyorm",slug:"/tinyorm/collections",permalink:"/tinyorm/collections",draft:!1,unlisted:!1,tags:[],version:"current",sidebarPosition:2,frontMatter:{sidebar_position:2,sidebar_label:"Collections",description:"The ModelsCollection is specialized container which provides a fluent, convenient wrapper for working with vector of models. Is much more powerful than vectors and expose a variety of map / reduce operations that may be chained using an intuitive interface. All TinyORM methods that return more than one model result will return instances of the ModelsCollection class.",keywords:["c++ orm","orm","collections","collection","model","tinyorm"]},sidebar:"tinyormSidebar",previous:{title:"Relationships",permalink:"/tinyorm/relationships"},next:{title:"Casts",permalink:"/tinyorm/casts"}},i={},c=[{value:"Introduction",id:"introduction",level:2},{value:"Collection Conversion",id:"collection-conversion",level:4},{value:"Creating Collections",id:"creating-collections",level:3},{value:"Available Methods",id:"available-methods",level:2},{value:"all()",id:"method-all",level:4},{value:"contains()",id:"method-contains",level:4},{value:"doesntContain()",id:"method-doesntcontain",level:4},{value:"each()",id:"method-each",level:4},{value:"except()",id:"method-except",level:4},{value:"filter()",id:"method-filter",level:4},{value:"find()",id:"method-find",level:4},{value:"first()",id:"method-first",level:4},{value:"firstWhere()",id:"method-first-where",level:4},{value:"fresh()",id:"method-fresh",level:4},{value:"implode()",id:"method-implode",level:4},{value:"isEmpty()",id:"method-isempty",level:4},{value:"isNotEmpty()",id:"method-isnotempty",level:4},{value:"last()",id:"method-last",level:4},{value:"load()",id:"method-load",level:4},{value:"map()",id:"method-map",level:4},{value:"mapWithKeys()",id:"method-mapwithkeys",level:4},{value:"mapWithModelKeys()",id:"method-mapwithmodelkeys",level:4},{value:"modelKeys()",id:"method-modelkeys",level:4},{value:"only()",id:"method-only",level:4},{value:"pluck()",id:"method-pluck",level:4},{value:"reject()",id:"method-reject",level:4},{value:"sort()",id:"method-sort",level:4},{value:"sortBy()",id:"method-sortby",level:4},{value:"sortByDesc()",id:"method-sortbydesc",level:4},{value:"sortDesc()",id:"method-sortdesc",level:4},{value:"stableSort()",id:"method-stablesort",level:4},{value:"stableSortBy()",id:"method-stablesortby",level:4},{value:"stableSortByDesc()",id:"method-stablesortbydesc",level:4},{value:"stableSortDesc()",id:"method-stablesortdesc",level:4},{value:"tap()",id:"method-tap",level:4},{value:"toBase()",id:"method-tobase",level:4},{value:"toJson()",id:"method-tojson",level:4},{value:"toJsonArray()",id:"method-tojsonarray",level:4},{value:"toJsonDocument()",id:"method-tojsondocument",level:4},{value:"toMap()",id:"method-tomap",level:4},{value:"toMapVariantList()",id:"method-tomapvariantlist",level:4},{value:"toQuery()",id:"method-toquery",level:4},{value:"toVector()",id:"method-tovector",level:4},{value:"toVectorVariantList()",id:"method-tovectorvariantlist",level:4},{value:"unique()",id:"method-unique",level:4},{value:"uniqueBy()",id:"method-uniqueby",level:4},{value:"uniqueRelaxed()",id:"method-uniquerelaxed",level:4},{value:"uniqueRelaxedBy()",id:"method-uniquerelaxedby",level:4},{value:"value()",id:"method-value",level:4},{value:"where()",id:"method-where",level:4},{value:"whereBetween()",id:"method-wherebetween",level:4},{value:"whereIn()",id:"method-wherein",level:4},{value:"whereNotBetween()",id:"method-wherenotbetween",level:4},{value:"whereNotIn()",id:"method-wherenotin",level:4},{value:"whereNotNull()",id:"method-wherenotnull",level:4},{value:"whereNull()",id:"method-wherenull",level:4}];function a(e){const n={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",h3:"h3",h4:"h4",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(n.h1,{id:"tinyorm-collections",children:"TinyORM: Collections"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.a,{href:"#introduction",children:"Introduction"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:(0,o.jsx)(n.a,{href:"#creating-collections",children:"Creating Collections"})}),"\n"]}),"\n"]}),"\n",(0,o.jsx)(n.li,{children:(0,o.jsx)(n.a,{href:"#available-methods",children:"Available Methods"})}),"\n"]}),"\n",(0,o.jsx)(n.h2,{id:"introduction",children:"Introduction"}),"\n",(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"Orm::Tiny::Types::ModelsCollection"})," is specialized container which provides a fluent, convenient wrapper for working with vector of models. All TinyORM methods that return more than one model result, will return instances of the ",(0,o.jsx)(n.code,{children:"ModelsCollection"})," class, including results retrieved via the ",(0,o.jsx)(n.code,{children:"get"})," method or methods that return relationships like the ",(0,o.jsx)(n.code,{children:"getRelation"})," and ",(0,o.jsx)(n.code,{children:"getRelationValue"}),"."]}),"\n",(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"ModelsCollection"})," class extends ",(0,o.jsx)(n.code,{children:"QVector"}),", so it naturally inherits dozens of methods used to work with the underlying vector of TinyORM models. Be sure to review the ",(0,o.jsx)(n.a,{href:"https://doc.qt.io/qt/qlist.html",children:(0,o.jsx)(n.code,{children:"QList"})})," documentation to learn all about these helpful methods!"]}),"\n",(0,o.jsx)(n.admonition,{type:"info",children:(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"ModelsCollection"})," template parameter can be declared only with the model type or a model type pointer, it also can't be ",(0,o.jsx)(n.code,{children:"const"})," and can't be a reference. It's constrained using the ",(0,o.jsx)(n.code,{children:"DerivedCollectionModel"})," concept."]})}),"\n",(0,o.jsxs)(n.p,{children:["You can iterate over the ",(0,o.jsx)(n.code,{children:"ModelsCollection"})," the same way as over the ",(0,o.jsx)(n.code,{children:"QVector"}),":"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'using Models::User;\n\nModelsCollection users = User::whereEq("active", true)->get();\n\nfor (const auto &user : users)\n qDebug() << user.getAttribute("name");\n'})}),"\n",(0,o.jsx)(n.p,{children:"However, as previously mentioned, collections are much more powerful than vectors and expose a variety of map / reduce operations that may be chained using an intuitive interface. For example, we may remove all active users and then gather the first name of each remaining user:"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'auto names = User::all().reject([](User *const user)\n{\n return user->getAttribute("active");\n})\n .pluck("name");\n'})}),"\n",(0,o.jsxs)(n.p,{children:["As you can see, the ",(0,o.jsx)(n.code,{children:"ModelsCollection"})," class allows you to chain its methods to perform fluent mapping and reducing of the underlying vector. In general, collections are immutable, meaning every ",(0,o.jsx)(n.code,{children:"ModelsCollection"})," method returns an entirely new ",(0,o.jsx)(n.code,{children:"ModelsCollection"})," instance."]}),"\n",(0,o.jsx)(n.admonition,{type:"info",children:(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"ModelsCollection"})," is returning from the Models' methods like ",(0,o.jsx)(n.code,{children:"get"}),", ",(0,o.jsx)(n.code,{children:"all"}),", ",(0,o.jsx)(n.code,{children:"findMany"}),", ",(0,o.jsx)(n.code,{children:"chunk"}),"; the ",(0,o.jsx)(n.code,{children:"ModelsCollection"})," is returning from the relationship-related methods as ",(0,o.jsx)(n.code,{children:"getRelation"})," and ",(0,o.jsx)(n.code,{children:"getRelationValue"}),"."]})}),"\n",(0,o.jsx)(n.h4,{id:"collection-conversion",children:"Collection Conversion"}),"\n",(0,o.jsxs)(n.p,{children:["While most TinyORM collection methods return a new instance of ",(0,o.jsx)(n.code,{children:"ModelsCollection"}),", the ",(0,o.jsx)(n.code,{children:"modelKeys"}),", ",(0,o.jsx)(n.code,{children:"mapWithKeys"}),", and ",(0,o.jsx)(n.code,{children:"pluck"})," methods return a base QVector or std unordered/map instances. Likewise, one of the ",(0,o.jsx)(n.code,{children:"map"})," methods overload returns the ",(0,o.jsx)(n.code,{children:"QVector"}),"."]}),"\n",(0,o.jsx)(n.h3,{id:"creating-collections",children:"Creating Collections"}),"\n",(0,o.jsxs)(n.p,{children:["Creating a ",(0,o.jsx)(n.code,{children:"ModelsCollection"})," is as simple as:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users {\n {{"name", "Kate"}, {"votes", 150}},\n {{"name", "John"}, {"votes", 200}},\n};\n'})}),"\n",(0,o.jsxs)(n.p,{children:["You can also create a collection of pointers, eg. ",(0,o.jsx)(n.code,{children:"ModelsCollection"}),":"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"ModelsCollection userPointers {\n &users[0], &users[1],\n};\n"})}),"\n",(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"ModelsCollection"})," is implicitly convertible and assignable from the ",(0,o.jsx)(n.code,{children:"QVector"}),":"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'QVector usersVector {\n {{"name", "Kate"}, {"votes", 150}},\n {{"name", "John"}, {"votes", 200}},\n};\n\nModelsCollection users(usersVector);\nusers = usersVector;\n'})}),"\n",(0,o.jsxs)(n.p,{children:["Alternatively, you can use the ",(0,o.jsx)(n.code,{children:"Orm::collect"})," helper function to create a ",(0,o.jsx)(n.code,{children:"ModelsCollection"})," from the given attributes:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users = Orm::collect({\n {{"name", "Kate"}, {"added_on", QDateTime::currentDateTimeUtc()}},\n {{"name", "John"}, {"added_on", QDateTime({2023, 6, 1}, {13, 46, 15}, Qt::UTC)}},\n});\n'})}),"\n",(0,o.jsx)(n.admonition,{type:"caution",children:(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"Orm::collect"})," function is ",(0,o.jsx)(n.strong,{children:"mandatory"})," if your attributes contain the ",(0,o.jsx)(n.code,{children:"QDateTime"})," instance, you can read more about this problem ",(0,o.jsx)(n.a,{href:"/tinyorm/getting-started#qdatetime-and-connection-name-problem",children:"here"}),"."]})}),"\n",(0,o.jsx)(n.admonition,{type:"note",children:(0,o.jsxs)(n.p,{children:["The results of ",(0,o.jsx)(n.a,{href:"/tinyorm/getting-started",children:"TinyORM"})," queries are always returned as ",(0,o.jsx)(n.code,{children:"ModelsCollection"})," instances."]})}),"\n",(0,o.jsx)(n.h2,{id:"available-methods",children:"Available Methods"}),"\n",(0,o.jsxs)(n.p,{children:["For the majority of the remaining collection documentation, we'll discuss each method available on the ",(0,o.jsx)(n.code,{children:"ModelsCollection"})," class. Remember, all of these methods may be chained to fluently manipulate the underlying vector."]}),"\n",(0,o.jsxs)(n.p,{children:["Furthermore, almost every method returns a new ",(0,o.jsx)(n.code,{children:"ModelsCollection"})," instance, allowing you to preserve the original copy of the collection when necessary:"]}),"\n",(0,o.jsx)("div",{className:"collection-methods-list",children:(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.a,{href:"#method-all",children:"all"}),"\n",(0,o.jsx)(n.a,{href:"#method-contains",children:"contains"}),"\n",(0,o.jsx)(n.a,{href:"#method-doesntcontain",children:"doesntContain"}),"\n",(0,o.jsx)(n.a,{href:"#method-each",children:"each"}),"\n",(0,o.jsx)(n.a,{href:"#method-except",children:"except"}),"\n",(0,o.jsx)(n.a,{href:"#method-filter",children:"filter"}),"\n",(0,o.jsx)(n.a,{href:"#method-find",children:"find"}),"\n",(0,o.jsx)(n.a,{href:"#method-first",children:"first"}),"\n",(0,o.jsx)(n.a,{href:"#method-first-where",children:"firstWhere"}),"\n",(0,o.jsx)(n.a,{href:"#method-fresh",children:"fresh"}),"\n",(0,o.jsx)(n.a,{href:"#method-implode",children:"implode"}),"\n",(0,o.jsx)(n.a,{href:"#method-isempty",children:"isEmpty"}),"\n",(0,o.jsx)(n.a,{href:"#method-isnotempty",children:"isNotEmpty"}),"\n",(0,o.jsx)(n.a,{href:"#method-last",children:"last"}),"\n",(0,o.jsx)(n.a,{href:"#method-load",children:"load"}),"\n",(0,o.jsx)(n.a,{href:"#method-map",children:"map"}),"\n",(0,o.jsx)(n.a,{href:"#method-mapwithkeys",children:"mapWithKeys"}),"\n",(0,o.jsx)(n.a,{href:"#method-mapwithmodelkeys",children:"mapWithModelKeys"}),"\n",(0,o.jsx)(n.a,{href:"#method-modelkeys",children:"modelKeys"}),"\n",(0,o.jsx)(n.a,{href:"#method-only",children:"only"}),"\n",(0,o.jsx)(n.a,{href:"#method-pluck",children:"pluck"}),"\n",(0,o.jsx)(n.a,{href:"#method-reject",children:"reject"}),"\n",(0,o.jsx)(n.a,{href:"#method-sort",children:"sort"}),"\n",(0,o.jsx)(n.a,{href:"#method-sortby",children:"sortBy"}),"\n",(0,o.jsx)(n.a,{href:"#method-sortbydesc",children:"sortByDesc"}),"\n",(0,o.jsx)(n.a,{href:"#method-sortdesc",children:"sortDesc"}),"\n",(0,o.jsx)(n.a,{href:"#method-stablesort",children:"stableSort"}),"\n",(0,o.jsx)(n.a,{href:"#method-stablesortby",children:"stableSortBy"}),"\n",(0,o.jsx)(n.a,{href:"#method-stablesortbydesc",children:"stableSortByDesc"}),"\n",(0,o.jsx)(n.a,{href:"#method-stablesortdesc",children:"stableSortDesc"}),"\n",(0,o.jsx)(n.a,{href:"#method-tap",children:"tap"}),"\n",(0,o.jsx)(n.a,{href:"#method-tobase",children:"toBase"}),"\n",(0,o.jsx)(n.a,{href:"#method-tojson",children:"toJson"}),"\n",(0,o.jsx)(n.a,{href:"#method-tojsonarray",children:"toJsonArray"}),"\n",(0,o.jsx)(n.a,{href:"#method-tojsondocument",children:"toJsonDocument"}),"\n",(0,o.jsx)(n.a,{href:"#method-tomap",children:"toMap"}),"\n",(0,o.jsx)(n.a,{href:"#method-tomapvariantlist",children:"toMapVariantList"}),"\n",(0,o.jsx)(n.a,{href:"#method-toquery",children:"toQuery"}),"\n",(0,o.jsx)(n.a,{href:"#method-tovector",children:"toVector"}),"\n",(0,o.jsx)(n.a,{href:"#method-tovectorvariantlist",children:"toVectorVariantList"}),"\n",(0,o.jsx)(n.a,{href:"#method-unique",children:"unique"}),"\n",(0,o.jsx)(n.a,{href:"#method-uniqueby",children:"uniqueBy"}),"\n",(0,o.jsx)(n.a,{href:"#method-uniquerelaxed",children:"uniqueRelaxed"}),"\n",(0,o.jsx)(n.a,{href:"#method-uniquerelaxedby",children:"uniqueRelaxedBy"}),"\n",(0,o.jsx)(n.a,{href:"#method-value",children:"value"}),"\n",(0,o.jsx)(n.a,{href:"#method-where",children:"where"}),"\n",(0,o.jsx)(n.a,{href:"#method-wherebetween",children:"whereBetween"}),"\n",(0,o.jsx)(n.a,{href:"#method-wherein",children:"whereIn"}),"\n",(0,o.jsx)(n.a,{href:"#method-wherenotbetween",children:"whereNotBetween"}),"\n",(0,o.jsx)(n.a,{href:"#method-wherenotin",children:"whereNotIn"}),"\n",(0,o.jsx)(n.a,{href:"#method-wherenotnull",children:"whereNotNull"}),"\n",(0,o.jsx)(n.a,{href:"#method-wherenull",children:"whereNull"})]})}),"\n",(0,o.jsx)(n.admonition,{type:"note",children:(0,o.jsxs)(n.p,{children:["For a better understanding of the following examples, many of the variable declarations below use actual types instead of the ",(0,o.jsx)(n.code,{children:"auto"})," keyword."]})}),"\n",(0,o.jsxs)("div",{className:"collection-methods",children:[(0,o.jsx)(n.h4,{id:"method-all",children:(0,o.jsx)(n.code,{children:"all()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"all"})," method returns a copy of the underlying vector represented by the collection:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"QVector = users.all();\n"})}),(0,o.jsx)(n.admonition,{type:"note",children:(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.a,{href:"#method-tobase",children:(0,o.jsx)(n.code,{children:"toBase"})})," is an alias to the ",(0,o.jsx)(n.code,{children:"all"})," method."]})}),(0,o.jsx)(n.h4,{id:"method-contains",children:(0,o.jsx)(n.code,{children:"contains()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"contains"})," method may be used to determine if a given model instance is contained by the collection. This method accepts a primary key or a model instance:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"users.contains(1);\n\nusers.contains(User::find(1));\n"})}),(0,o.jsxs)(n.p,{children:["Alternatively, you may pass a lambda expression to the ",(0,o.jsx)(n.code,{children:"contains"})," method to determine if a model exists in the collection matching a given truth test:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"users.contains([](const User *const user)\n{\n return user->getKeyCasted() == 2;\n});\n"})}),(0,o.jsxs)(n.p,{children:["For the inverse of ",(0,o.jsx)(n.code,{children:"contains"}),", see the ",(0,o.jsx)(n.a,{href:"#method-doesntcontain",children:"doesntContain"})," method."]}),(0,o.jsx)(n.h4,{id:"method-doesntcontain",children:(0,o.jsx)(n.code,{children:"doesntContain()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"doesntContain"})," method determines whether the collection does not contain a given item. This method accepts a primary key or a model instance:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"users.doesntContain(1);\n\nusers.doesntContain(User::find(1));\n"})}),(0,o.jsxs)(n.p,{children:["Alternatively, you may pass a lambda expression to the ",(0,o.jsx)(n.code,{children:"doesntContain"})," method to determine if a model does not exist in the collection matching a given truth test:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"users.doesntContain([](const User *const user)\n{\n return user->getKeyCasted() == 2;\n});\n"})}),(0,o.jsxs)(n.p,{children:["For the inverse of ",(0,o.jsx)(n.code,{children:"doesntContain"}),", see the ",(0,o.jsx)(n.a,{href:"#method-contains",children:"contains"})," method."]}),(0,o.jsx)(n.h4,{id:"method-each",children:(0,o.jsx)(n.code,{children:"each()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"each"})," method iterates over the models in the collection and passes each model to the lambda expression:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users = Post::whereEq("user_id", 1)->get();\n\nusers.each([](User *const user)\n{\n // ...\n});\n'})}),(0,o.jsxs)(n.p,{children:["If you would like to stop iterating through the models, you may return ",(0,o.jsx)(n.code,{children:"false"})," from your lambda expression:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"users.each([](User *const user)\n{\n if (/* condition */)\n return false;\n\n // Some logic\n\n return true;\n});\n"})}),(0,o.jsx)(n.p,{children:"You may also pass the lambda expression with two parameters, whereas the second one is an index:"}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"users.each([](User *const user, const std::size_t index)\n{\n // ...\n});\n"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"each"})," method returns an lvalue ",(0,o.jsx)(n.strong,{children:"reference"})," to the currently processed collection."]}),(0,o.jsxs)(n.p,{children:["It can be also called on ",(0,o.jsx)(n.code,{children:"ModelsCollection"})," rvalues, it returns an rvalue reference in this case."]}),(0,o.jsx)(n.h4,{id:"method-except",children:(0,o.jsx)(n.code,{children:"except()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"except"})," method returns all of the models that do not have the given primary keys:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"ModelsCollection usersResult = users.except({1, 2, 3});\n"})}),(0,o.jsxs)(n.p,{children:["All of the models are returned if the ",(0,o.jsx)(n.code,{children:"ids"})," argument is empty ",(0,o.jsx)(n.code,{children:"except({})"}),"."]}),(0,o.jsx)(n.p,{children:"The order of models in the collection is preserved."}),(0,o.jsxs)(n.p,{children:["For the inverse of ",(0,o.jsx)(n.code,{children:"except"}),", see the ",(0,o.jsx)(n.a,{href:"#method-only",children:"only"})," method."]}),(0,o.jsx)(n.h4,{id:"method-filter",children:(0,o.jsx)(n.code,{children:"filter()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"filter"})," method filters the collection using the lambda expression, keeping only those models that pass a given truth test:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'auto usersBanned = users.filter([](const User *const user)\n{\n return user->getAttribute("is_banned");\n});\n'})}),(0,o.jsx)(n.p,{children:"You may also pass the lambda expression with two parameters, whereas the second one is an index:"}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'auto usersBanned = users.filter([](const User *const user,\n const std::size_t index)\n{\n return index < 10 && user->getAttribute("is_banned");\n});\n'})}),(0,o.jsxs)(n.p,{children:["If no lambda expression is supplied, all models of the collection that are equivalent to the ",(0,o.jsx)(n.code,{children:"nullptr"})," will be removed:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"ModelsCollection usersRaw = User::findMany({1, 2});\nModelsCollection users {&usersRaw[0], nullptr, &usersRaw[1]};\n\nModelsCollection filtered = users.filter();\n\n// {1, 2}\n"})}),(0,o.jsxs)(n.p,{children:["For the inverse of ",(0,o.jsx)(n.code,{children:"filter"}),", see the ",(0,o.jsx)(n.a,{href:"#method-reject",children:"reject"})," method."]}),(0,o.jsx)(n.h4,{id:"method-find",children:(0,o.jsx)(n.code,{children:"find()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"find"})," method returns the model that has a primary key matching the given key:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"User *const user = users.find(1);\n"})}),(0,o.jsxs)(n.p,{children:["If you pass a model instance, ",(0,o.jsx)(n.code,{children:"find"})," will attempt to return a model matching the primary key:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"User *user = users.find(anotherUser);\n"})}),(0,o.jsxs)(n.p,{children:["The two overloads above also accept the second ",(0,o.jsx)(n.code,{children:"defaultModel"})," model argument, which will be returned if a model was not found in the collection, its default value is the ",(0,o.jsx)(n.code,{children:"nullptr"}),"."]}),(0,o.jsxs)(n.p,{children:["Alternatively, may pass more IDs and ",(0,o.jsx)(n.code,{children:"find"})," will return all models which have a primary key within the given unordered set:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"ModelsCollection usersMany = users.find({1, 2});\n"})}),(0,o.jsxs)(n.p,{children:["This overload internally calls the ",(0,o.jsx)(n.a,{href:"#method-only",children:(0,o.jsx)(n.code,{children:"only"})})," method."]}),(0,o.jsx)(n.h4,{id:"method-first",children:(0,o.jsx)(n.code,{children:"first()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"first"})," method returns the first model in the collection that passes a given truth test:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users {\n {{"name", "Kate"}, {"votes", 150}},\n {{"name", "John"}, {"votes", 200}},\n {{"name", "Jack"}, {"votes", 400}},\n};\n\nUser *user = users.first([](User *const user)\n{\n return user->getAttribute("votes") > 150;\n});\n\n// {{"name", "John"}, {"votes", 200}}\n'})}),(0,o.jsxs)(n.p,{children:["If no model passes a given truth test then the value of the second ",(0,o.jsx)(n.code,{children:"defaultModel"})," argument will be returned, its default value is the ",(0,o.jsx)(n.code,{children:"nullptr"}),"."]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'using NullVariant = Orm::Utils::NullVariant;\n\nUser defaultUser {{"name", NullVariant::QString()},\n {"votes", NullVariant::ULongLong()}};\n\nUser *user = users.first([](User *const user)\n{\n return user->getAttribute("votes") > 500;\n},\n &defaultUser);\n\n/*\n {{"name", NullVariant::QString()},\n {"votes", NullVariant::ULongLong()}}\n*/\n'})}),(0,o.jsxs)(n.p,{children:["You can also call all ",(0,o.jsx)(n.code,{children:"first"})," overloads provided by the ",(0,o.jsx)(n.a,{href:"https://doc.qt.io/qt/qlist.html#first",children:(0,o.jsx)(n.code,{children:"QList::first"})}),"."]}),(0,o.jsx)(n.h4,{id:"method-first-where",children:(0,o.jsx)(n.code,{children:"firstWhere()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"firstWhere"})," method returns the first model in the collection with the given column / value pair:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'using NullVariant = Orm::Utils::NullVariant;\n\nModelsCollection users {\n {{"name", "Leon"}, {"age", NullVariant::UShort()}},\n {{"name", "Jill"}, {"age", 14}},\n {{"name", "Jack"}, {"age", 23}},\n {{"name", "Jill"}, {"age", 84}},\n};\n\nauto user = users.firstWhereEq("name", "Linda");\n\n// {{"name", "Jill"}, {"age", 14}}\n'})}),(0,o.jsxs)(n.p,{children:["You may also call the ",(0,o.jsx)(n.code,{children:"firstWhere"})," method with a comparison operator:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'users.firstWhere("age", ">=", 18);\n\n// {{"name", "Jack"}, {"age", 23}}\n'})}),(0,o.jsx)(n.h4,{id:"method-fresh",children:(0,o.jsx)(n.code,{children:"fresh()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"fresh"})," method retrieves a fresh instance of each model in the collection from the database. In addition, any specified relationships will be eager loaded:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'auto usersFresh = users.fresh();\n\nauto usersFresh = users.fresh("comments");\n\nauto usersFresh = users.fresh("posts:id,name");\n\nauto usersFresh = users.fresh({"comments", "posts:id,name"});\n'})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"relations"})," argument format is the same as for TinyBuilder's ",(0,o.jsx)(n.a,{href:"/tinyorm/relationships#lazy-eager-loading",children:(0,o.jsx)(n.code,{children:"load"})})," method."]}),(0,o.jsx)(n.h4,{id:"method-implode",children:(0,o.jsx)(n.code,{children:"implode()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"implode"}),' method joins attributes by the given column and the "glue" string you wish to place between the values:']}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection products {\n {{"product", "Desk"}, {"price", 200}},\n {{"product", "Chair"}, {"price", 100}},\n};\n\nproducts.implode("product", ", ");\n\n// {Desk, Chair}\n'})}),(0,o.jsx)(n.p,{children:'The default "glue" value is an empty string "".'}),(0,o.jsx)(n.h4,{id:"method-isempty",children:(0,o.jsx)(n.code,{children:"isEmpty()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"isEmpty"})," method returns ",(0,o.jsx)(n.code,{children:"true"})," if the collection is empty; otherwise, ",(0,o.jsx)(n.code,{children:"false"})," is returned:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"ModelsCollection().isEmpty();\n\n// true\n"})}),(0,o.jsx)(n.h4,{id:"method-isnotempty",children:(0,o.jsx)(n.code,{children:"isNotEmpty()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"isNotEmpty"})," method returns ",(0,o.jsx)(n.code,{children:"true"})," if the collection is not empty; otherwise, ",(0,o.jsx)(n.code,{children:"false"})," is returned:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"ModelsCollection().isNotEmpty();\n\n// false\n"})}),(0,o.jsx)(n.h4,{id:"method-last",children:(0,o.jsx)(n.code,{children:"last()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"last"})," method returns the last model in the collection that passes a given truth test:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users {\n {{"name", "Kate"}, {"votes", 150}},\n {{"name", "John"}, {"votes", 200}},\n {{"name", "Jack"}, {"votes", 400}},\n {{"name", "Rose"}, {"votes", 350}},\n};\n\nUser *user = users.last([](User *const user)\n{\n return user->getAttribute("votes") < 300;\n});\n\n// {{"name", "John"}, {"votes", 200}}\n'})}),(0,o.jsxs)(n.p,{children:["If no model passes a given truth test then the value of the second ",(0,o.jsx)(n.code,{children:"defaultModel"})," argument will be returned, its default value is the ",(0,o.jsx)(n.code,{children:"nullptr"}),"."]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'using NullVariant = Orm::Utils::NullVariant;\n\nUser defaultUser {{"name", NullVariant::QString()},\n {"votes", NullVariant::ULongLong()}};\n\nUser *user = users.last([](User *const user)\n{\n return user->getAttribute("votes") < 100;\n},\n &defaultUser);\n\n/*\n {{"name", NullVariant::QString()},\n {"votes", NullVariant::ULongLong()}}\n*/\n'})}),(0,o.jsxs)(n.p,{children:["You can also call all ",(0,o.jsx)(n.code,{children:"last"})," overloads provided by the ",(0,o.jsx)(n.a,{href:"https://doc.qt.io/qt/qlist.html#last",children:(0,o.jsx)(n.code,{children:"QList::last"})}),"."]}),(0,o.jsx)(n.h4,{id:"method-load",children:(0,o.jsx)(n.code,{children:"load()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"load"})," method eager loads the given relationships for all models in the collection:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'users.load({"comments", "posts"});\n\nusers.load("comments.author");\n\nusers.load({{"comments"}, {"posts", [](auto &query)\n{\n query.whereEq("active", true);\n}}});\n'})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"relations"})," argument format is the same as for TinyBuilder's ",(0,o.jsx)(n.a,{href:"/tinyorm/relationships#lazy-eager-loading",children:(0,o.jsx)(n.code,{children:"load"})})," method."]}),(0,o.jsx)(n.h4,{id:"method-map",children:(0,o.jsx)(n.code,{children:"map()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"map"})," method iterates through the collection and passes a ",(0,o.jsx)(n.strong,{children:"copy"})," of each model to the given lambda expression. The lambda expression is free to modify the model and return it, thus forming a new collection of modified models:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users {\n {{"name", "John"}, {"votes", 200}},\n {{"name", "Jack"}, {"votes", 400}},\n};\n\nauto usersAdded = users.map([](User &&userCopy)\n{\n if (userCopy.getAttribute("name") == "John")\n userCopy["votes"] = userCopy.getAttribute("votes") + 1;\n\n return std::move(userCopy);\n});\n\n/*\n {\n {{"name", "John"}, {"price", 201}},\n {{"name", "Jack"}, {"price", 400}},\n }\n*/\n'})}),(0,o.jsxs)(n.p,{children:["The second ",(0,o.jsx)(n.code,{children:"map"})," overload allows to return the ",(0,o.jsx)(n.code,{children:"QVector"}),":"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'QVector usersAdded = users.map([](User &&userCopy)\n{\n const auto votesRef = userCopy["votes"];\n\n if (userCopy.getAttribute("name") == "John")\n votesRef = userCopy.getAttribute("votes") + 1;\n\n return votesRef->value();\n});\n\n// {201, 400}\n'})}),(0,o.jsxs)(n.p,{children:["Both overloads allow to pass the lambda expression with two arguments, whereas the second argument can be an index of the ",(0,o.jsx)(n.code,{children:"std::size_t"})," type."]}),(0,o.jsx)(n.admonition,{type:"caution",children:(0,o.jsxs)(n.p,{children:["Like most other collection methods, ",(0,o.jsx)(n.code,{children:"map"})," returns a new collection instance; it does not modify the collection it is called on. If you want to modify the original collection in place, use the ",(0,o.jsx)(n.a,{href:"#method-each",children:(0,o.jsx)(n.code,{children:"each"})})," method."]})}),(0,o.jsx)(n.admonition,{type:"info",children:(0,o.jsxs)(n.p,{children:["The model copy is passed to the lambda expression even if the ",(0,o.jsx)(n.code,{children:"map"})," iterates over a collection of model pointers ",(0,o.jsx)(n.code,{children:"ModelsCollection"}),". The models are dereferenced behind the scene."]})}),(0,o.jsx)(n.h4,{id:"method-mapwithkeys",children:(0,o.jsx)(n.code,{children:"mapWithKeys()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"mapWithKeys"})," method iterates through the collection and passes each model to the given lambda expression. It returns the ",(0,o.jsx)(n.code,{children:"std::unordered_map"})," and the lambda expression should return the ",(0,o.jsx)(n.code,{children:"std::pair"})," containing a single column / value pair:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users {\n {{"id", 1}, {"name", "John"}, {"email", "john@example.com"}},\n {{"id", 2}, {"name", "Jill"}, {"email", "jill@example.com"}},\n};\n\nauto usersMap = users.mapWithKeys(\n [](User *const user) -> std::pair\n{\n return {user->getKeyCasted(), user->getAttribute("name")};\n});\n\n// {{1, \'John\'}, {2, \'Jill\'}}\n'})}),(0,o.jsx)(n.p,{children:"You can also map IDs to the model pointers:"}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"auto usersMap = users.mapWithKeys(\n [](User *const user) -> std::pair\n{\n return {user->getKeyCasted(), user};\n});\n"})}),(0,o.jsx)(n.h4,{id:"method-mapwithmodelkeys",children:(0,o.jsx)(n.code,{children:"mapWithModelKeys()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"mapWithModelKeys"})," maps the primary keys to the ",(0,o.jsx)(n.code,{children:"Model *"}),", it returns the ",(0,o.jsx)(n.code,{children:"std::unordered_map"}),":"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"auto usersMap = users.mapWithModelKeys();\n"})}),(0,o.jsx)(n.h4,{id:"method-modelkeys",children:(0,o.jsx)(n.code,{children:"modelKeys()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"modelKeys"})," method returns the primary keys for all models in the collection:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users {\n {{"id", 1}, {"name", "John"}},\n {{"id", 2}, {"name", "Jill"}},\n {{"id", 3}, {"name", "Kate"}},\n {{"id", 5}, {"name", "Rose"}},\n};\n\nusers.modelKeys(); // Returns QVector\nusers.modelKeys();\n\n// {1, 2, 3, 5}\n'})}),(0,o.jsx)(n.h4,{id:"method-only",children:(0,o.jsx)(n.code,{children:"only()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"only"})," method returns all of the models that have the given primary keys:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"ModelsCollection usersResult = users.only({1, 2, 3});\n"})}),(0,o.jsxs)(n.p,{children:["An empty collection is returned if the ",(0,o.jsx)(n.code,{children:"ids"})," argument is empty ",(0,o.jsx)(n.code,{children:"only({})"}),"."]}),(0,o.jsx)(n.p,{children:"The order of models in the collection is preserved."}),(0,o.jsxs)(n.p,{children:["For the inverse of ",(0,o.jsx)(n.code,{children:"only"}),", see the ",(0,o.jsx)(n.a,{href:"#method-except",children:"except"})," method."]}),(0,o.jsx)(n.h4,{id:"method-pluck",children:(0,o.jsx)(n.code,{children:"pluck()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"pluck"})," method retrieves all of the values for a given column, the following overload returns the ",(0,o.jsx)(n.code,{children:"QVector"}),":"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection products {\n {{"id", 1}, {"name", "Desk"}},\n {{"id", 2}, {"name", "Chair"}},\n};\n\nauto plucked = products.pluck("name");\n\n// {Desk, Chair}\n'})}),(0,o.jsxs)(n.p,{children:["The second overload allows returning the custom type ",(0,o.jsx)(n.code,{children:"QVector"}),":"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'auto plucked = products.pluck("name");\n'})}),(0,o.jsxs)(n.p,{children:["You may also specify how you wish the resulting collection to be keyed, this overload returns the ",(0,o.jsx)(n.code,{children:"std::map"}),":"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'auto plucked = products.pluck("name", "id");\n\n// {{1, "Desk"}, {2, "Chair"}}\n'})}),(0,o.jsx)(n.p,{children:"If duplicate keys exist, the last matching attribute will be inserted into the plucked collection:"}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection collection {\n {{"brand", "Tesla"}, {"color", "red"}},\n {{"brand", "Pagani"}, {"color", "white"}},\n {{"brand", "Tesla"}, {"color", "black"}},\n {{"brand", "Pagani"}, {"color", "orange"}},\n};\n\nauto plucked = collection.pluck("color", "brand");\n\n// {{\'Tesla\', \'black\'}, {\'Pagani\', \'orange"}}\n'})}),(0,o.jsx)(n.h4,{id:"method-reject",children:(0,o.jsx)(n.code,{children:"reject()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"reject"})," method filters the collection using the given lambda expression. The lambda should return ",(0,o.jsx)(n.code,{children:"true"})," if the model should be removed from the resulting collection:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'auto usersWithNote = users.reject([](const User *const user)\n{\n return user->getAttribute("note").isNull();\n});\n'})}),(0,o.jsxs)(n.p,{children:["You may also pass the lambda expression with two arguments, whereas the second argument can be an index of the ",(0,o.jsx)(n.code,{children:"std::size_t"})," type."]}),(0,o.jsxs)(n.p,{children:["For the inverse of the ",(0,o.jsx)(n.code,{children:"reject"})," method, see the ",(0,o.jsx)(n.a,{href:"#method-filter",children:(0,o.jsx)(n.code,{children:"filter"})})," method."]}),(0,o.jsx)(n.h4,{id:"method-sort",children:(0,o.jsx)(n.code,{children:"sort()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"sort"})," method sorts the models collection by primary keys:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users {\n {{"id", 2}, {"name", "Kate"}},\n {{"id", 3}, {"name", "John"}},\n {{"id", 1}, {"name", "Jack"}},\n};\n\nauto sorted = users.sort();\n\n/*\n {\n {{"id", 1}, {"name", "Jack"}},\n {{"id", 2}, {"name", "Kate"}},\n {{"id", 3}, {"name", "John"}},\n }\n*/\n'})}),(0,o.jsxs)(n.p,{children:["You may pass a predicate and projection callbacks to the ",(0,o.jsx)(n.code,{children:"sort"})," method with your own algorithms. Refer to the CPP reference documentation on ",(0,o.jsx)(n.a,{href:"https://en.cppreference.com/w/cpp/algorithm/ranges/sort",children:(0,o.jsx)(n.code,{children:"ranges::sort"})}),", which is what the ",(0,o.jsx)(n.code,{children:"sort"})," method calls internally."]}),(0,o.jsxs)(n.p,{children:["You can eg. sort by multiple columns, for an alternative method of multi-column sorting look at ",(0,o.jsx)(n.a,{href:"#method-sortby",children:"sortBy"}),":"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users {\n {{"name", "Kate"}, {"votes", 350}},\n {{"name", "John"}, {"votes", 200}},\n {{"name", "John"}, {"votes", 150}},\n {{"name", "Kate"}, {"votes", 200}},\n};\n\nauto sorted = users.sort([](const User *const left,\n const User *const right)\n{\n const auto leftValue = left->getAttribute("name");\n const auto rightValue = right->getAttribute("name");\n\n if (leftValue == rightValue)\n return left->getAttribute("votes") <\n right->getAttribute("votes");\n\n return leftValue < rightValue;\n});\n\n/*\n {\n {{"name", "John"}, {"votes", 150}},\n {{"name", "John"}, {"votes", 200}},\n {{"name", "Kate"}, {"votes", 200}},\n {{"name", "Kate"}, {"votes", 350}},\n }\n*/\n'})}),(0,o.jsx)(n.p,{children:"The order of equal elements is not guaranteed to be preserved."}),(0,o.jsx)(n.admonition,{type:"info",children:(0,o.jsxs)(n.p,{children:["You can use the ",(0,o.jsx)(n.a,{href:"#method-stablesort",children:"stable"})," sort method variants to preserve the order of equal models."]})}),(0,o.jsx)(n.h4,{id:"method-sortby",children:(0,o.jsx)(n.code,{children:"sortBy()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"sortBy"})," method sorts the collection by the given column, this overload needs the template argument so it can cast the attribute value before comparing:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users {\n {{"name", "Kate"}, {"votes", 150}},\n {{"name", "John"}, {"votes", 200}},\n {{"name", "Jack"}, {"votes", 400}},\n};\n\nauto sorted = users.sortBy("name");\n\n/*\n {\n {{"name", "Jack"}, {"votes", 400}},\n {{"name", "John"}, {"votes", 200}},\n {{"name", "Kate"}, {"votes", 150}},\n }\n*/\n'})}),(0,o.jsx)(n.p,{children:"You may pass the projection callback to determine how to sort the collection's models:"}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'auto sorted = users.sortBy([](User *const user)\n{\n return user->getAttribute("votes");\n});\n\n/*\n {\n {{"name", "Kate"}, {"votes", 150}},\n {{"name", "John"}, {"votes", 200}},\n {{"name", "Jack"}, {"votes", 400}},\n }\n*/\n'})}),(0,o.jsxs)(n.p,{children:["If you would like to sort your collection by multiple columns, you may pass a vector of comparison lambda expressions that define each sort operation to the ",(0,o.jsx)(n.code,{children:"sortBy"})," method, in the following example is the ",(0,o.jsx)(n.code,{children:"name"})," column sorted in ascending order and the second ",(0,o.jsx)(n.code,{children:"votes"})," column is sorted in descending order:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'using AttributeUtils = Orm::Tiny::Utils::Attribute;\n\nModelsCollection users {\n {{"name", "Kate"}, {"votes", 350}},\n {{"name", "John"}, {"votes", 200}},\n {{"name", "John"}, {"votes", 150}},\n {{"name", "Kate"}, {"votes", 200}},\n};\n\nauto sorted = users.sortBy({\n [](const User *const left, const User *const right)\n {\n return AttributeUtils::compareForSortBy(\n left->getAttribute("name"),\n right->getAttribute("name"));\n },\n [](const User *const left, const User *const right)\n {\n return AttributeUtils::compareForSortByDesc(\n left->getAttribute("votes"),\n right->getAttribute("votes"));\n },\n});\n\n/*\n {\n {{"name", "John"}, {"votes", 200}},\n {{"name", "John"}, {"votes", 150}},\n {{"name", "Kate"}, {"votes", 350}},\n {{"name", "Kate"}, {"votes", 200}},\n }\n*/\n'})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"AttributeUtils::compareForSortBy"})," and ",(0,o.jsx)(n.code,{children:"compareForSortByDesc"})," methods are helper methods, they are needed because the Qt framework doesn't define ",(0,o.jsx)(n.code,{children:"<=>"})," spaceship operator on its types, it doesn't support the three-way comparison."]}),(0,o.jsx)(n.p,{children:"The order of equal elements is not guaranteed to be preserved."}),(0,o.jsx)(n.h4,{id:"method-sortbydesc",children:(0,o.jsx)(n.code,{children:"sortByDesc()"})}),(0,o.jsxs)(n.p,{children:["This method has the same signature as the ",(0,o.jsx)(n.a,{href:"#method-sortby",children:(0,o.jsx)(n.code,{children:"sortBy"})})," method but will sort the collection in the opposite order."]}),(0,o.jsx)(n.p,{children:"The order of equal elements is not guaranteed to be preserved."}),(0,o.jsx)(n.h4,{id:"method-sortdesc",children:(0,o.jsx)(n.code,{children:"sortDesc()"})}),(0,o.jsxs)(n.p,{children:["This method will sort the collection in the opposite order as the ",(0,o.jsx)(n.a,{href:"#method-sort",children:(0,o.jsx)(n.code,{children:"sort"})})," method:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users {\n {{"id", 2}, {"name", "Kate"}},\n {{"id", 3}, {"name", "John"}},\n {{"id", 1}, {"name", "Jack"}},\n};\n\nauto sorted = users.sortDesc();\n\n/*\n {\n {{"id", 3}, {"name", "John"}},\n {{"id", 2}, {"name", "Kate"}},\n {{"id", 1}, {"name", "Jack"}},\n }\n*/\n'})}),(0,o.jsx)(n.p,{children:"The order of equal elements is not guaranteed to be preserved."}),(0,o.jsx)(n.h4,{id:"method-stablesort",children:(0,o.jsx)(n.code,{children:"stableSort()"})}),(0,o.jsxs)(n.p,{children:["This method has the same signature as the ",(0,o.jsx)(n.a,{href:"#method-sort",children:(0,o.jsx)(n.code,{children:"sort"})})," method but will preserve the order of equal elements (guaranteed to be preserved)."]}),(0,o.jsx)(n.h4,{id:"method-stablesortby",children:(0,o.jsx)(n.code,{children:"stableSortBy()"})}),(0,o.jsxs)(n.p,{children:["This method has the same signature as the ",(0,o.jsx)(n.a,{href:"#method-sortby",children:(0,o.jsx)(n.code,{children:"sortBy"})})," method but will preserve the order of equal elements (guaranteed to be preserved)."]}),(0,o.jsx)(n.h4,{id:"method-stablesortbydesc",children:(0,o.jsx)(n.code,{children:"stableSortByDesc()"})}),(0,o.jsxs)(n.p,{children:["This method has the same signature as the ",(0,o.jsx)(n.a,{href:"#method-sortbydesc",children:(0,o.jsx)(n.code,{children:"sortByDesc"})})," method but will sort the collection in the opposite order and preserve the order of equal elements (guaranteed to be preserved)."]}),(0,o.jsx)(n.h4,{id:"method-stablesortdesc",children:(0,o.jsx)(n.code,{children:"stableSortDesc()"})}),(0,o.jsxs)(n.p,{children:["This method has the same signature as the ",(0,o.jsx)(n.a,{href:"#method-sortdesc",children:(0,o.jsx)(n.code,{children:"sortDesc"})})," method but will sort the collection in the opposite order and preserve the order of equal elements (guaranteed to be preserved)."]}),(0,o.jsx)(n.h4,{id:"method-tap",children:(0,o.jsx)(n.code,{children:"tap()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"tap"}),' method passes a collection to the given lambda expression, allowing you to "tap" into the collection at a specific point and do something with the models while not affecting the collection itself:']}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users {\n {{"id", 2}, {"name", "Kate"}},\n {{"id", 3}, {"name", "John"}},\n {{"id", 1}, {"name", "Jack"}},\n};\n\nusers.sort()\n .tap([](/*const */ModelsCollection &usersRef)\n{\n qDebug() << "IDs after sorting:"\n << usersRef.template modelKeys();\n})\n .value("id");\n\n// 1\n'})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"tap"})," method returns an lvalue ",(0,o.jsx)(n.strong,{children:"reference"})," to the currently processed collection."]}),(0,o.jsxs)(n.p,{children:["It can be also called on ",(0,o.jsx)(n.code,{children:"ModelsCollection"})," rvalues, it returns an rvalue reference in this case."]}),(0,o.jsx)(n.h4,{id:"method-tobase",children:(0,o.jsx)(n.code,{children:"toBase()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"toBase"})," method returns a copy of the underlying vector represented by the collection:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"QVector = users.toBase();\n"})}),(0,o.jsx)(n.admonition,{type:"note",children:(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.a,{href:"#method-tobase",children:(0,o.jsx)(n.code,{children:"toBase"})})," is an alias to the ",(0,o.jsx)(n.code,{children:"all"})," method."]})}),(0,o.jsx)(n.h4,{id:"method-tojson",children:(0,o.jsx)(n.code,{children:"toJson()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"toJson"})," method converts the collection of models with all nested relations into a JSON serialized ",(0,o.jsx)(n.a,{href:"https://doc.qt.io/qt/qbytearray.html",children:(0,o.jsx)(n.code,{children:"QByteArray"})}),"."]}),(0,o.jsxs)(n.p,{children:["It returns an empty array for empty ",(0,o.jsx)(n.code,{children:"many"})," type relations and ",(0,o.jsx)(n.code,{children:"null"})," for empty ",(0,o.jsx)(n.code,{children:"one"})," type relations."]}),(0,o.jsx)(n.admonition,{type:"info",children:(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"toJson"})," method accepts the ",(0,o.jsx)(n.a,{href:"https://doc.qt.io/qt/qjsondocument.html#JsonFormat-enum",children:(0,o.jsx)(n.code,{children:"QJsonDocument::JsonFormat"})}),", possible values are ",(0,o.jsx)(n.code,{children:"QJsonDocument::Indented"})," or ",(0,o.jsx)(n.code,{children:"QJsonDocument::Compact"}),"."]})}),(0,o.jsx)(n.h4,{id:"method-tojsonarray",children:(0,o.jsx)(n.code,{children:"toJsonArray()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"toJsonArray"})," method converts the collection of models with all nested relations into a ",(0,o.jsx)(n.a,{href:"https://doc.qt.io/qt/qjsonarray.html",children:(0,o.jsx)(n.code,{children:"QJsonArray"})}),"."]}),(0,o.jsx)(n.h4,{id:"method-tojsondocument",children:(0,o.jsx)(n.code,{children:"toJsonDocument()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"toJsonDocument"})," method converts the collection of models with all nested relations into a ",(0,o.jsx)(n.a,{href:"https://doc.qt.io/qt/qjsondocument.html",children:(0,o.jsx)(n.code,{children:"QJsonDocument"})}),"."]}),(0,o.jsx)(n.h4,{id:"method-tomap",children:(0,o.jsx)(n.code,{children:"toMap()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"toMap"})," method converts the collection of models with all nested relations into an attributes map ",(0,o.jsx)(n.code,{children:"QVector"}),"."]}),(0,o.jsxs)(n.p,{children:["It returns an empty ",(0,o.jsx)(n.code,{children:"QVariantList"})," for empty ",(0,o.jsx)(n.code,{children:"many"})," type relations and a null ",(0,o.jsx)("abbr",{title:"QVariant::fromValue(nullptr)",children:(0,o.jsx)(n.code,{children:"QVariant"})})," for empty ",(0,o.jsx)(n.code,{children:"one"})," type relations."]}),(0,o.jsx)(n.h4,{id:"method-tomapvariantlist",children:(0,o.jsx)(n.code,{children:"toMapVariantList()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"toMapVariantList"})," method converts the collection of models with all nested relations into an attributes map, but it returns the ",(0,o.jsx)("abbr",{title:"QList",children:(0,o.jsx)(n.code,{children:"QVariantList"})})," instead of the ",(0,o.jsx)(n.code,{children:"QVector"}),"."]}),(0,o.jsxs)(n.p,{children:["It returns an empty ",(0,o.jsx)(n.code,{children:"QVariantList"})," for empty ",(0,o.jsx)(n.code,{children:"many"})," type relations and a null ",(0,o.jsx)("abbr",{title:"QVariant::fromValue(nullptr)",children:(0,o.jsx)(n.code,{children:"QVariant"})})," for empty ",(0,o.jsx)(n.code,{children:"one"})," type relations."]}),(0,o.jsx)(n.admonition,{type:"note",children:(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"toMapVariantList"})," method is internally needed by the ",(0,o.jsx)(n.code,{children:"toJson"})," related methods."]})}),(0,o.jsx)(n.h4,{id:"method-toquery",children:(0,o.jsx)(n.code,{children:"toQuery()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"toQuery"})," method returns the ",(0,o.jsx)(n.code,{children:"TinyBuilder"})," instance containing a ",(0,o.jsx)(n.code,{children:"whereIn"})," constraint with the collection of models' primary keys:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'using Models::User;\n\nModelsCollection users = User::whereEq("status", "VIP")->get();\n\nusers.toQuery()->update({\n {"status", "Administrator"},\n});\n'})}),(0,o.jsx)(n.h4,{id:"method-tovector",children:(0,o.jsx)(n.code,{children:"toVector()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"toVector"})," method converts the collection of models with all nested relations into an attributes vector ",(0,o.jsx)(n.code,{children:"QVector>"}),"."]}),(0,o.jsxs)(n.p,{children:["It returns an empty ",(0,o.jsx)(n.code,{children:"QVariantList"})," for empty ",(0,o.jsx)(n.code,{children:"many"})," type relations and a null ",(0,o.jsx)("abbr",{title:"QVariant::fromValue(nullptr)",children:(0,o.jsx)(n.code,{children:"QVariant"})})," for empty ",(0,o.jsx)(n.code,{children:"one"})," type relations."]}),(0,o.jsx)(n.h4,{id:"method-tovectorvariantlist",children:(0,o.jsx)(n.code,{children:"toVectorVariantList()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"toVectorVariantList"})," method converts the collection of models with all nested relations into an attributes vector, but it returns the ",(0,o.jsx)("abbr",{title:"QList",children:(0,o.jsx)(n.code,{children:"QVariantList"})})," instead of the ",(0,o.jsx)(n.code,{children:"QVector>"}),"."]}),(0,o.jsxs)(n.p,{children:["It returns an empty ",(0,o.jsx)(n.code,{children:"QVariantList"})," for empty ",(0,o.jsx)(n.code,{children:"many"})," type relations and a null ",(0,o.jsx)("abbr",{title:"QVariant::fromValue(nullptr)",children:(0,o.jsx)(n.code,{children:"QVariant"})})," for empty ",(0,o.jsx)(n.code,{children:"one"})," type relations."]}),(0,o.jsx)(n.admonition,{type:"note",children:(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"toVectorVariantList"})," method is internally needed by the ",(0,o.jsx)(n.code,{children:"toJson"})," related methods."]})}),(0,o.jsx)(n.h4,{id:"method-unique",children:(0,o.jsx)(n.code,{children:"unique()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"unique"})," method returns all of the unique models in the ",(0,o.jsx)(n.strong,{children:"sorted"})," collection. Any models with the same primary key as another model in the collection are removed:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users {\n {{"id", 2}, {"name", "Kate"}},\n {{"id", 1}, {"name", "Jack"}},\n {{"id", 3}, {"name", "John"}},\n {{"id", 1}, {"name", "Jack"}},\n};\n\nauto unique = users.unique();\n\n/*\n {\n {{"id", 1}, {"name", "Jack"}},\n {{"id", 2}, {"name", "Kate"}},\n {{"id", 3}, {"name", "John"}},\n }\n*/\n'})}),(0,o.jsxs)(n.p,{children:["It sorts the collection internally because the ",(0,o.jsx)(n.a,{href:"https://en.cppreference.com/w/cpp/algorithm/ranges/unique",children:(0,o.jsx)(n.code,{children:"ranges::unique"})})," can correctly operate only on the sorted container. You can disable it by passing ",(0,o.jsx)(n.code,{children:"false"})," using the first ",(0,o.jsx)(n.code,{children:"sort"})," parameter:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'auto unique = users.sort().unique(false);\n\n/*\n {\n {{"id", 1}, {"name", "Jack"}},\n {{"id", 2}, {"name", "Kate"}},\n {{"id", 3}, {"name", "John"}},\n }\n*/\n'})}),(0,o.jsx)(n.h4,{id:"method-uniqueby",children:(0,o.jsx)(n.code,{children:"uniqueBy()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"uniqueBy"})," method returns all of the unique models in the ",(0,o.jsx)(n.strong,{children:"sorted"})," collection by the given column. Any models with the same column value as another model in the collection are removed. It needs the template argument, so it can cast the attribute value before comparing:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users {\n {{"name", "Kate"}},\n {{"name", "Jack"}},\n {{"name", "John"}},\n {{"name", "Jack"}},\n};\n\nauto unique = users.uniqueBy("name");\n\n/*\n {\n {{"name", "Jack"}},\n {{"name", "John"}},\n {{"name", "Kate"}},\n }\n*/\n'})}),(0,o.jsxs)(n.p,{children:["It sorts the collection internally because the ",(0,o.jsx)(n.a,{href:"https://en.cppreference.com/w/cpp/algorithm/ranges/unique",children:(0,o.jsx)(n.code,{children:"ranges::unique"})})," can correctly operate only on the sorted container. You can disable it by passing ",(0,o.jsx)(n.code,{children:"false"})," using the second ",(0,o.jsx)(n.code,{children:"sort"})," parameter:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'auto unique = users.sortBy("name")\n .uniqueBy("name", false);\n\n/*\n {\n {{"name", "Jack"}},\n {{"name", "John"}},\n {{"name", "Kate"}},\n }\n*/\n'})}),(0,o.jsx)(n.h4,{id:"method-uniquerelaxed",children:(0,o.jsx)(n.code,{children:"uniqueRelaxed()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"uniqueRelaxed"})," method returns all of the unique models in the collection, it doesn't need a sorted collection. Any models with the same primary key as another model in the collection are removed:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users {\n {{"id", 2}, {"name", "Kate"}},\n {{"id", 1}, {"name", "Jack"}},\n {{"id", 3}, {"name", "John"}},\n {{"id", 1}, {"name", "Jack"}},\n};\n\nauto unique = users.uniqueRelaxed();\n\n/*\n {\n {{"id", 2}, {"name", "Kate"}},\n {{"id", 1}, {"name", "Jack"}},\n {{"id", 3}, {"name", "John"}},\n }\n*/\n'})}),(0,o.jsx)(n.h4,{id:"method-uniquerelaxedby",children:(0,o.jsx)(n.code,{children:"uniqueRelaxedBy()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"uniqueRelaxedBy"})," method returns all of the unique models in the collection by the given column, it doesn't need a sorted collection, but it needs the template argument, so it can cast the attribute value before comparing:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users {\n {{"name", "Kate"}},\n {{"name", "Jack"}},\n {{"name", "John"}},\n {{"name", "Jack"}},\n};\n\nauto unique = users.uniqueRelaxedBy("name");\n\n/*\n {\n {{"name", "Kate"}},\n {{"name", "Jack"}},\n {{"name", "John"}},\n }\n*/\n'})}),(0,o.jsx)(n.h4,{id:"method-value",children:(0,o.jsx)(n.code,{children:"value()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"value"})," method retrieves a given value from the first model of the collection:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users {\n {{"name", "John"}, {"votes", 200}},\n {{"name", "Jack"}, {"votes", 400}},\n};\n\nQVariant votes = users.value("votes");\n\n// 200\n'})}),(0,o.jsxs)(n.p,{children:["Alternatively, you can cast an obtained ",(0,o.jsx)(n.code,{children:"QVariant"})," value to the given type by the second ",(0,o.jsx)(n.code,{children:"value"})," overload:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'quint64 votes = users.value("votes");\n'})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"value"})," method also accepts the second ",(0,o.jsx)(n.code,{children:"defaultValue"})," argument, which will be returned if a collection is empty, the first model is ",(0,o.jsx)(n.code,{children:"nullptr"}),", or a model doesn't contain the given column:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'auto votes = ModelsCollection().value("votes", 0);\n\n// 0\n'})}),(0,o.jsxs)(n.p,{children:["You can also call all ",(0,o.jsx)(n.code,{children:"value"})," overloads provided by the ",(0,o.jsx)(n.a,{href:"https://doc.qt.io/qt/qlist.html#value",children:(0,o.jsx)(n.code,{children:"QList::value"})}),"."]}),(0,o.jsx)(n.h4,{id:"method-where",children:(0,o.jsx)(n.code,{children:"where()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"where"})," method filters the collection by a given column / value pair:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection products {\n {{"product", "Desk"}, {"price", 200}},\n {{"product", "Chair"}, {"price", 100}},\n {{"product", "Bookcase"}, {"price", 150}},\n {{"product", "Door"}, {"price", 100}},\n};\n\nauto filtered = products.where("price", "=", 100);\n\n/*\n {\n {{"product", "Chair"}, {"price", 100}},\n {{"product", "Door"}, {"price", 100}},\n }\n*/\n'})}),(0,o.jsxs)(n.p,{children:["For convenience, if you want to verify that a column is ",(0,o.jsx)(n.code,{children:"="})," to a given value, you may call ",(0,o.jsx)(n.code,{children:"whereEq"})," method. Similar ",(0,o.jsx)(n.code,{children:"XxxEq"})," methods are also defined for other commands:"]}),(0,o.jsx)(n.p,{children:'auto filtered = products.whereEq("price", 100);'}),(0,o.jsxs)(n.p,{children:["Optionally, you may pass a comparison operator as the second argument.",(0,o.jsx)("br",{}),"Supported operators are ",(0,o.jsx)(n.code,{children:"="}),", ",(0,o.jsx)(n.code,{children:"!="}),", ",(0,o.jsx)(n.code,{children:"<"}),", ",(0,o.jsx)(n.code,{children:">"}),", ",(0,o.jsx)(n.code,{children:"<="}),", and ",(0,o.jsx)(n.code,{children:">="}),":"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection products {\n {{"product", "Desk"}, {"price", 200}},\n {{"product", "Chair"}, {"price", 100}},\n {{"product", "Bookcase"}, {"price", 150}},\n {{"product", "Door"}, {"price", 250}},\n};\n\nauto filtered = products.where("price", ">", 150);\n\n/*\n {\n {{"product", "Desk"}, {"price", 200}},\n {{"product", "Door"}, {"price", 250}},\n }\n*/\n'})}),(0,o.jsx)(n.h4,{id:"method-wherebetween",children:(0,o.jsx)(n.code,{children:"whereBetween()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"whereBetween"})," method filters the collection by determining if a specified models' attribute value is within a given range:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection products {\n {{"product", "Desk"}, {"price", 200}},\n {{"product", "Chair"}, {"price", 80}},\n {{"product", "Bookcase"}, {"price", 150}},\n {{"product", "Pencil"}, {"price", 30}},\n {{"product", "Door"}, {"price", 100}},\n};\n\nauto filtered = products.whereBetween("price", {100, 200});\n\n/*\n {\n {{"product", "Desk"}, {"price", 200}},\n {{"product", "Bookcase"}, {"price", 150}},\n {{"product", "Door"}, {"price", 100}},\n }\n*/\n'})}),(0,o.jsx)(n.h4,{id:"method-wherein",children:(0,o.jsx)(n.code,{children:"whereIn()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"whereIn"})," method filters models from the collection that have a specified attribute value that is contained within the given unordered set:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection products {\n {{"product", "Desk"}, {"price", 200}},\n {{"product", "Chair"}, {"price", 100}},\n {{"product", "Bookcase"}, {"price", 150}},\n {{"product", "Door"}, {"price", 250}},\n};\n\nauto filtered = products.whereIn("price", {100, 200});\n\n/*\n {\n {{"product", "Desk"}, {"price", 200}},\n {{"product", "Chair"}, {"price", 100}},\n }\n*/\n'})}),(0,o.jsxs)(n.p,{children:["An empty collection is returned if the ",(0,o.jsx)(n.code,{children:"values"})," argument is empty ",(0,o.jsx)(n.code,{children:'whereIn("price", {})'}),"."]}),(0,o.jsx)(n.p,{children:"The order of models in the collection is preserved."}),(0,o.jsx)(n.h4,{id:"method-wherenotbetween",children:(0,o.jsx)(n.code,{children:"whereNotBetween()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"whereNotBetween"})," method filters the collection by determining if a specified models' attribute value is outside of a given range:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection products {\n {{"product", "Desk"}, {"price", 200}},\n {{"product", "Chair"}, {"price", 80}},\n {{"product", "Bookcase"}, {"price", 150}},\n {{"product", "Pencil"}, {"price", 30}},\n {{"product", "Door"}, {"price", 100}},\n};\n\nauto filtered = products.whereNotBetween("price", {100, 200});\n\n/*\n {\n {{"product", "Chair"}, {"price", 80}},\n {{"product", "Pencil"}, {"price", 30}},\n }\n*/\n'})}),(0,o.jsx)(n.h4,{id:"method-wherenotin",children:(0,o.jsx)(n.code,{children:"whereNotIn()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"whereNotIn"})," method removes models from the collection that have a specified attribute value that is contained within the given unordered set:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection products {\n {{"product", "Desk"}, {"price", 200}},\n {{"product", "Chair"}, {"price", 100}},\n {{"product", "Bookcase"}, {"price", 150}},\n {{"product", "Door"}, {"price", 250}},\n};\n\nauto filtered = products.whereNotIn("price", {100, 200});\n\n/*\n {\n {{"product", "Bookcase"}, {"price", 150}},\n {{"product", "Door"}, {"price", 250}},\n }\n*/\n'})}),(0,o.jsxs)(n.p,{children:["All of the models are returned if the ",(0,o.jsx)(n.code,{children:"values"})," argument is empty ",(0,o.jsx)(n.code,{children:'whereNotIn("price", {})'}),"."]}),(0,o.jsx)(n.p,{children:"The order of models in the collection is preserved."}),(0,o.jsx)(n.h4,{id:"method-wherenotnull",children:(0,o.jsx)(n.code,{children:"whereNotNull()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"whereNotNull"})," method returns models from the collection where the given column is not ",(0,o.jsx)(n.code,{children:"null"})," QVariant:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'#include \n\nusing NullVariant = Orm::Utils::NullVariant;\n\nModelsCollection users {\n {{"name", "John"}},\n {{"name", NullVariant::QString()}},\n {{"name", "Jack"}},\n};\n\nauto filtered = users.whereNotNull("name");\n\n/*\n {\n {{"name", "John"}},\n {{"name", "Jack"}},\n }\n*/\n'})}),(0,o.jsx)(n.admonition,{type:"note",children:(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"NullVariant"})," class returns the correct ",(0,o.jsx)(n.code,{children:"null"})," QVariant for both Qt 5 ",(0,o.jsx)(n.code,{children:"QVariant(QVariant::String)"})," and also Qt 6 ",(0,o.jsx)(n.code,{children:"QVariant(QMetaType(QMetaType::QString))"}),"."]})}),(0,o.jsx)(n.h4,{id:"method-wherenull",children:(0,o.jsx)(n.code,{children:"whereNull()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"whereNull"})," method returns models from the collection where the given column is ",(0,o.jsx)(n.code,{children:"null"})," QVariant:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'#include \n\nusing NullVariant = Orm::Utils::NullVariant;\n\nModelsCollection users {\n {{"name", "John"}},\n {{"name", NullVariant::QString()}},\n {{"name", "Jack"}},\n};\n\nauto filtered = users.whereNotNull("name");\n\n// {{"name", NullVariant::QString()}}\n'})}),(0,o.jsx)(n.admonition,{type:"note",children:(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"NullVariant"})," class returns the correct ",(0,o.jsx)(n.code,{children:"null"})," QVariant for both Qt 5 ",(0,o.jsx)(n.code,{children:"QVariant(QVariant::String)"})," and also Qt 6 ",(0,o.jsx)(n.code,{children:"QVariant(QMetaType(QMetaType::QString))"}),"."]})})]})]})}function h(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,o.jsx)(n,{...e,children:(0,o.jsx)(a,{...e})}):a(e)}},8453:(e,n,t)=>{t.d(n,{R:()=>l,x:()=>d});var o=t(6540);const s={},r=o.createContext(s);function l(e){const n=o.useContext(r);return o.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function d(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:l(e.components),o.createElement(r.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/3dd307b5.4e5638cc.js b/assets/js/3dd307b5.4e5638cc.js new file mode 100644 index 000000000..894b47cb1 --- /dev/null +++ b/assets/js/3dd307b5.4e5638cc.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunktinyorm_org=self.webpackChunktinyorm_org||[]).push([[117],{4251:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>i,contentTitle:()=>l,default:()=>h,frontMatter:()=>r,metadata:()=>d,toc:()=>c});var o=t(4848),s=t(8453);const r={sidebar_position:2,sidebar_label:"Collections",description:"The ModelsCollection is specialized container which provides a fluent, convenient wrapper for working with vector of models. Is much more powerful than vectors and expose a variety of map / reduce operations that may be chained using an intuitive interface. All TinyORM methods that return more than one model result will return instances of the ModelsCollection class.",keywords:["c++ orm","orm","collections","collection","model","tinyorm"]},l="TinyORM: Collections",d={id:"tinyorm/collections",title:"TinyORM: Collections",description:"The ModelsCollection is specialized container which provides a fluent, convenient wrapper for working with vector of models. Is much more powerful than vectors and expose a variety of map / reduce operations that may be chained using an intuitive interface. All TinyORM methods that return more than one model result will return instances of the ModelsCollection class.",source:"@site/docs/tinyorm/collections.mdx",sourceDirName:"tinyorm",slug:"/tinyorm/collections",permalink:"/tinyorm/collections",draft:!1,unlisted:!1,tags:[],version:"current",sidebarPosition:2,frontMatter:{sidebar_position:2,sidebar_label:"Collections",description:"The ModelsCollection is specialized container which provides a fluent, convenient wrapper for working with vector of models. Is much more powerful than vectors and expose a variety of map / reduce operations that may be chained using an intuitive interface. All TinyORM methods that return more than one model result will return instances of the ModelsCollection class.",keywords:["c++ orm","orm","collections","collection","model","tinyorm"]},sidebar:"tinyormSidebar",previous:{title:"Relationships",permalink:"/tinyorm/relationships"},next:{title:"Casts",permalink:"/tinyorm/casts"}},i={},c=[{value:"Introduction",id:"introduction",level:2},{value:"Collection Conversion",id:"collection-conversion",level:4},{value:"Creating Collections",id:"creating-collections",level:3},{value:"Available Methods",id:"available-methods",level:2},{value:"all()",id:"method-all",level:4},{value:"contains()",id:"method-contains",level:4},{value:"doesntContain()",id:"method-doesntcontain",level:4},{value:"each()",id:"method-each",level:4},{value:"except()",id:"method-except",level:4},{value:"filter()",id:"method-filter",level:4},{value:"find()",id:"method-find",level:4},{value:"first()",id:"method-first",level:4},{value:"firstWhere()",id:"method-first-where",level:4},{value:"fresh()",id:"method-fresh",level:4},{value:"implode()",id:"method-implode",level:4},{value:"isEmpty()",id:"method-isempty",level:4},{value:"isNotEmpty()",id:"method-isnotempty",level:4},{value:"last()",id:"method-last",level:4},{value:"load()",id:"method-load",level:4},{value:"map()",id:"method-map",level:4},{value:"mapWithKeys()",id:"method-mapwithkeys",level:4},{value:"mapWithModelKeys()",id:"method-mapwithmodelkeys",level:4},{value:"modelKeys()",id:"method-modelkeys",level:4},{value:"only()",id:"method-only",level:4},{value:"pluck()",id:"method-pluck",level:4},{value:"reject()",id:"method-reject",level:4},{value:"sort()",id:"method-sort",level:4},{value:"sortBy()",id:"method-sortby",level:4},{value:"sortByDesc()",id:"method-sortbydesc",level:4},{value:"sortDesc()",id:"method-sortdesc",level:4},{value:"stableSort()",id:"method-stablesort",level:4},{value:"stableSortBy()",id:"method-stablesortby",level:4},{value:"stableSortByDesc()",id:"method-stablesortbydesc",level:4},{value:"stableSortDesc()",id:"method-stablesortdesc",level:4},{value:"tap()",id:"method-tap",level:4},{value:"toBase()",id:"method-tobase",level:4},{value:"toJson()",id:"method-tojson",level:4},{value:"toJsonArray()",id:"method-tojsonarray",level:4},{value:"toJsonDocument()",id:"method-tojsondocument",level:4},{value:"toMap()",id:"method-tomap",level:4},{value:"toMapVariantList()",id:"method-tomapvariantlist",level:4},{value:"toQuery()",id:"method-toquery",level:4},{value:"toVector()",id:"method-tovector",level:4},{value:"toVectorVariantList()",id:"method-tovectorvariantlist",level:4},{value:"unique()",id:"method-unique",level:4},{value:"uniqueBy()",id:"method-uniqueby",level:4},{value:"uniqueRelaxed()",id:"method-uniquerelaxed",level:4},{value:"uniqueRelaxedBy()",id:"method-uniquerelaxedby",level:4},{value:"value()",id:"method-value",level:4},{value:"where()",id:"method-where",level:4},{value:"whereBetween()",id:"method-wherebetween",level:4},{value:"whereIn()",id:"method-wherein",level:4},{value:"whereNotBetween()",id:"method-wherenotbetween",level:4},{value:"whereNotIn()",id:"method-wherenotin",level:4},{value:"whereNotNull()",id:"method-wherenotnull",level:4},{value:"whereNull()",id:"method-wherenull",level:4}];function a(e){const n={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",h3:"h3",h4:"h4",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(n.h1,{id:"tinyorm-collections",children:"TinyORM: Collections"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.a,{href:"#introduction",children:"Introduction"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:(0,o.jsx)(n.a,{href:"#creating-collections",children:"Creating Collections"})}),"\n"]}),"\n"]}),"\n",(0,o.jsx)(n.li,{children:(0,o.jsx)(n.a,{href:"#available-methods",children:"Available Methods"})}),"\n"]}),"\n",(0,o.jsx)(n.h2,{id:"introduction",children:"Introduction"}),"\n",(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"Orm::Tiny::Types::ModelsCollection"})," is specialized container which provides a fluent, convenient wrapper for working with vector of models. All TinyORM methods that return more than one model result, will return instances of the ",(0,o.jsx)(n.code,{children:"ModelsCollection"})," class, including results retrieved via the ",(0,o.jsx)(n.code,{children:"get"})," method or methods that return relationships like the ",(0,o.jsx)(n.code,{children:"getRelation"})," and ",(0,o.jsx)(n.code,{children:"getRelationValue"}),"."]}),"\n",(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"ModelsCollection"})," class extends ",(0,o.jsx)(n.code,{children:"QVector"}),", so it naturally inherits dozens of methods used to work with the underlying vector of TinyORM models. Be sure to review the ",(0,o.jsx)(n.a,{href:"https://doc.qt.io/qt/qlist.html",children:(0,o.jsx)(n.code,{children:"QList"})})," documentation to learn all about these helpful methods!"]}),"\n",(0,o.jsx)(n.admonition,{type:"info",children:(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"ModelsCollection"})," template parameter can be declared only with the model type or a model type pointer, it also can't be ",(0,o.jsx)(n.code,{children:"const"})," and can't be a reference. It's constrained using the ",(0,o.jsx)(n.code,{children:"DerivedCollectionModel"})," concept."]})}),"\n",(0,o.jsxs)(n.p,{children:["You can iterate over the ",(0,o.jsx)(n.code,{children:"ModelsCollection"})," the same way as over the ",(0,o.jsx)(n.code,{children:"QVector"}),":"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'using Models::User;\n\nModelsCollection users = User::whereEq("active", true)->get();\n\nfor (const auto &user : users)\n qDebug() << user.getAttribute("name");\n'})}),"\n",(0,o.jsx)(n.p,{children:"However, as previously mentioned, collections are much more powerful than vectors and expose a variety of map / reduce operations that may be chained using an intuitive interface. For example, we may remove all active users and then gather the first name of each remaining user:"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'auto names = User::all().reject([](User *const user)\n{\n return user->getAttribute("active");\n})\n .pluck("name");\n'})}),"\n",(0,o.jsxs)(n.p,{children:["As you can see, the ",(0,o.jsx)(n.code,{children:"ModelsCollection"})," class allows you to chain its methods to perform fluent mapping and reducing of the underlying vector. In general, collections are immutable, meaning every ",(0,o.jsx)(n.code,{children:"ModelsCollection"})," method returns an entirely new ",(0,o.jsx)(n.code,{children:"ModelsCollection"})," instance."]}),"\n",(0,o.jsx)(n.admonition,{type:"info",children:(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"ModelsCollection"})," is returning from the Models' methods like ",(0,o.jsx)(n.code,{children:"get"}),", ",(0,o.jsx)(n.code,{children:"all"}),", ",(0,o.jsx)(n.code,{children:"findMany"}),", ",(0,o.jsx)(n.code,{children:"chunk"}),"; the ",(0,o.jsx)(n.code,{children:"ModelsCollection"})," is returning from the relationship-related methods as ",(0,o.jsx)(n.code,{children:"getRelation"})," and ",(0,o.jsx)(n.code,{children:"getRelationValue"}),"."]})}),"\n",(0,o.jsx)(n.h4,{id:"collection-conversion",children:"Collection Conversion"}),"\n",(0,o.jsxs)(n.p,{children:["While most TinyORM collection methods return a new instance of ",(0,o.jsx)(n.code,{children:"ModelsCollection"}),", the ",(0,o.jsx)(n.code,{children:"modelKeys"}),", ",(0,o.jsx)(n.code,{children:"mapWithKeys"}),", and ",(0,o.jsx)(n.code,{children:"pluck"})," methods return a base QVector or std unordered/map instances. Likewise, one of the ",(0,o.jsx)(n.code,{children:"map"})," methods overload returns the ",(0,o.jsx)(n.code,{children:"QVector"}),"."]}),"\n",(0,o.jsx)(n.h3,{id:"creating-collections",children:"Creating Collections"}),"\n",(0,o.jsxs)(n.p,{children:["Creating a ",(0,o.jsx)(n.code,{children:"ModelsCollection"})," is as simple as:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users {\n {{"name", "Kate"}, {"votes", 150}},\n {{"name", "John"}, {"votes", 200}},\n};\n'})}),"\n",(0,o.jsxs)(n.p,{children:["You can also create a collection of pointers, eg. ",(0,o.jsx)(n.code,{children:"ModelsCollection"}),":"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"ModelsCollection userPointers {\n &users[0], &users[1],\n};\n"})}),"\n",(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"ModelsCollection"})," is implicitly convertible and assignable from the ",(0,o.jsx)(n.code,{children:"QVector"}),":"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'QVector usersVector {\n {{"name", "Kate"}, {"votes", 150}},\n {{"name", "John"}, {"votes", 200}},\n};\n\nModelsCollection users(usersVector);\nusers = usersVector;\n'})}),"\n",(0,o.jsxs)(n.p,{children:["Alternatively, you can use the ",(0,o.jsx)(n.code,{children:"Orm::collect"})," helper function to create a ",(0,o.jsx)(n.code,{children:"ModelsCollection"})," from the given attributes:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users = Orm::collect({\n {{"name", "Kate"}, {"added_on", QDateTime::currentDateTimeUtc()}},\n {{"name", "John"}, {"added_on", QDateTime({2023, 6, 1}, {13, 46, 15}, QTimeZone::UTC)}},\n});\n'})}),"\n",(0,o.jsx)(n.admonition,{type:"caution",children:(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"Orm::collect"})," function is ",(0,o.jsx)(n.strong,{children:"mandatory"})," if your attributes contain the ",(0,o.jsx)(n.code,{children:"QDateTime"})," instance, you can read more about this problem ",(0,o.jsx)(n.a,{href:"/tinyorm/getting-started#qdatetime-and-connection-name-problem",children:"here"}),"."]})}),"\n",(0,o.jsx)(n.admonition,{type:"note",children:(0,o.jsxs)(n.p,{children:["The results of ",(0,o.jsx)(n.a,{href:"/tinyorm/getting-started",children:"TinyORM"})," queries are always returned as ",(0,o.jsx)(n.code,{children:"ModelsCollection"})," instances."]})}),"\n",(0,o.jsx)(n.h2,{id:"available-methods",children:"Available Methods"}),"\n",(0,o.jsxs)(n.p,{children:["For the majority of the remaining collection documentation, we'll discuss each method available on the ",(0,o.jsx)(n.code,{children:"ModelsCollection"})," class. Remember, all of these methods may be chained to fluently manipulate the underlying vector."]}),"\n",(0,o.jsxs)(n.p,{children:["Furthermore, almost every method returns a new ",(0,o.jsx)(n.code,{children:"ModelsCollection"})," instance, allowing you to preserve the original copy of the collection when necessary:"]}),"\n",(0,o.jsx)("div",{className:"collection-methods-list",children:(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.a,{href:"#method-all",children:"all"}),"\n",(0,o.jsx)(n.a,{href:"#method-contains",children:"contains"}),"\n",(0,o.jsx)(n.a,{href:"#method-doesntcontain",children:"doesntContain"}),"\n",(0,o.jsx)(n.a,{href:"#method-each",children:"each"}),"\n",(0,o.jsx)(n.a,{href:"#method-except",children:"except"}),"\n",(0,o.jsx)(n.a,{href:"#method-filter",children:"filter"}),"\n",(0,o.jsx)(n.a,{href:"#method-find",children:"find"}),"\n",(0,o.jsx)(n.a,{href:"#method-first",children:"first"}),"\n",(0,o.jsx)(n.a,{href:"#method-first-where",children:"firstWhere"}),"\n",(0,o.jsx)(n.a,{href:"#method-fresh",children:"fresh"}),"\n",(0,o.jsx)(n.a,{href:"#method-implode",children:"implode"}),"\n",(0,o.jsx)(n.a,{href:"#method-isempty",children:"isEmpty"}),"\n",(0,o.jsx)(n.a,{href:"#method-isnotempty",children:"isNotEmpty"}),"\n",(0,o.jsx)(n.a,{href:"#method-last",children:"last"}),"\n",(0,o.jsx)(n.a,{href:"#method-load",children:"load"}),"\n",(0,o.jsx)(n.a,{href:"#method-map",children:"map"}),"\n",(0,o.jsx)(n.a,{href:"#method-mapwithkeys",children:"mapWithKeys"}),"\n",(0,o.jsx)(n.a,{href:"#method-mapwithmodelkeys",children:"mapWithModelKeys"}),"\n",(0,o.jsx)(n.a,{href:"#method-modelkeys",children:"modelKeys"}),"\n",(0,o.jsx)(n.a,{href:"#method-only",children:"only"}),"\n",(0,o.jsx)(n.a,{href:"#method-pluck",children:"pluck"}),"\n",(0,o.jsx)(n.a,{href:"#method-reject",children:"reject"}),"\n",(0,o.jsx)(n.a,{href:"#method-sort",children:"sort"}),"\n",(0,o.jsx)(n.a,{href:"#method-sortby",children:"sortBy"}),"\n",(0,o.jsx)(n.a,{href:"#method-sortbydesc",children:"sortByDesc"}),"\n",(0,o.jsx)(n.a,{href:"#method-sortdesc",children:"sortDesc"}),"\n",(0,o.jsx)(n.a,{href:"#method-stablesort",children:"stableSort"}),"\n",(0,o.jsx)(n.a,{href:"#method-stablesortby",children:"stableSortBy"}),"\n",(0,o.jsx)(n.a,{href:"#method-stablesortbydesc",children:"stableSortByDesc"}),"\n",(0,o.jsx)(n.a,{href:"#method-stablesortdesc",children:"stableSortDesc"}),"\n",(0,o.jsx)(n.a,{href:"#method-tap",children:"tap"}),"\n",(0,o.jsx)(n.a,{href:"#method-tobase",children:"toBase"}),"\n",(0,o.jsx)(n.a,{href:"#method-tojson",children:"toJson"}),"\n",(0,o.jsx)(n.a,{href:"#method-tojsonarray",children:"toJsonArray"}),"\n",(0,o.jsx)(n.a,{href:"#method-tojsondocument",children:"toJsonDocument"}),"\n",(0,o.jsx)(n.a,{href:"#method-tomap",children:"toMap"}),"\n",(0,o.jsx)(n.a,{href:"#method-tomapvariantlist",children:"toMapVariantList"}),"\n",(0,o.jsx)(n.a,{href:"#method-toquery",children:"toQuery"}),"\n",(0,o.jsx)(n.a,{href:"#method-tovector",children:"toVector"}),"\n",(0,o.jsx)(n.a,{href:"#method-tovectorvariantlist",children:"toVectorVariantList"}),"\n",(0,o.jsx)(n.a,{href:"#method-unique",children:"unique"}),"\n",(0,o.jsx)(n.a,{href:"#method-uniqueby",children:"uniqueBy"}),"\n",(0,o.jsx)(n.a,{href:"#method-uniquerelaxed",children:"uniqueRelaxed"}),"\n",(0,o.jsx)(n.a,{href:"#method-uniquerelaxedby",children:"uniqueRelaxedBy"}),"\n",(0,o.jsx)(n.a,{href:"#method-value",children:"value"}),"\n",(0,o.jsx)(n.a,{href:"#method-where",children:"where"}),"\n",(0,o.jsx)(n.a,{href:"#method-wherebetween",children:"whereBetween"}),"\n",(0,o.jsx)(n.a,{href:"#method-wherein",children:"whereIn"}),"\n",(0,o.jsx)(n.a,{href:"#method-wherenotbetween",children:"whereNotBetween"}),"\n",(0,o.jsx)(n.a,{href:"#method-wherenotin",children:"whereNotIn"}),"\n",(0,o.jsx)(n.a,{href:"#method-wherenotnull",children:"whereNotNull"}),"\n",(0,o.jsx)(n.a,{href:"#method-wherenull",children:"whereNull"})]})}),"\n",(0,o.jsx)(n.admonition,{type:"note",children:(0,o.jsxs)(n.p,{children:["For a better understanding of the following examples, many of the variable declarations below use actual types instead of the ",(0,o.jsx)(n.code,{children:"auto"})," keyword."]})}),"\n",(0,o.jsxs)("div",{className:"collection-methods",children:[(0,o.jsx)(n.h4,{id:"method-all",children:(0,o.jsx)(n.code,{children:"all()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"all"})," method returns a copy of the underlying vector represented by the collection:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"QVector = users.all();\n"})}),(0,o.jsx)(n.admonition,{type:"note",children:(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.a,{href:"#method-tobase",children:(0,o.jsx)(n.code,{children:"toBase"})})," is an alias to the ",(0,o.jsx)(n.code,{children:"all"})," method."]})}),(0,o.jsx)(n.h4,{id:"method-contains",children:(0,o.jsx)(n.code,{children:"contains()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"contains"})," method may be used to determine if a given model instance is contained by the collection. This method accepts a primary key or a model instance:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"users.contains(1);\n\nusers.contains(User::find(1));\n"})}),(0,o.jsxs)(n.p,{children:["Alternatively, you may pass a lambda expression to the ",(0,o.jsx)(n.code,{children:"contains"})," method to determine if a model exists in the collection matching a given truth test:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"users.contains([](const User *const user)\n{\n return user->getKeyCasted() == 2;\n});\n"})}),(0,o.jsxs)(n.p,{children:["For the inverse of ",(0,o.jsx)(n.code,{children:"contains"}),", see the ",(0,o.jsx)(n.a,{href:"#method-doesntcontain",children:"doesntContain"})," method."]}),(0,o.jsx)(n.h4,{id:"method-doesntcontain",children:(0,o.jsx)(n.code,{children:"doesntContain()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"doesntContain"})," method determines whether the collection does not contain a given item. This method accepts a primary key or a model instance:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"users.doesntContain(1);\n\nusers.doesntContain(User::find(1));\n"})}),(0,o.jsxs)(n.p,{children:["Alternatively, you may pass a lambda expression to the ",(0,o.jsx)(n.code,{children:"doesntContain"})," method to determine if a model does not exist in the collection matching a given truth test:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"users.doesntContain([](const User *const user)\n{\n return user->getKeyCasted() == 2;\n});\n"})}),(0,o.jsxs)(n.p,{children:["For the inverse of ",(0,o.jsx)(n.code,{children:"doesntContain"}),", see the ",(0,o.jsx)(n.a,{href:"#method-contains",children:"contains"})," method."]}),(0,o.jsx)(n.h4,{id:"method-each",children:(0,o.jsx)(n.code,{children:"each()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"each"})," method iterates over the models in the collection and passes each model to the lambda expression:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users = Post::whereEq("user_id", 1)->get();\n\nusers.each([](User *const user)\n{\n // ...\n});\n'})}),(0,o.jsxs)(n.p,{children:["If you would like to stop iterating through the models, you may return ",(0,o.jsx)(n.code,{children:"false"})," from your lambda expression:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"users.each([](User *const user)\n{\n if (/* condition */)\n return false;\n\n // Some logic\n\n return true;\n});\n"})}),(0,o.jsx)(n.p,{children:"You may also pass the lambda expression with two parameters, whereas the second one is an index:"}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"users.each([](User *const user, const std::size_t index)\n{\n // ...\n});\n"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"each"})," method returns an lvalue ",(0,o.jsx)(n.strong,{children:"reference"})," to the currently processed collection."]}),(0,o.jsxs)(n.p,{children:["It can be also called on ",(0,o.jsx)(n.code,{children:"ModelsCollection"})," rvalues, it returns an rvalue reference in this case."]}),(0,o.jsx)(n.h4,{id:"method-except",children:(0,o.jsx)(n.code,{children:"except()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"except"})," method returns all of the models that do not have the given primary keys:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"ModelsCollection usersResult = users.except({1, 2, 3});\n"})}),(0,o.jsxs)(n.p,{children:["All of the models are returned if the ",(0,o.jsx)(n.code,{children:"ids"})," argument is empty ",(0,o.jsx)(n.code,{children:"except({})"}),"."]}),(0,o.jsx)(n.p,{children:"The order of models in the collection is preserved."}),(0,o.jsxs)(n.p,{children:["For the inverse of ",(0,o.jsx)(n.code,{children:"except"}),", see the ",(0,o.jsx)(n.a,{href:"#method-only",children:"only"})," method."]}),(0,o.jsx)(n.h4,{id:"method-filter",children:(0,o.jsx)(n.code,{children:"filter()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"filter"})," method filters the collection using the lambda expression, keeping only those models that pass a given truth test:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'auto usersBanned = users.filter([](const User *const user)\n{\n return user->getAttribute("is_banned");\n});\n'})}),(0,o.jsx)(n.p,{children:"You may also pass the lambda expression with two parameters, whereas the second one is an index:"}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'auto usersBanned = users.filter([](const User *const user,\n const std::size_t index)\n{\n return index < 10 && user->getAttribute("is_banned");\n});\n'})}),(0,o.jsxs)(n.p,{children:["If no lambda expression is supplied, all models of the collection that are equivalent to the ",(0,o.jsx)(n.code,{children:"nullptr"})," will be removed:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"ModelsCollection usersRaw = User::findMany({1, 2});\nModelsCollection users {&usersRaw[0], nullptr, &usersRaw[1]};\n\nModelsCollection filtered = users.filter();\n\n// {1, 2}\n"})}),(0,o.jsxs)(n.p,{children:["For the inverse of ",(0,o.jsx)(n.code,{children:"filter"}),", see the ",(0,o.jsx)(n.a,{href:"#method-reject",children:"reject"})," method."]}),(0,o.jsx)(n.h4,{id:"method-find",children:(0,o.jsx)(n.code,{children:"find()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"find"})," method returns the model that has a primary key matching the given key:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"User *const user = users.find(1);\n"})}),(0,o.jsxs)(n.p,{children:["If you pass a model instance, ",(0,o.jsx)(n.code,{children:"find"})," will attempt to return a model matching the primary key:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"User *user = users.find(anotherUser);\n"})}),(0,o.jsxs)(n.p,{children:["The two overloads above also accept the second ",(0,o.jsx)(n.code,{children:"defaultModel"})," model argument, which will be returned if a model was not found in the collection, its default value is the ",(0,o.jsx)(n.code,{children:"nullptr"}),"."]}),(0,o.jsxs)(n.p,{children:["Alternatively, may pass more IDs and ",(0,o.jsx)(n.code,{children:"find"})," will return all models which have a primary key within the given unordered set:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"ModelsCollection usersMany = users.find({1, 2});\n"})}),(0,o.jsxs)(n.p,{children:["This overload internally calls the ",(0,o.jsx)(n.a,{href:"#method-only",children:(0,o.jsx)(n.code,{children:"only"})})," method."]}),(0,o.jsx)(n.h4,{id:"method-first",children:(0,o.jsx)(n.code,{children:"first()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"first"})," method returns the first model in the collection that passes a given truth test:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users {\n {{"name", "Kate"}, {"votes", 150}},\n {{"name", "John"}, {"votes", 200}},\n {{"name", "Jack"}, {"votes", 400}},\n};\n\nUser *user = users.first([](User *const user)\n{\n return user->getAttribute("votes") > 150;\n});\n\n// {{"name", "John"}, {"votes", 200}}\n'})}),(0,o.jsxs)(n.p,{children:["If no model passes a given truth test then the value of the second ",(0,o.jsx)(n.code,{children:"defaultModel"})," argument will be returned, its default value is the ",(0,o.jsx)(n.code,{children:"nullptr"}),"."]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'using NullVariant = Orm::Utils::NullVariant;\n\nUser defaultUser {{"name", NullVariant::QString()},\n {"votes", NullVariant::ULongLong()}};\n\nUser *user = users.first([](User *const user)\n{\n return user->getAttribute("votes") > 500;\n},\n &defaultUser);\n\n/*\n {{"name", NullVariant::QString()},\n {"votes", NullVariant::ULongLong()}}\n*/\n'})}),(0,o.jsxs)(n.p,{children:["You can also call all ",(0,o.jsx)(n.code,{children:"first"})," overloads provided by the ",(0,o.jsx)(n.a,{href:"https://doc.qt.io/qt/qlist.html#first",children:(0,o.jsx)(n.code,{children:"QList::first"})}),"."]}),(0,o.jsx)(n.h4,{id:"method-first-where",children:(0,o.jsx)(n.code,{children:"firstWhere()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"firstWhere"})," method returns the first model in the collection with the given column / value pair:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'using NullVariant = Orm::Utils::NullVariant;\n\nModelsCollection users {\n {{"name", "Leon"}, {"age", NullVariant::UShort()}},\n {{"name", "Jill"}, {"age", 14}},\n {{"name", "Jack"}, {"age", 23}},\n {{"name", "Jill"}, {"age", 84}},\n};\n\nauto user = users.firstWhereEq("name", "Linda");\n\n// {{"name", "Jill"}, {"age", 14}}\n'})}),(0,o.jsxs)(n.p,{children:["You may also call the ",(0,o.jsx)(n.code,{children:"firstWhere"})," method with a comparison operator:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'users.firstWhere("age", ">=", 18);\n\n// {{"name", "Jack"}, {"age", 23}}\n'})}),(0,o.jsx)(n.h4,{id:"method-fresh",children:(0,o.jsx)(n.code,{children:"fresh()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"fresh"})," method retrieves a fresh instance of each model in the collection from the database. In addition, any specified relationships will be eager loaded:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'auto usersFresh = users.fresh();\n\nauto usersFresh = users.fresh("comments");\n\nauto usersFresh = users.fresh("posts:id,name");\n\nauto usersFresh = users.fresh({"comments", "posts:id,name"});\n'})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"relations"})," argument format is the same as for TinyBuilder's ",(0,o.jsx)(n.a,{href:"/tinyorm/relationships#lazy-eager-loading",children:(0,o.jsx)(n.code,{children:"load"})})," method."]}),(0,o.jsx)(n.h4,{id:"method-implode",children:(0,o.jsx)(n.code,{children:"implode()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"implode"}),' method joins attributes by the given column and the "glue" string you wish to place between the values:']}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection products {\n {{"product", "Desk"}, {"price", 200}},\n {{"product", "Chair"}, {"price", 100}},\n};\n\nproducts.implode("product", ", ");\n\n// {Desk, Chair}\n'})}),(0,o.jsx)(n.p,{children:'The default "glue" value is an empty string "".'}),(0,o.jsx)(n.h4,{id:"method-isempty",children:(0,o.jsx)(n.code,{children:"isEmpty()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"isEmpty"})," method returns ",(0,o.jsx)(n.code,{children:"true"})," if the collection is empty; otherwise, ",(0,o.jsx)(n.code,{children:"false"})," is returned:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"ModelsCollection().isEmpty();\n\n// true\n"})}),(0,o.jsx)(n.h4,{id:"method-isnotempty",children:(0,o.jsx)(n.code,{children:"isNotEmpty()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"isNotEmpty"})," method returns ",(0,o.jsx)(n.code,{children:"true"})," if the collection is not empty; otherwise, ",(0,o.jsx)(n.code,{children:"false"})," is returned:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"ModelsCollection().isNotEmpty();\n\n// false\n"})}),(0,o.jsx)(n.h4,{id:"method-last",children:(0,o.jsx)(n.code,{children:"last()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"last"})," method returns the last model in the collection that passes a given truth test:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users {\n {{"name", "Kate"}, {"votes", 150}},\n {{"name", "John"}, {"votes", 200}},\n {{"name", "Jack"}, {"votes", 400}},\n {{"name", "Rose"}, {"votes", 350}},\n};\n\nUser *user = users.last([](User *const user)\n{\n return user->getAttribute("votes") < 300;\n});\n\n// {{"name", "John"}, {"votes", 200}}\n'})}),(0,o.jsxs)(n.p,{children:["If no model passes a given truth test then the value of the second ",(0,o.jsx)(n.code,{children:"defaultModel"})," argument will be returned, its default value is the ",(0,o.jsx)(n.code,{children:"nullptr"}),"."]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'using NullVariant = Orm::Utils::NullVariant;\n\nUser defaultUser {{"name", NullVariant::QString()},\n {"votes", NullVariant::ULongLong()}};\n\nUser *user = users.last([](User *const user)\n{\n return user->getAttribute("votes") < 100;\n},\n &defaultUser);\n\n/*\n {{"name", NullVariant::QString()},\n {"votes", NullVariant::ULongLong()}}\n*/\n'})}),(0,o.jsxs)(n.p,{children:["You can also call all ",(0,o.jsx)(n.code,{children:"last"})," overloads provided by the ",(0,o.jsx)(n.a,{href:"https://doc.qt.io/qt/qlist.html#last",children:(0,o.jsx)(n.code,{children:"QList::last"})}),"."]}),(0,o.jsx)(n.h4,{id:"method-load",children:(0,o.jsx)(n.code,{children:"load()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"load"})," method eager loads the given relationships for all models in the collection:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'users.load({"comments", "posts"});\n\nusers.load("comments.author");\n\nusers.load({{"comments"}, {"posts", [](auto &query)\n{\n query.whereEq("active", true);\n}}});\n'})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"relations"})," argument format is the same as for TinyBuilder's ",(0,o.jsx)(n.a,{href:"/tinyorm/relationships#lazy-eager-loading",children:(0,o.jsx)(n.code,{children:"load"})})," method."]}),(0,o.jsx)(n.h4,{id:"method-map",children:(0,o.jsx)(n.code,{children:"map()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"map"})," method iterates through the collection and passes a ",(0,o.jsx)(n.strong,{children:"copy"})," of each model to the given lambda expression. The lambda expression is free to modify the model and return it, thus forming a new collection of modified models:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users {\n {{"name", "John"}, {"votes", 200}},\n {{"name", "Jack"}, {"votes", 400}},\n};\n\nauto usersAdded = users.map([](User &&userCopy)\n{\n if (userCopy.getAttribute("name") == "John")\n userCopy["votes"] = userCopy.getAttribute("votes") + 1;\n\n return std::move(userCopy);\n});\n\n/*\n {\n {{"name", "John"}, {"price", 201}},\n {{"name", "Jack"}, {"price", 400}},\n }\n*/\n'})}),(0,o.jsxs)(n.p,{children:["The second ",(0,o.jsx)(n.code,{children:"map"})," overload allows to return the ",(0,o.jsx)(n.code,{children:"QVector"}),":"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'QVector usersAdded = users.map([](User &&userCopy)\n{\n const auto votesRef = userCopy["votes"];\n\n if (userCopy.getAttribute("name") == "John")\n votesRef = userCopy.getAttribute("votes") + 1;\n\n return votesRef->value();\n});\n\n// {201, 400}\n'})}),(0,o.jsxs)(n.p,{children:["Both overloads allow to pass the lambda expression with two arguments, whereas the second argument can be an index of the ",(0,o.jsx)(n.code,{children:"std::size_t"})," type."]}),(0,o.jsx)(n.admonition,{type:"caution",children:(0,o.jsxs)(n.p,{children:["Like most other collection methods, ",(0,o.jsx)(n.code,{children:"map"})," returns a new collection instance; it does not modify the collection it is called on. If you want to modify the original collection in place, use the ",(0,o.jsx)(n.a,{href:"#method-each",children:(0,o.jsx)(n.code,{children:"each"})})," method."]})}),(0,o.jsx)(n.admonition,{type:"info",children:(0,o.jsxs)(n.p,{children:["The model copy is passed to the lambda expression even if the ",(0,o.jsx)(n.code,{children:"map"})," iterates over a collection of model pointers ",(0,o.jsx)(n.code,{children:"ModelsCollection"}),". The models are dereferenced behind the scene."]})}),(0,o.jsx)(n.h4,{id:"method-mapwithkeys",children:(0,o.jsx)(n.code,{children:"mapWithKeys()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"mapWithKeys"})," method iterates through the collection and passes each model to the given lambda expression. It returns the ",(0,o.jsx)(n.code,{children:"std::unordered_map"})," and the lambda expression should return the ",(0,o.jsx)(n.code,{children:"std::pair"})," containing a single column / value pair:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users {\n {{"id", 1}, {"name", "John"}, {"email", "john@example.com"}},\n {{"id", 2}, {"name", "Jill"}, {"email", "jill@example.com"}},\n};\n\nauto usersMap = users.mapWithKeys(\n [](User *const user) -> std::pair\n{\n return {user->getKeyCasted(), user->getAttribute("name")};\n});\n\n// {{1, \'John\'}, {2, \'Jill\'}}\n'})}),(0,o.jsx)(n.p,{children:"You can also map IDs to the model pointers:"}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"auto usersMap = users.mapWithKeys(\n [](User *const user) -> std::pair\n{\n return {user->getKeyCasted(), user};\n});\n"})}),(0,o.jsx)(n.h4,{id:"method-mapwithmodelkeys",children:(0,o.jsx)(n.code,{children:"mapWithModelKeys()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"mapWithModelKeys"})," maps the primary keys to the ",(0,o.jsx)(n.code,{children:"Model *"}),", it returns the ",(0,o.jsx)(n.code,{children:"std::unordered_map"}),":"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"auto usersMap = users.mapWithModelKeys();\n"})}),(0,o.jsx)(n.h4,{id:"method-modelkeys",children:(0,o.jsx)(n.code,{children:"modelKeys()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"modelKeys"})," method returns the primary keys for all models in the collection:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users {\n {{"id", 1}, {"name", "John"}},\n {{"id", 2}, {"name", "Jill"}},\n {{"id", 3}, {"name", "Kate"}},\n {{"id", 5}, {"name", "Rose"}},\n};\n\nusers.modelKeys(); // Returns QVector\nusers.modelKeys();\n\n// {1, 2, 3, 5}\n'})}),(0,o.jsx)(n.h4,{id:"method-only",children:(0,o.jsx)(n.code,{children:"only()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"only"})," method returns all of the models that have the given primary keys:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"ModelsCollection usersResult = users.only({1, 2, 3});\n"})}),(0,o.jsxs)(n.p,{children:["An empty collection is returned if the ",(0,o.jsx)(n.code,{children:"ids"})," argument is empty ",(0,o.jsx)(n.code,{children:"only({})"}),"."]}),(0,o.jsx)(n.p,{children:"The order of models in the collection is preserved."}),(0,o.jsxs)(n.p,{children:["For the inverse of ",(0,o.jsx)(n.code,{children:"only"}),", see the ",(0,o.jsx)(n.a,{href:"#method-except",children:"except"})," method."]}),(0,o.jsx)(n.h4,{id:"method-pluck",children:(0,o.jsx)(n.code,{children:"pluck()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"pluck"})," method retrieves all of the values for a given column, the following overload returns the ",(0,o.jsx)(n.code,{children:"QVector"}),":"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection products {\n {{"id", 1}, {"name", "Desk"}},\n {{"id", 2}, {"name", "Chair"}},\n};\n\nauto plucked = products.pluck("name");\n\n// {Desk, Chair}\n'})}),(0,o.jsxs)(n.p,{children:["The second overload allows returning the custom type ",(0,o.jsx)(n.code,{children:"QVector"}),":"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'auto plucked = products.pluck("name");\n'})}),(0,o.jsxs)(n.p,{children:["You may also specify how you wish the resulting collection to be keyed, this overload returns the ",(0,o.jsx)(n.code,{children:"std::map"}),":"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'auto plucked = products.pluck("name", "id");\n\n// {{1, "Desk"}, {2, "Chair"}}\n'})}),(0,o.jsx)(n.p,{children:"If duplicate keys exist, the last matching attribute will be inserted into the plucked collection:"}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection collection {\n {{"brand", "Tesla"}, {"color", "red"}},\n {{"brand", "Pagani"}, {"color", "white"}},\n {{"brand", "Tesla"}, {"color", "black"}},\n {{"brand", "Pagani"}, {"color", "orange"}},\n};\n\nauto plucked = collection.pluck("color", "brand");\n\n// {{\'Tesla\', \'black\'}, {\'Pagani\', \'orange"}}\n'})}),(0,o.jsx)(n.h4,{id:"method-reject",children:(0,o.jsx)(n.code,{children:"reject()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"reject"})," method filters the collection using the given lambda expression. The lambda should return ",(0,o.jsx)(n.code,{children:"true"})," if the model should be removed from the resulting collection:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'auto usersWithNote = users.reject([](const User *const user)\n{\n return user->getAttribute("note").isNull();\n});\n'})}),(0,o.jsxs)(n.p,{children:["You may also pass the lambda expression with two arguments, whereas the second argument can be an index of the ",(0,o.jsx)(n.code,{children:"std::size_t"})," type."]}),(0,o.jsxs)(n.p,{children:["For the inverse of the ",(0,o.jsx)(n.code,{children:"reject"})," method, see the ",(0,o.jsx)(n.a,{href:"#method-filter",children:(0,o.jsx)(n.code,{children:"filter"})})," method."]}),(0,o.jsx)(n.h4,{id:"method-sort",children:(0,o.jsx)(n.code,{children:"sort()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"sort"})," method sorts the models collection by primary keys:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users {\n {{"id", 2}, {"name", "Kate"}},\n {{"id", 3}, {"name", "John"}},\n {{"id", 1}, {"name", "Jack"}},\n};\n\nauto sorted = users.sort();\n\n/*\n {\n {{"id", 1}, {"name", "Jack"}},\n {{"id", 2}, {"name", "Kate"}},\n {{"id", 3}, {"name", "John"}},\n }\n*/\n'})}),(0,o.jsxs)(n.p,{children:["You may pass a predicate and projection callbacks to the ",(0,o.jsx)(n.code,{children:"sort"})," method with your own algorithms. Refer to the CPP reference documentation on ",(0,o.jsx)(n.a,{href:"https://en.cppreference.com/w/cpp/algorithm/ranges/sort",children:(0,o.jsx)(n.code,{children:"ranges::sort"})}),", which is what the ",(0,o.jsx)(n.code,{children:"sort"})," method calls internally."]}),(0,o.jsxs)(n.p,{children:["You can eg. sort by multiple columns, for an alternative method of multi-column sorting look at ",(0,o.jsx)(n.a,{href:"#method-sortby",children:"sortBy"}),":"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users {\n {{"name", "Kate"}, {"votes", 350}},\n {{"name", "John"}, {"votes", 200}},\n {{"name", "John"}, {"votes", 150}},\n {{"name", "Kate"}, {"votes", 200}},\n};\n\nauto sorted = users.sort([](const User *const left,\n const User *const right)\n{\n const auto leftValue = left->getAttribute("name");\n const auto rightValue = right->getAttribute("name");\n\n if (leftValue == rightValue)\n return left->getAttribute("votes") <\n right->getAttribute("votes");\n\n return leftValue < rightValue;\n});\n\n/*\n {\n {{"name", "John"}, {"votes", 150}},\n {{"name", "John"}, {"votes", 200}},\n {{"name", "Kate"}, {"votes", 200}},\n {{"name", "Kate"}, {"votes", 350}},\n }\n*/\n'})}),(0,o.jsx)(n.p,{children:"The order of equal elements is not guaranteed to be preserved."}),(0,o.jsx)(n.admonition,{type:"info",children:(0,o.jsxs)(n.p,{children:["You can use the ",(0,o.jsx)(n.a,{href:"#method-stablesort",children:"stable"})," sort method variants to preserve the order of equal models."]})}),(0,o.jsx)(n.h4,{id:"method-sortby",children:(0,o.jsx)(n.code,{children:"sortBy()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"sortBy"})," method sorts the collection by the given column, this overload needs the template argument so it can cast the attribute value before comparing:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users {\n {{"name", "Kate"}, {"votes", 150}},\n {{"name", "John"}, {"votes", 200}},\n {{"name", "Jack"}, {"votes", 400}},\n};\n\nauto sorted = users.sortBy("name");\n\n/*\n {\n {{"name", "Jack"}, {"votes", 400}},\n {{"name", "John"}, {"votes", 200}},\n {{"name", "Kate"}, {"votes", 150}},\n }\n*/\n'})}),(0,o.jsx)(n.p,{children:"You may pass the projection callback to determine how to sort the collection's models:"}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'auto sorted = users.sortBy([](User *const user)\n{\n return user->getAttribute("votes");\n});\n\n/*\n {\n {{"name", "Kate"}, {"votes", 150}},\n {{"name", "John"}, {"votes", 200}},\n {{"name", "Jack"}, {"votes", 400}},\n }\n*/\n'})}),(0,o.jsxs)(n.p,{children:["If you would like to sort your collection by multiple columns, you may pass a vector of comparison lambda expressions that define each sort operation to the ",(0,o.jsx)(n.code,{children:"sortBy"})," method, in the following example is the ",(0,o.jsx)(n.code,{children:"name"})," column sorted in ascending order and the second ",(0,o.jsx)(n.code,{children:"votes"})," column is sorted in descending order:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'using AttributeUtils = Orm::Tiny::Utils::Attribute;\n\nModelsCollection users {\n {{"name", "Kate"}, {"votes", 350}},\n {{"name", "John"}, {"votes", 200}},\n {{"name", "John"}, {"votes", 150}},\n {{"name", "Kate"}, {"votes", 200}},\n};\n\nauto sorted = users.sortBy({\n [](const User *const left, const User *const right)\n {\n return AttributeUtils::compareForSortBy(\n left->getAttribute("name"),\n right->getAttribute("name"));\n },\n [](const User *const left, const User *const right)\n {\n return AttributeUtils::compareForSortByDesc(\n left->getAttribute("votes"),\n right->getAttribute("votes"));\n },\n});\n\n/*\n {\n {{"name", "John"}, {"votes", 200}},\n {{"name", "John"}, {"votes", 150}},\n {{"name", "Kate"}, {"votes", 350}},\n {{"name", "Kate"}, {"votes", 200}},\n }\n*/\n'})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"AttributeUtils::compareForSortBy"})," and ",(0,o.jsx)(n.code,{children:"compareForSortByDesc"})," methods are helper methods, they are needed because the Qt framework doesn't define ",(0,o.jsx)(n.code,{children:"<=>"})," spaceship operator on its types, it doesn't support the three-way comparison."]}),(0,o.jsx)(n.p,{children:"The order of equal elements is not guaranteed to be preserved."}),(0,o.jsx)(n.h4,{id:"method-sortbydesc",children:(0,o.jsx)(n.code,{children:"sortByDesc()"})}),(0,o.jsxs)(n.p,{children:["This method has the same signature as the ",(0,o.jsx)(n.a,{href:"#method-sortby",children:(0,o.jsx)(n.code,{children:"sortBy"})})," method but will sort the collection in the opposite order."]}),(0,o.jsx)(n.p,{children:"The order of equal elements is not guaranteed to be preserved."}),(0,o.jsx)(n.h4,{id:"method-sortdesc",children:(0,o.jsx)(n.code,{children:"sortDesc()"})}),(0,o.jsxs)(n.p,{children:["This method will sort the collection in the opposite order as the ",(0,o.jsx)(n.a,{href:"#method-sort",children:(0,o.jsx)(n.code,{children:"sort"})})," method:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users {\n {{"id", 2}, {"name", "Kate"}},\n {{"id", 3}, {"name", "John"}},\n {{"id", 1}, {"name", "Jack"}},\n};\n\nauto sorted = users.sortDesc();\n\n/*\n {\n {{"id", 3}, {"name", "John"}},\n {{"id", 2}, {"name", "Kate"}},\n {{"id", 1}, {"name", "Jack"}},\n }\n*/\n'})}),(0,o.jsx)(n.p,{children:"The order of equal elements is not guaranteed to be preserved."}),(0,o.jsx)(n.h4,{id:"method-stablesort",children:(0,o.jsx)(n.code,{children:"stableSort()"})}),(0,o.jsxs)(n.p,{children:["This method has the same signature as the ",(0,o.jsx)(n.a,{href:"#method-sort",children:(0,o.jsx)(n.code,{children:"sort"})})," method but will preserve the order of equal elements (guaranteed to be preserved)."]}),(0,o.jsx)(n.h4,{id:"method-stablesortby",children:(0,o.jsx)(n.code,{children:"stableSortBy()"})}),(0,o.jsxs)(n.p,{children:["This method has the same signature as the ",(0,o.jsx)(n.a,{href:"#method-sortby",children:(0,o.jsx)(n.code,{children:"sortBy"})})," method but will preserve the order of equal elements (guaranteed to be preserved)."]}),(0,o.jsx)(n.h4,{id:"method-stablesortbydesc",children:(0,o.jsx)(n.code,{children:"stableSortByDesc()"})}),(0,o.jsxs)(n.p,{children:["This method has the same signature as the ",(0,o.jsx)(n.a,{href:"#method-sortbydesc",children:(0,o.jsx)(n.code,{children:"sortByDesc"})})," method but will sort the collection in the opposite order and preserve the order of equal elements (guaranteed to be preserved)."]}),(0,o.jsx)(n.h4,{id:"method-stablesortdesc",children:(0,o.jsx)(n.code,{children:"stableSortDesc()"})}),(0,o.jsxs)(n.p,{children:["This method has the same signature as the ",(0,o.jsx)(n.a,{href:"#method-sortdesc",children:(0,o.jsx)(n.code,{children:"sortDesc"})})," method but will sort the collection in the opposite order and preserve the order of equal elements (guaranteed to be preserved)."]}),(0,o.jsx)(n.h4,{id:"method-tap",children:(0,o.jsx)(n.code,{children:"tap()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"tap"}),' method passes a collection to the given lambda expression, allowing you to "tap" into the collection at a specific point and do something with the models while not affecting the collection itself:']}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users {\n {{"id", 2}, {"name", "Kate"}},\n {{"id", 3}, {"name", "John"}},\n {{"id", 1}, {"name", "Jack"}},\n};\n\nusers.sort()\n .tap([](/*const */ModelsCollection &usersRef)\n{\n qDebug() << "IDs after sorting:"\n << usersRef.template modelKeys();\n})\n .value("id");\n\n// 1\n'})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"tap"})," method returns an lvalue ",(0,o.jsx)(n.strong,{children:"reference"})," to the currently processed collection."]}),(0,o.jsxs)(n.p,{children:["It can be also called on ",(0,o.jsx)(n.code,{children:"ModelsCollection"})," rvalues, it returns an rvalue reference in this case."]}),(0,o.jsx)(n.h4,{id:"method-tobase",children:(0,o.jsx)(n.code,{children:"toBase()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"toBase"})," method returns a copy of the underlying vector represented by the collection:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:"QVector = users.toBase();\n"})}),(0,o.jsx)(n.admonition,{type:"note",children:(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.a,{href:"#method-tobase",children:(0,o.jsx)(n.code,{children:"toBase"})})," is an alias to the ",(0,o.jsx)(n.code,{children:"all"})," method."]})}),(0,o.jsx)(n.h4,{id:"method-tojson",children:(0,o.jsx)(n.code,{children:"toJson()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"toJson"})," method converts the collection of models with all nested relations into a JSON serialized ",(0,o.jsx)(n.a,{href:"https://doc.qt.io/qt/qbytearray.html",children:(0,o.jsx)(n.code,{children:"QByteArray"})}),"."]}),(0,o.jsxs)(n.p,{children:["It returns an empty array for empty ",(0,o.jsx)(n.code,{children:"many"})," type relations and ",(0,o.jsx)(n.code,{children:"null"})," for empty ",(0,o.jsx)(n.code,{children:"one"})," type relations."]}),(0,o.jsx)(n.admonition,{type:"info",children:(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"toJson"})," method accepts the ",(0,o.jsx)(n.a,{href:"https://doc.qt.io/qt/qjsondocument.html#JsonFormat-enum",children:(0,o.jsx)(n.code,{children:"QJsonDocument::JsonFormat"})}),", possible values are ",(0,o.jsx)(n.code,{children:"QJsonDocument::Indented"})," or ",(0,o.jsx)(n.code,{children:"QJsonDocument::Compact"}),"."]})}),(0,o.jsx)(n.h4,{id:"method-tojsonarray",children:(0,o.jsx)(n.code,{children:"toJsonArray()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"toJsonArray"})," method converts the collection of models with all nested relations into a ",(0,o.jsx)(n.a,{href:"https://doc.qt.io/qt/qjsonarray.html",children:(0,o.jsx)(n.code,{children:"QJsonArray"})}),"."]}),(0,o.jsx)(n.h4,{id:"method-tojsondocument",children:(0,o.jsx)(n.code,{children:"toJsonDocument()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"toJsonDocument"})," method converts the collection of models with all nested relations into a ",(0,o.jsx)(n.a,{href:"https://doc.qt.io/qt/qjsondocument.html",children:(0,o.jsx)(n.code,{children:"QJsonDocument"})}),"."]}),(0,o.jsx)(n.h4,{id:"method-tomap",children:(0,o.jsx)(n.code,{children:"toMap()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"toMap"})," method converts the collection of models with all nested relations into an attributes map ",(0,o.jsx)(n.code,{children:"QVector"}),"."]}),(0,o.jsxs)(n.p,{children:["It returns an empty ",(0,o.jsx)(n.code,{children:"QVariantList"})," for empty ",(0,o.jsx)(n.code,{children:"many"})," type relations and a null ",(0,o.jsx)("abbr",{title:"QVariant::fromValue(nullptr)",children:(0,o.jsx)(n.code,{children:"QVariant"})})," for empty ",(0,o.jsx)(n.code,{children:"one"})," type relations."]}),(0,o.jsx)(n.h4,{id:"method-tomapvariantlist",children:(0,o.jsx)(n.code,{children:"toMapVariantList()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"toMapVariantList"})," method converts the collection of models with all nested relations into an attributes map, but it returns the ",(0,o.jsx)("abbr",{title:"QList",children:(0,o.jsx)(n.code,{children:"QVariantList"})})," instead of the ",(0,o.jsx)(n.code,{children:"QVector"}),"."]}),(0,o.jsxs)(n.p,{children:["It returns an empty ",(0,o.jsx)(n.code,{children:"QVariantList"})," for empty ",(0,o.jsx)(n.code,{children:"many"})," type relations and a null ",(0,o.jsx)("abbr",{title:"QVariant::fromValue(nullptr)",children:(0,o.jsx)(n.code,{children:"QVariant"})})," for empty ",(0,o.jsx)(n.code,{children:"one"})," type relations."]}),(0,o.jsx)(n.admonition,{type:"note",children:(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"toMapVariantList"})," method is internally needed by the ",(0,o.jsx)(n.code,{children:"toJson"})," related methods."]})}),(0,o.jsx)(n.h4,{id:"method-toquery",children:(0,o.jsx)(n.code,{children:"toQuery()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"toQuery"})," method returns the ",(0,o.jsx)(n.code,{children:"TinyBuilder"})," instance containing a ",(0,o.jsx)(n.code,{children:"whereIn"})," constraint with the collection of models' primary keys:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'using Models::User;\n\nModelsCollection users = User::whereEq("status", "VIP")->get();\n\nusers.toQuery()->update({\n {"status", "Administrator"},\n});\n'})}),(0,o.jsx)(n.h4,{id:"method-tovector",children:(0,o.jsx)(n.code,{children:"toVector()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"toVector"})," method converts the collection of models with all nested relations into an attributes vector ",(0,o.jsx)(n.code,{children:"QVector>"}),"."]}),(0,o.jsxs)(n.p,{children:["It returns an empty ",(0,o.jsx)(n.code,{children:"QVariantList"})," for empty ",(0,o.jsx)(n.code,{children:"many"})," type relations and a null ",(0,o.jsx)("abbr",{title:"QVariant::fromValue(nullptr)",children:(0,o.jsx)(n.code,{children:"QVariant"})})," for empty ",(0,o.jsx)(n.code,{children:"one"})," type relations."]}),(0,o.jsx)(n.h4,{id:"method-tovectorvariantlist",children:(0,o.jsx)(n.code,{children:"toVectorVariantList()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"toVectorVariantList"})," method converts the collection of models with all nested relations into an attributes vector, but it returns the ",(0,o.jsx)("abbr",{title:"QList",children:(0,o.jsx)(n.code,{children:"QVariantList"})})," instead of the ",(0,o.jsx)(n.code,{children:"QVector>"}),"."]}),(0,o.jsxs)(n.p,{children:["It returns an empty ",(0,o.jsx)(n.code,{children:"QVariantList"})," for empty ",(0,o.jsx)(n.code,{children:"many"})," type relations and a null ",(0,o.jsx)("abbr",{title:"QVariant::fromValue(nullptr)",children:(0,o.jsx)(n.code,{children:"QVariant"})})," for empty ",(0,o.jsx)(n.code,{children:"one"})," type relations."]}),(0,o.jsx)(n.admonition,{type:"note",children:(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"toVectorVariantList"})," method is internally needed by the ",(0,o.jsx)(n.code,{children:"toJson"})," related methods."]})}),(0,o.jsx)(n.h4,{id:"method-unique",children:(0,o.jsx)(n.code,{children:"unique()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"unique"})," method returns all of the unique models in the ",(0,o.jsx)(n.strong,{children:"sorted"})," collection. Any models with the same primary key as another model in the collection are removed:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users {\n {{"id", 2}, {"name", "Kate"}},\n {{"id", 1}, {"name", "Jack"}},\n {{"id", 3}, {"name", "John"}},\n {{"id", 1}, {"name", "Jack"}},\n};\n\nauto unique = users.unique();\n\n/*\n {\n {{"id", 1}, {"name", "Jack"}},\n {{"id", 2}, {"name", "Kate"}},\n {{"id", 3}, {"name", "John"}},\n }\n*/\n'})}),(0,o.jsxs)(n.p,{children:["It sorts the collection internally because the ",(0,o.jsx)(n.a,{href:"https://en.cppreference.com/w/cpp/algorithm/ranges/unique",children:(0,o.jsx)(n.code,{children:"ranges::unique"})})," can correctly operate only on the sorted container. You can disable it by passing ",(0,o.jsx)(n.code,{children:"false"})," using the first ",(0,o.jsx)(n.code,{children:"sort"})," parameter:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'auto unique = users.sort().unique(false);\n\n/*\n {\n {{"id", 1}, {"name", "Jack"}},\n {{"id", 2}, {"name", "Kate"}},\n {{"id", 3}, {"name", "John"}},\n }\n*/\n'})}),(0,o.jsx)(n.h4,{id:"method-uniqueby",children:(0,o.jsx)(n.code,{children:"uniqueBy()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"uniqueBy"})," method returns all of the unique models in the ",(0,o.jsx)(n.strong,{children:"sorted"})," collection by the given column. Any models with the same column value as another model in the collection are removed. It needs the template argument, so it can cast the attribute value before comparing:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users {\n {{"name", "Kate"}},\n {{"name", "Jack"}},\n {{"name", "John"}},\n {{"name", "Jack"}},\n};\n\nauto unique = users.uniqueBy("name");\n\n/*\n {\n {{"name", "Jack"}},\n {{"name", "John"}},\n {{"name", "Kate"}},\n }\n*/\n'})}),(0,o.jsxs)(n.p,{children:["It sorts the collection internally because the ",(0,o.jsx)(n.a,{href:"https://en.cppreference.com/w/cpp/algorithm/ranges/unique",children:(0,o.jsx)(n.code,{children:"ranges::unique"})})," can correctly operate only on the sorted container. You can disable it by passing ",(0,o.jsx)(n.code,{children:"false"})," using the second ",(0,o.jsx)(n.code,{children:"sort"})," parameter:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'auto unique = users.sortBy("name")\n .uniqueBy("name", false);\n\n/*\n {\n {{"name", "Jack"}},\n {{"name", "John"}},\n {{"name", "Kate"}},\n }\n*/\n'})}),(0,o.jsx)(n.h4,{id:"method-uniquerelaxed",children:(0,o.jsx)(n.code,{children:"uniqueRelaxed()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"uniqueRelaxed"})," method returns all of the unique models in the collection, it doesn't need a sorted collection. Any models with the same primary key as another model in the collection are removed:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users {\n {{"id", 2}, {"name", "Kate"}},\n {{"id", 1}, {"name", "Jack"}},\n {{"id", 3}, {"name", "John"}},\n {{"id", 1}, {"name", "Jack"}},\n};\n\nauto unique = users.uniqueRelaxed();\n\n/*\n {\n {{"id", 2}, {"name", "Kate"}},\n {{"id", 1}, {"name", "Jack"}},\n {{"id", 3}, {"name", "John"}},\n }\n*/\n'})}),(0,o.jsx)(n.h4,{id:"method-uniquerelaxedby",children:(0,o.jsx)(n.code,{children:"uniqueRelaxedBy()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"uniqueRelaxedBy"})," method returns all of the unique models in the collection by the given column, it doesn't need a sorted collection, but it needs the template argument, so it can cast the attribute value before comparing:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users {\n {{"name", "Kate"}},\n {{"name", "Jack"}},\n {{"name", "John"}},\n {{"name", "Jack"}},\n};\n\nauto unique = users.uniqueRelaxedBy("name");\n\n/*\n {\n {{"name", "Kate"}},\n {{"name", "Jack"}},\n {{"name", "John"}},\n }\n*/\n'})}),(0,o.jsx)(n.h4,{id:"method-value",children:(0,o.jsx)(n.code,{children:"value()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"value"})," method retrieves a given value from the first model of the collection:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection users {\n {{"name", "John"}, {"votes", 200}},\n {{"name", "Jack"}, {"votes", 400}},\n};\n\nQVariant votes = users.value("votes");\n\n// 200\n'})}),(0,o.jsxs)(n.p,{children:["Alternatively, you can cast an obtained ",(0,o.jsx)(n.code,{children:"QVariant"})," value to the given type by the second ",(0,o.jsx)(n.code,{children:"value"})," overload:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'quint64 votes = users.value("votes");\n'})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"value"})," method also accepts the second ",(0,o.jsx)(n.code,{children:"defaultValue"})," argument, which will be returned if a collection is empty, the first model is ",(0,o.jsx)(n.code,{children:"nullptr"}),", or a model doesn't contain the given column:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'auto votes = ModelsCollection().value("votes", 0);\n\n// 0\n'})}),(0,o.jsxs)(n.p,{children:["You can also call all ",(0,o.jsx)(n.code,{children:"value"})," overloads provided by the ",(0,o.jsx)(n.a,{href:"https://doc.qt.io/qt/qlist.html#value",children:(0,o.jsx)(n.code,{children:"QList::value"})}),"."]}),(0,o.jsx)(n.h4,{id:"method-where",children:(0,o.jsx)(n.code,{children:"where()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"where"})," method filters the collection by a given column / value pair:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection products {\n {{"product", "Desk"}, {"price", 200}},\n {{"product", "Chair"}, {"price", 100}},\n {{"product", "Bookcase"}, {"price", 150}},\n {{"product", "Door"}, {"price", 100}},\n};\n\nauto filtered = products.where("price", "=", 100);\n\n/*\n {\n {{"product", "Chair"}, {"price", 100}},\n {{"product", "Door"}, {"price", 100}},\n }\n*/\n'})}),(0,o.jsxs)(n.p,{children:["For convenience, if you want to verify that a column is ",(0,o.jsx)(n.code,{children:"="})," to a given value, you may call ",(0,o.jsx)(n.code,{children:"whereEq"})," method. Similar ",(0,o.jsx)(n.code,{children:"XxxEq"})," methods are also defined for other commands:"]}),(0,o.jsx)(n.p,{children:'auto filtered = products.whereEq("price", 100);'}),(0,o.jsxs)(n.p,{children:["Optionally, you may pass a comparison operator as the second argument.",(0,o.jsx)("br",{}),"Supported operators are ",(0,o.jsx)(n.code,{children:"="}),", ",(0,o.jsx)(n.code,{children:"!="}),", ",(0,o.jsx)(n.code,{children:"<"}),", ",(0,o.jsx)(n.code,{children:">"}),", ",(0,o.jsx)(n.code,{children:"<="}),", and ",(0,o.jsx)(n.code,{children:">="}),":"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection products {\n {{"product", "Desk"}, {"price", 200}},\n {{"product", "Chair"}, {"price", 100}},\n {{"product", "Bookcase"}, {"price", 150}},\n {{"product", "Door"}, {"price", 250}},\n};\n\nauto filtered = products.where("price", ">", 150);\n\n/*\n {\n {{"product", "Desk"}, {"price", 200}},\n {{"product", "Door"}, {"price", 250}},\n }\n*/\n'})}),(0,o.jsx)(n.h4,{id:"method-wherebetween",children:(0,o.jsx)(n.code,{children:"whereBetween()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"whereBetween"})," method filters the collection by determining if a specified models' attribute value is within a given range:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection products {\n {{"product", "Desk"}, {"price", 200}},\n {{"product", "Chair"}, {"price", 80}},\n {{"product", "Bookcase"}, {"price", 150}},\n {{"product", "Pencil"}, {"price", 30}},\n {{"product", "Door"}, {"price", 100}},\n};\n\nauto filtered = products.whereBetween("price", {100, 200});\n\n/*\n {\n {{"product", "Desk"}, {"price", 200}},\n {{"product", "Bookcase"}, {"price", 150}},\n {{"product", "Door"}, {"price", 100}},\n }\n*/\n'})}),(0,o.jsx)(n.h4,{id:"method-wherein",children:(0,o.jsx)(n.code,{children:"whereIn()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"whereIn"})," method filters models from the collection that have a specified attribute value that is contained within the given unordered set:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection products {\n {{"product", "Desk"}, {"price", 200}},\n {{"product", "Chair"}, {"price", 100}},\n {{"product", "Bookcase"}, {"price", 150}},\n {{"product", "Door"}, {"price", 250}},\n};\n\nauto filtered = products.whereIn("price", {100, 200});\n\n/*\n {\n {{"product", "Desk"}, {"price", 200}},\n {{"product", "Chair"}, {"price", 100}},\n }\n*/\n'})}),(0,o.jsxs)(n.p,{children:["An empty collection is returned if the ",(0,o.jsx)(n.code,{children:"values"})," argument is empty ",(0,o.jsx)(n.code,{children:'whereIn("price", {})'}),"."]}),(0,o.jsx)(n.p,{children:"The order of models in the collection is preserved."}),(0,o.jsx)(n.h4,{id:"method-wherenotbetween",children:(0,o.jsx)(n.code,{children:"whereNotBetween()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"whereNotBetween"})," method filters the collection by determining if a specified models' attribute value is outside of a given range:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection products {\n {{"product", "Desk"}, {"price", 200}},\n {{"product", "Chair"}, {"price", 80}},\n {{"product", "Bookcase"}, {"price", 150}},\n {{"product", "Pencil"}, {"price", 30}},\n {{"product", "Door"}, {"price", 100}},\n};\n\nauto filtered = products.whereNotBetween("price", {100, 200});\n\n/*\n {\n {{"product", "Chair"}, {"price", 80}},\n {{"product", "Pencil"}, {"price", 30}},\n }\n*/\n'})}),(0,o.jsx)(n.h4,{id:"method-wherenotin",children:(0,o.jsx)(n.code,{children:"whereNotIn()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"whereNotIn"})," method removes models from the collection that have a specified attribute value that is contained within the given unordered set:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'ModelsCollection products {\n {{"product", "Desk"}, {"price", 200}},\n {{"product", "Chair"}, {"price", 100}},\n {{"product", "Bookcase"}, {"price", 150}},\n {{"product", "Door"}, {"price", 250}},\n};\n\nauto filtered = products.whereNotIn("price", {100, 200});\n\n/*\n {\n {{"product", "Bookcase"}, {"price", 150}},\n {{"product", "Door"}, {"price", 250}},\n }\n*/\n'})}),(0,o.jsxs)(n.p,{children:["All of the models are returned if the ",(0,o.jsx)(n.code,{children:"values"})," argument is empty ",(0,o.jsx)(n.code,{children:'whereNotIn("price", {})'}),"."]}),(0,o.jsx)(n.p,{children:"The order of models in the collection is preserved."}),(0,o.jsx)(n.h4,{id:"method-wherenotnull",children:(0,o.jsx)(n.code,{children:"whereNotNull()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"whereNotNull"})," method returns models from the collection where the given column is not ",(0,o.jsx)(n.code,{children:"null"})," QVariant:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'#include \n\nusing NullVariant = Orm::Utils::NullVariant;\n\nModelsCollection users {\n {{"name", "John"}},\n {{"name", NullVariant::QString()}},\n {{"name", "Jack"}},\n};\n\nauto filtered = users.whereNotNull("name");\n\n/*\n {\n {{"name", "John"}},\n {{"name", "Jack"}},\n }\n*/\n'})}),(0,o.jsx)(n.admonition,{type:"note",children:(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"NullVariant"})," class returns the correct ",(0,o.jsx)(n.code,{children:"null"})," QVariant for both Qt 5 ",(0,o.jsx)(n.code,{children:"QVariant(QVariant::String)"})," and also Qt 6 ",(0,o.jsx)(n.code,{children:"QVariant(QMetaType(QMetaType::QString))"}),"."]})}),(0,o.jsx)(n.h4,{id:"method-wherenull",children:(0,o.jsx)(n.code,{children:"whereNull()"})}),(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"whereNull"})," method returns models from the collection where the given column is ",(0,o.jsx)(n.code,{children:"null"})," QVariant:"]}),(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-cpp",children:'#include \n\nusing NullVariant = Orm::Utils::NullVariant;\n\nModelsCollection users {\n {{"name", "John"}},\n {{"name", NullVariant::QString()}},\n {{"name", "Jack"}},\n};\n\nauto filtered = users.whereNotNull("name");\n\n// {{"name", NullVariant::QString()}}\n'})}),(0,o.jsx)(n.admonition,{type:"note",children:(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"NullVariant"})," class returns the correct ",(0,o.jsx)(n.code,{children:"null"})," QVariant for both Qt 5 ",(0,o.jsx)(n.code,{children:"QVariant(QVariant::String)"})," and also Qt 6 ",(0,o.jsx)(n.code,{children:"QVariant(QMetaType(QMetaType::QString))"}),"."]})})]})]})}function h(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,o.jsx)(n,{...e,children:(0,o.jsx)(a,{...e})}):a(e)}},8453:(e,n,t)=>{t.d(n,{R:()=>l,x:()=>d});var o=t(6540);const s={},r=o.createContext(s);function l(e){const n=o.useContext(r);return o.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function d(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:l(e.components),o.createElement(r.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/62a1276f.5965aaeb.js b/assets/js/62a1276f.5965aaeb.js deleted file mode 100644 index 3545c6055..000000000 --- a/assets/js/62a1276f.5965aaeb.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunktinyorm_org=self.webpackChunktinyorm_org||[]).push([[304],{2403:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>d,contentTitle:()=>r,default:()=>h,frontMatter:()=>a,metadata:()=>o,toc:()=>c});var s=n(4848),i=n(8453);const a={sidebar_position:3,sidebar_label:"Casts",description:"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.",keywords:["c++ orm","orm","casts","casting","attributes","tinyorm"]},r="TinyORM: Casting",o={id:"tinyorm/casts",title:"TinyORM: Casting",description:"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.",source:"@site/docs/tinyorm/casts.mdx",sourceDirName:"tinyorm",slug:"/tinyorm/casts",permalink:"/tinyorm/casts",draft:!1,unlisted:!1,tags:[],version:"current",sidebarPosition:3,frontMatter:{sidebar_position:3,sidebar_label:"Casts",description:"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.",keywords:["c++ orm","orm","casts","casting","attributes","tinyorm"]},sidebar:"tinyormSidebar",previous:{title:"Collections",permalink:"/tinyorm/collections"},next:{title:"Serialization",permalink:"/tinyorm/serialization"}},d={},c=[{value:"Introduction",id:"introduction",level:2},{value:"Accessors",id:"accessors",level:2},{value:"Defining An Accessor",id:"defining-an-accessor",level:3},{value:"Building Value From Multiple Attributes",id:"building-value-from-multiple-attributes",level:4},{value:"Accessor Caching",id:"accessor-caching",level:4},{value:"Attribute Casting",id:"attribute-casting",level:2},{value:"Date Casting",id:"date-casting",level:3},{value:"Date Casting, Serialization & Timezones",id:"date-casting-serialization-and-timezones",level:4},{value:"Query Time Casting",id:"query-time-casting",level:3}];function l(e){const t={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",h3:"h3",h4:"h4",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,i.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(t.h1,{id:"tinyorm-casting",children:"TinyORM: Casting"}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsx)(t.li,{children:(0,s.jsx)(t.a,{href:"#introduction",children:"Introduction"})}),"\n",(0,s.jsxs)(t.li,{children:[(0,s.jsx)(t.a,{href:"#accessors",children:"Accessors"}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsx)(t.li,{children:(0,s.jsx)(t.a,{href:"#defining-an-accessor",children:"Defining An Accessor"})}),"\n"]}),"\n"]}),"\n",(0,s.jsxs)(t.li,{children:[(0,s.jsx)(t.a,{href:"#attribute-casting",children:"Attribute Casting"}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsx)(t.li,{children:(0,s.jsx)(t.a,{href:"#date-casting",children:"Date Casting"})}),"\n",(0,s.jsx)(t.li,{children:(0,s.jsx)(t.a,{href:"#query-time-casting",children:"Query Time Casting"})}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,s.jsx)(t.h2,{id:"introduction",children:"Introduction"}),"\n",(0,s.jsxs)(t.p,{children:["Attribute casting allows you to transform TinyORM attribute values when you retrieve them on model instances. For example, you may want to convert a ",(0,s.jsx)(t.code,{children:"datetime"})," string that is stored in your database to the ",(0,s.jsx)(t.code,{children:"QDateTime"})," instance when it is accessed via your TinyORM model. Or, you may want to convert a ",(0,s.jsx)(t.code,{children:"tinyint"})," number that is stored in the database to the ",(0,s.jsx)(t.code,{children:"bool"})," when you access it on the TinyORM model."]}),"\n",(0,s.jsx)(t.h2,{id:"accessors",children:"Accessors"}),"\n",(0,s.jsx)(t.admonition,{type:"warning",children:(0,s.jsxs)(t.p,{children:["Accessors are currently only used during the serialization by the ",(0,s.jsx)(t.a,{href:"/tinyorm/serialization#appending-values-to-json",children:"Appending Values"})," feature. They are not used during the ",(0,s.jsx)(t.code,{children:"getAttribute"})," or ",(0,s.jsx)(t.code,{children:"getAttributeValue"})," methods calls."]})}),"\n",(0,s.jsx)(t.h3,{id:"defining-an-accessor",children:"Defining An Accessor"}),"\n",(0,s.jsxs)(t.p,{children:["An accessor transforms a TinyORM attribute value when it is accessed (currently during serialization only by the ",(0,s.jsx)(t.a,{href:"/tinyorm/serialization#appending-values-to-json",children:"Appending Values"}),' feature). To define an accessor, create a protected method on your model to represent the accessible attribute. This method name should correspond to the "camelCase" representation of the true underlying model attribute / database column when applicable.']}),"\n",(0,s.jsxs)(t.p,{children:["In this example, we'll define an accessor for the ",(0,s.jsx)(t.code,{children:"first_name"})," attribute. The accessor will automatically be called by TinyORM during serialization if the ",(0,s.jsx)(t.code,{children:"first_name"})," attribute is defined in the ",(0,s.jsx)(t.code,{children:"u_appends"})," data member set. All attribute accessor methods must return the ",(0,s.jsx)(t.code,{children:"Orm::Tiny::Casts::Attribute"}),":"]}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-cpp",children:'#include \n\nusing Orm::Tiny::Model;\n\nclass User final : public Model\n{\n friend Model;\n using Model::Model;\n\nprotected:\n /*! Get the user\'s first name (accessor). */\n Attribute firstName() const noexcept\n {\n return Attribute::make(/* get */ [this]() -> QVariant\n {\n auto firstName = getAttribute("first_name");\n\n if (!firstName.isEmpty())\n firstName[0] = firstName.at(0).toUpper();\n\n return firstName;\n });\n }\n\nprivate:\n /*! Map of mutator names to methods. */\n inline static const QHash u_mutators {\n {"first_name", &User::firstName},\n };\n};\n'})}),"\n",(0,s.jsxs)(t.p,{children:["All accessor methods return an ",(0,s.jsx)(t.code,{children:"Attribute"})," instance which defines how the attribute will be accessed. To do so, we supply the ",(0,s.jsx)(t.code,{children:"get"})," argument to the ",(0,s.jsx)(t.code,{children:"Attribute"})," class constructor or ",(0,s.jsx)(t.code,{children:"Attribute::make"})," factory method."]}),"\n",(0,s.jsxs)(t.p,{children:["As you can see, the current model is captured by-reference using the ",(0,s.jsx)(t.code,{children:"[this]"})," capture, allowing you to obtain a value by the ",(0,s.jsx)(t.code,{children:"getAttribute"})," method inside the lambda expression, manipulate it and return a new value."]}),"\n",(0,s.jsxs)(t.p,{children:["You can also use the second overload that allows you to pass the ",(0,s.jsx)(t.code,{children:"ModelAttributes"})," unordered map to the lambda expression:"]}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-cpp",children:'protected:\n /*! Get the user\'s first name (accessor). */\n Attribute firstName() const noexcept\n {\n return Attribute::make(\n /* get */ [](const ModelAttributes &attributes) -> QVariant\n {\n auto firstName = attributes.at("first_name");\n\n if (!firstName.isEmpty())\n firstName[0] = firstName.at(0).toUpper();\n\n return firstName;\n });\n }\n'})}),"\n",(0,s.jsxs)(t.p,{children:["The ",(0,s.jsx)(t.a,{href:"https://github.com/silverqx/TinyORM/blob/main/include/orm/tiny/types/modelattributes.hpp",children:(0,s.jsx)(t.code,{children:"ModelAttributes"})})," container extends the ",(0,s.jsx)(t.code,{children:"std::unordered_map"})," and adds the ",(0,s.jsx)(t.code,{children:"at"})," method that allows you to cast the underlying QVariant value."]}),"\n",(0,s.jsxs)(t.p,{children:["Special note should be given to the ",(0,s.jsx)(t.code,{children:"u_mutators"})," static data member map, which maps accessors' attribute names to its methods. This data member is ",(0,s.jsx)(t.strong,{children:"required"})," because C++ does not currently support reflection."]}),"\n",(0,s.jsx)(t.admonition,{type:"info",children:(0,s.jsxs)(t.p,{children:["If you would like these computed values to be added to the vector, map, or JSON representations of your model, ",(0,s.jsx)(t.a,{href:"/tinyorm/serialization#appending-values-to-json",children:"you will need to append them"}),"."]})}),"\n",(0,s.jsx)(t.admonition,{type:"danger",children:(0,s.jsx)(t.p,{children:"You must guarantee that the current model will live long enough to avoid the dangling reference and crash if the current model is captured by-reference. Of course, you can capture it by-copy in edge cases or if you can't guarantee this."})}),"\n",(0,s.jsx)(t.h4,{id:"building-value-from-multiple-attributes",children:"Building Value From Multiple Attributes"}),"\n",(0,s.jsx)(t.p,{children:"Sometimes your accessor may need to transform multiple model attributes into a single value. You can use both methods described above to accomplish this:"}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-cpp",children:'using Orm::Constants::SPACE_IN;\n\nprotected:\n /*! Get the user\'s full name (accessor). */\n Attribute fullName() const noexcept\n {\n return Attribute::make(/* get */ [this]() -> QVariant\n {\n return SPACE_IN.arg(getAttribute("first_name"),\n getAttribute("last_name"));\n });\n }\n'})}),"\n",(0,s.jsxs)(t.p,{children:["Or you can use the ",(0,s.jsx)(t.code,{children:"ModelAttributes"})," overload:"]}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-cpp",children:'/*! Get the user\'s full name (accessor). */\nAttribute fullName() const noexcept\n{\n return Attribute::make(\n /* get */ [](const ModelAttributes &attributes) -> QVariant\n {\n return SPACE_IN.arg(attributes.at("first_name"),\n attributes.at("last_name"));\n });\n}\n'})}),"\n",(0,s.jsx)(t.h4,{id:"accessor-caching",children:"Accessor Caching"}),"\n",(0,s.jsxs)(t.p,{children:["Sometimes computing an attribute value can be intensive, in this case, you can enable caching for this attribute value. To accomplish this, you have to invoke the ",(0,s.jsx)(t.code,{children:"shouldCache"})," method when defining your accessor:"]}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-cpp",children:'using Orm::Constants::SPACE_IN;\n\nprotected:\n /*! Get the user\'s full name (accessor). */\n Attribute fullName() const noexcept\n {\n return Attribute::make(/* get */ [this]() -> QVariant\n {\n return SPACE_IN.arg(getAttribute("first_name"),\n getAttribute("last_name"));\n }).shouldCache();\n }\n'})}),"\n",(0,s.jsx)(t.h2,{id:"attribute-casting",children:"Attribute Casting"}),"\n",(0,s.jsxs)(t.p,{children:["Attribute casting provides functionality that allows converting model attributes to the appropriate ",(0,s.jsx)(t.code,{children:"QVariant"})," ",(0,s.jsx)(t.strong,{children:"metatype"})," when it is accessed via your TinyORM model. The core of this functionality is a model's ",(0,s.jsx)(t.code,{children:"u_casts"})," static data member that provides a convenient method of converting attributes' ",(0,s.jsx)(t.code,{children:"QVariant"})," ",(0,s.jsx)(t.strong,{children:"internal types"})," to the defined cast types."]}),"\n",(0,s.jsxs)(t.p,{children:["The ",(0,s.jsx)(t.code,{children:"u_casts"})," static data member should be the ",(0,s.jsx)(t.code,{children:"std::unordered_map"})," 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:"]}),"\n",(0,s.jsx)("div",{id:"casts-types-list",children:(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsx)(t.li,{children:(0,s.jsx)(t.code,{children:"CastType::QString"})}),"\n",(0,s.jsxs)(t.li,{children:[(0,s.jsx)(t.code,{children:"CastType::Boolean"})," / ",(0,s.jsx)(t.code,{children:"CastType::Bool"})]}),"\n",(0,s.jsxs)(t.li,{children:[(0,s.jsx)(t.code,{children:"CastType::Integer"})," / ",(0,s.jsx)(t.code,{children:"CastType::Int"})]}),"\n",(0,s.jsxs)(t.li,{children:[(0,s.jsx)(t.code,{children:"CastType::UInteger"})," / ",(0,s.jsx)(t.code,{children:"CastType::UInt"})]}),"\n",(0,s.jsx)(t.li,{children:(0,s.jsx)(t.code,{children:"CastType::LongLong"})}),"\n",(0,s.jsx)(t.li,{children:(0,s.jsx)(t.code,{children:"CastType::ULongLong"})}),"\n",(0,s.jsx)(t.li,{children:(0,s.jsx)(t.code,{children:"CastType::Short"})}),"\n",(0,s.jsx)(t.li,{children:(0,s.jsx)(t.code,{children:"CastType::UShort"})}),"\n",(0,s.jsx)(t.li,{children:(0,s.jsx)(t.code,{children:"CastType::QDate"})}),"\n",(0,s.jsx)(t.li,{children:(0,s.jsx)(t.code,{children:"CastType::QDateTime"})}),"\n",(0,s.jsx)(t.li,{children:(0,s.jsx)(t.code,{children:"CastType::Timestamp"})}),"\n",(0,s.jsx)(t.li,{children:(0,s.jsx)(t.code,{children:"CastType::Real"})}),"\n",(0,s.jsx)(t.li,{children:(0,s.jsx)(t.code,{children:"CastType::Float"})}),"\n",(0,s.jsx)(t.li,{children:(0,s.jsx)(t.code,{children:"CastType::Double"})}),"\n",(0,s.jsxs)(t.li,{children:["\n",(0,s.jsx)("code",{children:"CastType::Decimal:"}),"\n"]}),"\n",(0,s.jsx)(t.li,{children:(0,s.jsx)(t.code,{children:"CastType::QByteArray"})}),"\n"]})}),"\n",(0,s.jsx)(t.admonition,{type:"info",children:(0,s.jsxs)(t.p,{children:["The primary key name defined by the ",(0,s.jsx)(t.code,{children:"u_primaryKey"})," model's data member is automatically cast to the ",(0,s.jsx)(t.code,{children:"CastType::ULongLong"})," for all database drivers if the ",(0,s.jsx)(t.code,{children:"u_incrementing"})," is set to true (its default value)."]})}),"\n",(0,s.jsxs)(t.p,{children:["To demonstrate attribute casting, let's cast the ",(0,s.jsx)(t.code,{children:"is_admin"})," attribute, which is stored in our database as an integer (",(0,s.jsx)(t.code,{children:"0"})," or ",(0,s.jsx)(t.code,{children:"1"}),") to a ",(0,s.jsx)(t.code,{children:"QVariant(bool)"})," value:"]}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-cpp",children:'#pragma once\n\n#include \n\nusing Orm::Tiny::Model;\n\nclass User final : public Model\n{\n friend Model;\n using Model::Model;\n\n /*! The attributes that should be cast. */\n inline static std::unordered_map u_casts {\n {"is_admin", CastType::Boolean},\n };\n};\n'})}),"\n",(0,s.jsxs)(t.p,{children:["After defining the cast, the ",(0,s.jsx)(t.code,{children:"is_admin"})," attribute will always be cast to a ",(0,s.jsx)(t.code,{children:"QVariant(bool)"})," when you access it, even if the underlying value is stored in the database as an integer:"]}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-cpp",children:'using Orm::Utils::Helpers;\n\nauto isAdmin = User::find(1)->getAttribute("is_admin");\n\n// Proof of the QVariant type\nQ_ASSERT(Helpers::qVariantTypeId(isAdmin) == QMetaType::Bool);\n\nif (isAdmin.value()) {\n //\n}\n'})}),"\n",(0,s.jsxs)(t.p,{children:["If you need to add a new, ",(0,s.jsx)(t.strong,{children:"temporary"})," cast at runtime, you may use the ",(0,s.jsx)(t.code,{children:"mergeCasts"})," method. These cast definitions will be added to any of the casts already defined on the model:"]}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-cpp",children:'user->mergeCasts({\n {"is_paid", CastType::Boolean},\n {"income", {CastType::Decimal, 2}},\n});\n'})}),"\n",(0,s.jsx)(t.admonition,{type:"warning",children:(0,s.jsx)(t.p,{children:"You should never define a cast (or an attribute) that has the same name as a relationship."})}),"\n",(0,s.jsx)(t.admonition,{type:"info",children:(0,s.jsxs)(t.p,{children:["Attributes that are ",(0,s.jsx)(t.code,{children:"null"})," ",(0,s.jsx)(t.strong,{children:"will also be"})," cast so that the ",(0,s.jsx)(t.code,{children:"QVariant"}),"'s internal type will have the correct type."]})}),"\n",(0,s.jsx)(t.h3,{id:"date-casting",children:"Date Casting"}),"\n",(0,s.jsxs)(t.p,{children:["By default, TinyORM will cast the ",(0,s.jsx)(t.code,{children:"created_at"})," and ",(0,s.jsx)(t.code,{children:"updated_at"})," columns to instances of ",(0,s.jsx)(t.code,{children:"QDateTime"}),". You may cast additional date attributes by defining additional date casts within your model's ",(0,s.jsx)(t.code,{children:"u_casts"})," static data member unordered map. Typically, dates should be cast using the ",(0,s.jsx)(t.code,{children:"CastType::QDateTime"}),", ",(0,s.jsx)(t.code,{children:"CastType::QDate"}),", or ",(0,s.jsx)(t.code,{children:"CastType::Timestamp"})," cast types."]}),"\n",(0,s.jsxs)(t.p,{children:["When a database column is of the date type, you may set the corresponding model attribute value to a Unix timestamp, date string (",(0,s.jsx)(t.code,{children:"Y-m-d"}),"), date-time string, ",(0,s.jsx)(t.code,{children:"QDate"}),", or ",(0,s.jsx)(t.code,{children:"QDateTime"})," instance. The date's value will be correctly converted and stored in your database.",(0,s.jsx)("br",{}),"\nThe 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 ",(0,s.jsx)(t.code,{children:"QDateTime"})," instance."]}),"\n",(0,s.jsxs)(t.p,{children:["When defining the ",(0,s.jsx)(t.code,{children:"CastType::QDate"})," or ",(0,s.jsx)(t.code,{children:"CastType::QDateTime"})," cast, you may also specify the date's format. In this case you must use the ",(0,s.jsx)(t.code,{children:"CastType::CustomQDate"})," or ",(0,s.jsx)(t.code,{children:"CastType::CustomQDateTime"})," cast types. This format will be used when the ",(0,s.jsx)(t.a,{href:"/tinyorm/serialization",children:"model is serialized to a vector, map, or JSON"}),":"]}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-cpp",children:'/*! The attributes that should be cast. */\ninline static std::unordered_map u_casts {\n {"created_at", {CastType::CustomQDateTime, "yyyy-MM-dd"}},\n};\n'})}),"\n",(0,s.jsx)(t.admonition,{type:"note",children:(0,s.jsxs)(t.p,{children:["The ",(0,s.jsx)(t.code,{children:"CastType::CustomQDate"})," and ",(0,s.jsx)(t.code,{children:"CastType::CustomQDateTime"})," cast types behave exactly like the ",(0,s.jsx)(t.code,{children:"CastType::QDate"})," and ",(0,s.jsx)(t.code,{children:"CastType::QDateTime"})," cast types with the additional ",(0,s.jsx)(t.strong,{children:"date's format"})," functionality during ",(0,s.jsx)("u",{children:(0,s.jsx)(t.strong,{children:"serialization"})}),"."]})}),"\n",(0,s.jsxs)(t.p,{children:["You may customize the ",(0,s.jsx)(t.a,{href:"/tinyorm/serialization#customizing-the-default-date-format",children:"default serialization format"})," for all of your model's dates or datetimes by defining a ",(0,s.jsx)(t.code,{children:"serializeDate"})," or ",(0,s.jsx)(t.code,{children:"serializeDateTime"})," methods on your model. These methods do not affect how your dates are formatted for storage in the database:"]}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-cpp",children:'/*! Prepare a date for vector, map, or JSON serialization. */\nQString serializeDate(const QDate date)\n{\n return date.toString("yyyy-MM-dd");\n}\n\n/*! Prepare a datetime for vector, map, or JSON serialization. */\nQString serializeDateTime(const QDateTime &datetime)\n{\n return datetime.toUTC().toString("yyyy-MM-ddTHH:mm:ssZ");\n}\n'})}),"\n",(0,s.jsxs)(t.p,{children:["To specify the format that should be used when actually storing a model's dates within your database, you should define a ",(0,s.jsx)(t.code,{children:"u_dateFormat"})," data member on your model:"]}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-cpp",children:"/*! The storage format of the model's date columns. */\ninline static QString u_dateFormat {QLatin1Char('U')};\n"})}),"\n",(0,s.jsxs)(t.p,{children:["This format can be any format that the QDateTime's ",(0,s.jsx)(t.code,{children:"fromString"})," or ",(0,s.jsx)(t.code,{children:"toString"})," methods accept or the special ",(0,s.jsx)(t.code,{children:"U"})," format that represents the Unix timestamp (this ",(0,s.jsx)(t.code,{children:"U"})," format is TinyORM-specific and isn't supported by ",(0,s.jsx)(t.code,{children:"QDateTime"}),")."]}),"\n",(0,s.jsxs)(t.p,{children:["Define a ",(0,s.jsx)(t.code,{children:"u_timeFormat"})," data member on your model to specify the format that should be used when storing a model's times within your database:"]}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-cpp",children:'/*! The storage format of the model\'s time columns. */\ninline static QString u_timeFormat {"HH:mm:ss.zzz"};\n'})}),"\n",(0,s.jsx)(t.h4,{id:"date-casting-serialization-and-timezones",children:"Date Casting, Serialization & Timezones"}),"\n",(0,s.jsxs)(t.p,{children:["By default, the ",(0,s.jsx)(t.code,{children:"CastType::CustomQDate"})," and ",(0,s.jsx)(t.code,{children:"CastType::CustomQDateTime"})," casts will serialize dates to a UTC ISO-8601 date string (",(0,s.jsx)(t.code,{children:"yyyy-MM-ddTHH:mm:ss.zzzZ"}),"), regardless of the timezone specified in your database connection's ",(0,s.jsx)(t.code,{children:"qt_timezone"})," configuration option. You are strongly encouraged to always use this serialization format, as well as to store your application's dates in the UTC timezone by not changing your database connection's ",(0,s.jsx)(t.code,{children:"qt_timezone"})," configuration option from its default ",(0,s.jsx)(t.code,{children:"Qt::UTC"})," value. Consistently using the UTC timezone throughout your application will provide the maximum level of interoperability with other date manipulation libraries or services written in any programming language."]}),"\n",(0,s.jsxs)(t.p,{children:["If a custom format is applied to the ",(0,s.jsx)(t.code,{children:"CastType::CustomQDate"})," or ",(0,s.jsx)(t.code,{children:"CastType::CustomQDateTime"})," cast types, such as ",(0,s.jsx)(t.code,{children:'{CastType::CustomQDateTime, "yyyy-MM-dd HH:mm:ss"}'}),", the inner timezone of the QDateTime instance will be used during date serialization. Typically, this will be the timezone specified in your database connection's ",(0,s.jsx)(t.code,{children:"qt_timezone"})," configuration option."]}),"\n",(0,s.jsx)(t.h3,{id:"query-time-casting",children:"Query Time Casting"}),"\n",(0,s.jsx)(t.p,{children:"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:"}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-cpp",children:'using Models::Post;\nusing Models::User;\n\nauto users = User::select("users.*")\n ->addSelect(\n Post::selectRaw("MAX(created_at)")\n ->whereColumnEq("user_id", "users.id")\n .toBase(),\n "last_posted_at"\n ).get();\n'})}),"\n",(0,s.jsxs)(t.p,{children:["The ",(0,s.jsx)(t.code,{children:"last_posted_at"})," attribute on the results of this query will be a simple string. It would be wonderful if we could apply a ",(0,s.jsx)(t.code,{children:"CastType::QDateTime"})," cast to this attribute when executing the query. Thankfully, we may accomplish this using the ",(0,s.jsx)(t.code,{children:"withCasts"})," or ",(0,s.jsx)(t.code,{children:"withCast"})," methods:"]}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-cpp",children:'auto users = User::select("users.*")\n ->addSelect(Post::selectRaw("MAX(created_at)")\n ->whereColumnEq("user_id", "users.id")\n .toBase(),\n "last_posted_at")\n .withCast({"last_posted_at", CastType::QDateTime})\n .get();\n'})})]})}function h(e={}){const{wrapper:t}={...(0,i.R)(),...e.components};return t?(0,s.jsx)(t,{...e,children:(0,s.jsx)(l,{...e})}):l(e)}},8453:(e,t,n)=>{n.d(t,{R:()=>r,x:()=>o});var s=n(6540);const i={},a=s.createContext(i);function r(e){const t=s.useContext(a);return s.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function o(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:r(e.components),s.createElement(a.Provider,{value:t},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/62a1276f.f25d0360.js b/assets/js/62a1276f.f25d0360.js new file mode 100644 index 000000000..1cc47c20f --- /dev/null +++ b/assets/js/62a1276f.f25d0360.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunktinyorm_org=self.webpackChunktinyorm_org||[]).push([[304],{2403:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>d,contentTitle:()=>r,default:()=>h,frontMatter:()=>a,metadata:()=>o,toc:()=>c});var i=n(4848),s=n(8453);const a={sidebar_position:3,sidebar_label:"Casts",description:"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.",keywords:["c++ orm","orm","casts","casting","attributes","tinyorm"]},r="TinyORM: Casting",o={id:"tinyorm/casts",title:"TinyORM: Casting",description:"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.",source:"@site/docs/tinyorm/casts.mdx",sourceDirName:"tinyorm",slug:"/tinyorm/casts",permalink:"/tinyorm/casts",draft:!1,unlisted:!1,tags:[],version:"current",sidebarPosition:3,frontMatter:{sidebar_position:3,sidebar_label:"Casts",description:"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.",keywords:["c++ orm","orm","casts","casting","attributes","tinyorm"]},sidebar:"tinyormSidebar",previous:{title:"Collections",permalink:"/tinyorm/collections"},next:{title:"Serialization",permalink:"/tinyorm/serialization"}},d={},c=[{value:"Introduction",id:"introduction",level:2},{value:"Accessors",id:"accessors",level:2},{value:"Defining An Accessor",id:"defining-an-accessor",level:3},{value:"Building Value From Multiple Attributes",id:"building-value-from-multiple-attributes",level:4},{value:"Accessor Caching",id:"accessor-caching",level:4},{value:"Attribute Casting",id:"attribute-casting",level:2},{value:"Date Casting",id:"date-casting",level:3},{value:"Date Casting, Serialization & Timezones",id:"date-casting-serialization-and-timezones",level:4},{value:"Query Time Casting",id:"query-time-casting",level:3}];function l(e){const t={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",h3:"h3",h4:"h4",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(t.h1,{id:"tinyorm-casting",children:"TinyORM: Casting"}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.a,{href:"#introduction",children:"Introduction"})}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.a,{href:"#accessors",children:"Accessors"}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.a,{href:"#defining-an-accessor",children:"Defining An Accessor"})}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.a,{href:"#attribute-casting",children:"Attribute Casting"}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.a,{href:"#date-casting",children:"Date Casting"})}),"\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.a,{href:"#query-time-casting",children:"Query Time Casting"})}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(t.h2,{id:"introduction",children:"Introduction"}),"\n",(0,i.jsxs)(t.p,{children:["Attribute casting allows you to transform TinyORM attribute values when you retrieve them on model instances. For example, you may want to convert a ",(0,i.jsx)(t.code,{children:"datetime"})," string that is stored in your database to the ",(0,i.jsx)(t.code,{children:"QDateTime"})," instance when it is accessed via your TinyORM model. Or, you may want to convert a ",(0,i.jsx)(t.code,{children:"tinyint"})," number that is stored in the database to the ",(0,i.jsx)(t.code,{children:"bool"})," when you access it on the TinyORM model."]}),"\n",(0,i.jsx)(t.h2,{id:"accessors",children:"Accessors"}),"\n",(0,i.jsx)(t.admonition,{type:"warning",children:(0,i.jsxs)(t.p,{children:["Accessors are currently only used during the serialization by the ",(0,i.jsx)(t.a,{href:"/tinyorm/serialization#appending-values-to-json",children:"Appending Values"})," feature. They are not used during the ",(0,i.jsx)(t.code,{children:"getAttribute"})," or ",(0,i.jsx)(t.code,{children:"getAttributeValue"})," methods calls."]})}),"\n",(0,i.jsx)(t.h3,{id:"defining-an-accessor",children:"Defining An Accessor"}),"\n",(0,i.jsxs)(t.p,{children:["An accessor transforms a TinyORM attribute value when it is accessed (currently during serialization only by the ",(0,i.jsx)(t.a,{href:"/tinyorm/serialization#appending-values-to-json",children:"Appending Values"}),' feature). To define an accessor, create a protected method on your model to represent the accessible attribute. This method name should correspond to the "camelCase" representation of the true underlying model attribute / database column when applicable.']}),"\n",(0,i.jsxs)(t.p,{children:["In this example, we'll define an accessor for the ",(0,i.jsx)(t.code,{children:"first_name"})," attribute. The accessor will automatically be called by TinyORM during serialization if the ",(0,i.jsx)(t.code,{children:"first_name"})," attribute is defined in the ",(0,i.jsx)(t.code,{children:"u_appends"})," data member set. All attribute accessor methods must return the ",(0,i.jsx)(t.code,{children:"Orm::Tiny::Casts::Attribute"}),":"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-cpp",children:'#include \n\nusing Orm::Tiny::Model;\n\nclass User final : public Model\n{\n friend Model;\n using Model::Model;\n\nprotected:\n /*! Get the user\'s first name (accessor). */\n Attribute firstName() const noexcept\n {\n return Attribute::make(/* get */ [this]() -> QVariant\n {\n auto firstName = getAttribute("first_name");\n\n if (!firstName.isEmpty())\n firstName[0] = firstName.at(0).toUpper();\n\n return firstName;\n });\n }\n\nprivate:\n /*! Map of mutator names to methods. */\n inline static const QHash u_mutators {\n {"first_name", &User::firstName},\n };\n};\n'})}),"\n",(0,i.jsxs)(t.p,{children:["All accessor methods return an ",(0,i.jsx)(t.code,{children:"Attribute"})," instance which defines how the attribute will be accessed. To do so, we supply the ",(0,i.jsx)(t.code,{children:"get"})," argument to the ",(0,i.jsx)(t.code,{children:"Attribute"})," class constructor or ",(0,i.jsx)(t.code,{children:"Attribute::make"})," factory method."]}),"\n",(0,i.jsxs)(t.p,{children:["As you can see, the current model is captured by-reference using the ",(0,i.jsx)(t.code,{children:"[this]"})," capture, allowing you to obtain a value by the ",(0,i.jsx)(t.code,{children:"getAttribute"})," method inside the lambda expression, manipulate it and return a new value."]}),"\n",(0,i.jsxs)(t.p,{children:["You can also use the second overload that allows you to pass the ",(0,i.jsx)(t.code,{children:"ModelAttributes"})," unordered map to the lambda expression:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-cpp",children:'protected:\n /*! Get the user\'s first name (accessor). */\n Attribute firstName() const noexcept\n {\n return Attribute::make(\n /* get */ [](const ModelAttributes &attributes) -> QVariant\n {\n auto firstName = attributes.at("first_name");\n\n if (!firstName.isEmpty())\n firstName[0] = firstName.at(0).toUpper();\n\n return firstName;\n });\n }\n'})}),"\n",(0,i.jsxs)(t.p,{children:["The ",(0,i.jsx)(t.a,{href:"https://github.com/silverqx/TinyORM/blob/main/include/orm/tiny/types/modelattributes.hpp",children:(0,i.jsx)(t.code,{children:"ModelAttributes"})})," container extends the ",(0,i.jsx)(t.code,{children:"std::unordered_map"})," and adds the ",(0,i.jsx)(t.code,{children:"at"})," method that allows you to cast the underlying QVariant value."]}),"\n",(0,i.jsxs)(t.p,{children:["Special note should be given to the ",(0,i.jsx)(t.code,{children:"u_mutators"})," static data member map, which maps accessors' attribute names to its methods. This data member is ",(0,i.jsx)(t.strong,{children:"required"})," because C++ does not currently support reflection."]}),"\n",(0,i.jsx)(t.admonition,{type:"info",children:(0,i.jsxs)(t.p,{children:["If you would like these computed values to be added to the vector, map, or JSON representations of your model, ",(0,i.jsx)(t.a,{href:"/tinyorm/serialization#appending-values-to-json",children:"you will need to append them"}),"."]})}),"\n",(0,i.jsx)(t.admonition,{type:"danger",children:(0,i.jsx)(t.p,{children:"You must guarantee that the current model will live long enough to avoid the dangling reference and crash if the current model is captured by-reference. Of course, you can capture it by-copy in edge cases or if you can't guarantee this."})}),"\n",(0,i.jsx)(t.h4,{id:"building-value-from-multiple-attributes",children:"Building Value From Multiple Attributes"}),"\n",(0,i.jsx)(t.p,{children:"Sometimes your accessor may need to transform multiple model attributes into a single value. You can use both methods described above to accomplish this:"}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-cpp",children:'using Orm::Constants::SPACE_IN;\n\nprotected:\n /*! Get the user\'s full name (accessor). */\n Attribute fullName() const noexcept\n {\n return Attribute::make(/* get */ [this]() -> QVariant\n {\n return SPACE_IN.arg(getAttribute("first_name"),\n getAttribute("last_name"));\n });\n }\n'})}),"\n",(0,i.jsxs)(t.p,{children:["Or you can use the ",(0,i.jsx)(t.code,{children:"ModelAttributes"})," overload:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-cpp",children:'/*! Get the user\'s full name (accessor). */\nAttribute fullName() const noexcept\n{\n return Attribute::make(\n /* get */ [](const ModelAttributes &attributes) -> QVariant\n {\n return SPACE_IN.arg(attributes.at("first_name"),\n attributes.at("last_name"));\n });\n}\n'})}),"\n",(0,i.jsx)(t.h4,{id:"accessor-caching",children:"Accessor Caching"}),"\n",(0,i.jsxs)(t.p,{children:["Sometimes computing an attribute value can be intensive, in this case, you can enable caching for this attribute value. To accomplish this, you have to invoke the ",(0,i.jsx)(t.code,{children:"shouldCache"})," method when defining your accessor:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-cpp",children:'using Orm::Constants::SPACE_IN;\n\nprotected:\n /*! Get the user\'s full name (accessor). */\n Attribute fullName() const noexcept\n {\n return Attribute::make(/* get */ [this]() -> QVariant\n {\n return SPACE_IN.arg(getAttribute("first_name"),\n getAttribute("last_name"));\n }).shouldCache();\n }\n'})}),"\n",(0,i.jsx)(t.h2,{id:"attribute-casting",children:"Attribute Casting"}),"\n",(0,i.jsxs)(t.p,{children:["Attribute casting provides functionality that allows converting model attributes to the appropriate ",(0,i.jsx)(t.code,{children:"QVariant"})," ",(0,i.jsx)(t.strong,{children:"metatype"})," when it is accessed via your TinyORM model. The core of this functionality is a model's ",(0,i.jsx)(t.code,{children:"u_casts"})," static data member that provides a convenient method of converting attributes' ",(0,i.jsx)(t.code,{children:"QVariant"})," ",(0,i.jsx)(t.strong,{children:"internal types"})," to the defined cast types."]}),"\n",(0,i.jsxs)(t.p,{children:["The ",(0,i.jsx)(t.code,{children:"u_casts"})," static data member should be the ",(0,i.jsx)(t.code,{children:"std::unordered_map"})," 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:"]}),"\n",(0,i.jsx)("div",{id:"casts-types-list",children:(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.code,{children:"CastType::QString"})}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.code,{children:"CastType::Boolean"})," / ",(0,i.jsx)(t.code,{children:"CastType::Bool"})]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.code,{children:"CastType::Integer"})," / ",(0,i.jsx)(t.code,{children:"CastType::Int"})]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.code,{children:"CastType::UInteger"})," / ",(0,i.jsx)(t.code,{children:"CastType::UInt"})]}),"\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.code,{children:"CastType::LongLong"})}),"\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.code,{children:"CastType::ULongLong"})}),"\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.code,{children:"CastType::Short"})}),"\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.code,{children:"CastType::UShort"})}),"\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.code,{children:"CastType::QDate"})}),"\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.code,{children:"CastType::QDateTime"})}),"\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.code,{children:"CastType::Timestamp"})}),"\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.code,{children:"CastType::Real"})}),"\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.code,{children:"CastType::Float"})}),"\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.code,{children:"CastType::Double"})}),"\n",(0,i.jsxs)(t.li,{children:["\n",(0,i.jsx)("code",{children:"CastType::Decimal:"}),"\n"]}),"\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.code,{children:"CastType::QByteArray"})}),"\n"]})}),"\n",(0,i.jsx)(t.admonition,{type:"info",children:(0,i.jsxs)(t.p,{children:["The primary key name defined by the ",(0,i.jsx)(t.code,{children:"u_primaryKey"})," model's data member is automatically cast to the ",(0,i.jsx)(t.code,{children:"CastType::ULongLong"})," for all database drivers if the ",(0,i.jsx)(t.code,{children:"u_incrementing"})," is set to true (its default value)."]})}),"\n",(0,i.jsxs)(t.p,{children:["To demonstrate attribute casting, let's cast the ",(0,i.jsx)(t.code,{children:"is_admin"})," attribute, which is stored in our database as an integer (",(0,i.jsx)(t.code,{children:"0"})," or ",(0,i.jsx)(t.code,{children:"1"}),") to a ",(0,i.jsx)(t.code,{children:"QVariant(bool)"})," value:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-cpp",children:'#pragma once\n\n#include \n\nusing Orm::Tiny::Model;\n\nclass User final : public Model\n{\n friend Model;\n using Model::Model;\n\n /*! The attributes that should be cast. */\n inline static std::unordered_map u_casts {\n {"is_admin", CastType::Boolean},\n };\n};\n'})}),"\n",(0,i.jsxs)(t.p,{children:["After defining the cast, the ",(0,i.jsx)(t.code,{children:"is_admin"})," attribute will always be cast to a ",(0,i.jsx)(t.code,{children:"QVariant(bool)"})," when you access it, even if the underlying value is stored in the database as an integer:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-cpp",children:'using Orm::Utils::Helpers;\n\nauto isAdmin = User::find(1)->getAttribute("is_admin");\n\n// Proof of the QVariant type\nQ_ASSERT(Helpers::qVariantTypeId(isAdmin) == QMetaType::Bool);\n\nif (isAdmin.value()) {\n //\n}\n'})}),"\n",(0,i.jsxs)(t.p,{children:["If you need to add a new, ",(0,i.jsx)(t.strong,{children:"temporary"})," cast at runtime, you may use the ",(0,i.jsx)(t.code,{children:"mergeCasts"})," method. These cast definitions will be added to any of the casts already defined on the model:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-cpp",children:'user->mergeCasts({\n {"is_paid", CastType::Boolean},\n {"income", {CastType::Decimal, 2}},\n});\n'})}),"\n",(0,i.jsx)(t.admonition,{type:"warning",children:(0,i.jsx)(t.p,{children:"You should never define a cast (or an attribute) that has the same name as a relationship."})}),"\n",(0,i.jsx)(t.admonition,{type:"info",children:(0,i.jsxs)(t.p,{children:["Attributes that are ",(0,i.jsx)(t.code,{children:"null"})," ",(0,i.jsx)(t.strong,{children:"will also be"})," cast so that the ",(0,i.jsx)(t.code,{children:"QVariant"}),"'s internal type will have the correct type."]})}),"\n",(0,i.jsx)(t.h3,{id:"date-casting",children:"Date Casting"}),"\n",(0,i.jsxs)(t.p,{children:["By default, TinyORM will cast the ",(0,i.jsx)(t.code,{children:"created_at"})," and ",(0,i.jsx)(t.code,{children:"updated_at"})," columns to instances of ",(0,i.jsx)(t.code,{children:"QDateTime"}),". You may cast additional date attributes by defining additional date casts within your model's ",(0,i.jsx)(t.code,{children:"u_casts"})," static data member unordered map. Typically, dates should be cast using the ",(0,i.jsx)(t.code,{children:"CastType::QDateTime"}),", ",(0,i.jsx)(t.code,{children:"CastType::QDate"}),", or ",(0,i.jsx)(t.code,{children:"CastType::Timestamp"})," cast types."]}),"\n",(0,i.jsxs)(t.p,{children:["When a database column is of the date type, you may set the corresponding model attribute value to a Unix timestamp, date string (",(0,i.jsx)(t.code,{children:"Y-m-d"}),"), date-time string, ",(0,i.jsx)(t.code,{children:"QDate"}),", or ",(0,i.jsx)(t.code,{children:"QDateTime"})," instance. The date's value will be correctly converted and stored in your database.",(0,i.jsx)("br",{}),"\nThe 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 ",(0,i.jsx)(t.code,{children:"QDateTime"})," instance."]}),"\n",(0,i.jsxs)(t.p,{children:["When defining the ",(0,i.jsx)(t.code,{children:"CastType::QDate"})," or ",(0,i.jsx)(t.code,{children:"CastType::QDateTime"})," cast, you may also specify the date's format. In this case you must use the ",(0,i.jsx)(t.code,{children:"CastType::CustomQDate"})," or ",(0,i.jsx)(t.code,{children:"CastType::CustomQDateTime"})," cast types. This format will be used when the ",(0,i.jsx)(t.a,{href:"/tinyorm/serialization",children:"model is serialized to a vector, map, or JSON"}),":"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-cpp",children:'/*! The attributes that should be cast. */\ninline static std::unordered_map u_casts {\n {"created_at", {CastType::CustomQDateTime, "yyyy-MM-dd"}},\n};\n'})}),"\n",(0,i.jsx)(t.admonition,{type:"note",children:(0,i.jsxs)(t.p,{children:["The ",(0,i.jsx)(t.code,{children:"CastType::CustomQDate"})," and ",(0,i.jsx)(t.code,{children:"CastType::CustomQDateTime"})," cast types behave exactly like the ",(0,i.jsx)(t.code,{children:"CastType::QDate"})," and ",(0,i.jsx)(t.code,{children:"CastType::QDateTime"})," cast types with the additional ",(0,i.jsx)(t.strong,{children:"date's format"})," functionality during ",(0,i.jsx)("u",{children:(0,i.jsx)(t.strong,{children:"serialization"})}),"."]})}),"\n",(0,i.jsxs)(t.p,{children:["You may customize the ",(0,i.jsx)(t.a,{href:"/tinyorm/serialization#customizing-the-default-date-format",children:"default serialization format"})," for all of your model's dates or datetimes by defining a ",(0,i.jsx)(t.code,{children:"serializeDate"})," or ",(0,i.jsx)(t.code,{children:"serializeDateTime"})," methods on your model. These methods do not affect how your dates are formatted for storage in the database:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-cpp",children:'/*! Prepare a date for vector, map, or JSON serialization. */\nQString serializeDate(const QDate date)\n{\n return date.toString("yyyy-MM-dd");\n}\n\n/*! Prepare a datetime for vector, map, or JSON serialization. */\nQString serializeDateTime(const QDateTime &datetime)\n{\n return datetime.toUTC().toString("yyyy-MM-ddTHH:mm:ssZ");\n}\n'})}),"\n",(0,i.jsxs)(t.p,{children:["To specify the format that should be used when actually storing a model's dates within your database, you should define a ",(0,i.jsx)(t.code,{children:"u_dateFormat"})," data member on your model:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-cpp",children:"/*! The storage format of the model's date columns. */\ninline static QString u_dateFormat {QLatin1Char('U')};\n"})}),"\n",(0,i.jsxs)(t.p,{children:["This format can be any format that the QDateTime's ",(0,i.jsx)(t.code,{children:"fromString"})," or ",(0,i.jsx)(t.code,{children:"toString"})," methods accept or the special ",(0,i.jsx)(t.code,{children:"U"})," format that represents the Unix timestamp (this ",(0,i.jsx)(t.code,{children:"U"})," format is TinyORM-specific and isn't supported by ",(0,i.jsx)(t.code,{children:"QDateTime"}),")."]}),"\n",(0,i.jsxs)(t.p,{children:["Define a ",(0,i.jsx)(t.code,{children:"u_timeFormat"})," data member on your model to specify the format that should be used when storing a model's times within your database:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-cpp",children:'/*! The storage format of the model\'s time columns. */\ninline static QString u_timeFormat {"HH:mm:ss.zzz"};\n'})}),"\n",(0,i.jsx)(t.h4,{id:"date-casting-serialization-and-timezones",children:"Date Casting, Serialization & Timezones"}),"\n",(0,i.jsxs)(t.p,{children:["By default, the ",(0,i.jsx)(t.code,{children:"CastType::CustomQDate"})," and ",(0,i.jsx)(t.code,{children:"CastType::CustomQDateTime"})," casts will serialize dates to a UTC ISO-8601 date string (",(0,i.jsx)(t.code,{children:"yyyy-MM-ddTHH:mm:ss.zzzZ"}),"), regardless of the timezone specified in your database connection's ",(0,i.jsx)(t.code,{children:"qt_timezone"})," configuration option. You are strongly encouraged to always use this serialization format, as well as to store your application's dates in the UTC timezone by not changing your database connection's ",(0,i.jsx)(t.code,{children:"qt_timezone"})," configuration option from its default ",(0,i.jsx)(t.code,{children:"QTimeZone::UTC"})," value. Consistently using the UTC timezone throughout your application will provide the maximum level of interoperability with other date manipulation libraries or services written in any programming language."]}),"\n",(0,i.jsxs)(t.p,{children:["If a custom format is applied to the ",(0,i.jsx)(t.code,{children:"CastType::CustomQDate"})," or ",(0,i.jsx)(t.code,{children:"CastType::CustomQDateTime"})," cast types, such as ",(0,i.jsx)(t.code,{children:'{CastType::CustomQDateTime, "yyyy-MM-dd HH:mm:ss"}'}),", the inner timezone of the QDateTime instance will be used during date serialization. Typically, this will be the timezone specified in your database connection's ",(0,i.jsx)(t.code,{children:"qt_timezone"})," configuration option."]}),"\n",(0,i.jsxs)(t.admonition,{type:"info",children:[(0,i.jsxs)(t.p,{children:["Passing the ",(0,i.jsx)(t.code,{children:"Qt::TimeSpec"})," (eg. ",(0,i.jsx)(t.code,{children:"Qt::UTC"})," or ",(0,i.jsx)(t.code,{children:"Qt::LocalTime"}),") to ",(0,i.jsx)(t.code,{children:"QDateTime"})," methods was deprecated in ",(0,i.jsx)(t.a,{href:"https://github.com/qt/qtbase/commit/8c8d6ff7b6e2e6b1b673051685f1499ae4d65e05?diff=unified&w=0",children:(0,i.jsx)(t.code,{children:"Qt >=6.5"})}),", it was replaced by the ",(0,i.jsx)(t.code,{children:"enum QTimeZone::Initialization"})," (",(0,i.jsx)(t.code,{children:"QTimeZone::UTC"})," and ",(0,i.jsx)(t.code,{children:"QTimeZone::LocalTime"}),")."]}),(0,i.jsxs)(t.p,{children:["If you need to support older and newer versions of Qt at the same time, you can use the ",(0,i.jsx)(t.code,{children:"Orm::QtTimeZoneConfig::utc()"})," or ",(0,i.jsx)(t.code,{children:"QtTimeZoneConfig::localTime()"})," factory methods to create the ",(0,i.jsx)(t.code,{children:"QtTimeZoneConfig"})," instance."]})]}),"\n",(0,i.jsx)(t.h3,{id:"query-time-casting",children:"Query Time Casting"}),"\n",(0,i.jsx)(t.p,{children:"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:"}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-cpp",children:'using Models::Post;\nusing Models::User;\n\nauto users = User::select("users.*")\n ->addSelect(\n Post::selectRaw("MAX(created_at)")\n ->whereColumnEq("user_id", "users.id")\n .toBase(),\n "last_posted_at"\n ).get();\n'})}),"\n",(0,i.jsxs)(t.p,{children:["The ",(0,i.jsx)(t.code,{children:"last_posted_at"})," attribute on the results of this query will be a simple string. It would be wonderful if we could apply a ",(0,i.jsx)(t.code,{children:"CastType::QDateTime"})," cast to this attribute when executing the query. Thankfully, we may accomplish this using the ",(0,i.jsx)(t.code,{children:"withCasts"})," or ",(0,i.jsx)(t.code,{children:"withCast"})," methods:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-cpp",children:'auto users = User::select("users.*")\n ->addSelect(Post::selectRaw("MAX(created_at)")\n ->whereColumnEq("user_id", "users.id")\n .toBase(),\n "last_posted_at")\n .withCast({"last_posted_at", CastType::QDateTime})\n .get();\n'})})]})}function h(e={}){const{wrapper:t}={...(0,s.R)(),...e.components};return t?(0,i.jsx)(t,{...e,children:(0,i.jsx)(l,{...e})}):l(e)}},8453:(e,t,n)=>{n.d(t,{R:()=>r,x:()=>o});var i=n(6540);const s={},a=i.createContext(s);function r(e){const t=i.useContext(a);return i.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function o(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:r(e.components),i.createElement(a.Provider,{value:t},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/8a8faf8d.1738552f.js b/assets/js/8a8faf8d.1738552f.js deleted file mode 100644 index 040ad650c..000000000 --- a/assets/js/8a8faf8d.1738552f.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunktinyorm_org=self.webpackChunktinyorm_org||[]).push([[129],{6455:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>p,contentTitle:()=>u,default:()=>j,frontMatter:()=>h,metadata:()=>m,toc:()=>g});var s=i(4848),t=i(8453),a=i(8774),r=i(2364),l=i(9365),o=i(1470),d=i(7324),c=i(6694);const h={sidebar_position:3,sidebar_label:"Migrations",description:"How to compile the TinyORM migrations (tom) C++ console application on Windows and Linux.",keywords:["c++ orm","building","migrations","tinyorm"]},u="Building: Migrations",m={id:"building/migrations",title:"Building: Migrations",description:"How to compile the TinyORM migrations (tom) C++ console application on Windows and Linux.",source:"@site/docs/building/migrations.mdx",sourceDirName:"building",slug:"/building/migrations",permalink:"/building/migrations",draft:!1,unlisted:!1,tags:[],version:"current",sidebarPosition:3,frontMatter:{sidebar_position:3,sidebar_label:"Migrations",description:"How to compile the TinyORM migrations (tom) C++ console application on Windows and Linux.",keywords:["c++ orm","building","migrations","tinyorm"]},sidebar:"tinyormSidebar",previous:{title:"Hello world",permalink:"/building/hello-world"},next:{title:"\ud83d\udcc4 Features Summary",permalink:"/features-summary"}},p={},g=[{value:"Introduction",id:"introduction",level:2},{value:"Install dependencies",id:"install-dependencies",level:2},{value:"Using vcpkg.json (manifest mode)",id:"using-vcpkg-json-manifest-mode",level:4},{value:"Using vcpkg install (manually)",id:"using-vcpkg-install-manually",level:4},{value:"Source code",id:"source-code",level:2},{value:"Main file",id:"main-file",level:3},{value:"Migrations",id:"migrations",level:3},{value:"Seeders",id:"seeders",level:3},{value:"Migrations with CMake",id:"migrations-with-cmake",level:2},{value:"CMake project",id:"cmake-project",level:3},{value:"Build migrations",id:"build-migrations-cmake",level:3},{value:"Execute migrations",id:"execute-migrations-cmake",level:3},{value:"Migrations with qmake",id:"migrations-with-qmake",level:2},{value:"qmake project",id:"qmake-project",level:3},{value:"Auto-configure using .qmake.conf and .env",id:"auto-configure-using-qmakeconf-and-env",level:4},{value:"Migrations source files",id:"migrations-source-files",level:4},{value:"Seeders source files",id:"seeders-source-files",level:4},{value:"Build migrations",id:"build-migrations-qmake",level:3},{value:"Execute migrations",id:"execute-migrations-qmake",level:3},{value:"Finish",id:"finish",level:2}];function x(e){const n={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",h3:"h3",h4:"h4",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,t.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(n.h1,{id:"building-migrations",children:"Building: Migrations"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#introduction",children:"Introduction"})}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.a,{href:"#install-dependencies",children:"Install dependencies"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#using-vcpkg-json-manifest-mode",children:"Using vcpkg.json"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#using-vcpkg-install-manually",children:"Using vcpkg install"})}),"\n"]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.a,{href:"#source-code",children:"Source code"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#main-file",children:"Main file"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#migrations",children:"Migrations"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#seeders",children:"Seeders"})}),"\n"]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.a,{href:"#migrations-with-cmake",children:"Migrations with CMake"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#cmake-project",children:"CMake project"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#build-migrations-cmake",children:"Build migrations"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#execute-migrations-cmake",children:"Execute migrations"})}),"\n"]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.a,{href:"#migrations-with-qmake",children:"Migrations with qmake"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#qmake-project",children:"qmake project"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#build-migrations-qmake",children:"Build migrations"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#execute-migrations-qmake",children:"Execute migrations"})}),"\n"]}),"\n"]}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#finish",children:"Finish"})}),"\n"]}),"\n",(0,s.jsx)(n.h2,{id:"introduction",children:"Introduction"}),"\n",(0,s.jsxs)(n.p,{children:["We will try to create a working migrations console application called as ",(0,s.jsx)("abbr",{title:"TinyORM migrations",children:(0,s.jsx)(n.code,{children:"tom"})})," in the terminal with the ",(0,s.jsx)(n.code,{children:"CMake"})," and in the ",(0,s.jsx)(n.code,{children:"QtCreator IDE"})," with the ",(0,s.jsx)(n.code,{children:"qmake"})," build systems."]}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"tom"})," console application also expects the following ",(0,s.jsx)(n.a,{href:"/building/tinyorm#folders-structure",children:"folders structure"}),", let's create them."]}),"\n",(0,s.jsxs)(o.A,{groupId:d.vf,children:[(0,s.jsx)(l.A,{value:d.b,label:d.ux,children:(0,s.jsx)(r.A,{className:"language-powershell",children:`cd ${(0,c.OZ)(d.b)}\nmkdir tom/tom\ncd tom`})}),(0,s.jsx)(l.A,{value:d.xj,label:d.gg,children:(0,s.jsx)(r.A,{className:"language-bash",children:`cd ${(0,c.OZ)(d.xj)}\nmkdir -p tom/tom\ncd tom`})})]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.code,{children:"TinyORM"})," source tree contains the ",(0,s.jsx)(n.code,{children:"tom"})," example application, you can inspire or look at the ",(0,s.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/tree/main/examples/tom",children:"source code"}),". Also, ",(0,s.jsx)(n.code,{children:"TinyORM"})," unit tests use a ",(0,s.jsx)(n.code,{children:"tom"})," migrations internally to create the database structure, internally called as the ",(0,s.jsxs)(n.a,{href:"https://github.com/silverqx/TinyORM/tree/main/tests/testdata_tom",children:[(0,s.jsx)(n.code,{children:"tom"})," migrations for unit tests"]}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["All these three console applications the ",(0,s.jsx)(n.code,{children:"tom"})," example, ",(0,s.jsx)(n.code,{children:"tom"})," migrations for unit tests, and the application described in this tutorial have practically identical source code (the main.cpp file)."]}),"\n",(0,s.jsx)(n.admonition,{type:"note",children:(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"tom"})," is able to generate ",(0,s.jsx)("a",{href:"https://en.wikipedia.org/wiki/Data_definition_language",title:"Data Definition Language",children:"DDL"})," queries for all the ",(0,s.jsx)(n.a,{href:"/database/getting-started#introduction",children:"supported databases"})," databases."]})}),"\n",(0,s.jsx)(n.admonition,{type:"info",children:(0,s.jsxs)(n.p,{children:["You can see the ",(0,s.jsx)(n.a,{href:"/features-summary#tom-console-application",children:"Tom showcase image"})," of how the resulting ",(0,s.jsx)(n.code,{children:"tom"})," console application will look like."]})}),"\n",(0,s.jsx)(n.h2,{id:"install-dependencies",children:"Install dependencies"}),"\n",(0,s.jsxs)(n.p,{children:["First, install the ",(0,s.jsx)(n.code,{children:"vcpkg"})," package manager as is described ",(0,s.jsx)(n.a,{href:"/building/tinyorm#vcpkg",children:"here"}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"range-v3"})," and ",(0,s.jsx)(n.code,{children:"tabulate"})," libraries are required dependencies because ",(0,s.jsx)(n.code,{children:"TinyORM"})," uses them in header files, you have to install them before you can use ",(0,s.jsx)(n.code,{children:"TinyORM"}),". The ",(0,s.jsx)(n.code,{children:"tabulate"})," library is only needed in the ",(0,s.jsx)(n.code,{children:"tom"})," migrations it's used by the ",(0,s.jsx)(n.code,{children:"migrate:status"})," command."]}),"\n",(0,s.jsxs)(n.p,{children:["There are two ways how to install the ",(0,s.jsx)(n.code,{children:"range-v3"})," and ",(0,s.jsx)(n.code,{children:"tabulate"})," libraries using ",(0,s.jsx)(n.code,{children:"vcpkg"}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["Also, don't forget to build the ",(0,s.jsx)(n.code,{children:"TinyORM"})," library with the ",(0,s.jsx)(n.code,{children:"tom"})," source code enabled (it's enabled by default) as is described ",(0,s.jsx)(n.a,{href:"/building/tinyorm",children:"here"}),"."]}),"\n",(0,s.jsxs)(n.h4,{id:"using-vcpkg-json-manifest-mode",children:["Using vcpkg.json ",(0,s.jsx)("small",{children:"(manifest mode)"})]}),"\n",(0,s.jsxs)(n.p,{children:["Create a ",(0,s.jsx)(n.code,{children:"vcpkg.json"})," file with the following content. ",(0,s.jsx)(n.code,{children:"CMake"})," example below uses this method."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"cd tom\nvim vcpkg.json\n"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-json",metastring:"title='vcpkg.json'",children:'{\n "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json",\n "name": "tom",\n "version-semver": "0.1.0",\n "maintainers": "Silver Zachara ",\n "description": "Tom console application for TinyORM C++ library",\n "homepage": "https://github.com/silverqx/TinyORM",\n "documentation": "https://www.tinyorm.org/building/migrations",\n "supports": "!(uwp | arm | android | emscripten | osx | ios | xbox | freebsd | openbsd | wasm32)",\n "dependencies": [\n "range-v3",\n "tabulate"\n ]\n}\n'})}),"\n",(0,s.jsx)(n.admonition,{type:"note",children:(0,s.jsxs)(n.p,{children:["Only ",(0,s.jsx)(n.code,{children:"CMake"})," via the ",(0,s.jsx)(n.code,{children:"toolchain file"})," supports this method."]})}),"\n",(0,s.jsxs)(n.h4,{id:"using-vcpkg-install-manually",children:["Using vcpkg install ",(0,s.jsx)("small",{children:"(manually)"})]}),"\n",(0,s.jsxs)(n.p,{children:["This method can be used with both ",(0,s.jsx)(n.code,{children:"CMake"})," and ",(0,s.jsx)(n.code,{children:"qmake"}),"."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"cd ../../vcpkg\n\nvcpkg search range-v3\nvcpkg search tabulate\nvcpkg install range-v3 tabulate\nvcpkg list\n"})}),"\n",(0,s.jsx)(n.h2,{id:"source-code",children:"Source code"}),"\n",(0,s.jsxs)(n.p,{children:["Let's start in the ",(0,s.jsx)(n.code,{children:"tom"})," project folder."]}),"\n",(0,s.jsxs)(o.A,{groupId:d.vf,children:[(0,s.jsx)(l.A,{value:d.b,label:d.ux,children:(0,s.jsx)(r.A,{className:"language-powershell",children:`cd ${(0,c.OZ)(d.b)}/tom/tom`})}),(0,s.jsx)(l.A,{value:d.xj,label:d.gg,children:(0,s.jsx)(r.A,{className:"language-bash",children:`cd ${(0,c.OZ)(d.xj)}/tom/tom`})})]}),"\n",(0,s.jsx)(n.h3,{id:"main-file",children:"Main file"}),"\n",(0,s.jsxs)(n.p,{children:["Create ",(0,s.jsx)(n.code,{children:"main.cpp"})," source file."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"vim main.cpp\n"})}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["To paste a source code correctly in ",(0,s.jsx)(n.code,{children:"vim"}),", press ",(0,s.jsx)("kbd",{children:"Shift"})," + ",(0,s.jsx)("kbd",{children:"p"}),"."]})}),"\n",(0,s.jsx)(n.p,{children:"And paste the following code."}),"\n",(0,s.jsx)(a.A,{id:"string-constants-example"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cpp",metastring:"title='main.cpp'",children:'#include \n\n#include \n\n#include "migrations/2014_10_12_000000_create_posts_table.hpp"\n\n#include "seeders/databaseseeder.hpp"\n\nusing Orm::DatabaseManager;\nusing Orm::DB;\n\nusing TomApplication = Tom::Application;\n\nusing namespace Migrations; // NOLINT(google-build-using-namespace)\nusing namespace Seeders; // NOLINT(google-build-using-namespace)\n\n/*! Create the database manager instance and add a database connection. */\nstd::shared_ptr setupDatabaseManager();\n\n/*! C++ main function. */\nint main(int argc, char *argv[])\n{\n try {\n // Ownership of the shared_ptr()\n auto db = setupDatabaseManager();\n\n return TomApplication(argc, argv, std::move(db), "TOM_EXAMPLE_ENV")\n .migrations()\n .seeders()\n // Fire it up \ud83d\udd25\ud83d\ude80\u2728\n .run();\n\n } catch (const std::exception &e) {\n\n TomApplication::logException(e);\n }\n\n return EXIT_FAILURE;\n}\n\nstd::shared_ptr setupDatabaseManager()\n{\n using namespace Orm::Constants; // NOLINT(google-build-using-namespace)\n\n // Ownership of the shared_ptr()\n return DB::create({\n {driver_, QMYSQL},\n {host_, qEnvironmentVariable("DB_MYSQL_HOST", H127001)},\n {port_, qEnvironmentVariable("DB_MYSQL_PORT", P3306)},\n {database_, qEnvironmentVariable("DB_MYSQL_DATABASE", EMPTY)},\n {username_, qEnvironmentVariable("DB_MYSQL_USERNAME", EMPTY)},\n {password_, qEnvironmentVariable("DB_MYSQL_PASSWORD", EMPTY)},\n {charset_, qEnvironmentVariable("DB_MYSQL_CHARSET", UTF8MB4)},\n {collation_, qEnvironmentVariable("DB_MYSQL_COLLATION", UTF8MB40900aici)},\n {timezone_, TZ00},\n /* Specifies what time zone all QDateTime-s will have, the overridden default is\n the Qt::UTC, set to the Qt::LocalTime or QtTimeZoneType::DontConvert to use\n the system local time. */\n {qt_timezone, QVariant::fromValue(Qt::UTC)},\n {strict_, true},\n },\n QStringLiteral("tinyorm_tom_mysql")); // shell:connection\n}\n'})}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["If you have defined more database connections then you can tag the lines with the database connection names with the ",(0,s.jsx)(n.code,{children:"// shell:connection"})," comment and this connection names will be provided to the bash, zsh, pwsh completions for the ",(0,s.jsx)(n.code,{children:"--database="})," option \ud83d\ude0e, ",(0,s.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/examples/tom/main.cpp#L74",children:"example"}),"."]})}),"\n",(0,s.jsx)(n.h3,{id:"migrations",children:"Migrations"}),"\n",(0,s.jsxs)(n.p,{children:["If you have already built the ",(0,s.jsx)(n.code,{children:"tom"})," application then you can generate a migrations using the ",(0,s.jsx)(n.a,{href:"/database/migrations#generating-migrations",children:(0,s.jsx)(n.code,{children:"make:migration"})})," command \ud83d\ude0e."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"tom make:migration create_posts_table\n"})}),"\n",(0,s.jsxs)(n.p,{children:["Below is the expected folders structure for the migrations. The ",(0,s.jsx)(n.a,{href:"#migrations-source-files",children:(0,s.jsx)(n.code,{children:"migrations.pri"})})," file is used only by the ",(0,s.jsx)(n.code,{children:"qmake"})," build system and is not needed with ",(0,s.jsx)(n.code,{children:"CMake"})," builds."]}),"\n",(0,s.jsx)(a.A,{id:"folders-structure"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-text",children:"tom/\n\u2514\u2500\u2500 database/\n \u251c\u2500\u2500 migrations/\n \u251c\u2500\u2500 seeders/\n \u251c\u2500\u2500 migrations.pri\n \u2514\u2500\u2500 seeders.pri\n"})}),"\n",(0,s.jsx)(n.p,{children:"Let's create the first migration manually."}),"\n",(0,s.jsxs)(o.A,{groupId:d.vf,children:[(0,s.jsx)(l.A,{value:d.b,label:d.ux,children:(0,s.jsx)(r.A,{className:"language-powershell",children:"mkdir database/migrations\n\nvim database/migrations/2014_10_12_000000_create_posts_table.hpp"})}),(0,s.jsx)(l.A,{value:d.xj,label:d.gg,children:(0,s.jsx)(r.A,{className:"language-bash",children:"mkdir -p database/migrations\n\nvim database/migrations/2014_10_12_000000_create_posts_table.hpp"})})]}),"\n",(0,s.jsx)(n.p,{children:"And paste the following code."}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cpp",metastring:"title='database/migrations/2014_10_12_000000_create_posts_table.hpp'",children:'#pragma once\n\n#include \n\nnamespace Migrations\n{\n\n struct CreatePostsTable : Migration\n {\n /*! Filename of the migration file. */\n T_MIGRATION\n\n /*! Run the migrations. */\n void up() const override\n {\n Schema::create("posts", [](Blueprint &table)\n {\n table.id();\n\n table.string(NAME);\n table.timestamps();\n });\n }\n\n /*! Reverse the migrations. */\n void down() const override\n {\n Schema::dropIfExists("posts");\n }\n };\n\n} // namespace Migrations\n'})}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"TinyORM"})," source tree contains the ",(0,s.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/tests/database/migrations/2014_10_12_000000_create_posts_table.hpp#L5",children:(0,s.jsx)(n.code,{children:"CreatePostsTable"})})," example migration that also acts as the full-fledged example migration. It has defined and also nicely commented all possible features that migration classes can use or define."]})}),"\n",(0,s.jsx)(n.admonition,{type:"info",children:(0,s.jsxs)(n.p,{children:["If you want, you can also build the ",(0,s.jsx)(n.code,{children:"tom"})," application without the migrations, simply comment out the ",(0,s.jsx)(n.code,{children:"migrations"})," method and the corresponding ",(0,s.jsx)(n.code,{children:'#include "migrations/xyz.hpp"'})," files."]})}),"\n",(0,s.jsx)(n.h3,{id:"seeders",children:"Seeders"}),"\n",(0,s.jsxs)(n.p,{children:["If you have already built the ",(0,s.jsx)(n.code,{children:"tom"})," application then you can generate a seeder using the ",(0,s.jsx)(n.a,{href:"/database/seeding#writing-seeders",children:(0,s.jsx)(n.code,{children:"make:seeder"})})," command \ud83d\ude0e."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"tom make:seeder PostSeeder\n"})}),"\n",(0,s.jsxs)(n.p,{children:["The expected folders structure is described a few paragraphs ",(0,s.jsx)(n.a,{href:"#folders-structure",children:"above"}),". The ",(0,s.jsx)(n.a,{href:"#seeders-source-files",children:(0,s.jsx)(n.code,{children:"seeders.pri"})})," file is used only by the ",(0,s.jsx)(n.code,{children:"qmake"})," build system and is not needed with ",(0,s.jsx)(n.code,{children:"CMake"})," builds."]}),"\n",(0,s.jsx)(n.p,{children:"Let's create the root seeder class manually."}),"\n",(0,s.jsxs)(o.A,{groupId:d.vf,children:[(0,s.jsx)(l.A,{value:d.b,label:d.ux,children:(0,s.jsx)(r.A,{className:"language-powershell",children:"mkdir database/seeders\n\nvim database/seeders/databaseseeder.hpp"})}),(0,s.jsx)(l.A,{value:d.xj,label:d.gg,children:(0,s.jsx)(r.A,{className:"language-bash",children:"mkdir -p database/seeders\n\nvim database/seeders/databaseseeder.hpp"})})]}),"\n",(0,s.jsx)(n.p,{children:"And paste the following code."}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cpp",metastring:"title='database/seeders/databaseseeder.hpp'",children:'#pragma once\n\n#include \n\nnamespace Seeders\n{\n\n /*! Main database seeder. */\n struct DatabaseSeeder : Seeder\n {\n /*! Run the database seeders. */\n void run() override\n {\n DB::table("posts")->insert({\n {{"name", "1. post"}},\n {{"name", "2. post"}},\n });\n }\n };\n\n} // namespace Seeders\n'})}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"TinyORM"})," source tree contains the ",(0,s.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/tests/database/seeders/databaseseeder.hpp#L8",children:(0,s.jsx)(n.code,{children:"DatabaseSeeder"})})," root seeder example class that also acts as the full-fledged example seeder. It has defined and also nicely commented all possible features that seeder classes can use or define."]})}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["You can create more seeder classes like this and use the ",(0,s.jsx)(n.code,{children:"call<>()"})," method to invoke them as is described in the ",(0,s.jsx)(n.a,{href:"/database/seeding#calling-additional-seeders",children:"Calling Additional Seeders"})," section."]})}),"\n",(0,s.jsx)(n.h2,{id:"migrations-with-cmake",children:"Migrations with CMake"}),"\n",(0,s.jsxs)(n.p,{children:["Create a folder for the ",(0,s.jsx)(n.code,{children:"CMake"})," build."]}),"\n",(0,s.jsxs)(o.A,{groupId:d.vf,children:[(0,s.jsx)(l.A,{value:d.b,label:d.ux,children:(0,s.jsx)(r.A,{className:"language-powershell",children:"cd ..\nmkdir tom-builds-cmake/build-debug\n\ncd tom"})}),(0,s.jsx)(l.A,{value:d.xj,label:d.gg,children:(0,s.jsx)(r.A,{className:"language-bash",children:"cd ..\nmkdir -p tom-builds-cmake/build-debug\n\ncd tom"})})]}),"\n",(0,s.jsx)(n.h3,{id:"cmake-project",children:"CMake project"}),"\n",(0,s.jsxs)(n.p,{children:["Create ",(0,s.jsx)(n.code,{children:"CMakeLists.txt"})," file with the following content. I leave the comments in the ",(0,s.jsx)(n.code,{children:"CMakeLists.txt"})," file because it's not as simple as the ",(0,s.jsx)(n.code,{children:"Hello world"})," example; to make it clear what's going on."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cmake",metastring:"title='CMakeLists.txt'",children:'cmake_minimum_required(VERSION VERSION 3.22...3.29 FATAL_ERROR)\n\n# Specify the C++ standard\nset(CMAKE_CXX_STANDARD 20)\nset(CMAKE_CXX_STANDARD_REQUIRED ON)\nset(CMAKE_CXX_EXTENSIONS OFF)\n\n# Initialize variables\n# ---\n\nset(Tom_ns tom)\nset(Tom_target tom)\n\nfile(REAL_PATH "../../TinyORM" TinyMainDir)\n\nset(TinyOrmSourceDir "${TinyMainDir}/TinyORM")\nset(TinyOrmBuildDir "${TinyMainDir}/TinyORM-builds-cmake/build-debug")\n\n# TinyORM CMake modules (needed to set the executable version and RC file on Windows)\nlist(APPEND CMAKE_MODULE_PATH "${TinyOrmSourceDir}/cmake/CommonModules")\n\n# build tree\nlist(APPEND CMAKE_PREFIX_PATH "${TinyOrmBuildDir}")\n\n# Initialize Project Version\n# ---\n\ninclude(TinyHelpers)\ntiny_read_version(TINY_VERSION\n TINY_VERSION_MAJOR TINY_VERSION_MINOR TINY_VERSION_PATCH TINY_VERSION_TWEAK\n VERSION_HEADER "${TinyOrmSourceDir}/tom/include/tom/version.hpp"\n PREFIX TINYTOM\n HEADER_FOR "${Tom_ns}"\n)\n\n# Basic project\n# ---\n\nproject(${Tom_ns}\n DESCRIPTION "Tom console application for TinyORM C++ library"\n HOMEPAGE_URL "https://www.tinyorm.org"\n LANGUAGES CXX\n VERSION ${TINY_VERSION}\n)\n\n# Tom command-line application\n# ---\n\nadd_executable(${Tom_target}\n main.cpp\n)\nadd_executable(${Tom_ns}::${Tom_target} ALIAS ${Tom_target})\n\n# Tom command-line application specific configuration\n# ---\n\nset_target_properties(${Tom_target}\n PROPERTIES\n C_VISIBILITY_PRESET "hidden"\n CXX_VISIBILITY_PRESET "hidden"\n VISIBILITY_INLINES_HIDDEN YES\n VERSION ${PROJECT_VERSION}\n)\n\ntarget_include_directories(${Tom_target}\n PRIVATE "$"\n)\n\n# Tom command-line application defines\n# ---\n\ntarget_compile_definitions(${Tom_target}\n PRIVATE\n PROJECT_TOM\n)\n\n# Windows resource and manifest files\n# ---\n\n# Find icons, tom/version.hpp, and Windows manifest file for MinGW\nif(CMAKE_SYSTEM_NAME STREQUAL "Windows")\n tiny_set_rc_flags("-I \\"${TinyOrmSourceDir}/tom/resources\\"")\nendif()\n\ninclude(TinyResourceAndManifest)\ntiny_resource_and_manifest(${Tom_target}\n OUTPUT_DIR "${TINY_BUILD_GENDIR}/tmp/"\n RESOURCES_DIR "${TinyOrmSourceDir}/tom/resources"\n)\n\n# Resolve and link dependencies\n# ---\n\nfind_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)\nfind_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core REQUIRED)\nfind_package(TinyOrm 0.37.3 CONFIG REQUIRED)\n\n# Unconditional dependencies\ntarget_link_libraries(${Tom_target}\n PRIVATE\n Qt${QT_VERSION_MAJOR}::Core\n TinyOrm::TinyOrm\n)\n'})}),"\n",(0,s.jsx)(n.h3,{id:"build-migrations-cmake",children:"Build migrations"}),"\n",(0,s.jsxs)(n.p,{children:["Now you are ready to configure ",(0,s.jsx)(n.code,{children:"tom"})," ",(0,s.jsx)(n.code,{children:"CMake"})," application. Don't forget to prepare the build environment with the ",(0,s.jsx)(n.a,{href:"/building/tinyorm#windows-prerequisites",children:(0,s.jsx)(n.code,{children:"qtenv6.ps1"})})," command if you are building with the ",(0,s.jsx)(n.code,{children:"MSVC"}),"."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"cd ../tom-builds-cmake/build-debug\n"})}),"\n",(0,s.jsxs)(o.A,{groupId:d.vf,children:[(0,s.jsx)(l.A,{value:d.b,label:d.ux,children:(0,s.jsx)(r.A,{className:"language-powershell",children:`cmake.exe \`\n-S "${(0,c.OZ)(d.b)}/tom/tom" \`\n-B "${(0,c.OZ)(d.b)}/tom/tom-builds-cmake/build-debug" \`\n-G 'Ninja' \`\n-D CMAKE_BUILD_TYPE:STRING='Debug' \`\n-D CMAKE_TOOLCHAIN_FILE:FILEPATH="${(0,c.Sn)(d.b)}/vcpkg/scripts/buildsystems/vcpkg.cmake" \`\n-D CMAKE_CXX_SCAN_FOR_MODULES:BOOL=OFF \`\n-D CMAKE_INSTALL_PREFIX:PATH="${(0,c.Sn)(d.b)}/tmp/tom"`})}),(0,s.jsx)(l.A,{value:d.xj,label:d.gg,children:(0,s.jsx)(r.A,{className:"language-bash",children:`cmake \\\n-S "${(0,c.OZ)(d.xj)}/tom/tom" \\\n-B "${(0,c.OZ)(d.xj)}/tom/tom-builds-cmake/build-debug" \\\n-G 'Ninja' \\\n-D CMAKE_BUILD_TYPE:STRING='Debug' \\\n-D CMAKE_TOOLCHAIN_FILE:FILEPATH="${(0,c.Sn)(d.xj)}/vcpkg/scripts/buildsystems/vcpkg.cmake" \\\n-D CMAKE_CXX_SCAN_FOR_MODULES:BOOL=OFF \\\n-D CMAKE_INSTALL_PREFIX:PATH="${(0,c.Sn)(d.xj)}/tmp/tom"`})})]}),"\n",(0,s.jsx)(n.p,{children:"And build."}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"cmake --build . --target all\n"})}),"\n",(0,s.jsx)(n.h3,{id:"execute-migrations-cmake",children:"Execute migrations"}),"\n",(0,s.jsxs)(n.p,{children:["Do not forget to add ",(0,s.jsx)(n.code,{children:"TinyOrm0d.dll"})," on the path on Windows and on the ",(0,s.jsx)(n.code,{children:"LD_LIBRARY_PATH"})," on Linux, so ",(0,s.jsx)(n.code,{children:"tom"})," application can find it during execution, as is described ",(0,s.jsx)(n.a,{href:"/building/tinyorm#tinyorm-on-path-cmake",children:"here"}),"."]}),"\n",(0,s.jsxs)(o.A,{groupId:d.vf,name:"tinyorm-on-path",children:[(0,s.jsx)(l.A,{value:d.b,label:d.ux,children:(0,s.jsx)(r.A,{className:"language-powershell",children:`$env:Path = "${(0,c.OZ)(d.b,!1)}\\TinyORM\\TinyORM-builds-cmake\\build-debug;" + $env:Path`})}),(0,s.jsx)(l.A,{value:d.xj,label:d.gg,children:(0,s.jsx)(r.A,{className:"language-bash",children:`export LD_LIBRARY_PATH=${(0,c.OZ)(d.xj)}/TinyORM/TinyORM-builds-cmake/build-debug\${PATH:+:}$PATH`})})]}),"\n",(0,s.jsxs)(n.p,{children:["Execute ",(0,s.jsx)(n.code,{children:"tom"})," application."]}),"\n",(0,s.jsxs)(o.A,{groupId:d.vf,children:[(0,s.jsx)(l.A,{value:d.b,label:d.ux,children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-powershell",children:".\\tom.exe migrate:status\n"})})}),(0,s.jsx)(l.A,{value:d.xj,label:d.gg,children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"./tom migrate:status\n"})})})]}),"\n",(0,s.jsx)(n.p,{children:"The output will look something like this."}),"\n",(0,s.jsx)("img",{src:i(53).A,alt:"Tom migrations - migrate:status command output",width:"660"}),"\n",(0,s.jsxs)(n.p,{children:["See also the ",(0,s.jsx)(n.a,{href:"#finish",children:"final thoughts"})," on how to verify the ",(0,s.jsx)(n.code,{children:"tom"})," executable file properties."]}),"\n",(0,s.jsx)(n.p,{children:"Happy migrating \ud83c\udf89\ud83d\udc4c"}),"\n",(0,s.jsx)(n.h2,{id:"migrations-with-qmake",children:"Migrations with qmake"}),"\n",(0,s.jsxs)(n.p,{children:["Create a folder for the ",(0,s.jsx)(n.code,{children:"qmake"})," build."]}),"\n",(0,s.jsxs)(o.A,{groupId:d.vf,children:[(0,s.jsx)(l.A,{value:d.b,label:d.ux,children:(0,s.jsx)(r.A,{className:"language-powershell",children:`cd ${(0,c.OZ)(d.b)}/tom\n\nmkdir tom-builds-qmake`})}),(0,s.jsx)(l.A,{value:d.xj,label:d.gg,children:(0,s.jsx)(r.A,{className:"language-bash",children:`cd ${(0,c.OZ)(d.xj)}/tom\n\nmkdir tom-builds-qmake`})})]}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.a,{href:"#source-code",children:(0,s.jsx)(n.code,{children:"source code"})})," is the same as for the ",(0,s.jsx)(n.code,{children:"Migrations with CMake"})," console application."]}),"\n",(0,s.jsx)(n.h3,{id:"qmake-project",children:"qmake project"}),"\n",(0,s.jsxs)(n.p,{children:["Create ",(0,s.jsx)(n.code,{children:"tom.pro"})," qmake file with the following content."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"cd tom\nvim tom.pro\n"})}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["To paste a source code correctly in ",(0,s.jsx)(n.code,{children:"vim"}),", press ",(0,s.jsx)("kbd",{children:"Shift"})," + ",(0,s.jsx)("kbd",{children:"p"}),"."]})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-qmake",metastring:"title='tom.pro'",children:"QT -= gui\n\nTEMPLATE = app\nTARGET = tom\n\nCONFIG *= cmdline\n\nDEFINES *= PROJECT_TOM\n\nSOURCES += $$PWD/main.cpp\n\n# Database migrations\ninclude($$PWD/database/migrations.pri)\n# Database seeders\ninclude($$PWD/database/seeders.pri)\n\n# Auto-configure TinyORM library for the migrations purposes \ud83d\udd25\ninclude($$TINY_MAIN_DIR/TinyORM/qmake/tom.pri)\n"})}),"\n",(0,s.jsx)(n.admonition,{type:"warning",children:(0,s.jsxs)(n.p,{children:["The exact ",(0,s.jsx)(n.a,{href:"/building/tinyorm#folders-structure",children:"folders structure"})," is crucial in this example because the paths to the ",(0,s.jsx)(n.code,{children:"TinyORM"})," source and build folders are relative."]})}),"\n",(0,s.jsx)(n.admonition,{type:"warning",children:(0,s.jsx)(n.p,{children:"Please pay special attention to letter casing in paths, especially TinyOrm vs TinyORM!"})}),"\n",(0,s.jsxs)(n.h4,{id:"auto-configure-using-qmakeconf-and-env",children:[(0,s.jsx)(n.code,{children:"Auto-configure"})," using ",(0,s.jsx)(n.code,{children:".qmake.conf"})," and ",(0,s.jsx)(n.code,{children:".env"})]}),"\n",(0,s.jsxs)(n.p,{children:["If you want to have properly configured ",(0,s.jsx)(n.code,{children:"DEFINES"})," (C preprocessor macros), have Qt headers marked as system headers, or eg. have properly set properties of an executable file such as version and description, then you need to specify a path to the ",(0,s.jsx)(n.code,{children:"TinyORM"})," qmake features (",(0,s.jsx)(n.code,{children:".prf"})," files) which handle this correctly; this path is provided by the ",(0,s.jsx)(n.code,{children:"QMAKEFEATURES"})," variable and can only be set in the ",(0,s.jsx)(n.code,{children:".qmake.conf"})," file."]}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["Read the ",(0,s.jsx)(n.a,{href:"/building/tinyorm#consume-tinyorm-library-qmake",children:"Consume TinyOrm library (qmake)"})," section, as everything that is described in that section applies here as well."]})}),"\n",(0,s.jsxs)(n.p,{children:["Create the ",(0,s.jsx)(n.code,{children:".qmake.conf"})," file in the ",(0,s.jsx)(n.code,{children:"tom"})," application root folder with the following content."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-qmake",metastring:"title='.qmake.conf'",children:"# Path to the PARENT folder of the TinyORM source folder\nTINY_MAIN_DIR = $$clean_path($$PWD/../../TinyORM/)\n# To find .env and .env.$$QMAKE_PLATFORM files\nTINY_DOTENV_ROOT = $$PWD\n# Path to the current build tree (used to guess the TinyORM build tree)\n#TINY_BUILD_TREE = $$shadowed($$PWD)\n\n# To find .prf files, needed by eg. CONFIG += tiny_system_headers inline/extern_constants\nQMAKEFEATURES *= $$quote($$TINY_MAIN_DIR/TinyORM/qmake/features)\n"})}),"\n",(0,s.jsxs)(n.p,{children:["Then, create a ",(0,s.jsx)("code",{children:".env.(win32|unix|mingw)"})," file in the ",(0,s.jsx)(n.code,{children:"tom"})," application root folder with the following content."]}),"\n",(0,s.jsxs)(o.A,{groupId:d.vf,children:[(0,s.jsx)(l.A,{value:d.b,label:".env.win32",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-qmake",children:"# Names and values of these qmake variables are crucial, they are used in the tom.pro\n# Please pay special attention to letter casing in paths, especially TinyOrm vs TinyORM!\n\n# Path to the TinyORM build folder\nTINYORM_BUILD_TREE = $$quote($$TINY_MAIN_DIR/TinyORM-builds-qmake/build-TinyORM-Desktop_Qt_6_7_2_MSVC2022_64bit-Debug/)\n\n# Path to the vcpkg - range-v3 and tabulate\n# Will use the TINY_VCPKG_ROOT or VCPKG_ROOT environment variable if is empty\nTINY_VCPKG_ROOT = $$clean_path($$PWD/../../../vcpkg/)\nTINY_VCPKG_TRIPLET = x64-windows\n\n# Enable ccache wrapper\n#CONFIG *= ccache\n"})})}),(0,s.jsx)(l.A,{value:d.xj,label:".env.unix",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-qmake",children:"# Names and values of these qmake variables are crucial, they are used in the tom.pro\n# Please pay special attention to letter casing in paths, especially TinyOrm vs TinyORM!\n\n# Path to the TinyORM build folder\nTINYORM_BUILD_TREE = $$quote($$TINY_MAIN_DIR/TinyORM-builds-qmake/build-TinyORM-Desktop_Qt_6_7_2_clang16_64bit_ccache-Debug/)\n\n# Path to the vcpkg - range-v3 and tabulate\n# Will use the TINY_VCPKG_ROOT or VCPKG_ROOT environment variable if is empty\nTINY_VCPKG_ROOT = $$clean_path($$PWD/../../../vcpkg/)\nTINY_VCPKG_TRIPLET = x64-linux\n\n# Use faster linker\nclang: CONFIG *= use_lld_linker\nelse: CONFIG *= use_gold_linker\n\n# Or use the mold linker\n#QMAKE_LFLAGS *= -fuse-ld=mold\n"})})}),(0,s.jsx)(l.A,{value:"mingw",label:".env.mingw",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-qmake",children:"# Names and values of these qmake variables are crucial, they are used in the tom.pro\n# Please pay special attention to letter casing in paths, especially TinyOrm vs TinyORM!\n\n# Path to the TinyORM build folder\nTINYORM_BUILD_TREE = $$quote($$TINY_MAIN_DIR/TinyORM-builds-qmake/build-TinyORM-Desktop_Qt_6_7_2_MSYS2_UCRT64_clang_64bit-Debug/)\n\n# Path to the vcpkg - range-v3 and tabulate\n# Will use the TINY_VCPKG_ROOT or VCPKG_ROOT environment variable if is empty\nTINY_VCPKG_ROOT = $$clean_path($$PWD/../../../vcpkg/)\nTINY_VCPKG_TRIPLET = x64-mingw-dynamic\n\n# Enable ccache wrapper\n#CONFIG *= ccache\n\n# Use faster linker (for both GCC and Clang)\n# CONFIG *= use_lld_linker does not work on MinGW\nQMAKE_LFLAGS *= -fuse-ld=lld\n"})})})]}),"\n",(0,s.jsxs)(n.p,{children:["Don't forget to update the ",(0,s.jsx)(n.code,{children:"TINYORM_BUILD_TREE"})," and ",(0,s.jsx)(n.code,{children:"TINY_VCPKG_ROOT"})," folder paths to your needs if you are not using the recommended ",(0,s.jsx)(n.a,{href:"/building/tinyorm#folders-structure",children:(0,s.jsx)(n.code,{children:"Folders structure"})}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["You can use the ",(0,s.jsxs)(n.a,{href:"/building/tinyorm#partial-guessing-of-the-tinyorm_build_tree",children:["Partial guessing of the ",(0,s.jsx)(n.code,{children:"TINYORM_BUILD_TREE"})]})," if you don't like to specify it manually. Just comment out the ",(0,s.jsx)(n.code,{children:"TINYORM_BUILD_TREE"})," and uncomment the ",(0,s.jsx)(n.code,{children:"TINY_BUILD_TREE = $$shadowed($$PWD)"})," in the ",(0,s.jsx)(n.code,{children:".qmake.conf"})," file."]}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["You can entirely avoid the ",(0,s.jsx)(n.code,{children:".env"})," files, just move the ",(0,s.jsx)(n.code,{children:"TINYORM_BUILD_TREE"})," to the ",(0,s.jsx)(n.code,{children:".qmake.conf"})," or remove it by help of ",(0,s.jsxs)(n.a,{href:"/building/tinyorm#partial-guessing-of-the-tinyorm_build_tree",children:["Partial guessing of the ",(0,s.jsx)(n.code,{children:"TINYORM_BUILD_TREE"})]})," and set the ",(0,s.jsx)(n.code,{children:"VCPKG_ROOT"})," environment variable at system level as is described in ",(0,s.jsx)(n.a,{href:"/building/tinyorm#set-up-vcpkg-environment",children:(0,s.jsx)(n.code,{children:"Set up vcpkg environment"})}),"."]})}),"\n",(0,s.jsx)(n.admonition,{type:"info",children:(0,s.jsxs)(n.p,{children:["Configuring by the ",(0,s.jsx)(n.code,{children:".qmake.conf"})," and ",(0,s.jsx)(n.code,{children:".env"})," files has one big advantage, which is that you don't have to modify the project files."]})}),"\n",(0,s.jsx)(n.h4,{id:"migrations-source-files",children:"Migrations source files"}),"\n",(0,s.jsxs)(n.p,{children:["Create ",(0,s.jsx)(n.code,{children:"database/migrations.pri"})," file and paste the following code."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-qmake",metastring:"title='database/migrations.pri'",children:"INCLUDEPATH *= $$PWD\n\nHEADERS += \\\n $$PWD/migrations/2014_10_12_000000_create_posts_table.hpp \\\n"})}),"\n",(0,s.jsx)(n.h4,{id:"seeders-source-files",children:"Seeders source files"}),"\n",(0,s.jsxs)(n.p,{children:["Create ",(0,s.jsx)(n.code,{children:"database/seeders.pri"})," file and paste the following code."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-qmake",metastring:"title='database/seeders.pri'",children:"INCLUDEPATH *= $$PWD\n\nHEADERS += \\\n $$PWD/seeders/databaseseeder.hpp \\\n"})}),"\n",(0,s.jsx)(n.h3,{id:"build-migrations-qmake",children:"Build migrations"}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["I recommend creating a new ",(0,s.jsx)(n.code,{children:"Session"})," in the ",(0,s.jsx)(n.code,{children:"QtCreator IDE"})," as is described ",(0,s.jsx)(n.a,{href:"/building/tinyorm#open-qtcreator-ide",children:"here"}),"."]})}),"\n",(0,s.jsxs)(n.p,{children:["Now you can open the ",(0,s.jsx)(n.code,{children:"tom.pro"})," project in the ",(0,s.jsx)(n.code,{children:"QtCreator IDE"}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["This will open the ",(0,s.jsx)(n.code,{children:"Configure Project"})," tab, select some kit and update build folder paths to meet our ",(0,s.jsx)(n.a,{href:"/building/tinyorm#folders-structure",children:"folders structure"})," or like you want."]}),"\n",(0,s.jsx)("img",{src:i(2394).A,alt:"tom - QtCreator - Configure Project",width:"760"}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["You can force the ",(0,s.jsx)(n.code,{children:"QtCreator"})," to generate a build folders structure as is described ",(0,s.jsx)(n.a,{href:"/building/tinyorm#qtcreator-default-build-directory",children:"here"}),"."]})}),"\n",(0,s.jsxs)(n.p,{children:["You are ready to configure build options, hit ",(0,s.jsx)("kbd",{children:"Ctrl"}),"+",(0,s.jsx)("kbd",{children:"5"})," to open ",(0,s.jsx)(n.code,{children:"Project Settings"})," tab and select ",(0,s.jsx)(n.code,{children:"Build"})," in the left sidebar to open the ",(0,s.jsx)(n.code,{children:"Build Settings"}),", it should look similar to the following picture."]}),"\n",(0,s.jsx)("img",{src:i(6106).A,className:"no-blurry",alt:"tom - QtCreator - Build Settings",width:"760"}),"\n",(0,s.jsxs)(n.p,{children:["Disable ",(0,s.jsx)(n.code,{children:"QML debugging and profiling"})," and ",(0,s.jsx)(n.code,{children:"Qt Quick Compiler"}),", they are not used."]}),"\n",(0,s.jsxs)(n.p,{children:["In the left sidebar open ",(0,s.jsx)(n.code,{children:"Dependencies"})," and check ",(0,s.jsx)(n.code,{children:"TinyORM"})," project and ",(0,s.jsx)(n.code,{children:"Synchronize configuration"}),", this setting ensures that the current project will be rebuilt correctly when the ",(0,s.jsx)(n.code,{children:"TinyORM"})," library source code changes."]}),"\n",(0,s.jsxs)(n.p,{children:["Everything is ready to build, you can press ",(0,s.jsx)("kbd",{children:"Ctrl"}),"+",(0,s.jsx)("kbd",{children:"b"})," to build the project."]}),"\n",(0,s.jsx)(n.h3,{id:"execute-migrations-qmake",children:"Execute migrations"}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"QtCreator"})," takes care of all the necessary configurations, sets up the build environment correctly, and also prepends dependency libraries on the system path on Windows and on the ",(0,s.jsx)(n.code,{children:"LD_LIBRARY_PATH"})," on Linux."]}),"\n",(0,s.jsxs)(n.p,{children:["The only thing you might want to change is to run the ",(0,s.jsx)(n.code,{children:"tom"})," application in the new terminal window. To do so, hit ",(0,s.jsx)("kbd",{children:"Ctrl"}),"+",(0,s.jsx)("kbd",{children:"5"})," to open the ",(0,s.jsx)(n.code,{children:"Project Settings"})," tab and select ",(0,s.jsx)(n.code,{children:"Run"})," in the left sidebar to open the ",(0,s.jsx)(n.code,{children:"Run Settings"}),", then in the ",(0,s.jsx)(n.code,{children:"Run"})," section select the ",(0,s.jsx)(n.code,{children:"Run in terminal"})," checkbox."]}),"\n",(0,s.jsxs)(n.p,{children:["You can also set the ",(0,s.jsx)(n.code,{children:"Command line arguments"})," in this ",(0,s.jsx)(n.code,{children:"Run"})," section, eg. the ",(0,s.jsx)(n.code,{children:"migrate:status"}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["To execute the ",(0,s.jsx)(n.code,{children:"tom"})," application press ",(0,s.jsx)("kbd",{children:"Ctrl"})," + ",(0,s.jsx)("kbd",{children:"r"}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["The output will look ",(0,s.jsx)(n.strong,{children:"very similar"})," to this if you add more migrations."]}),"\n",(0,s.jsx)("img",{src:i(53).A,alt:"Tom migrations - migrate:status command output",width:"660"}),"\n",(0,s.jsx)(n.p,{children:"Happy migrating \ud83c\udf89\ud83d\udc4c"}),"\n",(0,s.jsx)(n.h2,{id:"finish",children:"Finish"}),"\n",(0,s.jsxs)(n.p,{children:["As the last thing, you can check that all the file properties were correctly set by the ",(0,s.jsx)(n.a,{href:"https://docs.microsoft.com/en-us/windows/win32/menurc/resource-compiler",children:(0,s.jsx)(n.code,{children:"rc"})})," compiler."]}),"\n",(0,s.jsxs)(n.p,{children:["Find the ",(0,s.jsx)(n.code,{children:"tom.exe"})," file and press ",(0,s.jsx)("kbd",{children:"Alt"})," + ",(0,s.jsx)("kbd",{children:"Enter"})," to open the file properties. To check the executable manifest you can use eg. the ",(0,s.jsx)(n.a,{href:"http://www.angusj.com/resourcehacker/",children:"Resource Hacker"}),"."]}),"\n",(0,s.jsx)("img",{src:i(4679).A,alt:"tom.exe file properties detail",width:"440"})]})}function j(e={}){const{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,s.jsx)(n,{...e,children:(0,s.jsx)(x,{...e})}):x(e)}},9365:(e,n,i)=>{i.d(n,{A:()=>r});i(6540);var s=i(4164);const t={tabItem:"tabItem_Ymn6"};var a=i(4848);function r(e){let{children:n,hidden:i,className:r}=e;return(0,a.jsx)("div",{role:"tabpanel",className:(0,s.A)(t.tabItem,r),hidden:i,children:n})}},1470:(e,n,i)=>{i.d(n,{A:()=>v});var s=i(6540),t=i(4164),a=i(3104),r=i(6347),l=i(205),o=i(7485),d=i(1682),c=i(679);function h(e){return s.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,s.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function u(e){const{values:n,children:i}=e;return(0,s.useMemo)((()=>{const e=n??function(e){return h(e).map((e=>{let{props:{value:n,label:i,attributes:s,default:t}}=e;return{value:n,label:i,attributes:s,default:t}}))}(i);return function(e){const n=(0,d.X)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[n,i])}function m(e){let{value:n,tabValues:i}=e;return i.some((e=>e.value===n))}function p(e){let{queryString:n=!1,groupId:i}=e;const t=(0,r.W6)(),a=function(e){let{queryString:n=!1,groupId:i}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!i)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return i??null}({queryString:n,groupId:i});return[(0,o.aZ)(a),(0,s.useCallback)((e=>{if(!a)return;const n=new URLSearchParams(t.location.search);n.set(a,e),t.replace({...t.location,search:n.toString()})}),[a,t])]}function g(e){const{defaultValue:n,queryString:i=!1,groupId:t}=e,a=u(e),[r,o]=(0,s.useState)((()=>function(e){let{defaultValue:n,tabValues:i}=e;if(0===i.length)throw new Error("Docusaurus error: the component requires at least one children component");if(n){if(!m({value:n,tabValues:i}))throw new Error(`Docusaurus error: The has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${i.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}const s=i.find((e=>e.default))??i[0];if(!s)throw new Error("Unexpected error: 0 tabValues");return s.value}({defaultValue:n,tabValues:a}))),[d,h]=p({queryString:i,groupId:t}),[g,x]=function(e){let{groupId:n}=e;const i=function(e){return e?`docusaurus.tab.${e}`:null}(n),[t,a]=(0,c.Dv)(i);return[t,(0,s.useCallback)((e=>{i&&a.set(e)}),[i,a])]}({groupId:t}),j=(()=>{const e=d??g;return m({value:e,tabValues:a})?e:null})();(0,l.A)((()=>{j&&o(j)}),[j]);return{selectedValue:r,selectValue:(0,s.useCallback)((e=>{if(!m({value:e,tabValues:a}))throw new Error(`Can't select invalid tab value=${e}`);o(e),h(e),x(e)}),[h,x,a]),tabValues:a}}var x=i(2303);const j={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var b=i(4848);function f(e){let{className:n,block:i,selectedValue:s,selectValue:r,tabValues:l}=e;const o=[],{blockElementScrollPositionUntilNextRender:d}=(0,a.a_)(),c=e=>{const n=e.currentTarget,i=o.indexOf(n),t=l[i].value;t!==s&&(d(n),r(t))},h=e=>{let n=null;switch(e.key){case"Enter":c(e);break;case"ArrowRight":{const i=o.indexOf(e.currentTarget)+1;n=o[i]??o[0];break}case"ArrowLeft":{const i=o.indexOf(e.currentTarget)-1;n=o[i]??o[o.length-1];break}}n?.focus()};return(0,b.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,t.A)("tabs",{"tabs--block":i},n),children:l.map((e=>{let{value:n,label:i,attributes:a}=e;return(0,b.jsx)("li",{role:"tab",tabIndex:s===n?0:-1,"aria-selected":s===n,ref:e=>o.push(e),onKeyDown:h,onClick:c,...a,className:(0,t.A)("tabs__item",j.tabItem,a?.className,{"tabs__item--active":s===n}),children:i??n},n)}))})}function _(e){let{lazy:n,children:i,selectedValue:t}=e;const a=(Array.isArray(i)?i:[i]).filter(Boolean);if(n){const e=a.find((e=>e.props.value===t));return e?(0,s.cloneElement)(e,{className:"margin-top--md"}):null}return(0,b.jsx)("div",{className:"margin-top--md",children:a.map(((e,n)=>(0,s.cloneElement)(e,{key:n,hidden:e.props.value!==t})))})}function T(e){const n=g(e);return(0,b.jsxs)("div",{className:(0,t.A)("tabs-container",j.tabList),children:[(0,b.jsx)(f,{...n,...e}),(0,b.jsx)(_,{...n,...e})]})}function v(e){const n=(0,x.A)();return(0,b.jsx)(T,{...e,children:h(e.children)},String(n))}},7324:(e,n,i)=>{i.d(n,{$E:()=>x,A3:()=>b,CW:()=>j,Dx:()=>c,F4:()=>u,Fi:()=>d,J_:()=>v,LQ:()=>f,Lf:()=>y,OO:()=>t,Q7:()=>_,b:()=>l,cy:()=>o,gg:()=>p,kl:()=>m,os:()=>h,pW:()=>a,ux:()=>g,vf:()=>s,xj:()=>r,xt:()=>T});const s="shell",t="database",a="application",r="bash",l="pwsh",o="zsh",d="maria",c="mysql",h="postgres",u="sqlite",m="application",p="bash",g="pwsh",x="zsh",j="MariaDB",b="MySQL",f="PostgreSQL",_="SQLite",T="tinyorm.org",v="$HOME/Code/c/",y="$env:USERPROFILE\\Code\\c\\"},6362:(e,n,i)=>{i.d(n,{A:()=>a});var s=i(6540),t=i(1838);function a(){const e=(0,s.useContext)(t.A);if(null!=e)return e;throw new Error("useRootFolderContext is used outside of Layout component.")}},6694:(e,n,i)=>{i.d(n,{OZ:()=>o,Sn:()=>r,T3:()=>c,bw:()=>d,nC:()=>h,np:()=>l});var s=i(6362),t=i(2303),a=i(7324);const r=function(e,n){return void 0===n&&(n=!0),u((0,s.A)().rootFolder[e]??d(e),e,n)},l=()=>(0,s.A)().rootFolder[a.pW]??d(a.pW),o=function(e,n){if(void 0===n&&(n=!0),null==e)throw new Error("The groupId in the applicationFolderPath() can not be empty.");const i=n||e!==a.b?"/":"\\";return u(r(e)+i+l(),e,n)};function d(e){if(null==e)throw new Error("The groupId in the folderDefaultValue() can not be empty.");if(!(0,t.A)())return"";switch(e){case a.b:return a.Lf;case a.xj:return a.J_;case a.pW:return a.xt;default:throw new Error(`No default value for '${e}' groupId in the folderDefaultValue().`)}}function c(e){return e===a.pW}function h(e,n){if(null==n||""===n)return n;const i="$ENV{$1}$2";switch(e){case a.b:return p(n).replace(/\$env:(.+?)(\/.*)/,i);case a.xj:return n.replace(/\$(.+?)(\/.*)/,i);default:throw new Error(`Unsupported shell type '${e}' in the convertToCmakeEnvVariable().`)}}function u(e,n,i){if(void 0===i&&(i=!0),null==e||""===e)return e;if(n!==a.b)return m(e);const s=m(e);return i?p(s):function(e){return null==e||""===e?e:e.replaceAll(/\/+/g,"\\")}(s)}function m(e){return null==e||""===e?e:e.replace(/[/\\]+$/,"")}function p(e){return null==e||""===e?e:e.replaceAll(/\\+(?! )/g,"/")}},6106:(e,n,i)=>{i.d(n,{A:()=>s});const s=i.p+"assets/images/qmake-build_settings-e10927d1c4ed852620f9eb7564198940.png"},2394:(e,n,i)=>{i.d(n,{A:()=>s});const s=i.p+"assets/images/qmake-configure_project-4721257090370204b0272d166512adef.png"},4679:(e,n,i)=>{i.d(n,{A:()=>s});const s=i.p+"assets/images/tom_file_properties-0df513c47ceadd5c09165e41c6b53086.png"},53:(e,n,i)=>{i.d(n,{A:()=>s});const s=i.p+"assets/images/tom_migrate_status-63c129a10bfe6bffe8d2d5ea280860e5.png"}}]); \ No newline at end of file diff --git a/assets/js/8a8faf8d.335d5697.js b/assets/js/8a8faf8d.335d5697.js new file mode 100644 index 000000000..32252845f --- /dev/null +++ b/assets/js/8a8faf8d.335d5697.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunktinyorm_org=self.webpackChunktinyorm_org||[]).push([[129],{6455:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>p,contentTitle:()=>u,default:()=>j,frontMatter:()=>h,metadata:()=>m,toc:()=>g});var s=i(4848),t=i(8453),a=i(8774),r=i(2364),l=i(9365),o=i(1470),d=i(7324),c=i(6694);const h={sidebar_position:3,sidebar_label:"Migrations",description:"How to compile the TinyORM migrations (tom) C++ console application on Windows and Linux.",keywords:["c++ orm","building","migrations","tinyorm"]},u="Building: Migrations",m={id:"building/migrations",title:"Building: Migrations",description:"How to compile the TinyORM migrations (tom) C++ console application on Windows and Linux.",source:"@site/docs/building/migrations.mdx",sourceDirName:"building",slug:"/building/migrations",permalink:"/building/migrations",draft:!1,unlisted:!1,tags:[],version:"current",sidebarPosition:3,frontMatter:{sidebar_position:3,sidebar_label:"Migrations",description:"How to compile the TinyORM migrations (tom) C++ console application on Windows and Linux.",keywords:["c++ orm","building","migrations","tinyorm"]},sidebar:"tinyormSidebar",previous:{title:"Hello world",permalink:"/building/hello-world"},next:{title:"\ud83d\udcc4 Features Summary",permalink:"/features-summary"}},p={},g=[{value:"Introduction",id:"introduction",level:2},{value:"Install dependencies",id:"install-dependencies",level:2},{value:"Using vcpkg.json (manifest mode)",id:"using-vcpkg-json-manifest-mode",level:4},{value:"Using vcpkg install (manually)",id:"using-vcpkg-install-manually",level:4},{value:"Source code",id:"source-code",level:2},{value:"Main file",id:"main-file",level:3},{value:"Migrations",id:"migrations",level:3},{value:"Seeders",id:"seeders",level:3},{value:"Migrations with CMake",id:"migrations-with-cmake",level:2},{value:"CMake project",id:"cmake-project",level:3},{value:"Build migrations",id:"build-migrations-cmake",level:3},{value:"Execute migrations",id:"execute-migrations-cmake",level:3},{value:"Migrations with qmake",id:"migrations-with-qmake",level:2},{value:"qmake project",id:"qmake-project",level:3},{value:"Auto-configure using .qmake.conf and .env",id:"auto-configure-using-qmakeconf-and-env",level:4},{value:"Migrations source files",id:"migrations-source-files",level:4},{value:"Seeders source files",id:"seeders-source-files",level:4},{value:"Build migrations",id:"build-migrations-qmake",level:3},{value:"Execute migrations",id:"execute-migrations-qmake",level:3},{value:"Finish",id:"finish",level:2}];function x(e){const n={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",h3:"h3",h4:"h4",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,t.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(n.h1,{id:"building-migrations",children:"Building: Migrations"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#introduction",children:"Introduction"})}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.a,{href:"#install-dependencies",children:"Install dependencies"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#using-vcpkg-json-manifest-mode",children:"Using vcpkg.json"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#using-vcpkg-install-manually",children:"Using vcpkg install"})}),"\n"]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.a,{href:"#source-code",children:"Source code"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#main-file",children:"Main file"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#migrations",children:"Migrations"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#seeders",children:"Seeders"})}),"\n"]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.a,{href:"#migrations-with-cmake",children:"Migrations with CMake"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#cmake-project",children:"CMake project"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#build-migrations-cmake",children:"Build migrations"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#execute-migrations-cmake",children:"Execute migrations"})}),"\n"]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.a,{href:"#migrations-with-qmake",children:"Migrations with qmake"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#qmake-project",children:"qmake project"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#build-migrations-qmake",children:"Build migrations"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#execute-migrations-qmake",children:"Execute migrations"})}),"\n"]}),"\n"]}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#finish",children:"Finish"})}),"\n"]}),"\n",(0,s.jsx)(n.h2,{id:"introduction",children:"Introduction"}),"\n",(0,s.jsxs)(n.p,{children:["We will try to create a working migrations console application called as ",(0,s.jsx)("abbr",{title:"TinyORM migrations",children:(0,s.jsx)(n.code,{children:"tom"})})," in the terminal with the ",(0,s.jsx)(n.code,{children:"CMake"})," and in the ",(0,s.jsx)(n.code,{children:"QtCreator IDE"})," with the ",(0,s.jsx)(n.code,{children:"qmake"})," build systems."]}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"tom"})," console application also expects the following ",(0,s.jsx)(n.a,{href:"/building/tinyorm#folders-structure",children:"folders structure"}),", let's create them."]}),"\n",(0,s.jsxs)(o.A,{groupId:d.vf,children:[(0,s.jsx)(l.A,{value:d.b,label:d.ux,children:(0,s.jsx)(r.A,{className:"language-powershell",children:`cd ${(0,c.OZ)(d.b)}\nmkdir tom/tom\ncd tom`})}),(0,s.jsx)(l.A,{value:d.xj,label:d.gg,children:(0,s.jsx)(r.A,{className:"language-bash",children:`cd ${(0,c.OZ)(d.xj)}\nmkdir -p tom/tom\ncd tom`})})]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.code,{children:"TinyORM"})," source tree contains the ",(0,s.jsx)(n.code,{children:"tom"})," example application, you can inspire or look at the ",(0,s.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/tree/main/examples/tom",children:"source code"}),". Also, ",(0,s.jsx)(n.code,{children:"TinyORM"})," unit tests use a ",(0,s.jsx)(n.code,{children:"tom"})," migrations internally to create the database structure, internally called as the ",(0,s.jsxs)(n.a,{href:"https://github.com/silverqx/TinyORM/tree/main/tests/testdata_tom",children:[(0,s.jsx)(n.code,{children:"tom"})," migrations for unit tests"]}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["All these three console applications the ",(0,s.jsx)(n.code,{children:"tom"})," example, ",(0,s.jsx)(n.code,{children:"tom"})," migrations for unit tests, and the application described in this tutorial have practically identical source code (the main.cpp file)."]}),"\n",(0,s.jsx)(n.admonition,{type:"note",children:(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"tom"})," is able to generate ",(0,s.jsx)("a",{href:"https://en.wikipedia.org/wiki/Data_definition_language",title:"Data Definition Language",children:"DDL"})," queries for all the ",(0,s.jsx)(n.a,{href:"/database/getting-started#introduction",children:"supported databases"})," databases."]})}),"\n",(0,s.jsx)(n.admonition,{type:"info",children:(0,s.jsxs)(n.p,{children:["You can see the ",(0,s.jsx)(n.a,{href:"/features-summary#tom-console-application",children:"Tom showcase image"})," of how the resulting ",(0,s.jsx)(n.code,{children:"tom"})," console application will look like."]})}),"\n",(0,s.jsx)(n.h2,{id:"install-dependencies",children:"Install dependencies"}),"\n",(0,s.jsxs)(n.p,{children:["First, install the ",(0,s.jsx)(n.code,{children:"vcpkg"})," package manager as is described ",(0,s.jsx)(n.a,{href:"/building/tinyorm#vcpkg",children:"here"}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"range-v3"})," and ",(0,s.jsx)(n.code,{children:"tabulate"})," libraries are required dependencies because ",(0,s.jsx)(n.code,{children:"TinyORM"})," uses them in header files, you have to install them before you can use ",(0,s.jsx)(n.code,{children:"TinyORM"}),". The ",(0,s.jsx)(n.code,{children:"tabulate"})," library is only needed in the ",(0,s.jsx)(n.code,{children:"tom"})," migrations it's used by the ",(0,s.jsx)(n.code,{children:"migrate:status"})," command."]}),"\n",(0,s.jsxs)(n.p,{children:["There are two ways how to install the ",(0,s.jsx)(n.code,{children:"range-v3"})," and ",(0,s.jsx)(n.code,{children:"tabulate"})," libraries using ",(0,s.jsx)(n.code,{children:"vcpkg"}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["Also, don't forget to build the ",(0,s.jsx)(n.code,{children:"TinyORM"})," library with the ",(0,s.jsx)(n.code,{children:"tom"})," source code enabled (it's enabled by default) as is described ",(0,s.jsx)(n.a,{href:"/building/tinyorm",children:"here"}),"."]}),"\n",(0,s.jsxs)(n.h4,{id:"using-vcpkg-json-manifest-mode",children:["Using vcpkg.json ",(0,s.jsx)("small",{children:"(manifest mode)"})]}),"\n",(0,s.jsxs)(n.p,{children:["Create a ",(0,s.jsx)(n.code,{children:"vcpkg.json"})," file with the following content. ",(0,s.jsx)(n.code,{children:"CMake"})," example below uses this method."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"cd tom\nvim vcpkg.json\n"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-json",metastring:"title='vcpkg.json'",children:'{\n "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json",\n "name": "tom",\n "version-semver": "0.1.0",\n "maintainers": "Silver Zachara ",\n "description": "Tom console application for TinyORM C++ library",\n "homepage": "https://github.com/silverqx/TinyORM",\n "documentation": "https://www.tinyorm.org/building/migrations",\n "supports": "!(uwp | arm | android | emscripten | osx | ios | xbox | freebsd | openbsd | wasm32)",\n "dependencies": [\n "range-v3",\n "tabulate"\n ]\n}\n'})}),"\n",(0,s.jsx)(n.admonition,{type:"note",children:(0,s.jsxs)(n.p,{children:["Only ",(0,s.jsx)(n.code,{children:"CMake"})," via the ",(0,s.jsx)(n.code,{children:"toolchain file"})," supports this method."]})}),"\n",(0,s.jsxs)(n.h4,{id:"using-vcpkg-install-manually",children:["Using vcpkg install ",(0,s.jsx)("small",{children:"(manually)"})]}),"\n",(0,s.jsxs)(n.p,{children:["This method can be used with both ",(0,s.jsx)(n.code,{children:"CMake"})," and ",(0,s.jsx)(n.code,{children:"qmake"}),"."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"cd ../../vcpkg\n\nvcpkg search range-v3\nvcpkg search tabulate\nvcpkg install range-v3 tabulate\nvcpkg list\n"})}),"\n",(0,s.jsx)(n.h2,{id:"source-code",children:"Source code"}),"\n",(0,s.jsxs)(n.p,{children:["Let's start in the ",(0,s.jsx)(n.code,{children:"tom"})," project folder."]}),"\n",(0,s.jsxs)(o.A,{groupId:d.vf,children:[(0,s.jsx)(l.A,{value:d.b,label:d.ux,children:(0,s.jsx)(r.A,{className:"language-powershell",children:`cd ${(0,c.OZ)(d.b)}/tom/tom`})}),(0,s.jsx)(l.A,{value:d.xj,label:d.gg,children:(0,s.jsx)(r.A,{className:"language-bash",children:`cd ${(0,c.OZ)(d.xj)}/tom/tom`})})]}),"\n",(0,s.jsx)(n.h3,{id:"main-file",children:"Main file"}),"\n",(0,s.jsxs)(n.p,{children:["Create ",(0,s.jsx)(n.code,{children:"main.cpp"})," source file."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"vim main.cpp\n"})}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["To paste a source code correctly in ",(0,s.jsx)(n.code,{children:"vim"}),", press ",(0,s.jsx)("kbd",{children:"Shift"})," + ",(0,s.jsx)("kbd",{children:"p"}),"."]})}),"\n",(0,s.jsx)(n.p,{children:"And paste the following code."}),"\n",(0,s.jsx)(a.A,{id:"string-constants-example"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cpp",metastring:"title='main.cpp'",children:'#include \n\n#include \n\n#include "migrations/2014_10_12_000000_create_posts_table.hpp"\n\n#include "seeders/databaseseeder.hpp"\n\nusing Orm::DatabaseManager;\nusing Orm::DB;\n\nusing TomApplication = Tom::Application;\n\nusing namespace Migrations; // NOLINT(google-build-using-namespace)\nusing namespace Seeders; // NOLINT(google-build-using-namespace)\n\n/*! Create the database manager instance and add a database connection. */\nstd::shared_ptr setupDatabaseManager();\n\n/*! C++ main function. */\nint main(int argc, char *argv[])\n{\n try {\n // Ownership of the shared_ptr()\n auto db = setupDatabaseManager();\n\n return TomApplication(argc, argv, std::move(db), "TOM_EXAMPLE_ENV")\n .migrations()\n .seeders()\n // Fire it up \ud83d\udd25\ud83d\ude80\u2728\n .run();\n\n } catch (const std::exception &e) {\n\n TomApplication::logException(e);\n }\n\n return EXIT_FAILURE;\n}\n\nstd::shared_ptr setupDatabaseManager()\n{\n using namespace Orm::Constants; // NOLINT(google-build-using-namespace)\n\n // Ownership of the shared_ptr()\n return DB::create({\n {driver_, QMYSQL},\n {host_, qEnvironmentVariable("DB_MYSQL_HOST", H127001)},\n {port_, qEnvironmentVariable("DB_MYSQL_PORT", P3306)},\n {database_, qEnvironmentVariable("DB_MYSQL_DATABASE", EMPTY)},\n {username_, qEnvironmentVariable("DB_MYSQL_USERNAME", EMPTY)},\n {password_, qEnvironmentVariable("DB_MYSQL_PASSWORD", EMPTY)},\n {charset_, qEnvironmentVariable("DB_MYSQL_CHARSET", UTF8MB4)},\n {collation_, qEnvironmentVariable("DB_MYSQL_COLLATION", UTF8MB40900aici)},\n {timezone_, TZ00},\n /* Specifies what time zone all QDateTime-s will have, the overridden default is\n the QTimeZone::UTC, set to the QTimeZone::LocalTime or\n QtTimeZoneType::DontConvert to use the system local time. */\n {qt_timezone, QVariant::fromValue(QTimeZone::UTC)},\n {strict_, true},\n },\n QStringLiteral("tinyorm_tom_mysql")); // shell:connection\n}\n'})}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["If you have defined more database connections then you can tag the lines with the database connection names with the ",(0,s.jsx)(n.code,{children:"// shell:connection"})," comment and this connection names will be provided to the bash, zsh, pwsh completions for the ",(0,s.jsx)(n.code,{children:"--database="})," option \ud83d\ude0e, ",(0,s.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/examples/tom/main.cpp#L74",children:"example"}),"."]})}),"\n",(0,s.jsx)(n.h3,{id:"migrations",children:"Migrations"}),"\n",(0,s.jsxs)(n.p,{children:["If you have already built the ",(0,s.jsx)(n.code,{children:"tom"})," application then you can generate a migrations using the ",(0,s.jsx)(n.a,{href:"/database/migrations#generating-migrations",children:(0,s.jsx)(n.code,{children:"make:migration"})})," command \ud83d\ude0e."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"tom make:migration create_posts_table\n"})}),"\n",(0,s.jsxs)(n.p,{children:["Below is the expected folders structure for the migrations. The ",(0,s.jsx)(n.a,{href:"#migrations-source-files",children:(0,s.jsx)(n.code,{children:"migrations.pri"})})," file is used only by the ",(0,s.jsx)(n.code,{children:"qmake"})," build system and is not needed with ",(0,s.jsx)(n.code,{children:"CMake"})," builds."]}),"\n",(0,s.jsx)(a.A,{id:"folders-structure"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-text",children:"tom/\n\u2514\u2500\u2500 database/\n \u251c\u2500\u2500 migrations/\n \u251c\u2500\u2500 seeders/\n \u251c\u2500\u2500 migrations.pri\n \u2514\u2500\u2500 seeders.pri\n"})}),"\n",(0,s.jsx)(n.p,{children:"Let's create the first migration manually."}),"\n",(0,s.jsxs)(o.A,{groupId:d.vf,children:[(0,s.jsx)(l.A,{value:d.b,label:d.ux,children:(0,s.jsx)(r.A,{className:"language-powershell",children:"mkdir database/migrations\n\nvim database/migrations/2014_10_12_000000_create_posts_table.hpp"})}),(0,s.jsx)(l.A,{value:d.xj,label:d.gg,children:(0,s.jsx)(r.A,{className:"language-bash",children:"mkdir -p database/migrations\n\nvim database/migrations/2014_10_12_000000_create_posts_table.hpp"})})]}),"\n",(0,s.jsx)(n.p,{children:"And paste the following code."}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cpp",metastring:"title='database/migrations/2014_10_12_000000_create_posts_table.hpp'",children:'#pragma once\n\n#include \n\nnamespace Migrations\n{\n\n struct CreatePostsTable : Migration\n {\n /*! Filename of the migration file. */\n T_MIGRATION\n\n /*! Run the migrations. */\n void up() const override\n {\n Schema::create("posts", [](Blueprint &table)\n {\n table.id();\n\n table.string(NAME);\n table.timestamps();\n });\n }\n\n /*! Reverse the migrations. */\n void down() const override\n {\n Schema::dropIfExists("posts");\n }\n };\n\n} // namespace Migrations\n'})}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"TinyORM"})," source tree contains the ",(0,s.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/tests/database/migrations/2014_10_12_000000_create_posts_table.hpp#L5",children:(0,s.jsx)(n.code,{children:"CreatePostsTable"})})," example migration that also acts as the full-fledged example migration. It has defined and also nicely commented all possible features that migration classes can use or define."]})}),"\n",(0,s.jsx)(n.admonition,{type:"info",children:(0,s.jsxs)(n.p,{children:["If you want, you can also build the ",(0,s.jsx)(n.code,{children:"tom"})," application without the migrations, simply comment out the ",(0,s.jsx)(n.code,{children:"migrations"})," method and the corresponding ",(0,s.jsx)(n.code,{children:'#include "migrations/xyz.hpp"'})," files."]})}),"\n",(0,s.jsx)(n.h3,{id:"seeders",children:"Seeders"}),"\n",(0,s.jsxs)(n.p,{children:["If you have already built the ",(0,s.jsx)(n.code,{children:"tom"})," application then you can generate a seeder using the ",(0,s.jsx)(n.a,{href:"/database/seeding#writing-seeders",children:(0,s.jsx)(n.code,{children:"make:seeder"})})," command \ud83d\ude0e."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"tom make:seeder PostSeeder\n"})}),"\n",(0,s.jsxs)(n.p,{children:["The expected folders structure is described a few paragraphs ",(0,s.jsx)(n.a,{href:"#folders-structure",children:"above"}),". The ",(0,s.jsx)(n.a,{href:"#seeders-source-files",children:(0,s.jsx)(n.code,{children:"seeders.pri"})})," file is used only by the ",(0,s.jsx)(n.code,{children:"qmake"})," build system and is not needed with ",(0,s.jsx)(n.code,{children:"CMake"})," builds."]}),"\n",(0,s.jsx)(n.p,{children:"Let's create the root seeder class manually."}),"\n",(0,s.jsxs)(o.A,{groupId:d.vf,children:[(0,s.jsx)(l.A,{value:d.b,label:d.ux,children:(0,s.jsx)(r.A,{className:"language-powershell",children:"mkdir database/seeders\n\nvim database/seeders/databaseseeder.hpp"})}),(0,s.jsx)(l.A,{value:d.xj,label:d.gg,children:(0,s.jsx)(r.A,{className:"language-bash",children:"mkdir -p database/seeders\n\nvim database/seeders/databaseseeder.hpp"})})]}),"\n",(0,s.jsx)(n.p,{children:"And paste the following code."}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cpp",metastring:"title='database/seeders/databaseseeder.hpp'",children:'#pragma once\n\n#include \n\nnamespace Seeders\n{\n\n /*! Main database seeder. */\n struct DatabaseSeeder : Seeder\n {\n /*! Run the database seeders. */\n void run() override\n {\n DB::table("posts")->insert({\n {{"name", "1. post"}},\n {{"name", "2. post"}},\n });\n }\n };\n\n} // namespace Seeders\n'})}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"TinyORM"})," source tree contains the ",(0,s.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/tests/database/seeders/databaseseeder.hpp#L8",children:(0,s.jsx)(n.code,{children:"DatabaseSeeder"})})," root seeder example class that also acts as the full-fledged example seeder. It has defined and also nicely commented all possible features that seeder classes can use or define."]})}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["You can create more seeder classes like this and use the ",(0,s.jsx)(n.code,{children:"call<>()"})," method to invoke them as is described in the ",(0,s.jsx)(n.a,{href:"/database/seeding#calling-additional-seeders",children:"Calling Additional Seeders"})," section."]})}),"\n",(0,s.jsx)(n.h2,{id:"migrations-with-cmake",children:"Migrations with CMake"}),"\n",(0,s.jsxs)(n.p,{children:["Create a folder for the ",(0,s.jsx)(n.code,{children:"CMake"})," build."]}),"\n",(0,s.jsxs)(o.A,{groupId:d.vf,children:[(0,s.jsx)(l.A,{value:d.b,label:d.ux,children:(0,s.jsx)(r.A,{className:"language-powershell",children:"cd ..\nmkdir tom-builds-cmake/build-debug\n\ncd tom"})}),(0,s.jsx)(l.A,{value:d.xj,label:d.gg,children:(0,s.jsx)(r.A,{className:"language-bash",children:"cd ..\nmkdir -p tom-builds-cmake/build-debug\n\ncd tom"})})]}),"\n",(0,s.jsx)(n.h3,{id:"cmake-project",children:"CMake project"}),"\n",(0,s.jsxs)(n.p,{children:["Create ",(0,s.jsx)(n.code,{children:"CMakeLists.txt"})," file with the following content. I leave the comments in the ",(0,s.jsx)(n.code,{children:"CMakeLists.txt"})," file because it's not as simple as the ",(0,s.jsx)(n.code,{children:"Hello world"})," example; to make it clear what's going on."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cmake",metastring:"title='CMakeLists.txt'",children:'cmake_minimum_required(VERSION VERSION 3.22...3.29 FATAL_ERROR)\n\n# Specify the C++ standard\nset(CMAKE_CXX_STANDARD 20)\nset(CMAKE_CXX_STANDARD_REQUIRED ON)\nset(CMAKE_CXX_EXTENSIONS OFF)\n\n# Initialize variables\n# ---\n\nset(Tom_ns tom)\nset(Tom_target tom)\n\nfile(REAL_PATH "../../TinyORM" TinyMainDir)\n\nset(TinyOrmSourceDir "${TinyMainDir}/TinyORM")\nset(TinyOrmBuildDir "${TinyMainDir}/TinyORM-builds-cmake/build-debug")\n\n# TinyORM CMake modules (needed to set the executable version and RC file on Windows)\nlist(APPEND CMAKE_MODULE_PATH "${TinyOrmSourceDir}/cmake/CommonModules")\n\n# build tree\nlist(APPEND CMAKE_PREFIX_PATH "${TinyOrmBuildDir}")\n\n# Initialize Project Version\n# ---\n\ninclude(TinyHelpers)\ntiny_read_version(TINY_VERSION\n TINY_VERSION_MAJOR TINY_VERSION_MINOR TINY_VERSION_PATCH TINY_VERSION_TWEAK\n VERSION_HEADER "${TinyOrmSourceDir}/tom/include/tom/version.hpp"\n PREFIX TINYTOM\n HEADER_FOR "${Tom_ns}"\n)\n\n# Basic project\n# ---\n\nproject(${Tom_ns}\n DESCRIPTION "Tom console application for TinyORM C++ library"\n HOMEPAGE_URL "https://www.tinyorm.org"\n LANGUAGES CXX\n VERSION ${TINY_VERSION}\n)\n\n# Tom command-line application\n# ---\n\nadd_executable(${Tom_target}\n main.cpp\n)\nadd_executable(${Tom_ns}::${Tom_target} ALIAS ${Tom_target})\n\n# Tom command-line application specific configuration\n# ---\n\nset_target_properties(${Tom_target}\n PROPERTIES\n C_VISIBILITY_PRESET "hidden"\n CXX_VISIBILITY_PRESET "hidden"\n VISIBILITY_INLINES_HIDDEN YES\n VERSION ${PROJECT_VERSION}\n)\n\ntarget_include_directories(${Tom_target}\n PRIVATE "$"\n)\n\n# Tom command-line application defines\n# ---\n\ntarget_compile_definitions(${Tom_target}\n PRIVATE\n PROJECT_TOM\n)\n\n# Windows resource and manifest files\n# ---\n\n# Find icons, tom/version.hpp, and Windows manifest file for MinGW\nif(CMAKE_SYSTEM_NAME STREQUAL "Windows")\n tiny_set_rc_flags("-I \\"${TinyOrmSourceDir}/tom/resources\\"")\nendif()\n\ninclude(TinyResourceAndManifest)\ntiny_resource_and_manifest(${Tom_target}\n OUTPUT_DIR "${TINY_BUILD_GENDIR}/tmp/"\n RESOURCES_DIR "${TinyOrmSourceDir}/tom/resources"\n)\n\n# Resolve and link dependencies\n# ---\n\nfind_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)\nfind_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core REQUIRED)\nfind_package(TinyOrm 0.37.3 CONFIG REQUIRED)\n\n# Unconditional dependencies\ntarget_link_libraries(${Tom_target}\n PRIVATE\n Qt${QT_VERSION_MAJOR}::Core\n TinyOrm::TinyOrm\n)\n'})}),"\n",(0,s.jsx)(n.h3,{id:"build-migrations-cmake",children:"Build migrations"}),"\n",(0,s.jsxs)(n.p,{children:["Now you are ready to configure ",(0,s.jsx)(n.code,{children:"tom"})," ",(0,s.jsx)(n.code,{children:"CMake"})," application. Don't forget to prepare the build environment with the ",(0,s.jsx)(n.a,{href:"/building/tinyorm#windows-prerequisites",children:(0,s.jsx)(n.code,{children:"qtenv6.ps1"})})," command if you are building with the ",(0,s.jsx)(n.code,{children:"MSVC"}),"."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"cd ../tom-builds-cmake/build-debug\n"})}),"\n",(0,s.jsxs)(o.A,{groupId:d.vf,children:[(0,s.jsx)(l.A,{value:d.b,label:d.ux,children:(0,s.jsx)(r.A,{className:"language-powershell",children:`cmake.exe \`\n-S "${(0,c.OZ)(d.b)}/tom/tom" \`\n-B "${(0,c.OZ)(d.b)}/tom/tom-builds-cmake/build-debug" \`\n-G 'Ninja' \`\n-D CMAKE_BUILD_TYPE:STRING='Debug' \`\n-D CMAKE_TOOLCHAIN_FILE:FILEPATH="${(0,c.Sn)(d.b)}/vcpkg/scripts/buildsystems/vcpkg.cmake" \`\n-D CMAKE_CXX_SCAN_FOR_MODULES:BOOL=OFF \`\n-D CMAKE_INSTALL_PREFIX:PATH="${(0,c.Sn)(d.b)}/tmp/tom"`})}),(0,s.jsx)(l.A,{value:d.xj,label:d.gg,children:(0,s.jsx)(r.A,{className:"language-bash",children:`cmake \\\n-S "${(0,c.OZ)(d.xj)}/tom/tom" \\\n-B "${(0,c.OZ)(d.xj)}/tom/tom-builds-cmake/build-debug" \\\n-G 'Ninja' \\\n-D CMAKE_BUILD_TYPE:STRING='Debug' \\\n-D CMAKE_TOOLCHAIN_FILE:FILEPATH="${(0,c.Sn)(d.xj)}/vcpkg/scripts/buildsystems/vcpkg.cmake" \\\n-D CMAKE_CXX_SCAN_FOR_MODULES:BOOL=OFF \\\n-D CMAKE_INSTALL_PREFIX:PATH="${(0,c.Sn)(d.xj)}/tmp/tom"`})})]}),"\n",(0,s.jsx)(n.p,{children:"And build."}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"cmake --build . --target all\n"})}),"\n",(0,s.jsx)(n.h3,{id:"execute-migrations-cmake",children:"Execute migrations"}),"\n",(0,s.jsxs)(n.p,{children:["Do not forget to add ",(0,s.jsx)(n.code,{children:"TinyOrm0d.dll"})," on the path on Windows and on the ",(0,s.jsx)(n.code,{children:"LD_LIBRARY_PATH"})," on Linux, so ",(0,s.jsx)(n.code,{children:"tom"})," application can find it during execution, as is described ",(0,s.jsx)(n.a,{href:"/building/tinyorm#tinyorm-on-path-cmake",children:"here"}),"."]}),"\n",(0,s.jsxs)(o.A,{groupId:d.vf,name:"tinyorm-on-path",children:[(0,s.jsx)(l.A,{value:d.b,label:d.ux,children:(0,s.jsx)(r.A,{className:"language-powershell",children:`$env:Path = "${(0,c.OZ)(d.b,!1)}\\TinyORM\\TinyORM-builds-cmake\\build-debug;" + $env:Path`})}),(0,s.jsx)(l.A,{value:d.xj,label:d.gg,children:(0,s.jsx)(r.A,{className:"language-bash",children:`export LD_LIBRARY_PATH=${(0,c.OZ)(d.xj)}/TinyORM/TinyORM-builds-cmake/build-debug\${PATH:+:}$PATH`})})]}),"\n",(0,s.jsxs)(n.p,{children:["Execute ",(0,s.jsx)(n.code,{children:"tom"})," application."]}),"\n",(0,s.jsxs)(o.A,{groupId:d.vf,children:[(0,s.jsx)(l.A,{value:d.b,label:d.ux,children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-powershell",children:".\\tom.exe migrate:status\n"})})}),(0,s.jsx)(l.A,{value:d.xj,label:d.gg,children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"./tom migrate:status\n"})})})]}),"\n",(0,s.jsx)(n.p,{children:"The output will look something like this."}),"\n",(0,s.jsx)("img",{src:i(53).A,alt:"Tom migrations - migrate:status command output",width:"660"}),"\n",(0,s.jsxs)(n.p,{children:["See also the ",(0,s.jsx)(n.a,{href:"#finish",children:"final thoughts"})," on how to verify the ",(0,s.jsx)(n.code,{children:"tom"})," executable file properties."]}),"\n",(0,s.jsx)(n.p,{children:"Happy migrating \ud83c\udf89\ud83d\udc4c"}),"\n",(0,s.jsx)(n.h2,{id:"migrations-with-qmake",children:"Migrations with qmake"}),"\n",(0,s.jsxs)(n.p,{children:["Create a folder for the ",(0,s.jsx)(n.code,{children:"qmake"})," build."]}),"\n",(0,s.jsxs)(o.A,{groupId:d.vf,children:[(0,s.jsx)(l.A,{value:d.b,label:d.ux,children:(0,s.jsx)(r.A,{className:"language-powershell",children:`cd ${(0,c.OZ)(d.b)}/tom\n\nmkdir tom-builds-qmake`})}),(0,s.jsx)(l.A,{value:d.xj,label:d.gg,children:(0,s.jsx)(r.A,{className:"language-bash",children:`cd ${(0,c.OZ)(d.xj)}/tom\n\nmkdir tom-builds-qmake`})})]}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.a,{href:"#source-code",children:(0,s.jsx)(n.code,{children:"source code"})})," is the same as for the ",(0,s.jsx)(n.code,{children:"Migrations with CMake"})," console application."]}),"\n",(0,s.jsx)(n.h3,{id:"qmake-project",children:"qmake project"}),"\n",(0,s.jsxs)(n.p,{children:["Create ",(0,s.jsx)(n.code,{children:"tom.pro"})," qmake file with the following content."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"cd tom\nvim tom.pro\n"})}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["To paste a source code correctly in ",(0,s.jsx)(n.code,{children:"vim"}),", press ",(0,s.jsx)("kbd",{children:"Shift"})," + ",(0,s.jsx)("kbd",{children:"p"}),"."]})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-qmake",metastring:"title='tom.pro'",children:"QT -= gui\n\nTEMPLATE = app\nTARGET = tom\n\nCONFIG *= cmdline\n\nDEFINES *= PROJECT_TOM\n\nSOURCES += $$PWD/main.cpp\n\n# Database migrations\ninclude($$PWD/database/migrations.pri)\n# Database seeders\ninclude($$PWD/database/seeders.pri)\n\n# Auto-configure TinyORM library for the migrations purposes \ud83d\udd25\ninclude($$TINY_MAIN_DIR/TinyORM/qmake/tom.pri)\n"})}),"\n",(0,s.jsx)(n.admonition,{type:"warning",children:(0,s.jsxs)(n.p,{children:["The exact ",(0,s.jsx)(n.a,{href:"/building/tinyorm#folders-structure",children:"folders structure"})," is crucial in this example because the paths to the ",(0,s.jsx)(n.code,{children:"TinyORM"})," source and build folders are relative."]})}),"\n",(0,s.jsx)(n.admonition,{type:"warning",children:(0,s.jsx)(n.p,{children:"Please pay special attention to letter casing in paths, especially TinyOrm vs TinyORM!"})}),"\n",(0,s.jsxs)(n.h4,{id:"auto-configure-using-qmakeconf-and-env",children:[(0,s.jsx)(n.code,{children:"Auto-configure"})," using ",(0,s.jsx)(n.code,{children:".qmake.conf"})," and ",(0,s.jsx)(n.code,{children:".env"})]}),"\n",(0,s.jsxs)(n.p,{children:["If you want to have properly configured ",(0,s.jsx)(n.code,{children:"DEFINES"})," (C preprocessor macros), have Qt headers marked as system headers, or eg. have properly set properties of an executable file such as version and description, then you need to specify a path to the ",(0,s.jsx)(n.code,{children:"TinyORM"})," qmake features (",(0,s.jsx)(n.code,{children:".prf"})," files) which handle this correctly; this path is provided by the ",(0,s.jsx)(n.code,{children:"QMAKEFEATURES"})," variable and can only be set in the ",(0,s.jsx)(n.code,{children:".qmake.conf"})," file."]}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["Read the ",(0,s.jsx)(n.a,{href:"/building/tinyorm#consume-tinyorm-library-qmake",children:"Consume TinyOrm library (qmake)"})," section, as everything that is described in that section applies here as well."]})}),"\n",(0,s.jsxs)(n.p,{children:["Create the ",(0,s.jsx)(n.code,{children:".qmake.conf"})," file in the ",(0,s.jsx)(n.code,{children:"tom"})," application root folder with the following content."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-qmake",metastring:"title='.qmake.conf'",children:"# Path to the PARENT folder of the TinyORM source folder\nTINY_MAIN_DIR = $$clean_path($$PWD/../../TinyORM/)\n# To find .env and .env.$$QMAKE_PLATFORM files\nTINY_DOTENV_ROOT = $$PWD\n# Path to the current build tree (used to guess the TinyORM build tree)\n#TINY_BUILD_TREE = $$shadowed($$PWD)\n\n# To find .prf files, needed by eg. CONFIG += tiny_system_headers inline/extern_constants\nQMAKEFEATURES *= $$quote($$TINY_MAIN_DIR/TinyORM/qmake/features)\n"})}),"\n",(0,s.jsxs)(n.p,{children:["Then, create a ",(0,s.jsx)("code",{children:".env.(win32|unix|mingw)"})," file in the ",(0,s.jsx)(n.code,{children:"tom"})," application root folder with the following content."]}),"\n",(0,s.jsxs)(o.A,{groupId:d.vf,children:[(0,s.jsx)(l.A,{value:d.b,label:".env.win32",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-qmake",children:"# Names and values of these qmake variables are crucial, they are used in the tom.pro\n# Please pay special attention to letter casing in paths, especially TinyOrm vs TinyORM!\n\n# Path to the TinyORM build folder\nTINYORM_BUILD_TREE = $$quote($$TINY_MAIN_DIR/TinyORM-builds-qmake/build-TinyORM-Desktop_Qt_6_7_2_MSVC2022_64bit-Debug/)\n\n# Path to the vcpkg - range-v3 and tabulate\n# Will use the TINY_VCPKG_ROOT or VCPKG_ROOT environment variable if is empty\nTINY_VCPKG_ROOT = $$clean_path($$PWD/../../../vcpkg/)\nTINY_VCPKG_TRIPLET = x64-windows\n\n# Enable ccache wrapper\n#CONFIG *= ccache\n"})})}),(0,s.jsx)(l.A,{value:d.xj,label:".env.unix",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-qmake",children:"# Names and values of these qmake variables are crucial, they are used in the tom.pro\n# Please pay special attention to letter casing in paths, especially TinyOrm vs TinyORM!\n\n# Path to the TinyORM build folder\nTINYORM_BUILD_TREE = $$quote($$TINY_MAIN_DIR/TinyORM-builds-qmake/build-TinyORM-Desktop_Qt_6_7_2_clang16_64bit_ccache-Debug/)\n\n# Path to the vcpkg - range-v3 and tabulate\n# Will use the TINY_VCPKG_ROOT or VCPKG_ROOT environment variable if is empty\nTINY_VCPKG_ROOT = $$clean_path($$PWD/../../../vcpkg/)\nTINY_VCPKG_TRIPLET = x64-linux\n\n# Use faster linker\nclang: CONFIG *= use_lld_linker\nelse: CONFIG *= use_gold_linker\n\n# Or use the mold linker\n#QMAKE_LFLAGS *= -fuse-ld=mold\n"})})}),(0,s.jsx)(l.A,{value:"mingw",label:".env.mingw",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-qmake",children:"# Names and values of these qmake variables are crucial, they are used in the tom.pro\n# Please pay special attention to letter casing in paths, especially TinyOrm vs TinyORM!\n\n# Path to the TinyORM build folder\nTINYORM_BUILD_TREE = $$quote($$TINY_MAIN_DIR/TinyORM-builds-qmake/build-TinyORM-Desktop_Qt_6_7_2_MSYS2_UCRT64_clang_64bit-Debug/)\n\n# Path to the vcpkg - range-v3 and tabulate\n# Will use the TINY_VCPKG_ROOT or VCPKG_ROOT environment variable if is empty\nTINY_VCPKG_ROOT = $$clean_path($$PWD/../../../vcpkg/)\nTINY_VCPKG_TRIPLET = x64-mingw-dynamic\n\n# Enable ccache wrapper\n#CONFIG *= ccache\n\n# Use faster linker (for both GCC and Clang)\n# CONFIG *= use_lld_linker does not work on MinGW\nQMAKE_LFLAGS *= -fuse-ld=lld\n"})})})]}),"\n",(0,s.jsxs)(n.p,{children:["Don't forget to update the ",(0,s.jsx)(n.code,{children:"TINYORM_BUILD_TREE"})," and ",(0,s.jsx)(n.code,{children:"TINY_VCPKG_ROOT"})," folder paths to your needs if you are not using the recommended ",(0,s.jsx)(n.a,{href:"/building/tinyorm#folders-structure",children:(0,s.jsx)(n.code,{children:"Folders structure"})}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["You can use the ",(0,s.jsxs)(n.a,{href:"/building/tinyorm#partial-guessing-of-the-tinyorm_build_tree",children:["Partial guessing of the ",(0,s.jsx)(n.code,{children:"TINYORM_BUILD_TREE"})]})," if you don't like to specify it manually. Just comment out the ",(0,s.jsx)(n.code,{children:"TINYORM_BUILD_TREE"})," and uncomment the ",(0,s.jsx)(n.code,{children:"TINY_BUILD_TREE = $$shadowed($$PWD)"})," in the ",(0,s.jsx)(n.code,{children:".qmake.conf"})," file."]}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["You can entirely avoid the ",(0,s.jsx)(n.code,{children:".env"})," files, just move the ",(0,s.jsx)(n.code,{children:"TINYORM_BUILD_TREE"})," to the ",(0,s.jsx)(n.code,{children:".qmake.conf"})," or remove it by help of ",(0,s.jsxs)(n.a,{href:"/building/tinyorm#partial-guessing-of-the-tinyorm_build_tree",children:["Partial guessing of the ",(0,s.jsx)(n.code,{children:"TINYORM_BUILD_TREE"})]})," and set the ",(0,s.jsx)(n.code,{children:"VCPKG_ROOT"})," environment variable at system level as is described in ",(0,s.jsx)(n.a,{href:"/building/tinyorm#set-up-vcpkg-environment",children:(0,s.jsx)(n.code,{children:"Set up vcpkg environment"})}),"."]})}),"\n",(0,s.jsx)(n.admonition,{type:"info",children:(0,s.jsxs)(n.p,{children:["Configuring by the ",(0,s.jsx)(n.code,{children:".qmake.conf"})," and ",(0,s.jsx)(n.code,{children:".env"})," files has one big advantage, which is that you don't have to modify the project files."]})}),"\n",(0,s.jsx)(n.h4,{id:"migrations-source-files",children:"Migrations source files"}),"\n",(0,s.jsxs)(n.p,{children:["Create ",(0,s.jsx)(n.code,{children:"database/migrations.pri"})," file and paste the following code."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-qmake",metastring:"title='database/migrations.pri'",children:"INCLUDEPATH *= $$PWD\n\nHEADERS += \\\n $$PWD/migrations/2014_10_12_000000_create_posts_table.hpp \\\n"})}),"\n",(0,s.jsx)(n.h4,{id:"seeders-source-files",children:"Seeders source files"}),"\n",(0,s.jsxs)(n.p,{children:["Create ",(0,s.jsx)(n.code,{children:"database/seeders.pri"})," file and paste the following code."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-qmake",metastring:"title='database/seeders.pri'",children:"INCLUDEPATH *= $$PWD\n\nHEADERS += \\\n $$PWD/seeders/databaseseeder.hpp \\\n"})}),"\n",(0,s.jsx)(n.h3,{id:"build-migrations-qmake",children:"Build migrations"}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["I recommend creating a new ",(0,s.jsx)(n.code,{children:"Session"})," in the ",(0,s.jsx)(n.code,{children:"QtCreator IDE"})," as is described ",(0,s.jsx)(n.a,{href:"/building/tinyorm#open-qtcreator-ide",children:"here"}),"."]})}),"\n",(0,s.jsxs)(n.p,{children:["Now you can open the ",(0,s.jsx)(n.code,{children:"tom.pro"})," project in the ",(0,s.jsx)(n.code,{children:"QtCreator IDE"}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["This will open the ",(0,s.jsx)(n.code,{children:"Configure Project"})," tab, select some kit and update build folder paths to meet our ",(0,s.jsx)(n.a,{href:"/building/tinyorm#folders-structure",children:"folders structure"})," or like you want."]}),"\n",(0,s.jsx)("img",{src:i(2394).A,alt:"tom - QtCreator - Configure Project",width:"760"}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:["You can force the ",(0,s.jsx)(n.code,{children:"QtCreator"})," to generate a build folders structure as is described ",(0,s.jsx)(n.a,{href:"/building/tinyorm#qtcreator-default-build-directory",children:"here"}),"."]})}),"\n",(0,s.jsxs)(n.p,{children:["You are ready to configure build options, hit ",(0,s.jsx)("kbd",{children:"Ctrl"}),"+",(0,s.jsx)("kbd",{children:"5"})," to open ",(0,s.jsx)(n.code,{children:"Project Settings"})," tab and select ",(0,s.jsx)(n.code,{children:"Build"})," in the left sidebar to open the ",(0,s.jsx)(n.code,{children:"Build Settings"}),", it should look similar to the following picture."]}),"\n",(0,s.jsx)("img",{src:i(6106).A,className:"no-blurry",alt:"tom - QtCreator - Build Settings",width:"760"}),"\n",(0,s.jsxs)(n.p,{children:["Disable ",(0,s.jsx)(n.code,{children:"QML debugging and profiling"})," and ",(0,s.jsx)(n.code,{children:"Qt Quick Compiler"}),", they are not used."]}),"\n",(0,s.jsxs)(n.p,{children:["In the left sidebar open ",(0,s.jsx)(n.code,{children:"Dependencies"})," and check ",(0,s.jsx)(n.code,{children:"TinyORM"})," project and ",(0,s.jsx)(n.code,{children:"Synchronize configuration"}),", this setting ensures that the current project will be rebuilt correctly when the ",(0,s.jsx)(n.code,{children:"TinyORM"})," library source code changes."]}),"\n",(0,s.jsxs)(n.p,{children:["Everything is ready to build, you can press ",(0,s.jsx)("kbd",{children:"Ctrl"}),"+",(0,s.jsx)("kbd",{children:"b"})," to build the project."]}),"\n",(0,s.jsx)(n.h3,{id:"execute-migrations-qmake",children:"Execute migrations"}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"QtCreator"})," takes care of all the necessary configurations, sets up the build environment correctly, and also prepends dependency libraries on the system path on Windows and on the ",(0,s.jsx)(n.code,{children:"LD_LIBRARY_PATH"})," on Linux."]}),"\n",(0,s.jsxs)(n.p,{children:["The only thing you might want to change is to run the ",(0,s.jsx)(n.code,{children:"tom"})," application in the new terminal window. To do so, hit ",(0,s.jsx)("kbd",{children:"Ctrl"}),"+",(0,s.jsx)("kbd",{children:"5"})," to open the ",(0,s.jsx)(n.code,{children:"Project Settings"})," tab and select ",(0,s.jsx)(n.code,{children:"Run"})," in the left sidebar to open the ",(0,s.jsx)(n.code,{children:"Run Settings"}),", then in the ",(0,s.jsx)(n.code,{children:"Run"})," section select the ",(0,s.jsx)(n.code,{children:"Run in terminal"})," checkbox."]}),"\n",(0,s.jsxs)(n.p,{children:["You can also set the ",(0,s.jsx)(n.code,{children:"Command line arguments"})," in this ",(0,s.jsx)(n.code,{children:"Run"})," section, eg. the ",(0,s.jsx)(n.code,{children:"migrate:status"}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["To execute the ",(0,s.jsx)(n.code,{children:"tom"})," application press ",(0,s.jsx)("kbd",{children:"Ctrl"})," + ",(0,s.jsx)("kbd",{children:"r"}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["The output will look ",(0,s.jsx)(n.strong,{children:"very similar"})," to this if you add more migrations."]}),"\n",(0,s.jsx)("img",{src:i(53).A,alt:"Tom migrations - migrate:status command output",width:"660"}),"\n",(0,s.jsx)(n.p,{children:"Happy migrating \ud83c\udf89\ud83d\udc4c"}),"\n",(0,s.jsx)(n.h2,{id:"finish",children:"Finish"}),"\n",(0,s.jsxs)(n.p,{children:["As the last thing, you can check that all the file properties were correctly set by the ",(0,s.jsx)(n.a,{href:"https://docs.microsoft.com/en-us/windows/win32/menurc/resource-compiler",children:(0,s.jsx)(n.code,{children:"rc"})})," compiler."]}),"\n",(0,s.jsxs)(n.p,{children:["Find the ",(0,s.jsx)(n.code,{children:"tom.exe"})," file and press ",(0,s.jsx)("kbd",{children:"Alt"})," + ",(0,s.jsx)("kbd",{children:"Enter"})," to open the file properties. To check the executable manifest you can use eg. the ",(0,s.jsx)(n.a,{href:"http://www.angusj.com/resourcehacker/",children:"Resource Hacker"}),"."]}),"\n",(0,s.jsx)("img",{src:i(4679).A,alt:"tom.exe file properties detail",width:"440"})]})}function j(e={}){const{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,s.jsx)(n,{...e,children:(0,s.jsx)(x,{...e})}):x(e)}},9365:(e,n,i)=>{i.d(n,{A:()=>r});i(6540);var s=i(4164);const t={tabItem:"tabItem_Ymn6"};var a=i(4848);function r(e){let{children:n,hidden:i,className:r}=e;return(0,a.jsx)("div",{role:"tabpanel",className:(0,s.A)(t.tabItem,r),hidden:i,children:n})}},1470:(e,n,i)=>{i.d(n,{A:()=>v});var s=i(6540),t=i(4164),a=i(3104),r=i(6347),l=i(205),o=i(7485),d=i(1682),c=i(679);function h(e){return s.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,s.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function u(e){const{values:n,children:i}=e;return(0,s.useMemo)((()=>{const e=n??function(e){return h(e).map((e=>{let{props:{value:n,label:i,attributes:s,default:t}}=e;return{value:n,label:i,attributes:s,default:t}}))}(i);return function(e){const n=(0,d.X)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[n,i])}function m(e){let{value:n,tabValues:i}=e;return i.some((e=>e.value===n))}function p(e){let{queryString:n=!1,groupId:i}=e;const t=(0,r.W6)(),a=function(e){let{queryString:n=!1,groupId:i}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!i)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return i??null}({queryString:n,groupId:i});return[(0,o.aZ)(a),(0,s.useCallback)((e=>{if(!a)return;const n=new URLSearchParams(t.location.search);n.set(a,e),t.replace({...t.location,search:n.toString()})}),[a,t])]}function g(e){const{defaultValue:n,queryString:i=!1,groupId:t}=e,a=u(e),[r,o]=(0,s.useState)((()=>function(e){let{defaultValue:n,tabValues:i}=e;if(0===i.length)throw new Error("Docusaurus error: the component requires at least one children component");if(n){if(!m({value:n,tabValues:i}))throw new Error(`Docusaurus error: The has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${i.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}const s=i.find((e=>e.default))??i[0];if(!s)throw new Error("Unexpected error: 0 tabValues");return s.value}({defaultValue:n,tabValues:a}))),[d,h]=p({queryString:i,groupId:t}),[g,x]=function(e){let{groupId:n}=e;const i=function(e){return e?`docusaurus.tab.${e}`:null}(n),[t,a]=(0,c.Dv)(i);return[t,(0,s.useCallback)((e=>{i&&a.set(e)}),[i,a])]}({groupId:t}),j=(()=>{const e=d??g;return m({value:e,tabValues:a})?e:null})();(0,l.A)((()=>{j&&o(j)}),[j]);return{selectedValue:r,selectValue:(0,s.useCallback)((e=>{if(!m({value:e,tabValues:a}))throw new Error(`Can't select invalid tab value=${e}`);o(e),h(e),x(e)}),[h,x,a]),tabValues:a}}var x=i(2303);const j={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var b=i(4848);function f(e){let{className:n,block:i,selectedValue:s,selectValue:r,tabValues:l}=e;const o=[],{blockElementScrollPositionUntilNextRender:d}=(0,a.a_)(),c=e=>{const n=e.currentTarget,i=o.indexOf(n),t=l[i].value;t!==s&&(d(n),r(t))},h=e=>{let n=null;switch(e.key){case"Enter":c(e);break;case"ArrowRight":{const i=o.indexOf(e.currentTarget)+1;n=o[i]??o[0];break}case"ArrowLeft":{const i=o.indexOf(e.currentTarget)-1;n=o[i]??o[o.length-1];break}}n?.focus()};return(0,b.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,t.A)("tabs",{"tabs--block":i},n),children:l.map((e=>{let{value:n,label:i,attributes:a}=e;return(0,b.jsx)("li",{role:"tab",tabIndex:s===n?0:-1,"aria-selected":s===n,ref:e=>o.push(e),onKeyDown:h,onClick:c,...a,className:(0,t.A)("tabs__item",j.tabItem,a?.className,{"tabs__item--active":s===n}),children:i??n},n)}))})}function _(e){let{lazy:n,children:i,selectedValue:t}=e;const a=(Array.isArray(i)?i:[i]).filter(Boolean);if(n){const e=a.find((e=>e.props.value===t));return e?(0,s.cloneElement)(e,{className:"margin-top--md"}):null}return(0,b.jsx)("div",{className:"margin-top--md",children:a.map(((e,n)=>(0,s.cloneElement)(e,{key:n,hidden:e.props.value!==t})))})}function T(e){const n=g(e);return(0,b.jsxs)("div",{className:(0,t.A)("tabs-container",j.tabList),children:[(0,b.jsx)(f,{...n,...e}),(0,b.jsx)(_,{...n,...e})]})}function v(e){const n=(0,x.A)();return(0,b.jsx)(T,{...e,children:h(e.children)},String(n))}},7324:(e,n,i)=>{i.d(n,{$E:()=>x,A3:()=>b,CW:()=>j,Dx:()=>c,F4:()=>u,Fi:()=>d,J_:()=>v,LQ:()=>f,Lf:()=>y,OO:()=>t,Q7:()=>_,b:()=>l,cy:()=>o,gg:()=>p,kl:()=>m,os:()=>h,pW:()=>a,ux:()=>g,vf:()=>s,xj:()=>r,xt:()=>T});const s="shell",t="database",a="application",r="bash",l="pwsh",o="zsh",d="maria",c="mysql",h="postgres",u="sqlite",m="application",p="bash",g="pwsh",x="zsh",j="MariaDB",b="MySQL",f="PostgreSQL",_="SQLite",T="tinyorm.org",v="$HOME/Code/c/",y="$env:USERPROFILE\\Code\\c\\"},6362:(e,n,i)=>{i.d(n,{A:()=>a});var s=i(6540),t=i(1838);function a(){const e=(0,s.useContext)(t.A);if(null!=e)return e;throw new Error("useRootFolderContext is used outside of Layout component.")}},6694:(e,n,i)=>{i.d(n,{OZ:()=>o,Sn:()=>r,T3:()=>c,bw:()=>d,nC:()=>h,np:()=>l});var s=i(6362),t=i(2303),a=i(7324);const r=function(e,n){return void 0===n&&(n=!0),u((0,s.A)().rootFolder[e]??d(e),e,n)},l=()=>(0,s.A)().rootFolder[a.pW]??d(a.pW),o=function(e,n){if(void 0===n&&(n=!0),null==e)throw new Error("The groupId in the applicationFolderPath() can not be empty.");const i=n||e!==a.b?"/":"\\";return u(r(e)+i+l(),e,n)};function d(e){if(null==e)throw new Error("The groupId in the folderDefaultValue() can not be empty.");if(!(0,t.A)())return"";switch(e){case a.b:return a.Lf;case a.xj:return a.J_;case a.pW:return a.xt;default:throw new Error(`No default value for '${e}' groupId in the folderDefaultValue().`)}}function c(e){return e===a.pW}function h(e,n){if(null==n||""===n)return n;const i="$ENV{$1}$2";switch(e){case a.b:return p(n).replace(/\$env:(.+?)(\/.*)/,i);case a.xj:return n.replace(/\$(.+?)(\/.*)/,i);default:throw new Error(`Unsupported shell type '${e}' in the convertToCmakeEnvVariable().`)}}function u(e,n,i){if(void 0===i&&(i=!0),null==e||""===e)return e;if(n!==a.b)return m(e);const s=m(e);return i?p(s):function(e){return null==e||""===e?e:e.replaceAll(/\/+/g,"\\")}(s)}function m(e){return null==e||""===e?e:e.replace(/[/\\]+$/,"")}function p(e){return null==e||""===e?e:e.replaceAll(/\\+(?! )/g,"/")}},6106:(e,n,i)=>{i.d(n,{A:()=>s});const s=i.p+"assets/images/qmake-build_settings-e10927d1c4ed852620f9eb7564198940.png"},2394:(e,n,i)=>{i.d(n,{A:()=>s});const s=i.p+"assets/images/qmake-configure_project-4721257090370204b0272d166512adef.png"},4679:(e,n,i)=>{i.d(n,{A:()=>s});const s=i.p+"assets/images/tom_file_properties-0df513c47ceadd5c09165e41c6b53086.png"},53:(e,n,i)=>{i.d(n,{A:()=>s});const s=i.p+"assets/images/tom_migrate_status-63c129a10bfe6bffe8d2d5ea280860e5.png"}}]); \ No newline at end of file diff --git a/assets/js/ba3d4959.52113abf.js b/assets/js/ba3d4959.52113abf.js new file mode 100644 index 000000000..fc18ee56c --- /dev/null +++ b/assets/js/ba3d4959.52113abf.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunktinyorm_org=self.webpackChunktinyorm_org||[]).push([[170],{2512:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>T,contentTitle:()=>v,default:()=>L,frontMatter:()=>y,metadata:()=>_,toc:()=>Q});var i=t(4848),r=t(8453),a=t(9365),s=t(1470),o=t(6540);let l=null,c=null;function d(){const[e,n]=(0,o.useState)(!1),[t,r]=(0,o.useState)(!1);return(0,o.useEffect)((()=>(h(),()=>{h()})),[]),(0,i.jsxs)("form",{children:[(0,i.jsxs)("div",{className:"tinyorm-configuration-row",children:[(0,i.jsx)("label",{htmlFor:"tinyorm-toggle-full",children:"Full configuration"}),(0,i.jsx)("input",{id:"tinyorm-toggle-full",type:"checkbox",checked:e,onChange:()=>n(u),title:"Show full configuration example"})]}),(0,i.jsxs)("div",{className:"tinyorm-configuration-row",children:[(0,i.jsx)("label",{htmlFor:"tinyorm-toggle-prefix",children:"Prefix environment"}),(0,i.jsx)("input",{id:"tinyorm-toggle-prefix",type:"checkbox",checked:t,onChange:()=>r(j),title:"Prefix all environment variables by the database type"})]})]})}function h(){l=null,c=null}function u(e){const n=l||(l=document.getElementById("tinyorm-configuration")?.querySelectorAll(".tabs-container")),t=!e;if(!n)return e;if(2!==n.length)throw new Error(`Wrong number of .tabs-container (!== 2) found in the toggle configuration feature, found ${n.length}.`);return t?(n[0].style.display="none",n[1].style.display="block"):(n[0].style.display="block",n[1].style.display="none"),!e}const m='"DB_',p="MARIA",g="MYSQL",x="PGSQL",f="SQLITE",b=[g,x,f,p,g,x,f,p];function j(e){const n=function(){if(c)return c;const e=document.getElementById("tinyorm-configuration")?.querySelectorAll(".prism-code > code");if(!e)return e;if(8!==e.length)throw new Error(`Wrong number of .prism-code > code (!== 8) found in the prefix env. feature, found ${e.length}.`);return c=new Array(e.length),e.forEach(((e,n)=>{c[n]=Array.from(e.querySelectorAll(".token.string")).filter((e=>e?.textContent.startsWith(m)))})),c}(),t=!e;return n?(n.forEach(((e,n)=>{e.forEach((e=>{const i=e.textContent,r=`${m}${b[n]}_`;if(t)e.textContent=`${r}${i.substring(4)}`;else{if(!i.startsWith(r))throw new Error(`Token doesn't start with the '${r}' prefix.`);e.textContent=`${m}${i.substring(r.length)}`}}))})),!e):e}var S=t(7324);const y={sidebar_position:0,sidebar_label:"Getting Started",description:"TinyORM makes interacting with a database extremely simple using raw SQL, a fluent query builder, and the TinyORM. It provides first-party support for four databases MySQL/MariaDB, PostgreSQL, and SQLite.",keywords:["c++ orm","database","getting started","tinyorm"]},v="Database: Getting Started",_={id:"database/getting-started",title:"Database: Getting Started",description:"TinyORM makes interacting with a database extremely simple using raw SQL, a fluent query builder, and the TinyORM. It provides first-party support for four databases MySQL/MariaDB, PostgreSQL, and SQLite.",source:"@site/docs/database/getting-started.mdx",sourceDirName:"database",slug:"/database/getting-started",permalink:"/database/getting-started",draft:!1,unlisted:!1,tags:[],version:"current",sidebarPosition:0,frontMatter:{sidebar_position:0,sidebar_label:"Getting Started",description:"TinyORM makes interacting with a database extremely simple using raw SQL, a fluent query builder, and the TinyORM. It provides first-party support for four databases MySQL/MariaDB, PostgreSQL, and SQLite.",keywords:["c++ orm","database","getting started","tinyorm"]},sidebar:"tinyormSidebar",previous:{title:"\ud83d\ude80 Supported Compilers",permalink:"/supported-compilers"},next:{title:"Query Builder",permalink:"/database/query-builder"}},T={},Q=[{value:"Introduction",id:"introduction",level:2},{value:"Configuration",id:"configuration",level:3},{value:"SQLite Configuration",id:"sqlite-configuration",level:4},{value:"SSL Connections",id:"ssl-connections",level:3},{value:"MySQL",id:"mysql",level:5},{value:"PostgreSQL",id:"postgresql",level:5},{value:"Running SQL Queries",id:"running-sql-queries",level:2},{value:"Running A Select Query",id:"running-a-select-query",level:4},{value:"Selecting Scalar Values",id:"selecting-scalar-values",level:4},{value:"Running An Insert Statement",id:"running-an-insert-statement",level:4},{value:"Running An Update Statement",id:"running-an-update-statement",level:4},{value:"Running A Delete Statement",id:"running-a-delete-statement",level:4},{value:"Running A General Statement",id:"running-a-general-statement",level:4},{value:"Running An Unprepared Statement",id:"running-an-unprepared-statement",level:4},{value:"Implicit Commits",id:"implicit-commits",level:4},{value:"Using Multiple Database Connections",id:"using-multiple-database-connections",level:3},{value:"Database Transactions",id:"database-transactions",level:2},{value:"Manually Using Transactions",id:"manually-using-transactions",level:4},{value:"Multi-threading support",id:"multi-threading-support",level:2}];function D(e){const n={a:"a",admonition:"admonition",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",h4:"h4",h5:"h5",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,r.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.h1,{id:"database-getting-started",children:"Database: Getting Started"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"#introduction",children:"Introduction"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"#configuration",children:"Configuration"})}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"#ssl-connections",children:"SSL Connections"})}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"#running-sql-queries",children:"Running SQL Queries"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"#using-multiple-database-connections",children:"Using Multiple Database Connections"})}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"#database-transactions",children:"Database Transactions"})}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"#multi-threading-support",children:"Multi-threading support"})}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"introduction",children:"Introduction"}),"\n",(0,i.jsxs)(n.p,{children:["Almost every modern application interacts with a database. TinyORM makes interacting with a database extremely simple using raw SQL, a ",(0,i.jsx)(n.a,{href:"/database/query-builder",children:"fluent query builder"}),", and the ",(0,i.jsx)(n.a,{href:"/tinyorm/getting-started",children:"TinyORM"}),". Currently, TinyORM provides first-party support for four databases:"]}),"\n",(0,i.jsx)("div",{id:"databases-supported-versions",children:(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["MySQL ",(0,i.jsx)(n.code,{children:">=5.7"})," (",(0,i.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/MySQL#Release_history",children:"Version Policy"}),")"]}),"\n",(0,i.jsxs)(n.li,{children:["MariaDB ",(0,i.jsx)(n.code,{children:">=10.3"})," (",(0,i.jsx)(n.a,{href:"https://mariadb.org/about/#maintenance-policy",children:"Version Policy"}),")"]}),"\n",(0,i.jsxs)(n.li,{children:["PostgreSQL ",(0,i.jsx)(n.code,{children:">=11"})," (",(0,i.jsx)(n.a,{href:"https://www.postgresql.org/support/versioning/",children:"Version Policy"}),")"]}),"\n",(0,i.jsxs)(n.li,{children:["SQLite ",(0,i.jsx)(n.code,{children:">=3.8.8"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["native rename column ",(0,i.jsx)(n.code,{children:">=3.25.0"})," (",(0,i.jsx)(n.a,{href:"https://www.sqlite.org/lang_altertable.html#alter_table_rename_column",children:"docs"})," , ",(0,i.jsx)(n.a,{href:"https://www.sqlite.org/releaselog/3_25_0.html",children:"release notes"}),")"]}),"\n",(0,i.jsxs)(n.li,{children:["native drop column ",(0,i.jsx)(n.code,{children:">=3.35.0"})," (",(0,i.jsx)(n.a,{href:"https://www.sqlite.org/lang_altertable.html#alter_table_drop_column",children:"docs"}),", ",(0,i.jsx)(n.a,{href:"https://www.sqlite.org/releaselog/3_35_0.html",children:"release notes"}),")"]}),"\n",(0,i.jsxs)(n.li,{children:["generated columns ",(0,i.jsx)(n.code,{children:">=3.31.0"})," (",(0,i.jsx)(n.a,{href:"https://www.sqlite.org/gencol.html",children:"docs"}),", ",(0,i.jsx)(n.a,{href:"https://www.sqlite.org/releaselog/3_31_0.html",children:"release notes"}),")"]}),"\n"]}),"\n"]}),"\n"]})}),"\n",(0,i.jsxs)(n.p,{children:["TinyORM internally uses ",(0,i.jsx)(n.code,{children:"QtSql"})," module, you can look for ",(0,i.jsx)(n.a,{href:"https://doc.qt.io/qt/sql-driver.html#supported-databases",children:"supported databases"}),"."]}),"\n",(0,i.jsx)(n.admonition,{type:"note",children:(0,i.jsx)(n.p,{children:"TinyORM's code is ready and designed to simply add support for the SQL Server."})}),"\n",(0,i.jsx)(n.h3,{id:"configuration",children:"Configuration"}),"\n",(0,i.jsxs)(n.p,{children:["You can create and configure a new database connection using the ",(0,i.jsx)(n.code,{children:"create"})," method provided by the ",(0,i.jsx)(n.code,{children:"DB"})," facade:"]}),"\n",(0,i.jsxs)("div",{id:"tinyorm-configuration",children:[(0,i.jsx)(d,{}),(0,i.jsxs)(s.A,{className:"tinyorm-configuration-basic",groupId:S.OO,children:[(0,i.jsx)(a.A,{value:S.Dx,label:S.A3,children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\nusing Orm::DB;\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {"driver", "QMYSQL"},\n {"host", qEnvironmentVariable("DB_HOST", "127.0.0.1")},\n {"port", qEnvironmentVariable("DB_PORT", "3306")},\n {"database", qEnvironmentVariable("DB_DATABASE", "")},\n {"username", qEnvironmentVariable("DB_USERNAME", "root")},\n {"password", qEnvironmentVariable("DB_PASSWORD", "")},\n {"charset", qEnvironmentVariable("DB_CHARSET", "utf8mb4")},\n {"collation", qEnvironmentVariable("DB_COLLATION", "utf8mb4_0900_ai_ci")},\n {"timezone", "+00:00"},\n /* Specifies what time zone all QDateTime-s will have, the overridden default is\n the QTimeZone::UTC, set to the QTimeZone::LocalTime or\n QtTimeZoneType::DontConvert to use the system local time. */\n {"qt_timezone", QVariant::fromValue(QTimeZone::UTC)},\n {"prefix", ""},\n {"prefix_indexes", false},\n {"strict", true},\n {"engine", "InnoDB"},\n {"options", QVariantHash()},\n});\n'})})}),(0,i.jsx)(a.A,{value:S.os,label:S.LQ,children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\nusing Orm::DB;\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {"driver", "QPSQL"},\n {"host", qEnvironmentVariable("DB_HOST", "127.0.0.1")},\n {"port", qEnvironmentVariable("DB_PORT", "5432")},\n {"database", qEnvironmentVariable("DB_DATABASE", "")},\n {"search_path", qEnvironmentVariable("DB_SEARCHPATH", "public")},\n {"username", qEnvironmentVariable("DB_USERNAME", "postgres")},\n {"password", qEnvironmentVariable("DB_PASSWORD", "")},\n {"charset", qEnvironmentVariable("DB_CHARSET", "utf8")},\n {"timezone", "UTC"},\n /* Specifies what time zone all QDateTime-s will have, the overridden default is\n the QTimeZone::UTC, set to the QTimeZone::LocalTime or\n QtTimeZoneType::DontConvert to use the system local time. */\n {"qt_timezone", QVariant::fromValue(QTimeZone::UTC)},\n {"prefix", ""},\n {"prefix_indexes", false},\n {"options", QVariantHash()},\n});\n'})})}),(0,i.jsx)(a.A,{value:S.F4,label:S.Q7,children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\nusing Orm::DB;\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {"driver", "QSQLITE"},\n {"database", qEnvironmentVariable("DB_DATABASE", "/example/example.sqlite3")},\n {"foreign_key_constraints", qEnvironmentVariable("DB_FOREIGN_KEYS", "true")},\n {"check_database_exists", true},\n /* Specifies what time zone all QDateTime-s will have, the overridden default is\n the QTimeZone::UTC, set to the QTimeZone::LocalTime or\n QtTimeZoneType::DontConvert to use the system local time. */\n {"qt_timezone", QVariant::fromValue(QTimeZone::UTC)},\n /* Return a QDateTime/QDate with the correct time zone instead of the QString,\n only works when the qt_timezone isn\'t set to the DontConvert. */\n {"return_qdatetime", true},\n {"prefix", ""},\n {"prefix_indexes", false},\n});\n'})})}),(0,i.jsx)(a.A,{value:S.Fi,label:S.CW,children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\nusing Orm::DB;\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {"driver", "QMYSQL"},\n {"host", qEnvironmentVariable("DB_HOST", "127.0.0.1")},\n {"port", qEnvironmentVariable("DB_PORT", "3306")},\n {"database", qEnvironmentVariable("DB_DATABASE", "")},\n {"username", qEnvironmentVariable("DB_USERNAME", "root")},\n {"password", qEnvironmentVariable("DB_PASSWORD", "")},\n {"charset", qEnvironmentVariable("DB_CHARSET", "utf8mb4")},\n {"collation", qEnvironmentVariable("DB_COLLATION", "utf8mb4_unicode_520_ci")},\n {"timezone", "+00:00"},\n /* Specifies what time zone all QDateTime-s will have, the overridden default is\n the QTimeZone::UTC, set to the QTimeZone::LocalTime or\n QtTimeZoneType::DontConvert to use the system local time. */\n {"qt_timezone", QVariant::fromValue(QTimeZone::UTC)},\n {"prefix", ""},\n {"prefix_indexes", false},\n {"strict", true},\n {"engine", "InnoDB"},\n {"options", QVariantHash()},\n});\n'})})})]}),(0,i.jsxs)(s.A,{className:"tinyorm-configuration-full",groupId:S.OO,children:[(0,i.jsx)(a.A,{value:S.Dx,label:S.A3,children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n#include \n\nusing Orm::DB;\n\nusing ConfigUtils = Orm::Utils::Configuration;\n\nusing namespace Orm::Constants; // NOLINT(google-build-using-namespace)\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {driver_, QMYSQL},\n {host_, qEnvironmentVariable("DB_HOST", H127001)},\n {port_, qEnvironmentVariable("DB_PORT", P3306)},\n {database_, qEnvironmentVariable("DB_DATABASE", EMPTY)},\n {username_, qEnvironmentVariable("DB_USERNAME", ROOT)},\n {password_, qEnvironmentVariable("DB_PASSWORD", EMPTY)},\n {charset_, qEnvironmentVariable("DB_CHARSET", UTF8MB4)},\n {collation_, qEnvironmentVariable("DB_COLLATION", UTF8MB40900aici)},\n // SSL-related\n {ssl_ca, QStringLiteral("C:/mysql/data/ca.pem")},\n {ssl_cert, QStringLiteral("C:/mysql/data/client-cert.pem")},\n {ssl_key, QStringLiteral("C:/mysql/data/client-key.pem")},\n {ssl_mode, VerifyCA},\n // Or\n// {options, ConfigUtils::mysqlSslOptions()},\n {timezone_, TZ00},\n /* Specifies what time zone all QDateTime-s will have, the overridden default is\n the QTimeZone::UTC, set to the QTimeZone::LocalTime or\n QtTimeZoneType::DontConvert to use the system local time. */\n {qt_timezone, QVariant::fromValue(QTimeZone::UTC)},\n {prefix_, EMPTY},\n {prefix_indexes, false},\n {strict_, true},\n// {isolation_level, QStringLiteral("REPEATABLE READ")}, // MySQL default is REPEATABLE READ for InnoDB\n {engine_, InnoDB},\n {Version, {}}, // Autodetect\n {options_, QVariantHash()},\n // Examples\n// {options_, QStringLiteral("MYSQL_OPT_CONNECT_TIMEOUT = 1 ; MYSQL_OPT_READ_TIMEOUT=1")},\n// {options_, QVariantHash {{QStringLiteral("MYSQL_OPT_CONNECT_TIMEOUT"), 1},\n// {QStringLiteral("MYSQL_OPT_READ_TIMEOUT"), 1}}},\n});\n'})})}),(0,i.jsx)(a.A,{value:S.os,label:S.LQ,children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n#include \n\nusing Orm::DB;\n\nusing ConfigUtils = Orm::Utils::Configuration;\n\nusing namespace Orm::Constants; // NOLINT(google-build-using-namespace)\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {driver_, QPSQL},\n {application_name, QStringLiteral("Example application")},\n {host_, qEnvironmentVariable("DB_HOST", H127001)},\n {port_, qEnvironmentVariable("DB_PORT", P5432)},\n {database_, qEnvironmentVariable("DB_DATABASE", EMPTY)},\n {search_path, qEnvironmentVariable("DB_SEARCHPATH", PUBLIC)},\n {username_, qEnvironmentVariable("DB_USERNAME", postgres_)},\n {password_, qEnvironmentVariable("DB_PASSWORD", EMPTY)},\n {charset_, qEnvironmentVariable("DB_CHARSET", UTF8)},\n // SSL-related\n {sslmode_, QStringLiteral("verify-full")},\n {sslcert, QStringLiteral("C:/example/postgres.crt")},\n {sslkey, QStringLiteral("C:/example/postgres.key")},\n {sslrootcert, QStringLiteral("C:/example/root.crt")},\n // Or\n// {options_, ConfigUtils::postgresSslOptions()},\n {timezone_, UTC},\n /* Specifies what time zone all QDateTime-s will have, the overridden default is\n the QTimeZone::UTC, set to the QTimeZone::LocalTime or\n QtTimeZoneType::DontConvert to use the system local time. */\n {qt_timezone, QVariant::fromValue(QTimeZone::UTC)},\n {prefix_, EMPTY},\n {prefix_indexes, false},\n// {isolation_level, QStringLiteral("REPEATABLE READ")}, // Postgres default is READ COMMITTED\n// {synchronous_commit, QStringLiteral("off")}, // Postgres default is on\n // ConnectionFactory provides a default value for this, this is only for reference\n// {dont_drop, QStringList {spatial_ref_sys}},\n {options_, QVariantHash()},\n});\n'})})}),(0,i.jsx)(a.A,{value:S.F4,label:S.Q7,children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n#include \n\nusing Orm::DB;\n\nusing ConfigUtils = Orm::Utils::Configuration;\n\nusing namespace Orm::Constants; // NOLINT(google-build-using-namespace)\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {driver_, QSQLITE},\n {database_, qEnvironmentVariable("DB_DATABASE", "C:/SQLite/example.sqlite3")},\n {foreign_key_constraints, qEnvironmentVariable("DB_FOREIGN_KEYS", "true")},\n {check_database_exists, true},\n /* Specifies what time zone all QDateTime-s will have, the overridden default is\n the QTimeZone::UTC, set to the QTimeZone::LocalTime or\n QtTimeZoneType::DontConvert to use the system local time. */\n {qt_timezone, QVariant::fromValue(QTimeZone::UTC)},\n /* Return a QDateTime/QDate with the correct time zone instead of the QString,\n only works when the qt_timezone isn\'t set to the DontConvert. */\n {return_qdatetime, true},\n {prefix_, EMPTY},\n {prefix_indexes, false},\n});\n'})})}),(0,i.jsx)(a.A,{value:S.Fi,label:S.CW,children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n#include \n\nusing Orm::DB;\n\nusing ConfigUtils = Orm::Utils::Configuration;\n\nusing namespace Orm::Constants; // NOLINT(google-build-using-namespace)\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {driver_, QMYSQL},\n {host_, qEnvironmentVariable("DB_HOST", H127001)},\n {port_, qEnvironmentVariable("DB_PORT", P3306)},\n {database_, qEnvironmentVariable("DB_DATABASE", EMPTY)},\n {username_, qEnvironmentVariable("DB_USERNAME", ROOT)},\n {password_, qEnvironmentVariable("DB_PASSWORD", EMPTY)},\n {charset_, qEnvironmentVariable("DB_CHARSET", UTF8MB4)},\n {collation_, qEnvironmentVariable("DB_COLLATION", UTF8MB4Unicode520ci)},\n // SSL-related\n {ssl_ca, QStringLiteral("C:/maria/data/ca.pem")},\n {ssl_cert, QStringLiteral("C:/maria/data/client-cert.pem")},\n {ssl_key, QStringLiteral("C:/maria/data/client-key.pem")},\n // Or\n// {options, ConfigUtils::mariaSslOptions()},\n {timezone_, TZ00},\n /* Specifies what time zone all QDateTime-s will have, the overridden default is\n the QTimeZone::UTC, set to the QTimeZone::LocalTime or\n QtTimeZoneType::DontConvert to use the system local time. */\n {qt_timezone, QVariant::fromValue(QTimeZone::UTC)},\n {prefix_, EMPTY},\n {prefix_indexes, false},\n {strict_, true},\n// {isolation_level, QStringLiteral("REPEATABLE READ")}, // MariaDB default is REPEATABLE READ for InnoDB\n {engine_, InnoDB},\n {Version, {}}, // Autodetect\n {options_, QVariantHash()},\n // Examples\n// {options_, QStringLiteral("MYSQL_OPT_CONNECT_TIMEOUT = 1 ; MYSQL_OPT_READ_TIMEOUT=1")},\n// {options_, QVariantHash {{QStringLiteral("MYSQL_OPT_CONNECT_TIMEOUT"), 1},\n// {QStringLiteral("MYSQL_OPT_READ_TIMEOUT"), 1}}},\n});\n'})})})]})]}),"\n",(0,i.jsxs)(n.p,{children:["The first argument is configuration hash which is of type ",(0,i.jsx)(n.code,{children:"QVariantHash"})," and the second argument specifies the name of the ",(0,i.jsx)(n.em,{children:"connection"}),", this connection will also be a ",(0,i.jsx)(n.em,{children:"default connection"}),". You can configure multiple database connections at once and choose the needed one before executing SQL query, section ",(0,i.jsx)(n.a,{href:"#using-multiple-database-connections",children:"Using Multiple Database Connections"})," describes how to create and use multiple database connections."]}),"\n",(0,i.jsxs)(n.p,{children:["You may also configure connection options by ",(0,i.jsx)(n.code,{children:"options"})," key as ",(0,i.jsx)(n.code,{children:"QVariantHash"})," or ",(0,i.jsx)(n.code,{children:"QString"}),", you can pass any ",(0,i.jsx)(n.a,{href:"https://doc.qt.io/qt/qsqldatabase.html#setConnectOptions",children:"connection options"})," supported by ",(0,i.jsx)(n.code,{children:"QSqlDatabase"}),"."]}),"\n",(0,i.jsxs)(n.p,{children:["You can also configure ",(0,i.jsx)(n.a,{href:"https://dev.mysql.com/doc/refman/8.4/en/innodb-transaction-isolation-levels.html",children:"Transaction Isolation Levels"})," for MySQL connection with the ",(0,i.jsx)(n.code,{children:"isolation_level"})," configuration option."]}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.code,{children:"version"})," option is relevant only for the MySQL connections and you can save/avoid one database query (select version()) if you provide it manually. On the base of this version will be decided which ",(0,i.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/src/orm/connectors/mysqlconnector.cpp#L154",children:"session variables"})," will be set if strict mode is enabled and whether to use an ",(0,i.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/src/orm/query/grammars/mysqlgrammar.cpp#L36",children:"alias"})," during the ",(0,i.jsx)(n.code,{children:"upsert"})," method call."]}),"\n",(0,i.jsxs)(n.p,{children:["Breaking values are as follows; use an upsert alias on the MySQL >=8.0.19 and remove the ",(0,i.jsx)(n.code,{children:"NO_AUTO_CREATE_USER"})," sql mode on the MySQL >=8.0.11 if the strict mode is enabled."]}),"\n",(0,i.jsx)(n.admonition,{type:"info",children:(0,i.jsxs)(n.p,{children:["A database connection is resolved lazily, which means that the connection configuration is only saved after the ",(0,i.jsx)(n.code,{children:"DB::create"})," method call. The connection will be resolved after you run some query or you can create it using the ",(0,i.jsx)(n.code,{children:"DB::connection"})," method."]})}),"\n",(0,i.jsx)(n.admonition,{type:"tip",children:(0,i.jsxs)(n.p,{children:["You can also use predefined string constants to avoid unnecessary ",(0,i.jsx)(n.code,{children:"QString"})," instantiations, as used in the ",(0,i.jsx)(n.code,{children:"tom"})," migrations ",(0,i.jsx)(n.a,{href:"/building/migrations#string-constants-example",children:"example"}),"."]})}),"\n",(0,i.jsx)(n.admonition,{type:"info",children:(0,i.jsxs)(n.p,{children:["See ",(0,i.jsx)(n.a,{href:"/tinyorm/casts#date-casting-serialization-and-timezones",children:"Date Casting, Serialization & Timezones"})," if you have problems with ",(0,i.jsx)(n.code,{children:"qt_timezone"})," configuration option and ",(0,i.jsx)(n.code,{children:"QTimeZone"})," value on older ",(0,i.jsx)(n.code,{children:"Qt <6.5"})," versions."]})}),"\n",(0,i.jsx)(n.h4,{id:"sqlite-configuration",children:"SQLite Configuration"}),"\n",(0,i.jsxs)(n.p,{children:["SQLite databases are contained within a single file on your filesystem. You can create a new SQLite database using the ",(0,i.jsx)(n.code,{children:"touch"})," command in your terminal: ",(0,i.jsx)(n.code,{children:"touch database.sqlite3"}),". After the database has been created, you may configure SQLite database connection:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {"driver", "QSQLITE"},\n {"database", qEnvironmentVariable("DB_DATABASE", "/absolute/path/to/database.sqlite3")},\n {"foreign_key_constraints", qEnvironmentVariable("DB_FOREIGN_KEYS", "true")},\n {"check_database_exists", true},\n {"prefix", ""},\n});\n'})}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.code,{children:"database"})," configuration value is the absolute path to the database. To enable foreign key constraints for SQLite connections, you should set the ",(0,i.jsx)(n.code,{children:"foreign_key_constraints"})," configuration value to ",(0,i.jsx)(n.code,{children:"true"}),", if this configuration value is not set, then the default of the SQLite driver will be used (currently the default is ",(0,i.jsx)(n.strong,{children:"disabled"}),")."]}),"\n",(0,i.jsxs)(n.p,{children:["If the ",(0,i.jsx)(n.code,{children:"check_database_exists"})," configuration value is set to the ",(0,i.jsx)(n.code,{children:"true"})," value, then the database connection throws an ",(0,i.jsx)(n.code,{children:"Orm::InvalidArgumentError"})," exception, when the SQLite database file doesn't exist. If it is set to the ",(0,i.jsx)(n.code,{children:"false"})," value and the SQLite database file doesn't exist, then it will be created for you by SQLite driver. The default value is ",(0,i.jsx)(n.code,{children:"true"}),"."]}),"\n",(0,i.jsx)(n.h3,{id:"ssl-connections",children:"SSL Connections"}),"\n",(0,i.jsxs)(n.p,{children:["SSL connections are supported for the ",(0,i.jsx)(n.code,{children:"MySQL"})," and ",(0,i.jsx)(n.code,{children:"PostgreSQL"})," databases. They can be set using the ",(0,i.jsx)(n.code,{children:"options"})," configuration option."]}),"\n",(0,i.jsx)(n.admonition,{type:"info",children:(0,i.jsxs)(n.p,{children:["This feature is heavily dependent on the underlying ",(0,i.jsx)(n.code,{children:"QSqlDatabase"})," module. What means that you can pass the same ",(0,i.jsx)(n.a,{href:"https://doc.qt.io/qt/qsqldatabase.html#setConnectOptions",children:"connection options"})," to the ",(0,i.jsx)(n.code,{children:"TinyORM"})," that the ",(0,i.jsx)(n.code,{children:"QSqlDatabase"})," accepts."]})}),"\n",(0,i.jsx)(n.h5,{id:"mysql",children:"MySQL"}),"\n",(0,i.jsxs)(n.p,{children:["You have to pass the ",(0,i.jsx)(n.code,{children:"SSL_CA"}),", ",(0,i.jsx)(n.code,{children:"SSL_CERT"}),", ",(0,i.jsx)(n.code,{children:"SSL_KEY"}),", and ",(0,i.jsx)(n.code,{children:"MYSQL_OPT_SSL_MODE"})," options."]}),"\n",(0,i.jsxs)(s.A,{groupId:S.vf,children:[(0,i.jsx)(a.A,{value:S.b,label:S.ux,children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\nusing Orm::DB;\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {"driver", "QMYSQL"},\n {"host", qEnvironmentVariable("DB_MYSQL_HOST", "127.0.0.1")},\n ...\n // highlight-start\n {"options", QVariantHash({{"SSL_CA", "C:/mysql/data/ca.pem"},\n {"SSL_CERT", "C:/mysql/data/client-cert.pem"},\n {"SSL_KEY", "C:/mysql/data/client-key.pem"},\n {"MYSQL_OPT_SSL_MODE", "VERIFY_CA"}})},\n // highlight-end\n});\n'})})}),(0,i.jsx)(a.A,{value:S.xj,label:S.gg,children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\nusing Orm::DB;\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {"driver", "QMYSQL"},\n {"host", qEnvironmentVariable("DB_MYSQL_HOST", "127.0.0.1")},\n ...\n // highlight-start\n {"options", QVariantHash({{"SSL_CA", "~/.local/share/TinyORM/ssl/ca.pem"},\n {"SSL_CERT", "~/.local/share/TinyORM/ssl/client-cert.pem"},\n {"SSL_KEY", "~/.local/share/TinyORM/ssl/client-key.pem"},\n {"MYSQL_OPT_SSL_MODE", "VERIFY_CA"}})},\n // highlight-end\n});\n'})})})]}),"\n",(0,i.jsxs)(n.p,{children:["You may also use the ",(0,i.jsx)(n.code,{children:"ConfigUtils::mysqlSslOptions()"})," or the ",(0,i.jsx)(n.code,{children:"ConfigUtils::insertMySqlSslOptions()"})," methods to insert these options for you and define them using the ",(0,i.jsx)(n.code,{children:"DB_MYSQL_SSL_CA"}),", ",(0,i.jsx)(n.code,{children:"DB_MYSQL_SSL_CERT"}),", ",(0,i.jsx)(n.code,{children:"DB_MYSQL_SSL_KEY"}),", and ",(0,i.jsx)(n.code,{children:"DB_MYSQL_SSL_MODE"})," environment variables."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n#include \n\nusing Orm::DB;\n\nusing ConfigUtils = Orm::Utils::Configuration;\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {"driver", "QMYSQL"},\n {"host", qEnvironmentVariable("DB_MYSQL_HOST", "127.0.0.1")},\n ...\n // highlight-next-line\n {"options", ConfigUtils::mysqlSslOptions()},\n});\n'})}),"\n",(0,i.jsxs)(n.p,{children:["You can define these SSL-related options in the top-level configuration, they will be copied to the ",(0,i.jsx)(n.code,{children:"options"})," option hash during configuration parsing. The top-level configuration takes precedence and overwrites the options in the ",(0,i.jsx)(n.code,{children:"options"})," hash."]}),"\n",(0,i.jsxs)(s.A,{groupId:S.vf,children:[(0,i.jsx)(a.A,{value:S.b,label:S.ux,children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\nusing Orm::DB;\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {"driver", "QMYSQL"},\n {"host", qEnvironmentVariable("DB_MYSQL_HOST", "127.0.0.1")},\n ...\n // highlight-start\n {"SSL_CA", "C:/mysql/data/ca.pem"},\n {"SSL_CERT", "C:/mysql/data/client-cert.pem"},\n {"SSL_KEY", "C:/mysql/data/client-key.pem"},\n {"MYSQL_OPT_SSL_MODE", "VERIFY_CA"},\n // highlight-end\n});\n'})})}),(0,i.jsx)(a.A,{value:S.xj,label:S.gg,children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\nusing Orm::DB;\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {"driver", "QMYSQL"},\n {"host", qEnvironmentVariable("DB_MYSQL_HOST", "127.0.0.1")},\n ...\n // highlight-start\n {"SSL_CA", "~/.local/share/TinyORM/ssl/ca.pem"},\n {"SSL_CERT", "~/.local/share/TinyORM/ssl/client-cert.pem"},\n {"SSL_KEY", "~/.local/share/TinyORM/ssl/client-key.pem"},\n {"MYSQL_OPT_SSL_MODE", "VERIFY_CA"},\n // highlight-end\n});\n'})})})]}),"\n",(0,i.jsx)(n.admonition,{type:"tip",children:(0,i.jsxs)(n.p,{children:["You can take a look at the GitHub actions how the ",(0,i.jsx)(n.code,{children:"MySQL"})," certificates are generated in the CI pipeline for ",(0,i.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/.github/workflows/msvc2022-qt6.yml",children:"Windows"})," and ",(0,i.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/.github/workflows/linux-qt6.yml",children:"Linux"}),"."]})}),"\n",(0,i.jsx)(n.admonition,{type:"tip",children:(0,i.jsxs)(n.p,{children:["You can also pass the ",(0,i.jsx)(n.code,{children:"QString"})," to the ",(0,i.jsx)(n.code,{children:"options"})," configuration separated by the ",(0,i.jsx)(n.code,{children:";"})," semicolon character and use the ",(0,i.jsx)(n.code,{children:"="})," to assign values."]})}),"\n",(0,i.jsx)(n.h5,{id:"postgresql",children:"PostgreSQL"}),"\n",(0,i.jsxs)(n.p,{children:["You have to pass the ",(0,i.jsx)(n.a,{href:"https://www.postgresql.org/docs/current/libpq-ssl.html#LIBPQ-SSL-SSLMODE-STATEMENTS",children:(0,i.jsx)(n.code,{children:"sslmode"})})," or the deprecated ",(0,i.jsx)(n.a,{href:"https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNECT-REQUIRESSL",children:(0,i.jsx)(n.code,{children:"requiressl"})})," options."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\nusing Orm::DB;\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {"driver", "QPSQL"},\n {"host", qEnvironmentVariable("DB_PGSQL_HOST", "127.0.0.1")},\n ...\n // highlight-next-line\n {"options", QVariantHash({{"sslmode", "verify-full"}})},\n});\n'})}),"\n",(0,i.jsxs)(n.p,{children:["And place your ",(0,i.jsx)(n.strong,{children:"client"})," certificates to the ",(0,i.jsx)(n.code,{children:"~/.postgres/"})," on ",(0,i.jsx)(n.a,{href:"https://www.postgresql.org/docs/current/libpq-ssl.html#LIBPQ-SSL-FILE-USAGE",children:"Linux"})," and ",(0,i.jsx)(n.code,{children:"$env:APPDATA/postgres/"})," on Windows. Everything is described in the PostgreSQL's ",(0,i.jsx)(n.a,{href:"https://www.postgresql.org/docs/current/libpq-ssl.html",children:"libpq client"})," and ",(0,i.jsx)(n.a,{href:"https://www.postgresql.org/docs/15/ssl-tcp.html#SSL-FILE-USAGE",children:"server"})," documentation."]}),"\n",(0,i.jsxs)(n.p,{children:["If you want to keep your ",(0,i.jsx)(n.strong,{children:"client"})," certificates in your own location, you can set the ",(0,i.jsx)(n.code,{children:"sslcert"}),", ",(0,i.jsx)(n.code,{children:"sslkey"}),", and ",(0,i.jsx)(n.code,{children:"sslrootcert"})," options."]}),"\n",(0,i.jsxs)(s.A,{groupId:S.vf,children:[(0,i.jsx)(a.A,{value:S.b,label:S.ux,children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\nusing Orm::DB;\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {"driver", "QPSQL"},\n {"host", qEnvironmentVariable("DB_PGSQL_HOST", "127.0.0.1")},\n ...\n // highlight-next-line\n {"options", QVariantHash({{"sslmode", "verify-full"},\n // highlight-start\n {"sslcert", "C:/example/postgres.crt"},\n {"sslkey", "C:/example/postgres.key"},\n {"sslrootcert", "C:/example/root.crt"}})},\n // highlight-end\n});\n'})})}),(0,i.jsx)(a.A,{value:S.xj,label:S.gg,children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\nusing Orm::DB;\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {"driver", "QPSQL"},\n {"host", qEnvironmentVariable("DB_PGSQL_HOST", "127.0.0.1")},\n ...\n // highlight-next-line\n {"options", QVariantHash({{"sslmode", "verify-full"},\n // highlight-start\n {"sslcert", "/example/postgres.crt"},\n {"sslkey", "/example/postgres.key"},\n {"sslrootcert", "/example/root.crt"}})},\n // highlight-end\n});\n'})})})]}),"\n",(0,i.jsxs)(n.p,{children:["You can define these SSL-related options in the top-level configuration, they will be copied to the ",(0,i.jsx)(n.code,{children:"options"})," option hash during a configuration parsing. The top-level configuration takes precedence and overwrites the options in the ",(0,i.jsx)(n.code,{children:"options"})," hash."]}),"\n",(0,i.jsxs)(s.A,{groupId:S.vf,children:[(0,i.jsx)(a.A,{value:S.b,label:S.ux,children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\nusing Orm::DB;\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {"driver", "QPSQL"},\n {"host", qEnvironmentVariable("DB_PGSQL_HOST", "127.0.0.1")},\n ...\n // highlight-start\n {"sslmode", "verify-full"},\n {"sslcert", "C:/example/postgres.crt"},\n {"sslkey", "C:/example/postgres.key"},\n {"sslrootcert", "C:/example/root.crt"},\n // highlight-end\n});\n'})})}),(0,i.jsx)(a.A,{value:S.xj,label:S.gg,children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\nusing Orm::DB;\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {"driver", "QPSQL"},\n {"host", qEnvironmentVariable("DB_PGSQL_HOST", "127.0.0.1")},\n ...\n // highlight-start\n {"sslmode", "verify-full"},\n {"sslcert", "/example/postgres.crt"},\n {"sslkey", "/example/postgres.key"},\n {"sslrootcert", "/example/root.crt"},\n // highlight-end\n});\n'})})})]}),"\n",(0,i.jsxs)(n.p,{children:["You may also use the ",(0,i.jsx)(n.code,{children:"ConfigUtils::postgresSslOptions()"})," or the ",(0,i.jsx)(n.code,{children:"ConfigUtils::insertPostgresSslOptions()"})," methods to insert the ",(0,i.jsx)(n.code,{children:"sslmode"}),", ",(0,i.jsx)(n.code,{children:"sslcert"}),", ",(0,i.jsx)(n.code,{children:"sslkey"}),", and ",(0,i.jsx)(n.code,{children:"sslrootcert"})," options for you and define them using the ",(0,i.jsx)(n.code,{children:"DB_PGSQL_SSLMODE"}),", ",(0,i.jsx)(n.code,{children:"DB_PGSQL_SSLCERT"}),", ",(0,i.jsx)(n.code,{children:"DB_PGSQL_SSLKEY"}),", and ",(0,i.jsx)(n.code,{children:"DB_PGSQL_SSLROOTCERT"})," environment variable."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n#include \n\nusing Orm::DB;\n\nusing ConfigUtils = Orm::Utils::Configuration;\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {"driver", "QPSQL"},\n {"host", qEnvironmentVariable("DB_PGSQL_HOST", "127.0.0.1")},\n ...\n // highlight-next-line\n {"options", ConfigUtils::postgresSslOptions()},\n});\n'})}),"\n",(0,i.jsx)(n.admonition,{type:"info",children:(0,i.jsxs)(n.p,{children:["The PostgreSQL's libpq client library provides the ",(0,i.jsx)(n.a,{href:"https://www.postgresql.org/docs/current/libpq-envars.html#id-1.7.3.22.3.4.13.1.1",children:(0,i.jsx)(n.code,{children:"PGSSLMODE"})}),", ",(0,i.jsx)(n.a,{href:"https://www.postgresql.org/docs/current/libpq-envars.html#id-1.7.3.22.3.4.16.1.1",children:(0,i.jsx)(n.code,{children:"PGSSLCERT"})}),", ",(0,i.jsx)(n.a,{href:"https://www.postgresql.org/docs/current/libpq-envars.html#id-1.7.3.22.3.4.17.1.1",children:(0,i.jsx)(n.code,{children:"PGSSLKEY"})}),", and ",(0,i.jsx)(n.a,{href:"https://www.postgresql.org/docs/current/libpq-envars.html#id-1.7.3.22.3.4.18.1.1",children:(0,i.jsx)(n.code,{children:"PGSSLROOTCERT"})})," environment variables, so you don't have to use TinyORM's ",(0,i.jsx)(n.code,{children:"options"})," configuration and may use these environment variables instead."]})}),"\n",(0,i.jsx)(n.admonition,{type:"tip",children:(0,i.jsxs)(n.p,{children:["You can take a look at the GitHub actions how the ",(0,i.jsx)(n.code,{children:"PostgreSQL"})," certificates are generated in the CI pipeline for ",(0,i.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/.github/workflows/msvc2022-qt6.yml",children:"Windows"})," and ",(0,i.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/.github/workflows/linux-qt6.yml",children:"Linux"}),"."]})}),"\n",(0,i.jsx)(n.h2,{id:"running-sql-queries",children:"Running SQL Queries"}),"\n",(0,i.jsxs)(n.p,{children:["Once you have configured your database connection, you may run queries using the ",(0,i.jsx)(n.code,{children:"DB"})," facade. The ",(0,i.jsx)(n.code,{children:"DB"})," facade provides methods for each type of query: ",(0,i.jsx)(n.code,{children:"select"}),", ",(0,i.jsx)(n.code,{children:"update"}),", ",(0,i.jsx)(n.code,{children:"insert"}),", ",(0,i.jsx)(n.code,{children:"delete"}),", and ",(0,i.jsx)(n.code,{children:"statement"}),"."]}),"\n",(0,i.jsx)(n.h4,{id:"running-a-select-query",children:"Running A Select Query"}),"\n",(0,i.jsxs)(n.p,{children:["To run a basic SELECT query, you may use the ",(0,i.jsx)(n.code,{children:"select"})," method on the ",(0,i.jsx)(n.code,{children:"DB"})," facade:"]}),"\n",(0,i.jsxs)(n.p,{children:['auto users = DB::select("select * from users where active = ?", ',1,");"]}),"\n",(0,i.jsxs)(n.p,{children:["The first argument passed to the ",(0,i.jsx)(n.code,{children:"select"})," method is the SQL query, while the second argument is any parameter bindings that need to be bound to the query. Typically, these are the values of the ",(0,i.jsx)(n.code,{children:"where"})," clause constraints. Parameter binding provides protection against SQL injection."]}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.code,{children:"select"})," method returns a ",(0,i.jsx)(n.code,{children:"QSqlQuery"})," containing the results of the query, where each result can be accessed by ",(0,i.jsx)(n.code,{children:"QSqlQuery::next"})," method. Look into the ",(0,i.jsx)(n.code,{children:"QSqlQuery"}),' documentation on how to obtain results from the "query". You may access each column\'s value by ',(0,i.jsx)(n.code,{children:"QSqlQuery::value"})," method. The first ",(0,i.jsx)(n.code,{children:"bool"})," return value is the value returned from ",(0,i.jsx)(n.code,{children:"QSqlQuery::exec"})," method:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\n#include \n\nauto users = DB::select("select * from users");\n\nwhile(users.next())\n qDebug() << users.value("name").toString();\n'})}),"\n",(0,i.jsx)(n.h4,{id:"selecting-scalar-values",children:"Selecting Scalar Values"}),"\n",(0,i.jsxs)(n.p,{children:["Sometimes your database query may result in a single, scalar value. Instead of being required to retrieve the query's scalar result from a record instance, TinyORM allows you to retrieve this value directly using the ",(0,i.jsx)(n.code,{children:"scalar"})," shortcut method:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\nauto states = DB::scalar(\n "select count(case when state = \'pending\' then 1 end) as states "\n "from comments"\n);\n\n// With binding\nauto states = DB::scalar(\n "select count(case when state = ? then 1 end) as states from comments",\n {"pending"}\n);\n'})}),"\n",(0,i.jsx)(n.h4,{id:"running-an-insert-statement",children:"Running An Insert Statement"}),"\n",(0,i.jsxs)(n.p,{children:["To execute an ",(0,i.jsx)(n.code,{children:"insert"})," statement, you may use the ",(0,i.jsx)(n.code,{children:"insert"})," method on the ",(0,i.jsx)(n.code,{children:"DB"})," facade. Like ",(0,i.jsx)(n.code,{children:"select"}),", this method accepts the SQL query as its first argument and bindings as its second argument and returns ",(0,i.jsx)(n.code,{children:"QSqlQuery"}),":"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\nDB::insert("insert into users (id, name) values (?, ?)", {1, "Marc"});\n'})}),"\n",(0,i.jsx)(n.h4,{id:"running-an-update-statement",children:"Running An Update Statement"}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.code,{children:"update"})," method should be used to update existing records in the database. The number of rows affected by the statement and ",(0,i.jsx)(n.code,{children:"QSqlQuery"})," is returned by the method as ",(0,i.jsx)(n.code,{children:"std::tuple"}),":"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\n#include \n\nauto [affected, query] = DB::update(\n "update users set updated_at = ? where name = ?",\n {QDateTime::currentDateTimeUtc(), "Anita"}\n);\n\nif (!affected)\n qDebug() << "Any record was updated.";\n'})}),"\n",(0,i.jsx)(n.h4,{id:"running-a-delete-statement",children:"Running A Delete Statement"}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.code,{children:"remove"})," method should be used to delete records from the database. Like ",(0,i.jsx)(n.code,{children:"update"}),", the number of affected rows and ",(0,i.jsx)(n.code,{children:"QSqlQuery"})," will be returned by the method as ",(0,i.jsx)(n.code,{children:"std::tuple"}),":"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\nauto [affected, query] = DB::remove("delete from users");\n'})}),"\n",(0,i.jsx)(n.admonition,{type:"note",children:(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.code,{children:"delete"})," can not be used as the method name because it is the reserved word."]})}),"\n",(0,i.jsx)(n.h4,{id:"running-a-general-statement",children:"Running A General Statement"}),"\n",(0,i.jsxs)(n.p,{children:["Some database statements do not return any value. For these types of operations, you may use the ",(0,i.jsx)(n.code,{children:"statement"})," method on the ",(0,i.jsx)(n.code,{children:"DB"})," facade:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'DB::statement("drop table users");\n'})}),"\n",(0,i.jsx)(n.admonition,{type:"tip",children:(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.code,{children:"DB::statement"})," method should be used for ",(0,i.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/Data_definition_language",children:"DDL"}),' queries, don\'t use it for "select" queries because it internally calls ',(0,i.jsx)(n.code,{children:"recordsHaveBeenModified"})," method."]})}),"\n",(0,i.jsx)(n.h4,{id:"running-an-unprepared-statement",children:"Running An Unprepared Statement"}),"\n",(0,i.jsxs)(n.p,{children:["Sometimes you may want to execute an SQL statement without binding any values. You may use the ",(0,i.jsx)(n.code,{children:"DB"})," facade's ",(0,i.jsx)(n.code,{children:"unprepared"})," method to accomplish this:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:"DB::unprepared(\"update users set votes = 100 where name = 'Dries'\");\n"})}),"\n",(0,i.jsx)(n.admonition,{type:"warning",children:(0,i.jsx)(n.p,{children:"Since unprepared statements do not bind parameters, they may be vulnerable to SQL injection. You should never allow user controlled values within an unprepared statement."})}),"\n",(0,i.jsx)(n.h4,{id:"implicit-commits",children:"Implicit Commits"}),"\n",(0,i.jsxs)(n.p,{children:["When using the ",(0,i.jsx)(n.code,{children:"DB"})," facade's ",(0,i.jsx)(n.code,{children:"statement"})," methods within transactions, you must be careful to avoid statements that cause ",(0,i.jsx)(n.a,{href:"https://dev.mysql.com/doc/refman/8.4/en/implicit-commit.html",children:"implicit commits"}),". These statements will cause the database engine to indirectly commit the entire transaction, leaving TinyORM unaware of the database's transaction level. An example of such a statement is creating a database table:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'DB::statement("create table users (name varchar(255) null)");\n'})}),"\n",(0,i.jsxs)(n.p,{children:["Please refer to the MySQL manual for ",(0,i.jsx)(n.a,{href:"https://dev.mysql.com/doc/refman/8.4/en/implicit-commit.html",children:"a list of all statements"})," that trigger implicit commits."]}),"\n",(0,i.jsx)(n.h3,{id:"using-multiple-database-connections",children:"Using Multiple Database Connections"}),"\n",(0,i.jsxs)(n.p,{children:["You can configure multiple database connections at once during ",(0,i.jsx)(n.code,{children:"DatabaseManager"})," instantiation using the ",(0,i.jsx)(n.code,{children:"DB::create"})," overload, where the first argument is a hash of multiple connections and is of type ",(0,i.jsx)(n.code,{children:"QHash"})," and the second argument is the name of the default connection:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {"mysql", {\n {"driver", "QMYSQL"},\n {"host", qEnvironmentVariable("DB_MYSQL_HOST", "127.0.0.1")},\n {"port", qEnvironmentVariable("DB_MYSQL_PORT", "3306")},\n {"database", qEnvironmentVariable("DB_MYSQL_DATABASE", "")},\n {"username", qEnvironmentVariable("DB_MYSQL_USERNAME", "root")},\n {"password", qEnvironmentVariable("DB_MYSQL_PASSWORD", "")},\n {"charset", qEnvironmentVariable("DB_MYSQL_CHARSET", "utf8mb4")},\n {"collation", qEnvironmentVariable("DB_MYSQL_COLLATION", "utf8mb4_0900_ai_ci")},\n {"strict", true},\n {"options", QVariantHash()},\n }},\n {"sqlite", {\n {"driver", "QSQLITE"},\n {"database", qEnvironmentVariable("DB_SQLITE_DATABASE", "")},\n {"foreign_key_constraints", qEnvironmentVariable("DB_SQLITE_FOREIGN_KEYS", "true")},\n {"check_database_exists", true},\n {"prefix", ""},\n }},\n}, "mysql");\n'})}),"\n",(0,i.jsxs)(n.p,{children:["If your application needs to use multiple connections, you may access each connection via the ",(0,i.jsx)(n.code,{children:"connection"})," method provided by the ",(0,i.jsx)(n.code,{children:"DB"})," facade. The connection name passed to the ",(0,i.jsx)(n.code,{children:"connection"})," method should correspond to one of the connections key listed in your configuration:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\nauto query = DB::connection("mysql_test").select(...);\n'})}),"\n",(0,i.jsxs)(n.p,{children:["You may access the raw underlying ",(0,i.jsx)(n.code,{children:"QSqlQuery"})," instance of a connection using the ",(0,i.jsx)(n.code,{children:"getQtQuery"})," method on a connection instance:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:"auto query = DB::connection().getQtQuery();\n"})}),"\n",(0,i.jsxs)(n.p,{children:["Or you can use the shortcut method ",(0,i.jsx)(n.code,{children:"qtQuery"})," provided by the ",(0,i.jsx)(n.code,{children:"DB"})," facade:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:"auto query = DB::qtQuery();\n"})}),"\n",(0,i.jsx)(n.h2,{id:"database-transactions",children:"Database Transactions"}),"\n",(0,i.jsx)(n.h4,{id:"manually-using-transactions",children:"Manually Using Transactions"}),"\n",(0,i.jsxs)(n.p,{children:["If you would like to begin a transaction manually and have complete control over rollbacks and commits, you may use the ",(0,i.jsx)(n.code,{children:"beginTransaction"})," method provided by the ",(0,i.jsx)(n.code,{children:"DB"})," facade:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:"#include \n\nDB::beginTransaction();\n"})}),"\n",(0,i.jsxs)(n.p,{children:["You can rollback the transaction via the ",(0,i.jsx)(n.code,{children:"rollBack"})," method:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:"DB::rollBack();\n"})}),"\n",(0,i.jsxs)(n.p,{children:["Lastly, you can commit a transaction via the ",(0,i.jsx)(n.code,{children:"commit"})," method:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:"DB::commit();\n"})}),"\n",(0,i.jsx)(n.p,{children:"All transaction methods accept a connection name as the optional argument:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'DB::beginTransaction("mysql_test");\n'})}),"\n",(0,i.jsx)(n.admonition,{type:"tip",children:(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.code,{children:"DB"})," facade's transaction methods control the transactions for both the ",(0,i.jsx)(n.a,{href:"/database/query-builder",children:"query builder"})," and ",(0,i.jsx)(n.a,{href:"/tinyorm/getting-started",children:"TinyORM"}),"."]})}),"\n",(0,i.jsx)(n.h2,{id:"multi-threading-support",children:"Multi-threading support"}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.code,{children:"TinyORM"})," supports multi-threading for the ",(0,i.jsx)(n.code,{children:"MSVC"})," and ",(0,i.jsx)(n.code,{children:"GCC"})," on Linux compilers. Multi-threading is disabled for the ",(0,i.jsx)(n.code,{children:"Clang <14.0.3"})," compiler on MSYS2, ",(0,i.jsx)(n.code,{children:"Clang <14.0.4"})," on Linux and for the ",(0,i.jsx)(n.code,{children:"GCC"})," compiler on MSYS2. The reason are bugs in the ",(0,i.jsx)(n.code,{children:"TLS"})," wrapper that is generated by the ",(0,i.jsx)(n.a,{href:"https://en.cppreference.com/w/cpp/keyword/thread_local",children:(0,i.jsx)(n.code,{children:"thread_local"})})," keyword."]}),"\n",(0,i.jsx)(n.p,{children:"A connection can only be used from within the thread that created it. Moving connections between threads or creating queries from a different thread where the connection was created is not supported."}),"\n",(0,i.jsxs)(n.p,{children:["In addition, the third party libraries used by the ",(0,i.jsx)(n.code,{children:"QSqlDrivers"})," can impose further restrictions on using the SQL Module in a multithreaded program."]}),"\n",(0,i.jsxs)(n.p,{children:["In short, if you create a ",(0,i.jsx)(n.code,{children:"DB::connection"})," in some thread then you have to use this connection only from this particular thread and of course all queries that will be executed on this connection."]}),"\n",(0,i.jsx)(n.p,{children:"If you want to execute some query from another thread for the same connection then you have to create a new connection first and if you have a new connection you can send a query from this new thread to the database."}),"\n",(0,i.jsx)(n.admonition,{type:"warning",children:(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.a,{href:"/database/migrations#tables",children:(0,i.jsx)(n.code,{children:"schema builder"})})," and ",(0,i.jsx)(n.a,{href:"/database/migrations",children:(0,i.jsx)(n.code,{children:"migrations"})})," don't support multi-threading."]})})]})}function L(e={}){const{wrapper:n}={...(0,r.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(D,{...e})}):D(e)}},9365:(e,n,t)=>{t.d(n,{A:()=>s});t(6540);var i=t(4164);const r={tabItem:"tabItem_Ymn6"};var a=t(4848);function s(e){let{children:n,hidden:t,className:s}=e;return(0,a.jsx)("div",{role:"tabpanel",className:(0,i.A)(r.tabItem,s),hidden:t,children:n})}},1470:(e,n,t)=>{t.d(n,{A:()=>v});var i=t(6540),r=t(4164),a=t(3104),s=t(6347),o=t(205),l=t(7485),c=t(1682),d=t(679);function h(e){return i.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,i.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function u(e){const{values:n,children:t}=e;return(0,i.useMemo)((()=>{const e=n??function(e){return h(e).map((e=>{let{props:{value:n,label:t,attributes:i,default:r}}=e;return{value:n,label:t,attributes:i,default:r}}))}(t);return function(e){const n=(0,c.X)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[n,t])}function m(e){let{value:n,tabValues:t}=e;return t.some((e=>e.value===n))}function p(e){let{queryString:n=!1,groupId:t}=e;const r=(0,s.W6)(),a=function(e){let{queryString:n=!1,groupId:t}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!t)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return t??null}({queryString:n,groupId:t});return[(0,l.aZ)(a),(0,i.useCallback)((e=>{if(!a)return;const n=new URLSearchParams(r.location.search);n.set(a,e),r.replace({...r.location,search:n.toString()})}),[a,r])]}function g(e){const{defaultValue:n,queryString:t=!1,groupId:r}=e,a=u(e),[s,l]=(0,i.useState)((()=>function(e){let{defaultValue:n,tabValues:t}=e;if(0===t.length)throw new Error("Docusaurus error: the component requires at least one children component");if(n){if(!m({value:n,tabValues:t}))throw new Error(`Docusaurus error: The has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${t.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}const i=t.find((e=>e.default))??t[0];if(!i)throw new Error("Unexpected error: 0 tabValues");return i.value}({defaultValue:n,tabValues:a}))),[c,h]=p({queryString:t,groupId:r}),[g,x]=function(e){let{groupId:n}=e;const t=function(e){return e?`docusaurus.tab.${e}`:null}(n),[r,a]=(0,d.Dv)(t);return[r,(0,i.useCallback)((e=>{t&&a.set(e)}),[t,a])]}({groupId:r}),f=(()=>{const e=c??g;return m({value:e,tabValues:a})?e:null})();(0,o.A)((()=>{f&&l(f)}),[f]);return{selectedValue:s,selectValue:(0,i.useCallback)((e=>{if(!m({value:e,tabValues:a}))throw new Error(`Can't select invalid tab value=${e}`);l(e),h(e),x(e)}),[h,x,a]),tabValues:a}}var x=t(2303);const f={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var b=t(4848);function j(e){let{className:n,block:t,selectedValue:i,selectValue:s,tabValues:o}=e;const l=[],{blockElementScrollPositionUntilNextRender:c}=(0,a.a_)(),d=e=>{const n=e.currentTarget,t=l.indexOf(n),r=o[t].value;r!==i&&(c(n),s(r))},h=e=>{let n=null;switch(e.key){case"Enter":d(e);break;case"ArrowRight":{const t=l.indexOf(e.currentTarget)+1;n=l[t]??l[0];break}case"ArrowLeft":{const t=l.indexOf(e.currentTarget)-1;n=l[t]??l[l.length-1];break}}n?.focus()};return(0,b.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,r.A)("tabs",{"tabs--block":t},n),children:o.map((e=>{let{value:n,label:t,attributes:a}=e;return(0,b.jsx)("li",{role:"tab",tabIndex:i===n?0:-1,"aria-selected":i===n,ref:e=>l.push(e),onKeyDown:h,onClick:d,...a,className:(0,r.A)("tabs__item",f.tabItem,a?.className,{"tabs__item--active":i===n}),children:t??n},n)}))})}function S(e){let{lazy:n,children:t,selectedValue:r}=e;const a=(Array.isArray(t)?t:[t]).filter(Boolean);if(n){const e=a.find((e=>e.props.value===r));return e?(0,i.cloneElement)(e,{className:"margin-top--md"}):null}return(0,b.jsx)("div",{className:"margin-top--md",children:a.map(((e,n)=>(0,i.cloneElement)(e,{key:n,hidden:e.props.value!==r})))})}function y(e){const n=g(e);return(0,b.jsxs)("div",{className:(0,r.A)("tabs-container",f.tabList),children:[(0,b.jsx)(j,{...n,...e}),(0,b.jsx)(S,{...n,...e})]})}function v(e){const n=(0,x.A)();return(0,b.jsx)(y,{...e,children:h(e.children)},String(n))}},7324:(e,n,t)=>{t.d(n,{$E:()=>x,A3:()=>b,CW:()=>f,Dx:()=>d,F4:()=>u,Fi:()=>c,J_:()=>v,LQ:()=>j,Lf:()=>_,OO:()=>r,Q7:()=>S,b:()=>o,cy:()=>l,gg:()=>p,kl:()=>m,os:()=>h,pW:()=>a,ux:()=>g,vf:()=>i,xj:()=>s,xt:()=>y});const i="shell",r="database",a="application",s="bash",o="pwsh",l="zsh",c="maria",d="mysql",h="postgres",u="sqlite",m="application",p="bash",g="pwsh",x="zsh",f="MariaDB",b="MySQL",j="PostgreSQL",S="SQLite",y="tinyorm.org",v="$HOME/Code/c/",_="$env:USERPROFILE\\Code\\c\\"},8453:(e,n,t)=>{t.d(n,{R:()=>s,x:()=>o});var i=t(6540);const r={},a=i.createContext(r);function s(e){const n=i.useContext(a);return i.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function o(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:s(e.components),i.createElement(a.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/ba3d4959.73f88dfd.js b/assets/js/ba3d4959.73f88dfd.js deleted file mode 100644 index dfee75cde..000000000 --- a/assets/js/ba3d4959.73f88dfd.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunktinyorm_org=self.webpackChunktinyorm_org||[]).push([[170],{2512:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>Q,contentTitle:()=>v,default:()=>L,frontMatter:()=>y,metadata:()=>_,toc:()=>T});var i=t(4848),r=t(8453),a=t(9365),s=t(1470),o=t(6540);let l=null,c=null;function d(){const[e,n]=(0,o.useState)(!1),[t,r]=(0,o.useState)(!1);return(0,o.useEffect)((()=>(h(),()=>{h()})),[]),(0,i.jsxs)("form",{children:[(0,i.jsxs)("div",{className:"tinyorm-configuration-row",children:[(0,i.jsx)("label",{htmlFor:"tinyorm-toggle-full",children:"Full configuration"}),(0,i.jsx)("input",{id:"tinyorm-toggle-full",type:"checkbox",checked:e,onChange:()=>n(u),title:"Show full configuration example"})]}),(0,i.jsxs)("div",{className:"tinyorm-configuration-row",children:[(0,i.jsx)("label",{htmlFor:"tinyorm-toggle-prefix",children:"Prefix environment"}),(0,i.jsx)("input",{id:"tinyorm-toggle-prefix",type:"checkbox",checked:t,onChange:()=>r(j),title:"Prefix all environment variables by the database type"})]})]})}function h(){l=null,c=null}function u(e){const n=l||(l=document.getElementById("tinyorm-configuration")?.querySelectorAll(".tabs-container")),t=!e;if(!n)return e;if(2!==n.length)throw new Error(`Wrong number of .tabs-container (!== 2) found in the toggle configuration feature, found ${n.length}.`);return t?(n[0].style.display="none",n[1].style.display="block"):(n[0].style.display="block",n[1].style.display="none"),!e}const p='"DB_',m="MARIA",g="MYSQL",x="PGSQL",f="SQLITE",b=[g,x,f,m,g,x,f,m];function j(e){const n=function(){if(c)return c;const e=document.getElementById("tinyorm-configuration")?.querySelectorAll(".prism-code > code");if(!e)return e;if(8!==e.length)throw new Error(`Wrong number of .prism-code > code (!== 8) found in the prefix env. feature, found ${e.length}.`);return c=new Array(e.length),e.forEach(((e,n)=>{c[n]=Array.from(e.querySelectorAll(".token.string")).filter((e=>e?.textContent.startsWith(p)))})),c}(),t=!e;return n?(n.forEach(((e,n)=>{e.forEach((e=>{const i=e.textContent,r=`${p}${b[n]}_`;if(t)e.textContent=`${r}${i.substring(4)}`;else{if(!i.startsWith(r))throw new Error(`Token doesn't start with the '${r}' prefix.`);e.textContent=`${p}${i.substring(r.length)}`}}))})),!e):e}var S=t(7324);const y={sidebar_position:0,sidebar_label:"Getting Started",description:"TinyORM makes interacting with a database extremely simple using raw SQL, a fluent query builder, and the TinyORM. It provides first-party support for four databases MySQL/MariaDB, PostgreSQL, and SQLite.",keywords:["c++ orm","database","getting started","tinyorm"]},v="Database: Getting Started",_={id:"database/getting-started",title:"Database: Getting Started",description:"TinyORM makes interacting with a database extremely simple using raw SQL, a fluent query builder, and the TinyORM. It provides first-party support for four databases MySQL/MariaDB, PostgreSQL, and SQLite.",source:"@site/docs/database/getting-started.mdx",sourceDirName:"database",slug:"/database/getting-started",permalink:"/database/getting-started",draft:!1,unlisted:!1,tags:[],version:"current",sidebarPosition:0,frontMatter:{sidebar_position:0,sidebar_label:"Getting Started",description:"TinyORM makes interacting with a database extremely simple using raw SQL, a fluent query builder, and the TinyORM. It provides first-party support for four databases MySQL/MariaDB, PostgreSQL, and SQLite.",keywords:["c++ orm","database","getting started","tinyorm"]},sidebar:"tinyormSidebar",previous:{title:"\ud83d\ude80 Supported Compilers",permalink:"/supported-compilers"},next:{title:"Query Builder",permalink:"/database/query-builder"}},Q={},T=[{value:"Introduction",id:"introduction",level:2},{value:"Configuration",id:"configuration",level:3},{value:"SQLite Configuration",id:"sqlite-configuration",level:4},{value:"SSL Connections",id:"ssl-connections",level:3},{value:"MySQL",id:"mysql",level:5},{value:"PostgreSQL",id:"postgresql",level:5},{value:"Running SQL Queries",id:"running-sql-queries",level:2},{value:"Running A Select Query",id:"running-a-select-query",level:4},{value:"Selecting Scalar Values",id:"selecting-scalar-values",level:4},{value:"Running An Insert Statement",id:"running-an-insert-statement",level:4},{value:"Running An Update Statement",id:"running-an-update-statement",level:4},{value:"Running A Delete Statement",id:"running-a-delete-statement",level:4},{value:"Running A General Statement",id:"running-a-general-statement",level:4},{value:"Running An Unprepared Statement",id:"running-an-unprepared-statement",level:4},{value:"Implicit Commits",id:"implicit-commits",level:4},{value:"Using Multiple Database Connections",id:"using-multiple-database-connections",level:3},{value:"Database Transactions",id:"database-transactions",level:2},{value:"Manually Using Transactions",id:"manually-using-transactions",level:4},{value:"Multi-threading support",id:"multi-threading-support",level:2}];function D(e){const n={a:"a",admonition:"admonition",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",h4:"h4",h5:"h5",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,r.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.h1,{id:"database-getting-started",children:"Database: Getting Started"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"#introduction",children:"Introduction"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"#configuration",children:"Configuration"})}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"#ssl-connections",children:"SSL Connections"})}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"#running-sql-queries",children:"Running SQL Queries"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"#using-multiple-database-connections",children:"Using Multiple Database Connections"})}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"#database-transactions",children:"Database Transactions"})}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"#multi-threading-support",children:"Multi-threading support"})}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"introduction",children:"Introduction"}),"\n",(0,i.jsxs)(n.p,{children:["Almost every modern application interacts with a database. TinyORM makes interacting with a database extremely simple using raw SQL, a ",(0,i.jsx)(n.a,{href:"/database/query-builder",children:"fluent query builder"}),", and the ",(0,i.jsx)(n.a,{href:"/tinyorm/getting-started",children:"TinyORM"}),". Currently, TinyORM provides first-party support for four databases:"]}),"\n",(0,i.jsx)("div",{id:"databases-supported-versions",children:(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["MySQL ",(0,i.jsx)(n.code,{children:">=5.7"})," (",(0,i.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/MySQL#Release_history",children:"Version Policy"}),")"]}),"\n",(0,i.jsxs)(n.li,{children:["MariaDB ",(0,i.jsx)(n.code,{children:">=10.3"})," (",(0,i.jsx)(n.a,{href:"https://mariadb.org/about/#maintenance-policy",children:"Version Policy"}),")"]}),"\n",(0,i.jsxs)(n.li,{children:["PostgreSQL ",(0,i.jsx)(n.code,{children:">=11"})," (",(0,i.jsx)(n.a,{href:"https://www.postgresql.org/support/versioning/",children:"Version Policy"}),")"]}),"\n",(0,i.jsxs)(n.li,{children:["SQLite ",(0,i.jsx)(n.code,{children:">=3.8.8"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["native rename column ",(0,i.jsx)(n.code,{children:">=3.25.0"})," (",(0,i.jsx)(n.a,{href:"https://www.sqlite.org/lang_altertable.html#alter_table_rename_column",children:"docs"})," , ",(0,i.jsx)(n.a,{href:"https://www.sqlite.org/releaselog/3_25_0.html",children:"release notes"}),")"]}),"\n",(0,i.jsxs)(n.li,{children:["native drop column ",(0,i.jsx)(n.code,{children:">=3.35.0"})," (",(0,i.jsx)(n.a,{href:"https://www.sqlite.org/lang_altertable.html#alter_table_drop_column",children:"docs"}),", ",(0,i.jsx)(n.a,{href:"https://www.sqlite.org/releaselog/3_35_0.html",children:"release notes"}),")"]}),"\n",(0,i.jsxs)(n.li,{children:["generated columns ",(0,i.jsx)(n.code,{children:">=3.31.0"})," (",(0,i.jsx)(n.a,{href:"https://www.sqlite.org/gencol.html",children:"docs"}),", ",(0,i.jsx)(n.a,{href:"https://www.sqlite.org/releaselog/3_31_0.html",children:"release notes"}),")"]}),"\n"]}),"\n"]}),"\n"]})}),"\n",(0,i.jsxs)(n.p,{children:["TinyORM internally uses ",(0,i.jsx)(n.code,{children:"QtSql"})," module, you can look for ",(0,i.jsx)(n.a,{href:"https://doc.qt.io/qt/sql-driver.html#supported-databases",children:"supported databases"}),"."]}),"\n",(0,i.jsx)(n.admonition,{type:"note",children:(0,i.jsx)(n.p,{children:"TinyORM's code is ready and designed to simply add support for the SQL Server."})}),"\n",(0,i.jsx)(n.h3,{id:"configuration",children:"Configuration"}),"\n",(0,i.jsxs)(n.p,{children:["You can create and configure a new database connection using the ",(0,i.jsx)(n.code,{children:"create"})," method provided by the ",(0,i.jsx)(n.code,{children:"DB"})," facade:"]}),"\n",(0,i.jsxs)("div",{id:"tinyorm-configuration",children:[(0,i.jsx)(d,{}),(0,i.jsxs)(s.A,{className:"tinyorm-configuration-basic",groupId:S.OO,children:[(0,i.jsx)(a.A,{value:S.Dx,label:S.A3,children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\nusing Orm::DB;\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {"driver", "QMYSQL"},\n {"host", qEnvironmentVariable("DB_HOST", "127.0.0.1")},\n {"port", qEnvironmentVariable("DB_PORT", "3306")},\n {"database", qEnvironmentVariable("DB_DATABASE", "")},\n {"username", qEnvironmentVariable("DB_USERNAME", "root")},\n {"password", qEnvironmentVariable("DB_PASSWORD", "")},\n {"charset", qEnvironmentVariable("DB_CHARSET", "utf8mb4")},\n {"collation", qEnvironmentVariable("DB_COLLATION", "utf8mb4_0900_ai_ci")},\n {"timezone", "+00:00"},\n /* Specifies what time zone all QDateTime-s will have, the overridden default is\n the Qt::UTC, set to the Qt::LocalTime or QtTimeZoneType::DontConvert to use\n the system local time. */\n {"qt_timezone", QVariant::fromValue(Qt::UTC)},\n {"prefix", ""},\n {"prefix_indexes", false},\n {"strict", true},\n {"engine", "InnoDB"},\n {"options", QVariantHash()},\n});\n'})})}),(0,i.jsx)(a.A,{value:S.os,label:S.LQ,children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\nusing Orm::DB;\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {"driver", "QPSQL"},\n {"host", qEnvironmentVariable("DB_HOST", "127.0.0.1")},\n {"port", qEnvironmentVariable("DB_PORT", "5432")},\n {"database", qEnvironmentVariable("DB_DATABASE", "")},\n {"search_path", qEnvironmentVariable("DB_SEARCHPATH", "public")},\n {"username", qEnvironmentVariable("DB_USERNAME", "postgres")},\n {"password", qEnvironmentVariable("DB_PASSWORD", "")},\n {"charset", qEnvironmentVariable("DB_CHARSET", "utf8")},\n {"timezone", "UTC"},\n /* Specifies what time zone all QDateTime-s will have, the overridden default is\n the Qt::UTC, set to the Qt::LocalTime or QtTimeZoneType::DontConvert to use\n the system local time. */\n {"qt_timezone", QVariant::fromValue(Qt::UTC)},\n {"prefix", ""},\n {"prefix_indexes", false},\n {"options", QVariantHash()},\n});\n'})})}),(0,i.jsx)(a.A,{value:S.F4,label:S.Q7,children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\nusing Orm::DB;\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {"driver", "QSQLITE"},\n {"database", qEnvironmentVariable("DB_DATABASE", "/example/example.sqlite3")},\n {"foreign_key_constraints", qEnvironmentVariable("DB_FOREIGN_KEYS", "true")},\n {"check_database_exists", true},\n /* Specifies what time zone all QDateTime-s will have, the overridden default is\n the Qt::UTC, set to the Qt::LocalTime or QtTimeZoneType::DontConvert to use\n the system local time. */\n {"qt_timezone", QVariant::fromValue(Qt::UTC)},\n /* Return a QDateTime/QDate with the correct time zone instead of the QString,\n only works when the qt_timezone isn\'t set to the DontConvert. */\n {"return_qdatetime", true},\n {"prefix", ""},\n {"prefix_indexes", false},\n});\n'})})}),(0,i.jsx)(a.A,{value:S.Fi,label:S.CW,children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\nusing Orm::DB;\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {"driver", "QMYSQL"},\n {"host", qEnvironmentVariable("DB_HOST", "127.0.0.1")},\n {"port", qEnvironmentVariable("DB_PORT", "3306")},\n {"database", qEnvironmentVariable("DB_DATABASE", "")},\n {"username", qEnvironmentVariable("DB_USERNAME", "root")},\n {"password", qEnvironmentVariable("DB_PASSWORD", "")},\n {"charset", qEnvironmentVariable("DB_CHARSET", "utf8mb4")},\n {"collation", qEnvironmentVariable("DB_COLLATION", "utf8mb4_unicode_520_ci")},\n {"timezone", "+00:00"},\n /* Specifies what time zone all QDateTime-s will have, the overridden default is\n the Qt::UTC, set to the Qt::LocalTime or QtTimeZoneType::DontConvert to use\n the system local time. */\n {"qt_timezone", QVariant::fromValue(Qt::UTC)},\n {"prefix", ""},\n {"prefix_indexes", false},\n {"strict", true},\n {"engine", "InnoDB"},\n {"options", QVariantHash()},\n});\n'})})})]}),(0,i.jsxs)(s.A,{className:"tinyorm-configuration-full",groupId:S.OO,children:[(0,i.jsx)(a.A,{value:S.Dx,label:S.A3,children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n#include \n\nusing Orm::DB;\n\nusing ConfigUtils = Orm::Utils::Configuration;\n\nusing namespace Orm::Constants; // NOLINT(google-build-using-namespace)\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {driver_, QMYSQL},\n {host_, qEnvironmentVariable("DB_HOST", H127001)},\n {port_, qEnvironmentVariable("DB_PORT", P3306)},\n {database_, qEnvironmentVariable("DB_DATABASE", EMPTY)},\n {username_, qEnvironmentVariable("DB_USERNAME", ROOT)},\n {password_, qEnvironmentVariable("DB_PASSWORD", EMPTY)},\n {charset_, qEnvironmentVariable("DB_CHARSET", UTF8MB4)},\n {collation_, qEnvironmentVariable("DB_COLLATION", UTF8MB40900aici)},\n // SSL-related\n {ssl_ca, QStringLiteral("C:/mysql/data/ca.pem")},\n {ssl_cert, QStringLiteral("C:/mysql/data/client-cert.pem")},\n {ssl_key, QStringLiteral("C:/mysql/data/client-key.pem")},\n {ssl_mode, VerifyCA},\n // Or\n// {options, ConfigUtils::mysqlSslOptions()},\n {timezone_, TZ00},\n /* Specifies what time zone all QDateTime-s will have, the overridden default is\n the Qt::UTC, set to the Qt::LocalTime or QtTimeZoneType::DontConvert to use\n the system local time. */\n {qt_timezone, QVariant::fromValue(Qt::UTC)},\n {prefix_, EMPTY},\n {prefix_indexes, false},\n {strict_, true},\n// {isolation_level, QStringLiteral("REPEATABLE READ")}, // MySQL default is REPEATABLE READ for InnoDB\n {engine_, InnoDB},\n {Version, {}}, // Autodetect\n {options_, QVariantHash()},\n // Examples\n// {options_, QStringLiteral("MYSQL_OPT_CONNECT_TIMEOUT = 1 ; MYSQL_OPT_READ_TIMEOUT=1")},\n// {options_, QVariantHash {{QStringLiteral("MYSQL_OPT_CONNECT_TIMEOUT"), 1},\n// {QStringLiteral("MYSQL_OPT_READ_TIMEOUT"), 1}}},\n});\n'})})}),(0,i.jsx)(a.A,{value:S.os,label:S.LQ,children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n#include \n\nusing Orm::DB;\n\nusing ConfigUtils = Orm::Utils::Configuration;\n\nusing namespace Orm::Constants; // NOLINT(google-build-using-namespace)\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {driver_, QPSQL},\n {application_name, QStringLiteral("Example application")},\n {host_, qEnvironmentVariable("DB_HOST", H127001)},\n {port_, qEnvironmentVariable("DB_PORT", P5432)},\n {database_, qEnvironmentVariable("DB_DATABASE", EMPTY)},\n {search_path, qEnvironmentVariable("DB_SEARCHPATH", PUBLIC)},\n {username_, qEnvironmentVariable("DB_USERNAME", postgres_)},\n {password_, qEnvironmentVariable("DB_PASSWORD", EMPTY)},\n {charset_, qEnvironmentVariable("DB_CHARSET", UTF8)},\n // SSL-related\n {sslmode_, QStringLiteral("verify-full")},\n {sslcert, QStringLiteral("C:/example/postgres.crt")},\n {sslkey, QStringLiteral("C:/example/postgres.key")},\n {sslrootcert, QStringLiteral("C:/example/root.crt")},\n // Or\n// {options_, ConfigUtils::postgresSslOptions()},\n {timezone_, UTC},\n /* Specifies what time zone all QDateTime-s will have, the overridden default is\n the Qt::UTC, set to the Qt::LocalTime or QtTimeZoneType::DontConvert to use\n the system local time. */\n {qt_timezone, QVariant::fromValue(Qt::UTC)},\n {prefix_, EMPTY},\n {prefix_indexes, false},\n// {isolation_level, QStringLiteral("REPEATABLE READ")}, // Postgres default is READ COMMITTED\n// {synchronous_commit, QStringLiteral("off")}, // Postgres default is on\n // ConnectionFactory provides a default value for this, this is only for reference\n// {dont_drop, QStringList {spatial_ref_sys}},\n {options_, QVariantHash()},\n});\n'})})}),(0,i.jsx)(a.A,{value:S.F4,label:S.Q7,children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n#include \n\nusing Orm::DB;\n\nusing ConfigUtils = Orm::Utils::Configuration;\n\nusing namespace Orm::Constants; // NOLINT(google-build-using-namespace)\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {driver_, QSQLITE},\n {database_, qEnvironmentVariable("DB_DATABASE", "C:/SQLite/example.sqlite3")},\n {foreign_key_constraints, qEnvironmentVariable("DB_FOREIGN_KEYS", "true")},\n {check_database_exists, true},\n /* Specifies what time zone all QDateTime-s will have, the overridden default is\n the Qt::UTC, set to the Qt::LocalTime or QtTimeZoneType::DontConvert to use\n the system local time. */\n {qt_timezone, QVariant::fromValue(Qt::UTC)},\n /* Return a QDateTime/QDate with the correct time zone instead of the QString,\n only works when the qt_timezone isn\'t set to the DontConvert. */\n {return_qdatetime, true},\n {prefix_, EMPTY},\n {prefix_indexes, false},\n});\n'})})}),(0,i.jsx)(a.A,{value:S.Fi,label:S.CW,children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n#include \n\nusing Orm::DB;\n\nusing ConfigUtils = Orm::Utils::Configuration;\n\nusing namespace Orm::Constants; // NOLINT(google-build-using-namespace)\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {driver_, QMYSQL},\n {host_, qEnvironmentVariable("DB_HOST", H127001)},\n {port_, qEnvironmentVariable("DB_PORT", P3306)},\n {database_, qEnvironmentVariable("DB_DATABASE", EMPTY)},\n {username_, qEnvironmentVariable("DB_USERNAME", ROOT)},\n {password_, qEnvironmentVariable("DB_PASSWORD", EMPTY)},\n {charset_, qEnvironmentVariable("DB_CHARSET", UTF8MB4)},\n {collation_, qEnvironmentVariable("DB_COLLATION", UTF8MB4Unicode520ci)},\n // SSL-related\n {ssl_ca, QStringLiteral("C:/maria/data/ca.pem")},\n {ssl_cert, QStringLiteral("C:/maria/data/client-cert.pem")},\n {ssl_key, QStringLiteral("C:/maria/data/client-key.pem")},\n // Or\n// {options, ConfigUtils::mariaSslOptions()},\n {timezone_, TZ00},\n /* Specifies what time zone all QDateTime-s will have, the overridden default is\n the Qt::UTC, set to the Qt::LocalTime or QtTimeZoneType::DontConvert to use\n the system local time. */\n {qt_timezone, QVariant::fromValue(Qt::UTC)},\n {prefix_, EMPTY},\n {prefix_indexes, false},\n {strict_, true},\n// {isolation_level, QStringLiteral("REPEATABLE READ")}, // MariaDB default is REPEATABLE READ for InnoDB\n {engine_, InnoDB},\n {Version, {}}, // Autodetect\n {options_, QVariantHash()},\n // Examples\n// {options_, QStringLiteral("MYSQL_OPT_CONNECT_TIMEOUT = 1 ; MYSQL_OPT_READ_TIMEOUT=1")},\n// {options_, QVariantHash {{QStringLiteral("MYSQL_OPT_CONNECT_TIMEOUT"), 1},\n// {QStringLiteral("MYSQL_OPT_READ_TIMEOUT"), 1}}},\n});\n'})})})]})]}),"\n",(0,i.jsxs)(n.p,{children:["The first argument is configuration hash which is of type ",(0,i.jsx)(n.code,{children:"QVariantHash"})," and the second argument specifies the name of the ",(0,i.jsx)(n.em,{children:"connection"}),", this connection will also be a ",(0,i.jsx)(n.em,{children:"default connection"}),". You can configure multiple database connections at once and choose the needed one before executing SQL query, section ",(0,i.jsx)(n.a,{href:"#using-multiple-database-connections",children:"Using Multiple Database Connections"})," describes how to create and use multiple database connections."]}),"\n",(0,i.jsxs)(n.p,{children:["You may also configure connection options by ",(0,i.jsx)(n.code,{children:"options"})," key as ",(0,i.jsx)(n.code,{children:"QVariantHash"})," or ",(0,i.jsx)(n.code,{children:"QString"}),", you can pass any ",(0,i.jsx)(n.a,{href:"https://doc.qt.io/qt/qsqldatabase.html#setConnectOptions",children:"connection options"})," supported by ",(0,i.jsx)(n.code,{children:"QSqlDatabase"}),"."]}),"\n",(0,i.jsxs)(n.p,{children:["You can also configure ",(0,i.jsx)(n.a,{href:"https://dev.mysql.com/doc/refman/8.4/en/innodb-transaction-isolation-levels.html",children:"Transaction Isolation Levels"})," for MySQL connection with the ",(0,i.jsx)(n.code,{children:"isolation_level"})," configuration option."]}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.code,{children:"version"})," option is relevant only for the MySQL connections and you can save/avoid one database query (select version()) if you provide it manually. On the base of this version will be decided which ",(0,i.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/src/orm/connectors/mysqlconnector.cpp#L154",children:"session variables"})," will be set if strict mode is enabled and whether to use an ",(0,i.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/src/orm/query/grammars/mysqlgrammar.cpp#L36",children:"alias"})," during the ",(0,i.jsx)(n.code,{children:"upsert"})," method call."]}),"\n",(0,i.jsxs)(n.p,{children:["Breaking values are as follows; use an upsert alias on the MySQL >=8.0.19 and remove the ",(0,i.jsx)(n.code,{children:"NO_AUTO_CREATE_USER"})," sql mode on the MySQL >=8.0.11 if the strict mode is enabled."]}),"\n",(0,i.jsx)(n.admonition,{type:"info",children:(0,i.jsxs)(n.p,{children:["A database connection is resolved lazily, which means that the connection configuration is only saved after the ",(0,i.jsx)(n.code,{children:"DB::create"})," method call. The connection will be resolved after you run some query or you can create it using the ",(0,i.jsx)(n.code,{children:"DB::connection"})," method."]})}),"\n",(0,i.jsx)(n.admonition,{type:"tip",children:(0,i.jsxs)(n.p,{children:["You can also use predefined string constants to avoid unnecessary ",(0,i.jsx)(n.code,{children:"QString"})," instantiations, as used in the ",(0,i.jsx)(n.code,{children:"tom"})," migrations ",(0,i.jsx)(n.a,{href:"/building/migrations#string-constants-example",children:"example"}),"."]})}),"\n",(0,i.jsx)(n.h4,{id:"sqlite-configuration",children:"SQLite Configuration"}),"\n",(0,i.jsxs)(n.p,{children:["SQLite databases are contained within a single file on your filesystem. You can create a new SQLite database using the ",(0,i.jsx)(n.code,{children:"touch"})," command in your terminal: ",(0,i.jsx)(n.code,{children:"touch database.sqlite3"}),". After the database has been created, you may configure SQLite database connection:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {"driver", "QSQLITE"},\n {"database", qEnvironmentVariable("DB_DATABASE", "/absolute/path/to/database.sqlite3")},\n {"foreign_key_constraints", qEnvironmentVariable("DB_FOREIGN_KEYS", "true")},\n {"check_database_exists", true},\n {"prefix", ""},\n});\n'})}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.code,{children:"database"})," configuration value is the absolute path to the database. To enable foreign key constraints for SQLite connections, you should set the ",(0,i.jsx)(n.code,{children:"foreign_key_constraints"})," configuration value to ",(0,i.jsx)(n.code,{children:"true"}),", if this configuration value is not set, then the default of the SQLite driver will be used (currently the default is ",(0,i.jsx)(n.strong,{children:"disabled"}),")."]}),"\n",(0,i.jsxs)(n.p,{children:["If the ",(0,i.jsx)(n.code,{children:"check_database_exists"})," configuration value is set to the ",(0,i.jsx)(n.code,{children:"true"})," value, then the database connection throws an ",(0,i.jsx)(n.code,{children:"Orm::InvalidArgumentError"})," exception, when the SQLite database file doesn't exist. If it is set to the ",(0,i.jsx)(n.code,{children:"false"})," value and the SQLite database file doesn't exist, then it will be created for you by SQLite driver. The default value is ",(0,i.jsx)(n.code,{children:"true"}),"."]}),"\n",(0,i.jsx)(n.h3,{id:"ssl-connections",children:"SSL Connections"}),"\n",(0,i.jsxs)(n.p,{children:["SSL connections are supported for the ",(0,i.jsx)(n.code,{children:"MySQL"})," and ",(0,i.jsx)(n.code,{children:"PostgreSQL"})," databases. They can be set using the ",(0,i.jsx)(n.code,{children:"options"})," configuration option."]}),"\n",(0,i.jsx)(n.admonition,{type:"info",children:(0,i.jsxs)(n.p,{children:["This feature is heavily dependent on the underlying ",(0,i.jsx)(n.code,{children:"QSqlDatabase"})," module. What means that you can pass the same ",(0,i.jsx)(n.a,{href:"https://doc.qt.io/qt/qsqldatabase.html#setConnectOptions",children:"connection options"})," to the ",(0,i.jsx)(n.code,{children:"TinyORM"})," that the ",(0,i.jsx)(n.code,{children:"QSqlDatabase"})," accepts."]})}),"\n",(0,i.jsx)(n.h5,{id:"mysql",children:"MySQL"}),"\n",(0,i.jsxs)(n.p,{children:["You have to pass the ",(0,i.jsx)(n.code,{children:"SSL_CA"}),", ",(0,i.jsx)(n.code,{children:"SSL_CERT"}),", ",(0,i.jsx)(n.code,{children:"SSL_KEY"}),", and ",(0,i.jsx)(n.code,{children:"MYSQL_OPT_SSL_MODE"})," options."]}),"\n",(0,i.jsxs)(s.A,{groupId:S.vf,children:[(0,i.jsx)(a.A,{value:S.b,label:S.ux,children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\nusing Orm::DB;\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {"driver", "QMYSQL"},\n {"host", qEnvironmentVariable("DB_MYSQL_HOST", "127.0.0.1")},\n ...\n // highlight-start\n {"options", QVariantHash({{"SSL_CA", "C:/mysql/data/ca.pem"},\n {"SSL_CERT", "C:/mysql/data/client-cert.pem"},\n {"SSL_KEY", "C:/mysql/data/client-key.pem"},\n {"MYSQL_OPT_SSL_MODE", "VERIFY_CA"}})},\n // highlight-end\n});\n'})})}),(0,i.jsx)(a.A,{value:S.xj,label:S.gg,children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\nusing Orm::DB;\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {"driver", "QMYSQL"},\n {"host", qEnvironmentVariable("DB_MYSQL_HOST", "127.0.0.1")},\n ...\n // highlight-start\n {"options", QVariantHash({{"SSL_CA", "~/.local/share/TinyORM/ssl/ca.pem"},\n {"SSL_CERT", "~/.local/share/TinyORM/ssl/client-cert.pem"},\n {"SSL_KEY", "~/.local/share/TinyORM/ssl/client-key.pem"},\n {"MYSQL_OPT_SSL_MODE", "VERIFY_CA"}})},\n // highlight-end\n});\n'})})})]}),"\n",(0,i.jsxs)(n.p,{children:["You may also use the ",(0,i.jsx)(n.code,{children:"ConfigUtils::mysqlSslOptions()"})," or the ",(0,i.jsx)(n.code,{children:"ConfigUtils::insertMySqlSslOptions()"})," methods to insert these options for you and define them using the ",(0,i.jsx)(n.code,{children:"DB_MYSQL_SSL_CA"}),", ",(0,i.jsx)(n.code,{children:"DB_MYSQL_SSL_CERT"}),", ",(0,i.jsx)(n.code,{children:"DB_MYSQL_SSL_KEY"}),", and ",(0,i.jsx)(n.code,{children:"DB_MYSQL_SSL_MODE"})," environment variables."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n#include \n\nusing Orm::DB;\n\nusing ConfigUtils = Orm::Utils::Configuration;\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {"driver", "QMYSQL"},\n {"host", qEnvironmentVariable("DB_MYSQL_HOST", "127.0.0.1")},\n ...\n // highlight-next-line\n {"options", ConfigUtils::mysqlSslOptions()},\n});\n'})}),"\n",(0,i.jsxs)(n.p,{children:["You can define these SSL-related options in the top-level configuration, they will be copied to the ",(0,i.jsx)(n.code,{children:"options"})," option hash during configuration parsing. The top-level configuration takes precedence and overwrites the options in the ",(0,i.jsx)(n.code,{children:"options"})," hash."]}),"\n",(0,i.jsxs)(s.A,{groupId:S.vf,children:[(0,i.jsx)(a.A,{value:S.b,label:S.ux,children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\nusing Orm::DB;\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {"driver", "QMYSQL"},\n {"host", qEnvironmentVariable("DB_MYSQL_HOST", "127.0.0.1")},\n ...\n // highlight-start\n {"SSL_CA", "C:/mysql/data/ca.pem"},\n {"SSL_CERT", "C:/mysql/data/client-cert.pem"},\n {"SSL_KEY", "C:/mysql/data/client-key.pem"},\n {"MYSQL_OPT_SSL_MODE", "VERIFY_CA"},\n // highlight-end\n});\n'})})}),(0,i.jsx)(a.A,{value:S.xj,label:S.gg,children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\nusing Orm::DB;\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {"driver", "QMYSQL"},\n {"host", qEnvironmentVariable("DB_MYSQL_HOST", "127.0.0.1")},\n ...\n // highlight-start\n {"SSL_CA", "~/.local/share/TinyORM/ssl/ca.pem"},\n {"SSL_CERT", "~/.local/share/TinyORM/ssl/client-cert.pem"},\n {"SSL_KEY", "~/.local/share/TinyORM/ssl/client-key.pem"},\n {"MYSQL_OPT_SSL_MODE", "VERIFY_CA"},\n // highlight-end\n});\n'})})})]}),"\n",(0,i.jsx)(n.admonition,{type:"tip",children:(0,i.jsxs)(n.p,{children:["You can take a look at the GitHub actions how the ",(0,i.jsx)(n.code,{children:"MySQL"})," certificates are generated in the CI pipeline for ",(0,i.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/.github/workflows/msvc2022-qt6.yml",children:"Windows"})," and ",(0,i.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/.github/workflows/linux-qt6.yml",children:"Linux"}),"."]})}),"\n",(0,i.jsx)(n.admonition,{type:"tip",children:(0,i.jsxs)(n.p,{children:["You can also pass the ",(0,i.jsx)(n.code,{children:"QString"})," to the ",(0,i.jsx)(n.code,{children:"options"})," configuration separated by the ",(0,i.jsx)(n.code,{children:";"})," semicolon character and use the ",(0,i.jsx)(n.code,{children:"="})," to assign values."]})}),"\n",(0,i.jsx)(n.h5,{id:"postgresql",children:"PostgreSQL"}),"\n",(0,i.jsxs)(n.p,{children:["You have to pass the ",(0,i.jsx)(n.a,{href:"https://www.postgresql.org/docs/current/libpq-ssl.html#LIBPQ-SSL-SSLMODE-STATEMENTS",children:(0,i.jsx)(n.code,{children:"sslmode"})})," or the deprecated ",(0,i.jsx)(n.a,{href:"https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNECT-REQUIRESSL",children:(0,i.jsx)(n.code,{children:"requiressl"})})," options."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\nusing Orm::DB;\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {"driver", "QPSQL"},\n {"host", qEnvironmentVariable("DB_PGSQL_HOST", "127.0.0.1")},\n ...\n // highlight-next-line\n {"options", QVariantHash({{"sslmode", "verify-full"}})},\n});\n'})}),"\n",(0,i.jsxs)(n.p,{children:["And place your ",(0,i.jsx)(n.strong,{children:"client"})," certificates to the ",(0,i.jsx)(n.code,{children:"~/.postgres/"})," on ",(0,i.jsx)(n.a,{href:"https://www.postgresql.org/docs/current/libpq-ssl.html#LIBPQ-SSL-FILE-USAGE",children:"Linux"})," and ",(0,i.jsx)(n.code,{children:"$env:APPDATA/postgres/"})," on Windows. Everything is described in the PostgreSQL's ",(0,i.jsx)(n.a,{href:"https://www.postgresql.org/docs/current/libpq-ssl.html",children:"libpq client"})," and ",(0,i.jsx)(n.a,{href:"https://www.postgresql.org/docs/15/ssl-tcp.html#SSL-FILE-USAGE",children:"server"})," documentation."]}),"\n",(0,i.jsxs)(n.p,{children:["If you want to keep your ",(0,i.jsx)(n.strong,{children:"client"})," certificates in your own location, you can set the ",(0,i.jsx)(n.code,{children:"sslcert"}),", ",(0,i.jsx)(n.code,{children:"sslkey"}),", and ",(0,i.jsx)(n.code,{children:"sslrootcert"})," options."]}),"\n",(0,i.jsxs)(s.A,{groupId:S.vf,children:[(0,i.jsx)(a.A,{value:S.b,label:S.ux,children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\nusing Orm::DB;\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {"driver", "QPSQL"},\n {"host", qEnvironmentVariable("DB_PGSQL_HOST", "127.0.0.1")},\n ...\n // highlight-next-line\n {"options", QVariantHash({{"sslmode", "verify-full"},\n // highlight-start\n {"sslcert", "C:/example/postgres.crt"},\n {"sslkey", "C:/example/postgres.key"},\n {"sslrootcert", "C:/example/root.crt"}})},\n // highlight-end\n});\n'})})}),(0,i.jsx)(a.A,{value:S.xj,label:S.gg,children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\nusing Orm::DB;\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {"driver", "QPSQL"},\n {"host", qEnvironmentVariable("DB_PGSQL_HOST", "127.0.0.1")},\n ...\n // highlight-next-line\n {"options", QVariantHash({{"sslmode", "verify-full"},\n // highlight-start\n {"sslcert", "/example/postgres.crt"},\n {"sslkey", "/example/postgres.key"},\n {"sslrootcert", "/example/root.crt"}})},\n // highlight-end\n});\n'})})})]}),"\n",(0,i.jsxs)(n.p,{children:["You can define these SSL-related options in the top-level configuration, they will be copied to the ",(0,i.jsx)(n.code,{children:"options"})," option hash during a configuration parsing. The top-level configuration takes precedence and overwrites the options in the ",(0,i.jsx)(n.code,{children:"options"})," hash."]}),"\n",(0,i.jsxs)(s.A,{groupId:S.vf,children:[(0,i.jsx)(a.A,{value:S.b,label:S.ux,children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\nusing Orm::DB;\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {"driver", "QPSQL"},\n {"host", qEnvironmentVariable("DB_PGSQL_HOST", "127.0.0.1")},\n ...\n // highlight-start\n {"sslmode", "verify-full"},\n {"sslcert", "C:/example/postgres.crt"},\n {"sslkey", "C:/example/postgres.key"},\n {"sslrootcert", "C:/example/root.crt"},\n // highlight-end\n});\n'})})}),(0,i.jsx)(a.A,{value:S.xj,label:S.gg,children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\nusing Orm::DB;\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {"driver", "QPSQL"},\n {"host", qEnvironmentVariable("DB_PGSQL_HOST", "127.0.0.1")},\n ...\n // highlight-start\n {"sslmode", "verify-full"},\n {"sslcert", "/example/postgres.crt"},\n {"sslkey", "/example/postgres.key"},\n {"sslrootcert", "/example/root.crt"},\n // highlight-end\n});\n'})})})]}),"\n",(0,i.jsxs)(n.p,{children:["You may also use the ",(0,i.jsx)(n.code,{children:"ConfigUtils::postgresSslOptions()"})," or the ",(0,i.jsx)(n.code,{children:"ConfigUtils::insertPostgresSslOptions()"})," methods to insert the ",(0,i.jsx)(n.code,{children:"sslmode"}),", ",(0,i.jsx)(n.code,{children:"sslcert"}),", ",(0,i.jsx)(n.code,{children:"sslkey"}),", and ",(0,i.jsx)(n.code,{children:"sslrootcert"})," options for you and define them using the ",(0,i.jsx)(n.code,{children:"DB_PGSQL_SSLMODE"}),", ",(0,i.jsx)(n.code,{children:"DB_PGSQL_SSLCERT"}),", ",(0,i.jsx)(n.code,{children:"DB_PGSQL_SSLKEY"}),", and ",(0,i.jsx)(n.code,{children:"DB_PGSQL_SSLROOTCERT"})," environment variable."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n#include \n\nusing Orm::DB;\n\nusing ConfigUtils = Orm::Utils::Configuration;\n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {"driver", "QPSQL"},\n {"host", qEnvironmentVariable("DB_PGSQL_HOST", "127.0.0.1")},\n ...\n // highlight-next-line\n {"options", ConfigUtils::postgresSslOptions()},\n});\n'})}),"\n",(0,i.jsx)(n.admonition,{type:"info",children:(0,i.jsxs)(n.p,{children:["The PostgreSQL's libpq client library provides the ",(0,i.jsx)(n.a,{href:"https://www.postgresql.org/docs/current/libpq-envars.html#id-1.7.3.22.3.4.13.1.1",children:(0,i.jsx)(n.code,{children:"PGSSLMODE"})}),", ",(0,i.jsx)(n.a,{href:"https://www.postgresql.org/docs/current/libpq-envars.html#id-1.7.3.22.3.4.16.1.1",children:(0,i.jsx)(n.code,{children:"PGSSLCERT"})}),", ",(0,i.jsx)(n.a,{href:"https://www.postgresql.org/docs/current/libpq-envars.html#id-1.7.3.22.3.4.17.1.1",children:(0,i.jsx)(n.code,{children:"PGSSLKEY"})}),", and ",(0,i.jsx)(n.a,{href:"https://www.postgresql.org/docs/current/libpq-envars.html#id-1.7.3.22.3.4.18.1.1",children:(0,i.jsx)(n.code,{children:"PGSSLROOTCERT"})})," environment variables, so you don't have to use TinyORM's ",(0,i.jsx)(n.code,{children:"options"})," configuration and may use these environment variables instead."]})}),"\n",(0,i.jsx)(n.admonition,{type:"tip",children:(0,i.jsxs)(n.p,{children:["You can take a look at the GitHub actions how the ",(0,i.jsx)(n.code,{children:"PostgreSQL"})," certificates are generated in the CI pipeline for ",(0,i.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/.github/workflows/msvc2022-qt6.yml",children:"Windows"})," and ",(0,i.jsx)(n.a,{href:"https://github.com/silverqx/TinyORM/blob/main/.github/workflows/linux-qt6.yml",children:"Linux"}),"."]})}),"\n",(0,i.jsx)(n.h2,{id:"running-sql-queries",children:"Running SQL Queries"}),"\n",(0,i.jsxs)(n.p,{children:["Once you have configured your database connection, you may run queries using the ",(0,i.jsx)(n.code,{children:"DB"})," facade. The ",(0,i.jsx)(n.code,{children:"DB"})," facade provides methods for each type of query: ",(0,i.jsx)(n.code,{children:"select"}),", ",(0,i.jsx)(n.code,{children:"update"}),", ",(0,i.jsx)(n.code,{children:"insert"}),", ",(0,i.jsx)(n.code,{children:"delete"}),", and ",(0,i.jsx)(n.code,{children:"statement"}),"."]}),"\n",(0,i.jsx)(n.h4,{id:"running-a-select-query",children:"Running A Select Query"}),"\n",(0,i.jsxs)(n.p,{children:["To run a basic SELECT query, you may use the ",(0,i.jsx)(n.code,{children:"select"})," method on the ",(0,i.jsx)(n.code,{children:"DB"})," facade:"]}),"\n",(0,i.jsxs)(n.p,{children:['auto users = DB::select("select * from users where active = ?", ',1,");"]}),"\n",(0,i.jsxs)(n.p,{children:["The first argument passed to the ",(0,i.jsx)(n.code,{children:"select"})," method is the SQL query, while the second argument is any parameter bindings that need to be bound to the query. Typically, these are the values of the ",(0,i.jsx)(n.code,{children:"where"})," clause constraints. Parameter binding provides protection against SQL injection."]}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.code,{children:"select"})," method returns a ",(0,i.jsx)(n.code,{children:"QSqlQuery"})," containing the results of the query, where each result can be accessed by ",(0,i.jsx)(n.code,{children:"QSqlQuery::next"})," method. Look into the ",(0,i.jsx)(n.code,{children:"QSqlQuery"}),' documentation on how to obtain results from the "query". You may access each column\'s value by ',(0,i.jsx)(n.code,{children:"QSqlQuery::value"})," method. The first ",(0,i.jsx)(n.code,{children:"bool"})," return value is the value returned from ",(0,i.jsx)(n.code,{children:"QSqlQuery::exec"})," method:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\n#include \n\nauto users = DB::select("select * from users");\n\nwhile(users.next())\n qDebug() << users.value("name").toString();\n'})}),"\n",(0,i.jsx)(n.h4,{id:"selecting-scalar-values",children:"Selecting Scalar Values"}),"\n",(0,i.jsxs)(n.p,{children:["Sometimes your database query may result in a single, scalar value. Instead of being required to retrieve the query's scalar result from a record instance, TinyORM allows you to retrieve this value directly using the ",(0,i.jsx)(n.code,{children:"scalar"})," shortcut method:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\nauto states = DB::scalar(\n "select count(case when state = \'pending\' then 1 end) as states "\n "from comments"\n);\n\n// With binding\nauto states = DB::scalar(\n "select count(case when state = ? then 1 end) as states from comments",\n {"pending"}\n);\n'})}),"\n",(0,i.jsx)(n.h4,{id:"running-an-insert-statement",children:"Running An Insert Statement"}),"\n",(0,i.jsxs)(n.p,{children:["To execute an ",(0,i.jsx)(n.code,{children:"insert"})," statement, you may use the ",(0,i.jsx)(n.code,{children:"insert"})," method on the ",(0,i.jsx)(n.code,{children:"DB"})," facade. Like ",(0,i.jsx)(n.code,{children:"select"}),", this method accepts the SQL query as its first argument and bindings as its second argument and returns ",(0,i.jsx)(n.code,{children:"QSqlQuery"}),":"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\nDB::insert("insert into users (id, name) values (?, ?)", {1, "Marc"});\n'})}),"\n",(0,i.jsx)(n.h4,{id:"running-an-update-statement",children:"Running An Update Statement"}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.code,{children:"update"})," method should be used to update existing records in the database. The number of rows affected by the statement and ",(0,i.jsx)(n.code,{children:"QSqlQuery"})," is returned by the method as ",(0,i.jsx)(n.code,{children:"std::tuple"}),":"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\n#include \n\nauto [affected, query] = DB::update(\n "update users set updated_at = ? where name = ?",\n {QDateTime::currentDateTimeUtc(), "Anita"}\n);\n\nif (!affected)\n qDebug() << "Any record was updated.";\n'})}),"\n",(0,i.jsx)(n.h4,{id:"running-a-delete-statement",children:"Running A Delete Statement"}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.code,{children:"remove"})," method should be used to delete records from the database. Like ",(0,i.jsx)(n.code,{children:"update"}),", the number of affected rows and ",(0,i.jsx)(n.code,{children:"QSqlQuery"})," will be returned by the method as ",(0,i.jsx)(n.code,{children:"std::tuple"}),":"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\nauto [affected, query] = DB::remove("delete from users");\n'})}),"\n",(0,i.jsx)(n.admonition,{type:"note",children:(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.code,{children:"delete"})," can not be used as the method name because it is the reserved word."]})}),"\n",(0,i.jsx)(n.h4,{id:"running-a-general-statement",children:"Running A General Statement"}),"\n",(0,i.jsxs)(n.p,{children:["Some database statements do not return any value. For these types of operations, you may use the ",(0,i.jsx)(n.code,{children:"statement"})," method on the ",(0,i.jsx)(n.code,{children:"DB"})," facade:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'DB::statement("drop table users");\n'})}),"\n",(0,i.jsx)(n.admonition,{type:"tip",children:(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.code,{children:"DB::statement"})," method should be used for ",(0,i.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/Data_definition_language",children:"DDL"}),' queries, don\'t use it for "select" queries because it internally calls ',(0,i.jsx)(n.code,{children:"recordsHaveBeenModified"})," method."]})}),"\n",(0,i.jsx)(n.h4,{id:"running-an-unprepared-statement",children:"Running An Unprepared Statement"}),"\n",(0,i.jsxs)(n.p,{children:["Sometimes you may want to execute an SQL statement without binding any values. You may use the ",(0,i.jsx)(n.code,{children:"DB"})," facade's ",(0,i.jsx)(n.code,{children:"unprepared"})," method to accomplish this:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:"DB::unprepared(\"update users set votes = 100 where name = 'Dries'\");\n"})}),"\n",(0,i.jsx)(n.admonition,{type:"warning",children:(0,i.jsx)(n.p,{children:"Since unprepared statements do not bind parameters, they may be vulnerable to SQL injection. You should never allow user controlled values within an unprepared statement."})}),"\n",(0,i.jsx)(n.h4,{id:"implicit-commits",children:"Implicit Commits"}),"\n",(0,i.jsxs)(n.p,{children:["When using the ",(0,i.jsx)(n.code,{children:"DB"})," facade's ",(0,i.jsx)(n.code,{children:"statement"})," methods within transactions, you must be careful to avoid statements that cause ",(0,i.jsx)(n.a,{href:"https://dev.mysql.com/doc/refman/8.4/en/implicit-commit.html",children:"implicit commits"}),". These statements will cause the database engine to indirectly commit the entire transaction, leaving TinyORM unaware of the database's transaction level. An example of such a statement is creating a database table:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'DB::statement("create table users (name varchar(255) null)");\n'})}),"\n",(0,i.jsxs)(n.p,{children:["Please refer to the MySQL manual for ",(0,i.jsx)(n.a,{href:"https://dev.mysql.com/doc/refman/8.4/en/implicit-commit.html",children:"a list of all statements"})," that trigger implicit commits."]}),"\n",(0,i.jsx)(n.h3,{id:"using-multiple-database-connections",children:"Using Multiple Database Connections"}),"\n",(0,i.jsxs)(n.p,{children:["You can configure multiple database connections at once during ",(0,i.jsx)(n.code,{children:"DatabaseManager"})," instantiation using the ",(0,i.jsx)(n.code,{children:"DB::create"})," overload, where the first argument is a hash of multiple connections and is of type ",(0,i.jsx)(n.code,{children:"QHash"})," and the second argument is the name of the default connection:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\n// Ownership of a shared_ptr()\nauto manager = DB::create({\n {"mysql", {\n {"driver", "QMYSQL"},\n {"host", qEnvironmentVariable("DB_MYSQL_HOST", "127.0.0.1")},\n {"port", qEnvironmentVariable("DB_MYSQL_PORT", "3306")},\n {"database", qEnvironmentVariable("DB_MYSQL_DATABASE", "")},\n {"username", qEnvironmentVariable("DB_MYSQL_USERNAME", "root")},\n {"password", qEnvironmentVariable("DB_MYSQL_PASSWORD", "")},\n {"charset", qEnvironmentVariable("DB_MYSQL_CHARSET", "utf8mb4")},\n {"collation", qEnvironmentVariable("DB_MYSQL_COLLATION", "utf8mb4_0900_ai_ci")},\n {"strict", true},\n {"options", QVariantHash()},\n }},\n {"sqlite", {\n {"driver", "QSQLITE"},\n {"database", qEnvironmentVariable("DB_SQLITE_DATABASE", "")},\n {"foreign_key_constraints", qEnvironmentVariable("DB_SQLITE_FOREIGN_KEYS", "true")},\n {"check_database_exists", true},\n {"prefix", ""},\n }},\n}, "mysql");\n'})}),"\n",(0,i.jsxs)(n.p,{children:["If your application needs to use multiple connections, you may access each connection via the ",(0,i.jsx)(n.code,{children:"connection"})," method provided by the ",(0,i.jsx)(n.code,{children:"DB"})," facade. The connection name passed to the ",(0,i.jsx)(n.code,{children:"connection"})," method should correspond to one of the connections key listed in your configuration:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n\nauto query = DB::connection("mysql_test").select(...);\n'})}),"\n",(0,i.jsxs)(n.p,{children:["You may access the raw underlying ",(0,i.jsx)(n.code,{children:"QSqlQuery"})," instance of a connection using the ",(0,i.jsx)(n.code,{children:"getQtQuery"})," method on a connection instance:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:"auto query = DB::connection().getQtQuery();\n"})}),"\n",(0,i.jsxs)(n.p,{children:["Or you can use the shortcut method ",(0,i.jsx)(n.code,{children:"qtQuery"})," provided by the ",(0,i.jsx)(n.code,{children:"DB"})," facade:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:"auto query = DB::qtQuery();\n"})}),"\n",(0,i.jsx)(n.h2,{id:"database-transactions",children:"Database Transactions"}),"\n",(0,i.jsx)(n.h4,{id:"manually-using-transactions",children:"Manually Using Transactions"}),"\n",(0,i.jsxs)(n.p,{children:["If you would like to begin a transaction manually and have complete control over rollbacks and commits, you may use the ",(0,i.jsx)(n.code,{children:"beginTransaction"})," method provided by the ",(0,i.jsx)(n.code,{children:"DB"})," facade:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:"#include \n\nDB::beginTransaction();\n"})}),"\n",(0,i.jsxs)(n.p,{children:["You can rollback the transaction via the ",(0,i.jsx)(n.code,{children:"rollBack"})," method:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:"DB::rollBack();\n"})}),"\n",(0,i.jsxs)(n.p,{children:["Lastly, you can commit a transaction via the ",(0,i.jsx)(n.code,{children:"commit"})," method:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:"DB::commit();\n"})}),"\n",(0,i.jsx)(n.p,{children:"All transaction methods accept a connection name as the optional argument:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'DB::beginTransaction("mysql_test");\n'})}),"\n",(0,i.jsx)(n.admonition,{type:"tip",children:(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.code,{children:"DB"})," facade's transaction methods control the transactions for both the ",(0,i.jsx)(n.a,{href:"/database/query-builder",children:"query builder"})," and ",(0,i.jsx)(n.a,{href:"/tinyorm/getting-started",children:"TinyORM"}),"."]})}),"\n",(0,i.jsx)(n.h2,{id:"multi-threading-support",children:"Multi-threading support"}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.code,{children:"TinyORM"})," supports multi-threading for the ",(0,i.jsx)(n.code,{children:"MSVC"})," and ",(0,i.jsx)(n.code,{children:"GCC"})," on Linux compilers. Multi-threading is disabled for the ",(0,i.jsx)(n.code,{children:"Clang <14.0.3"})," compiler on MSYS2, ",(0,i.jsx)(n.code,{children:"Clang <14.0.4"})," on Linux and for the ",(0,i.jsx)(n.code,{children:"GCC"})," compiler on MSYS2. The reason are bugs in the ",(0,i.jsx)(n.code,{children:"TLS"})," wrapper that is generated by the ",(0,i.jsx)(n.a,{href:"https://en.cppreference.com/w/cpp/keyword/thread_local",children:(0,i.jsx)(n.code,{children:"thread_local"})})," keyword."]}),"\n",(0,i.jsx)(n.p,{children:"A connection can only be used from within the thread that created it. Moving connections between threads or creating queries from a different thread where the connection was created is not supported."}),"\n",(0,i.jsxs)(n.p,{children:["In addition, the third party libraries used by the ",(0,i.jsx)(n.code,{children:"QSqlDrivers"})," can impose further restrictions on using the SQL Module in a multithreaded program."]}),"\n",(0,i.jsxs)(n.p,{children:["In short, if you create a ",(0,i.jsx)(n.code,{children:"DB::connection"})," in some thread then you have to use this connection only from this particular thread and of course all queries that will be executed on this connection."]}),"\n",(0,i.jsx)(n.p,{children:"If you want to execute some query from another thread for the same connection then you have to create a new connection first and if you have a new connection you can send a query from this new thread to the database."}),"\n",(0,i.jsx)(n.admonition,{type:"warning",children:(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.a,{href:"/database/migrations#tables",children:(0,i.jsx)(n.code,{children:"schema builder"})})," and ",(0,i.jsx)(n.a,{href:"/database/migrations",children:(0,i.jsx)(n.code,{children:"migrations"})})," don't support multi-threading."]})})]})}function L(e={}){const{wrapper:n}={...(0,r.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(D,{...e})}):D(e)}},9365:(e,n,t)=>{t.d(n,{A:()=>s});t(6540);var i=t(4164);const r={tabItem:"tabItem_Ymn6"};var a=t(4848);function s(e){let{children:n,hidden:t,className:s}=e;return(0,a.jsx)("div",{role:"tabpanel",className:(0,i.A)(r.tabItem,s),hidden:t,children:n})}},1470:(e,n,t)=>{t.d(n,{A:()=>v});var i=t(6540),r=t(4164),a=t(3104),s=t(6347),o=t(205),l=t(7485),c=t(1682),d=t(679);function h(e){return i.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,i.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function u(e){const{values:n,children:t}=e;return(0,i.useMemo)((()=>{const e=n??function(e){return h(e).map((e=>{let{props:{value:n,label:t,attributes:i,default:r}}=e;return{value:n,label:t,attributes:i,default:r}}))}(t);return function(e){const n=(0,c.X)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[n,t])}function p(e){let{value:n,tabValues:t}=e;return t.some((e=>e.value===n))}function m(e){let{queryString:n=!1,groupId:t}=e;const r=(0,s.W6)(),a=function(e){let{queryString:n=!1,groupId:t}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!t)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return t??null}({queryString:n,groupId:t});return[(0,l.aZ)(a),(0,i.useCallback)((e=>{if(!a)return;const n=new URLSearchParams(r.location.search);n.set(a,e),r.replace({...r.location,search:n.toString()})}),[a,r])]}function g(e){const{defaultValue:n,queryString:t=!1,groupId:r}=e,a=u(e),[s,l]=(0,i.useState)((()=>function(e){let{defaultValue:n,tabValues:t}=e;if(0===t.length)throw new Error("Docusaurus error: the component requires at least one children component");if(n){if(!p({value:n,tabValues:t}))throw new Error(`Docusaurus error: The has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${t.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}const i=t.find((e=>e.default))??t[0];if(!i)throw new Error("Unexpected error: 0 tabValues");return i.value}({defaultValue:n,tabValues:a}))),[c,h]=m({queryString:t,groupId:r}),[g,x]=function(e){let{groupId:n}=e;const t=function(e){return e?`docusaurus.tab.${e}`:null}(n),[r,a]=(0,d.Dv)(t);return[r,(0,i.useCallback)((e=>{t&&a.set(e)}),[t,a])]}({groupId:r}),f=(()=>{const e=c??g;return p({value:e,tabValues:a})?e:null})();(0,o.A)((()=>{f&&l(f)}),[f]);return{selectedValue:s,selectValue:(0,i.useCallback)((e=>{if(!p({value:e,tabValues:a}))throw new Error(`Can't select invalid tab value=${e}`);l(e),h(e),x(e)}),[h,x,a]),tabValues:a}}var x=t(2303);const f={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var b=t(4848);function j(e){let{className:n,block:t,selectedValue:i,selectValue:s,tabValues:o}=e;const l=[],{blockElementScrollPositionUntilNextRender:c}=(0,a.a_)(),d=e=>{const n=e.currentTarget,t=l.indexOf(n),r=o[t].value;r!==i&&(c(n),s(r))},h=e=>{let n=null;switch(e.key){case"Enter":d(e);break;case"ArrowRight":{const t=l.indexOf(e.currentTarget)+1;n=l[t]??l[0];break}case"ArrowLeft":{const t=l.indexOf(e.currentTarget)-1;n=l[t]??l[l.length-1];break}}n?.focus()};return(0,b.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,r.A)("tabs",{"tabs--block":t},n),children:o.map((e=>{let{value:n,label:t,attributes:a}=e;return(0,b.jsx)("li",{role:"tab",tabIndex:i===n?0:-1,"aria-selected":i===n,ref:e=>l.push(e),onKeyDown:h,onClick:d,...a,className:(0,r.A)("tabs__item",f.tabItem,a?.className,{"tabs__item--active":i===n}),children:t??n},n)}))})}function S(e){let{lazy:n,children:t,selectedValue:r}=e;const a=(Array.isArray(t)?t:[t]).filter(Boolean);if(n){const e=a.find((e=>e.props.value===r));return e?(0,i.cloneElement)(e,{className:"margin-top--md"}):null}return(0,b.jsx)("div",{className:"margin-top--md",children:a.map(((e,n)=>(0,i.cloneElement)(e,{key:n,hidden:e.props.value!==r})))})}function y(e){const n=g(e);return(0,b.jsxs)("div",{className:(0,r.A)("tabs-container",f.tabList),children:[(0,b.jsx)(j,{...n,...e}),(0,b.jsx)(S,{...n,...e})]})}function v(e){const n=(0,x.A)();return(0,b.jsx)(y,{...e,children:h(e.children)},String(n))}},7324:(e,n,t)=>{t.d(n,{$E:()=>x,A3:()=>b,CW:()=>f,Dx:()=>d,F4:()=>u,Fi:()=>c,J_:()=>v,LQ:()=>j,Lf:()=>_,OO:()=>r,Q7:()=>S,b:()=>o,cy:()=>l,gg:()=>m,kl:()=>p,os:()=>h,pW:()=>a,ux:()=>g,vf:()=>i,xj:()=>s,xt:()=>y});const i="shell",r="database",a="application",s="bash",o="pwsh",l="zsh",c="maria",d="mysql",h="postgres",u="sqlite",p="application",m="bash",g="pwsh",x="zsh",f="MariaDB",b="MySQL",j="PostgreSQL",S="SQLite",y="tinyorm.org",v="$HOME/Code/c/",_="$env:USERPROFILE\\Code\\c\\"},8453:(e,n,t)=>{t.d(n,{R:()=>s,x:()=>o});var i=t(6540);const r={},a=i.createContext(r);function s(e){const n=i.useContext(a);return i.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function o(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:s(e.components),i.createElement(a.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/runtime~main.0072e5b9.js b/assets/js/runtime~main.0072e5b9.js deleted file mode 100644 index 10d3cb915..000000000 --- a/assets/js/runtime~main.0072e5b9.js +++ /dev/null @@ -1 +0,0 @@ -(()=>{"use strict";var e,a,t,r,o,n={},c={};function f(e){var a=c[e];if(void 0!==a)return a.exports;var t=c[e]={exports:{}};return n[e].call(t.exports,t,t.exports,f),t.exports}f.m=n,e=[],f.O=(a,t,r,o)=>{if(!t){var n=1/0;for(i=0;i=o)&&Object.keys(f.O).every((e=>f.O[e](t[d])))?t.splice(d--,1):(c=!1,o0&&e[i-1][2]>o;i--)e[i]=e[i-1];e[i]=[t,r,o]},f.n=e=>{var a=e&&e.__esModule?()=>e.default:()=>e;return f.d(a,{a:a}),a},t=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,f.t=function(e,r){if(1&r&&(e=this(e)),8&r)return e;if("object"==typeof e&&e){if(4&r&&e.__esModule)return e;if(16&r&&"function"==typeof e.then)return e}var o=Object.create(null);f.r(o);var n={};a=a||[null,t({}),t([]),t(t)];for(var c=2&r&&e;"object"==typeof c&&!~a.indexOf(c);c=t(c))Object.getOwnPropertyNames(c).forEach((a=>n[a]=()=>e[a]));return n.default=()=>e,f.d(o,n),o},f.d=(e,a)=>{for(var t in a)f.o(a,t)&&!f.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:a[t]})},f.f={},f.e=e=>Promise.all(Object.keys(f.f).reduce(((a,t)=>(f.f[t](e,a),a)),[])),f.u=e=>"assets/js/"+({27:"d459b1c4",48:"a94703ab",69:"e19c288b",82:"5b254f70",98:"a7bd4aaa",117:"3dd307b5",129:"8a8faf8d",138:"1a4e3797",153:"1222ea4e",170:"ba3d4959",258:"cb1e72f9",295:"21dc2778",304:"62a1276f",395:"0ab078a9",401:"17896441",467:"e3ac21cb",485:"59b1a96c",567:"22dd74f7",638:"7333c691",647:"5e95c892",742:"aba21aa0",755:"a4d3e054",871:"fb313d4e",957:"c141421f",983:"feaee7f3",995:"cbe663fe"}[e]||e)+"."+{27:"0427f3e3",48:"2ad1c777",69:"810e57ca",82:"45cd6b2c",98:"80cc43c0",117:"0ac9dd32",129:"1738552f",138:"1fc0de54",153:"85a6075d",170:"73f88dfd",237:"d109f2ba",258:"d5c02736",295:"83f6540a",304:"5965aaeb",395:"5134d01b",401:"7e577731",416:"a3ad28f7",446:"d7af1da2",462:"0011555c",467:"31fe5c57",485:"712dff19",567:"732ea904",638:"f1deefd4",647:"7024aad0",742:"cb1d9d7d",755:"74ed2104",871:"07531018",913:"3fa60236",957:"2356f0d5",983:"05b1279c",995:"201ad298"}[e]+".js",f.miniCssF=e=>{},f.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),f.o=(e,a)=>Object.prototype.hasOwnProperty.call(e,a),r={},o="tinyorm.org:",f.l=(e,a,t,n)=>{if(r[e])r[e].push(a);else{var c,d;if(void 0!==t)for(var b=document.getElementsByTagName("script"),i=0;i{c.onerror=c.onload=null,clearTimeout(s);var o=r[e];if(delete r[e],c.parentNode&&c.parentNode.removeChild(c),o&&o.forEach((e=>e(t))),a)return a(t)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:c}),12e4);c.onerror=l.bind(null,c.onerror),c.onload=l.bind(null,c.onload),d&&document.head.appendChild(c)}},f.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.p="/",f.gca=function(e){return e={17896441:"401",d459b1c4:"27",a94703ab:"48",e19c288b:"69","5b254f70":"82",a7bd4aaa:"98","3dd307b5":"117","8a8faf8d":"129","1a4e3797":"138","1222ea4e":"153",ba3d4959:"170",cb1e72f9:"258","21dc2778":"295","62a1276f":"304","0ab078a9":"395",e3ac21cb:"467","59b1a96c":"485","22dd74f7":"567","7333c691":"638","5e95c892":"647",aba21aa0:"742",a4d3e054:"755",fb313d4e:"871",c141421f:"957",feaee7f3:"983",cbe663fe:"995"}[e]||e,f.p+f.u(e)},(()=>{var e={354:0,869:0};f.f.j=(a,t)=>{var r=f.o(e,a)?e[a]:void 0;if(0!==r)if(r)t.push(r[2]);else if(/^(354|869)$/.test(a))e[a]=0;else{var o=new Promise(((t,o)=>r=e[a]=[t,o]));t.push(r[2]=o);var n=f.p+f.u(a),c=new Error;f.l(n,(t=>{if(f.o(e,a)&&(0!==(r=e[a])&&(e[a]=void 0),r)){var o=t&&("load"===t.type?"missing":t.type),n=t&&t.target&&t.target.src;c.message="Loading chunk "+a+" failed.\n("+o+": "+n+")",c.name="ChunkLoadError",c.type=o,c.request=n,r[1](c)}}),"chunk-"+a,a)}},f.O.j=a=>0===e[a];var a=(a,t)=>{var r,o,n=t[0],c=t[1],d=t[2],b=0;if(n.some((a=>0!==e[a]))){for(r in c)f.o(c,r)&&(f.m[r]=c[r]);if(d)var i=d(f)}for(a&&a(t);b{"use strict";var e,t,r,a,o,n={},c={};function f(e){var t=c[e];if(void 0!==t)return t.exports;var r=c[e]={exports:{}};return n[e].call(r.exports,r,r.exports,f),r.exports}f.m=n,e=[],f.O=(t,r,a,o)=>{if(!r){var n=1/0;for(i=0;i=o)&&Object.keys(f.O).every((e=>f.O[e](r[b])))?r.splice(b--,1):(c=!1,o0&&e[i-1][2]>o;i--)e[i]=e[i-1];e[i]=[r,a,o]},f.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return f.d(t,{a:t}),t},r=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,f.t=function(e,a){if(1&a&&(e=this(e)),8&a)return e;if("object"==typeof e&&e){if(4&a&&e.__esModule)return e;if(16&a&&"function"==typeof e.then)return e}var o=Object.create(null);f.r(o);var n={};t=t||[null,r({}),r([]),r(r)];for(var c=2&a&&e;"object"==typeof c&&!~t.indexOf(c);c=r(c))Object.getOwnPropertyNames(c).forEach((t=>n[t]=()=>e[t]));return n.default=()=>e,f.d(o,n),o},f.d=(e,t)=>{for(var r in t)f.o(t,r)&&!f.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},f.f={},f.e=e=>Promise.all(Object.keys(f.f).reduce(((t,r)=>(f.f[r](e,t),t)),[])),f.u=e=>"assets/js/"+({27:"d459b1c4",48:"a94703ab",69:"e19c288b",82:"5b254f70",98:"a7bd4aaa",117:"3dd307b5",129:"8a8faf8d",138:"1a4e3797",153:"1222ea4e",170:"ba3d4959",258:"cb1e72f9",295:"21dc2778",304:"62a1276f",395:"0ab078a9",401:"17896441",467:"e3ac21cb",485:"59b1a96c",567:"22dd74f7",638:"7333c691",647:"5e95c892",742:"aba21aa0",755:"a4d3e054",871:"fb313d4e",957:"c141421f",983:"feaee7f3",995:"cbe663fe"}[e]||e)+"."+{27:"0427f3e3",48:"2ad1c777",69:"810e57ca",82:"45cd6b2c",98:"80cc43c0",117:"4e5638cc",129:"335d5697",138:"1fc0de54",153:"85a6075d",170:"52113abf",237:"d109f2ba",258:"d5c02736",295:"83f6540a",304:"f25d0360",395:"2fa2fa93",401:"7e577731",416:"a3ad28f7",446:"d7af1da2",462:"0011555c",467:"31fe5c57",485:"712dff19",567:"732ea904",638:"f1deefd4",647:"7024aad0",742:"cb1d9d7d",755:"74ed2104",871:"07531018",913:"3fa60236",957:"2356f0d5",983:"05b1279c",995:"201ad298"}[e]+".js",f.miniCssF=e=>{},f.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),f.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),a={},o="tinyorm.org:",f.l=(e,t,r,n)=>{if(a[e])a[e].push(t);else{var c,b;if(void 0!==r)for(var d=document.getElementsByTagName("script"),i=0;i{c.onerror=c.onload=null,clearTimeout(s);var o=a[e];if(delete a[e],c.parentNode&&c.parentNode.removeChild(c),o&&o.forEach((e=>e(r))),t)return t(r)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:c}),12e4);c.onerror=l.bind(null,c.onerror),c.onload=l.bind(null,c.onload),b&&document.head.appendChild(c)}},f.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.p="/",f.gca=function(e){return e={17896441:"401",d459b1c4:"27",a94703ab:"48",e19c288b:"69","5b254f70":"82",a7bd4aaa:"98","3dd307b5":"117","8a8faf8d":"129","1a4e3797":"138","1222ea4e":"153",ba3d4959:"170",cb1e72f9:"258","21dc2778":"295","62a1276f":"304","0ab078a9":"395",e3ac21cb:"467","59b1a96c":"485","22dd74f7":"567","7333c691":"638","5e95c892":"647",aba21aa0:"742",a4d3e054:"755",fb313d4e:"871",c141421f:"957",feaee7f3:"983",cbe663fe:"995"}[e]||e,f.p+f.u(e)},(()=>{var e={354:0,869:0};f.f.j=(t,r)=>{var a=f.o(e,t)?e[t]:void 0;if(0!==a)if(a)r.push(a[2]);else if(/^(354|869)$/.test(t))e[t]=0;else{var o=new Promise(((r,o)=>a=e[t]=[r,o]));r.push(a[2]=o);var n=f.p+f.u(t),c=new Error;f.l(n,(r=>{if(f.o(e,t)&&(0!==(a=e[t])&&(e[t]=void 0),a)){var o=r&&("load"===r.type?"missing":r.type),n=r&&r.target&&r.target.src;c.message="Loading chunk "+t+" failed.\n("+o+": "+n+")",c.name="ChunkLoadError",c.type=o,c.request=n,a[1](c)}}),"chunk-"+t,t)}},f.O.j=t=>0===e[t];var t=(t,r)=>{var a,o,n=r[0],c=r[1],b=r[2],d=0;if(n.some((t=>0!==e[t]))){for(a in c)f.o(c,a)&&(f.m[a]=c[a]);if(b)var i=b(f)}for(t&&t(r);dgtag("config","AW-989655383"),gtag("event","conversion",{send_to:"AW-989655383/vDYzCM--ks4DENfi89cD"}) - + diff --git a/building/migrations.html b/building/migrations.html index 82870b8c4..1792ce99a 100644 --- a/building/migrations.html +++ b/building/migrations.html @@ -14,7 +14,7 @@ - + @@ -80,7 +80,7 @@

Main file
tip

To paste a source code correctly in vim, press Shift + p.

And paste the following code.

-
main.cpp
#include <orm/db.hpp>

#include <tom/application.hpp>

#include "migrations/2014_10_12_000000_create_posts_table.hpp"

#include "seeders/databaseseeder.hpp"

using Orm::DatabaseManager;
using Orm::DB;

using TomApplication = Tom::Application;

using namespace Migrations; // NOLINT(google-build-using-namespace)
using namespace Seeders; // NOLINT(google-build-using-namespace)

/*! Create the database manager instance and add a database connection. */
std::shared_ptr<DatabaseManager> setupDatabaseManager();

/*! C++ main function. */
int main(int argc, char *argv[])
{
try {
// Ownership of the shared_ptr()
auto db = setupDatabaseManager();

return TomApplication(argc, argv, std::move(db), "TOM_EXAMPLE_ENV")
.migrations<CreatePostsTable>()
.seeders<DatabaseSeeder>()
// Fire it up 🔥🚀✨
.run();

} catch (const std::exception &e) {

TomApplication::logException(e);
}

return EXIT_FAILURE;
}

std::shared_ptr<DatabaseManager> setupDatabaseManager()
{
using namespace Orm::Constants; // NOLINT(google-build-using-namespace)

// Ownership of the shared_ptr()
return DB::create({
{driver_, QMYSQL},
{host_, qEnvironmentVariable("DB_MYSQL_HOST", H127001)},
{port_, qEnvironmentVariable("DB_MYSQL_PORT", P3306)},
{database_, qEnvironmentVariable("DB_MYSQL_DATABASE", EMPTY)},
{username_, qEnvironmentVariable("DB_MYSQL_USERNAME", EMPTY)},
{password_, qEnvironmentVariable("DB_MYSQL_PASSWORD", EMPTY)},
{charset_, qEnvironmentVariable("DB_MYSQL_CHARSET", UTF8MB4)},
{collation_, qEnvironmentVariable("DB_MYSQL_COLLATION", UTF8MB40900aici)},
{timezone_, TZ00},
/* Specifies what time zone all QDateTime-s will have, the overridden default is
the Qt::UTC, set to the Qt::LocalTime or QtTimeZoneType::DontConvert to use
the system local time. */
{qt_timezone, QVariant::fromValue(Qt::UTC)},
{strict_, true},
},
QStringLiteral("tinyorm_tom_mysql")); // shell:connection
}
+
main.cpp
#include <orm/db.hpp>

#include <tom/application.hpp>

#include "migrations/2014_10_12_000000_create_posts_table.hpp"

#include "seeders/databaseseeder.hpp"

using Orm::DatabaseManager;
using Orm::DB;

using TomApplication = Tom::Application;

using namespace Migrations; // NOLINT(google-build-using-namespace)
using namespace Seeders; // NOLINT(google-build-using-namespace)

/*! Create the database manager instance and add a database connection. */
std::shared_ptr<DatabaseManager> setupDatabaseManager();

/*! C++ main function. */
int main(int argc, char *argv[])
{
try {
// Ownership of the shared_ptr()
auto db = setupDatabaseManager();

return TomApplication(argc, argv, std::move(db), "TOM_EXAMPLE_ENV")
.migrations<CreatePostsTable>()
.seeders<DatabaseSeeder>()
// Fire it up 🔥🚀✨
.run();

} catch (const std::exception &e) {

TomApplication::logException(e);
}

return EXIT_FAILURE;
}

std::shared_ptr<DatabaseManager> setupDatabaseManager()
{
using namespace Orm::Constants; // NOLINT(google-build-using-namespace)

// Ownership of the shared_ptr()
return DB::create({
{driver_, QMYSQL},
{host_, qEnvironmentVariable("DB_MYSQL_HOST", H127001)},
{port_, qEnvironmentVariable("DB_MYSQL_PORT", P3306)},
{database_, qEnvironmentVariable("DB_MYSQL_DATABASE", EMPTY)},
{username_, qEnvironmentVariable("DB_MYSQL_USERNAME", EMPTY)},
{password_, qEnvironmentVariable("DB_MYSQL_PASSWORD", EMPTY)},
{charset_, qEnvironmentVariable("DB_MYSQL_CHARSET", UTF8MB4)},
{collation_, qEnvironmentVariable("DB_MYSQL_COLLATION", UTF8MB40900aici)},
{timezone_, TZ00},
/* Specifies what time zone all QDateTime-s will have, the overridden default is
the QTimeZone::UTC, set to the QTimeZone::LocalTime or
QtTimeZoneType::DontConvert to use the system local time. */
{qt_timezone, QVariant::fromValue(QTimeZone::UTC)},
{strict_, true},
},
QStringLiteral("tinyorm_tom_mysql")); // shell:connection
}
tip

If you have defined more database connections then you can tag the lines with the database connection names with the // shell:connection comment and this connection names will be provided to the bash, zsh, pwsh completions for the --database= option 😎, example.

Migrations

If you have already built the tom application then you can generate a migrations using the make:migration command 😎.

diff --git a/building/tinyorm.html b/building/tinyorm.html index 97b4a4d0f..20ab1f1f4 100644 --- a/building/tinyorm.html +++ b/building/tinyorm.html @@ -14,7 +14,7 @@ - + @@ -227,7 +227,7 @@
# vcpkg - range-v3 and tabulate
# ---
INCLUDEPATH += $$quote(<your_path>/vcpkg/installed/x64-windows/include/)

You can also use TinyORM's qmake function tiny_add_system_includepath() which handles INCLUDEPATH in a cross-platform way.

-
# vcpkg - range-v3 and tabulate
# ---
load(tiny_system_includepath)
tiny_add_system_includepath(<your_path>/vcpkg/installed/x64-linux/include/)
+
# vcpkg - range-v3 and tabulate
# ---
load(private/tiny_system_includepath)
tiny_add_system_includepath(<your_path>/vcpkg/installed/x64-linux/include/)

Do not forget to add TinyOrm0.dll on the path on Windows and on the LD_LIBRARY_PATH on Linux, so your application can find it during execution.

$env:Path = "\TinyORM\TinyORM-builds-qmake\build-debug;" + $env:Path
tip

On Linux -isystem marks the directory as a system directory, it prevents warnings.

On Windows you can use QMAKE_CXXFLAGS_WARN_ON = -external:anglebrackets -external:W0, it applies a warning level 0 to the angel bracket includes; #include <file>.

With the Clang-cl with MSVC you can use -imsvc.

diff --git a/database/getting-started.html b/database/getting-started.html index b3ab7c7f3..d5f07ab4a 100644 --- a/database/getting-started.html +++ b/database/getting-started.html @@ -14,7 +14,7 @@ - + @@ -52,7 +52,7 @@

Introduct
note

TinyORM's code is ready and designed to simply add support for the SQL Server.

Configuration

You can create and configure a new database connection using the create method provided by the DB facade:

-
#include <orm/db.hpp>

using Orm::DB;

// Ownership of a shared_ptr()
auto manager = DB::create({
{"driver", "QMYSQL"},
{"host", qEnvironmentVariable("DB_HOST", "127.0.0.1")},
{"port", qEnvironmentVariable("DB_PORT", "3306")},
{"database", qEnvironmentVariable("DB_DATABASE", "")},
{"username", qEnvironmentVariable("DB_USERNAME", "root")},
{"password", qEnvironmentVariable("DB_PASSWORD", "")},
{"charset", qEnvironmentVariable("DB_CHARSET", "utf8mb4")},
{"collation", qEnvironmentVariable("DB_COLLATION", "utf8mb4_0900_ai_ci")},
{"timezone", "+00:00"},
/* Specifies what time zone all QDateTime-s will have, the overridden default is
the Qt::UTC, set to the Qt::LocalTime or QtTimeZoneType::DontConvert to use
the system local time. */
{"qt_timezone", QVariant::fromValue(Qt::UTC)},
{"prefix", ""},
{"prefix_indexes", false},
{"strict", true},
{"engine", "InnoDB"},
{"options", QVariantHash()},
});
#include <orm/db.hpp>
#include <orm/utils/configuration.hpp>

using Orm::DB;

using ConfigUtils = Orm::Utils::Configuration;

using namespace Orm::Constants; // NOLINT(google-build-using-namespace)

// Ownership of a shared_ptr()
auto manager = DB::create({
{driver_, QMYSQL},
{host_, qEnvironmentVariable("DB_HOST", H127001)},
{port_, qEnvironmentVariable("DB_PORT", P3306)},
{database_, qEnvironmentVariable("DB_DATABASE", EMPTY)},
{username_, qEnvironmentVariable("DB_USERNAME", ROOT)},
{password_, qEnvironmentVariable("DB_PASSWORD", EMPTY)},
{charset_, qEnvironmentVariable("DB_CHARSET", UTF8MB4)},
{collation_, qEnvironmentVariable("DB_COLLATION", UTF8MB40900aici)},
// SSL-related
{ssl_ca, QStringLiteral("C:/mysql/data/ca.pem")},
{ssl_cert, QStringLiteral("C:/mysql/data/client-cert.pem")},
{ssl_key, QStringLiteral("C:/mysql/data/client-key.pem")},
{ssl_mode, VerifyCA},
// Or
// {options, ConfigUtils::mysqlSslOptions()},
{timezone_, TZ00},
/* Specifies what time zone all QDateTime-s will have, the overridden default is
the Qt::UTC, set to the Qt::LocalTime or QtTimeZoneType::DontConvert to use
the system local time. */
{qt_timezone, QVariant::fromValue(Qt::UTC)},
{prefix_, EMPTY},
{prefix_indexes, false},
{strict_, true},
// {isolation_level, QStringLiteral("REPEATABLE READ")}, // MySQL default is REPEATABLE READ for InnoDB
{engine_, InnoDB},
{Version, {}}, // Autodetect
{options_, QVariantHash()},
// Examples
// {options_, QStringLiteral("MYSQL_OPT_CONNECT_TIMEOUT = 1 ; MYSQL_OPT_READ_TIMEOUT=1")},
// {options_, QVariantHash {{QStringLiteral("MYSQL_OPT_CONNECT_TIMEOUT"), 1},
// {QStringLiteral("MYSQL_OPT_READ_TIMEOUT"), 1}}},
});
+
#include <orm/db.hpp>

using Orm::DB;

// Ownership of a shared_ptr()
auto manager = DB::create({
{"driver", "QMYSQL"},
{"host", qEnvironmentVariable("DB_HOST", "127.0.0.1")},
{"port", qEnvironmentVariable("DB_PORT", "3306")},
{"database", qEnvironmentVariable("DB_DATABASE", "")},
{"username", qEnvironmentVariable("DB_USERNAME", "root")},
{"password", qEnvironmentVariable("DB_PASSWORD", "")},
{"charset", qEnvironmentVariable("DB_CHARSET", "utf8mb4")},
{"collation", qEnvironmentVariable("DB_COLLATION", "utf8mb4_0900_ai_ci")},
{"timezone", "+00:00"},
/* Specifies what time zone all QDateTime-s will have, the overridden default is
the QTimeZone::UTC, set to the QTimeZone::LocalTime or
QtTimeZoneType::DontConvert to use the system local time. */
{"qt_timezone", QVariant::fromValue(QTimeZone::UTC)},
{"prefix", ""},
{"prefix_indexes", false},
{"strict", true},
{"engine", "InnoDB"},
{"options", QVariantHash()},
});
#include <orm/db.hpp>
#include <orm/utils/configuration.hpp>

using Orm::DB;

using ConfigUtils = Orm::Utils::Configuration;

using namespace Orm::Constants; // NOLINT(google-build-using-namespace)

// Ownership of a shared_ptr()
auto manager = DB::create({
{driver_, QMYSQL},
{host_, qEnvironmentVariable("DB_HOST", H127001)},
{port_, qEnvironmentVariable("DB_PORT", P3306)},
{database_, qEnvironmentVariable("DB_DATABASE", EMPTY)},
{username_, qEnvironmentVariable("DB_USERNAME", ROOT)},
{password_, qEnvironmentVariable("DB_PASSWORD", EMPTY)},
{charset_, qEnvironmentVariable("DB_CHARSET", UTF8MB4)},
{collation_, qEnvironmentVariable("DB_COLLATION", UTF8MB40900aici)},
// SSL-related
{ssl_ca, QStringLiteral("C:/mysql/data/ca.pem")},
{ssl_cert, QStringLiteral("C:/mysql/data/client-cert.pem")},
{ssl_key, QStringLiteral("C:/mysql/data/client-key.pem")},
{ssl_mode, VerifyCA},
// Or
// {options, ConfigUtils::mysqlSslOptions()},
{timezone_, TZ00},
/* Specifies what time zone all QDateTime-s will have, the overridden default is
the QTimeZone::UTC, set to the QTimeZone::LocalTime or
QtTimeZoneType::DontConvert to use the system local time. */
{qt_timezone, QVariant::fromValue(QTimeZone::UTC)},
{prefix_, EMPTY},
{prefix_indexes, false},
{strict_, true},
// {isolation_level, QStringLiteral("REPEATABLE READ")}, // MySQL default is REPEATABLE READ for InnoDB
{engine_, InnoDB},
{Version, {}}, // Autodetect
{options_, QVariantHash()},
// Examples
// {options_, QStringLiteral("MYSQL_OPT_CONNECT_TIMEOUT = 1 ; MYSQL_OPT_READ_TIMEOUT=1")},
// {options_, QVariantHash {{QStringLiteral("MYSQL_OPT_CONNECT_TIMEOUT"), 1},
// {QStringLiteral("MYSQL_OPT_READ_TIMEOUT"), 1}}},
});

The first argument is configuration hash which is of type QVariantHash and the second argument specifies the name of the connection, this connection will also be a default connection. You can configure multiple database connections at once and choose the needed one before executing SQL query, section Using Multiple Database Connections describes how to create and use multiple database connections.

You may also configure connection options by options key as QVariantHash or QString, you can pass any connection options supported by QSqlDatabase.

You can also configure Transaction Isolation Levels for MySQL connection with the isolation_level configuration option.

@@ -60,6 +60,7 @@

Configur

Breaking values are as follows; use an upsert alias on the MySQL >=8.0.19 and remove the NO_AUTO_CREATE_USER sql mode on the MySQL >=8.0.11 if the strict mode is enabled.

info

A database connection is resolved lazily, which means that the connection configuration is only saved after the DB::create method call. The connection will be resolved after you run some query or you can create it using the DB::connection method.

tip

You can also use predefined string constants to avoid unnecessary QString instantiations, as used in the tom migrations example.

+
info

See Date Casting, Serialization & Timezones if you have problems with qt_timezone configuration option and QTimeZone value on older Qt <6.5 versions.

SQLite Configuration

SQLite databases are contained within a single file on your filesystem. You can create a new SQLite database using the touch command in your terminal: touch database.sqlite3. After the database has been created, you may configure SQLite database connection:

#include <orm/db.hpp>

// Ownership of a shared_ptr()
auto manager = DB::create({
{"driver", "QSQLITE"},
{"database", qEnvironmentVariable("DB_DATABASE", "/absolute/path/to/database.sqlite3")},
{"foreign_key_constraints", qEnvironmentVariable("DB_FOREIGN_KEYS", "true")},
{"check_database_exists", true},
{"prefix", ""},
});
diff --git a/database/migrations.html b/database/migrations.html index 7c515878a..30e560577 100644 --- a/database/migrations.html +++ b/database/migrations.html @@ -14,7 +14,7 @@ - + diff --git a/database/query-builder.html b/database/query-builder.html index af1d11256..24fbba876 100644 --- a/database/query-builder.html +++ b/database/query-builder.html @@ -14,7 +14,7 @@ - + diff --git a/database/seeding.html b/database/seeding.html index 38bbdc123..87d6794a2 100644 --- a/database/seeding.html +++ b/database/seeding.html @@ -14,7 +14,7 @@ - + diff --git a/dependencies.html b/dependencies.html index ab3842932..26fd64cb9 100644 --- a/dependencies.html +++ b/dependencies.html @@ -14,7 +14,7 @@ - + diff --git a/features-summary.html b/features-summary.html index 4764ab0e5..7a0dfd1a4 100644 --- a/features-summary.html +++ b/features-summary.html @@ -14,7 +14,7 @@ - + diff --git a/index.html b/index.html index 30c9fb91a..8f252bcc6 100644 --- a/index.html +++ b/index.html @@ -14,7 +14,7 @@ - + diff --git a/search.html b/search.html index ad0678aa1..2935e7b69 100644 --- a/search.html +++ b/search.html @@ -14,7 +14,7 @@ - + diff --git a/sponsors.html b/sponsors.html index 0dd179467..4d73dd2bb 100644 --- a/sponsors.html +++ b/sponsors.html @@ -14,7 +14,7 @@ - + diff --git a/supported-compilers.html b/supported-compilers.html index 281de8eed..ba74453d8 100644 --- a/supported-compilers.html +++ b/supported-compilers.html @@ -14,7 +14,7 @@ - + diff --git a/tinydrivers/getting-started.html b/tinydrivers/getting-started.html index c90420533..2c79d4e1b 100644 --- a/tinydrivers/getting-started.html +++ b/tinydrivers/getting-started.html @@ -14,7 +14,7 @@ - + diff --git a/tinyorm/casts.html b/tinyorm/casts.html index 8af519f6e..a8ef879e3 100644 --- a/tinyorm/casts.html +++ b/tinyorm/casts.html @@ -14,7 +14,7 @@ - + @@ -104,8 +104,9 @@

Date Cast

Define a u_timeFormat data member on your model to specify the format that should be used when storing a model's times within your database:

/*! The storage format of the model's time columns. */
inline static QString u_timeFormat {"HH:mm:ss.zzz"};

Date Casting, Serialization & Timezones

-

By default, the CastType::CustomQDate and CastType::CustomQDateTime casts will serialize dates to a UTC ISO-8601 date string (yyyy-MM-ddTHH:mm:ss.zzzZ), regardless of the timezone specified in your database connection's qt_timezone configuration option. You are strongly encouraged to always use this serialization format, as well as to store your application's dates in the UTC timezone by not changing your database connection's qt_timezone configuration option from its default Qt::UTC value. Consistently using the UTC timezone throughout your application will provide the maximum level of interoperability with other date manipulation libraries or services written in any programming language.

+

By default, the CastType::CustomQDate and CastType::CustomQDateTime casts will serialize dates to a UTC ISO-8601 date string (yyyy-MM-ddTHH:mm:ss.zzzZ), regardless of the timezone specified in your database connection's qt_timezone configuration option. You are strongly encouraged to always use this serialization format, as well as to store your application's dates in the UTC timezone by not changing your database connection's qt_timezone configuration option from its default QTimeZone::UTC value. Consistently using the UTC timezone throughout your application will provide the maximum level of interoperability with other date manipulation libraries or services written in any programming language.

If a custom format is applied to the CastType::CustomQDate or CastType::CustomQDateTime cast types, such as {CastType::CustomQDateTime, "yyyy-MM-dd HH:mm:ss"}, the inner timezone of the QDateTime instance will be used during date serialization. Typically, this will be the timezone specified in your database connection's qt_timezone configuration option.

+
info

Passing the Qt::TimeSpec (eg. Qt::UTC or Qt::LocalTime) to QDateTime methods was deprecated in Qt >=6.5, it was replaced by the enum QTimeZone::Initialization (QTimeZone::UTC and QTimeZone::LocalTime).

If you need to support older and newer versions of Qt at the same time, you can use the Orm::QtTimeZoneConfig::utc() or QtTimeZoneConfig::localTime() factory methods to create the QtTimeZoneConfig instance.

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();
diff --git a/tinyorm/collections.html b/tinyorm/collections.html index bfb34e375..d599ddf3e 100644 --- a/tinyorm/collections.html +++ b/tinyorm/collections.html @@ -14,7 +14,7 @@ - + @@ -47,7 +47,7 @@

C

The ModelsCollection<Model> is implicitly convertible and assignable from the QVector<Model>:

QVector<User> usersVector {
{{"name", "Kate"}, {"votes", 150}},
{{"name", "John"}, {"votes", 200}},
};

ModelsCollection<User> users(usersVector);
users = usersVector;

Alternatively, you can use the Orm::collect<Model> helper function to create a ModelsCollection from the given attributes:

-
ModelsCollection<User> users = Orm::collect<User>({
{{"name", "Kate"}, {"added_on", QDateTime::currentDateTimeUtc()}},
{{"name", "John"}, {"added_on", QDateTime({2023, 6, 1}, {13, 46, 15}, Qt::UTC)}},
});
+
ModelsCollection<User> users = Orm::collect<User>({
{{"name", "Kate"}, {"added_on", QDateTime::currentDateTimeUtc()}},
{{"name", "John"}, {"added_on", QDateTime({2023, 6, 1}, {13, 46, 15}, QTimeZone::UTC)}},
});
caution

The Orm::collect<Model> function is mandatory if your attributes contain the QDateTime instance, you can read more about this problem here.

note

The results of TinyORM queries are always returned as ModelsCollection instances.

Available Methods

@@ -106,6 +106,6 @@

Avai whereNotNull whereNull

note

For a better understanding of the following examples, many of the variable declarations below use actual types instead of the auto keyword.

-

all()

The all method returns a copy of the underlying vector represented by the collection:

QVector<User> = users.all();
note

The toBase is an alias to the all method.

contains()

The contains method may be used to determine if a given model instance is contained by the collection. This method accepts a primary key or a model instance:

users.contains(1);

users.contains(User::find(1));

Alternatively, you may pass a lambda expression to the contains method to determine if a model exists in the collection matching a given truth test:

users.contains([](const User *const user)
{
return user->getKeyCasted() == 2;
});

For the inverse of contains, see the doesntContain method.

doesntContain()

The doesntContain method determines whether the collection does not contain a given item. This method accepts a primary key or a model instance:

users.doesntContain(1);

users.doesntContain(User::find(1));

Alternatively, you may pass a lambda expression to the doesntContain method to determine if a model does not exist in the collection matching a given truth test:

users.doesntContain([](const User *const user)
{
return user->getKeyCasted() == 2;
});

For the inverse of doesntContain, see the contains method.

each()

The each method iterates over the models in the collection and passes each model to the lambda expression:

ModelsCollection<User> users = Post::whereEq("user_id", 1)->get();

users.each([](User *const user)
{
// ...
});

If you would like to stop iterating through the models, you may return false from your lambda expression:

users.each([](User *const user)
{
if (/* condition */)
return false;

// Some logic

return true;
});

You may also pass the lambda expression with two parameters, whereas the second one is an index:

users.each([](User *const user, const std::size_t index)
{
// ...
});

The each method returns an lvalue reference to the currently processed collection.

It can be also called on ModelsCollection rvalues, it returns an rvalue reference in this case.

except()

The except method returns all of the models that do not have the given primary keys:

ModelsCollection<User *> usersResult = users.except({1, 2, 3});

All of the models are returned if the ids argument is empty except({}).

The order of models in the collection is preserved.

For the inverse of except, see the only method.

filter()

The filter method filters the collection using the lambda expression, keeping only those models that pass a given truth test:

auto usersBanned = users.filter([](const User *const user)
{
return user->getAttribute<bool>("is_banned");
});

You may also pass the lambda expression with two parameters, whereas the second one is an index:

auto usersBanned = users.filter([](const User *const user,
const std::size_t index)
{
return index < 10 && user->getAttribute<bool>("is_banned");
});

If no lambda expression is supplied, all models of the collection that are equivalent to the nullptr will be removed:

ModelsCollection<User> usersRaw = User::findMany({1, 2});
ModelsCollection<User *> users {&usersRaw[0], nullptr, &usersRaw[1]};

ModelsCollection<User *> filtered = users.filter();

// {1, 2}

For the inverse of filter, see the reject method.

find()

The find method returns the model that has a primary key matching the given key:

User *const user = users.find(1);

If you pass a model instance, find will attempt to return a model matching the primary key:

User *user = users.find(anotherUser);

The two overloads above also accept the second defaultModel model argument, which will be returned if a model was not found in the collection, its default value is the nullptr.

Alternatively, may pass more IDs and find will return all models which have a primary key within the given unordered set:

ModelsCollection<User *> usersMany = users.find({1, 2});

This overload internally calls the only method.

first()

The first method returns the first model in the collection that passes a given truth test:

ModelsCollection<User> users {
{{"name", "Kate"}, {"votes", 150}},
{{"name", "John"}, {"votes", 200}},
{{"name", "Jack"}, {"votes", 400}},
};

User *user = users.first([](User *const user)
{
return user->getAttribute<quint64>("votes") > 150;
});

// {{"name", "John"}, {"votes", 200}}

If no model passes a given truth test then the value of the second defaultModel argument will be returned, its default value is the nullptr.

using NullVariant = Orm::Utils::NullVariant;

User defaultUser {{"name", NullVariant::QString()},
{"votes", NullVariant::ULongLong()}};

User *user = users.first([](User *const user)
{
return user->getAttribute<quint64>("votes") > 500;
},
&defaultUser);

/*
{{"name", NullVariant::QString()},
{"votes", NullVariant::ULongLong()}}
*/

You can also call all first overloads provided by the QList::first.

firstWhere()

The firstWhere method returns the first model in the collection with the given column / value pair:

using NullVariant = Orm::Utils::NullVariant;

ModelsCollection<User> users {
{{"name", "Leon"}, {"age", NullVariant::UShort()}},
{{"name", "Jill"}, {"age", 14}},
{{"name", "Jack"}, {"age", 23}},
{{"name", "Jill"}, {"age", 84}},
};

auto user = users.firstWhereEq("name", "Linda");

// {{"name", "Jill"}, {"age", 14}}

You may also call the firstWhere method with a comparison operator:

users.firstWhere("age", ">=", 18);

// {{"name", "Jack"}, {"age", 23}}

fresh()

The fresh method retrieves a fresh instance of each model in the collection from the database. In addition, any specified relationships will be eager loaded:

auto usersFresh = users.fresh();

auto usersFresh = users.fresh("comments");

auto usersFresh = users.fresh("posts:id,name");

auto usersFresh = users.fresh({"comments", "posts:id,name"});

The relations argument format is the same as for TinyBuilder's load method.

implode()

The implode method joins attributes by the given column and the "glue" string you wish to place between the values:

ModelsCollection<Product> products {
{{"product", "Desk"}, {"price", 200}},
{{"product", "Chair"}, {"price", 100}},
};

products.implode("product", ", ");

// {Desk, Chair}

The default "glue" value is an empty string "".

isEmpty()

The isEmpty method returns true if the collection is empty; otherwise, false is returned:

ModelsCollection<User>().isEmpty();

// true

isNotEmpty()

The isNotEmpty method returns true if the collection is not empty; otherwise, false is returned:

ModelsCollection<User>().isNotEmpty();

// false

last()

The last method returns the last model in the collection that passes a given truth test:

ModelsCollection<User> users {
{{"name", "Kate"}, {"votes", 150}},
{{"name", "John"}, {"votes", 200}},
{{"name", "Jack"}, {"votes", 400}},
{{"name", "Rose"}, {"votes", 350}},
};

User *user = users.last([](User *const user)
{
return user->getAttribute<quint64>("votes") < 300;
});

// {{"name", "John"}, {"votes", 200}}

If no model passes a given truth test then the value of the second defaultModel argument will be returned, its default value is the nullptr.

using NullVariant = Orm::Utils::NullVariant;

User defaultUser {{"name", NullVariant::QString()},
{"votes", NullVariant::ULongLong()}};

User *user = users.last([](User *const user)
{
return user->getAttribute<quint64>("votes") < 100;
},
&defaultUser);

/*
{{"name", NullVariant::QString()},
{"votes", NullVariant::ULongLong()}}
*/

You can also call all last overloads provided by the QList::last.

load()

The load method eager loads the given relationships for all models in the collection:

users.load({"comments", "posts"});

users.load("comments.author");

users.load({{"comments"}, {"posts", [](auto &query)
{
query.whereEq("active", true);
}}});

The relations argument format is the same as for TinyBuilder's load method.

map()

The map method iterates through the collection and passes a copy of each model to the given lambda expression. The lambda expression is free to modify the model and return it, thus forming a new collection of modified models:

ModelsCollection<User> users {
{{"name", "John"}, {"votes", 200}},
{{"name", "Jack"}, {"votes", 400}},
};

auto usersAdded = users.map([](User &&userCopy)
{
if (userCopy.getAttribute<QString>("name") == "John")
userCopy["votes"] = userCopy.getAttribute<quint64>("votes") + 1;

return std::move(userCopy);
});

/*
{
{{"name", "John"}, {"price", 201}},
{{"name", "Jack"}, {"price", 400}},
}
*/

The second map overload allows to return the QVector<T>:

QVector<quint64> usersAdded = users.map<quint64>([](User &&userCopy)
{
const auto votesRef = userCopy["votes"];

if (userCopy.getAttribute<QString>("name") == "John")
votesRef = userCopy.getAttribute<quint64>("votes") + 1;

return votesRef->value<quint64>();
});

// {201, 400}

Both overloads allow to pass the lambda expression with two arguments, whereas the second argument can be an index of the std::size_t type.

caution

Like most other collection methods, map returns a new collection instance; it does not modify the collection it is called on. If you want to modify the original collection in place, use the each method.

info

The model copy is passed to the lambda expression even if the map iterates over a collection of model pointers ModelsCollection<Model *>. The models are dereferenced behind the scene.

mapWithKeys()

The mapWithKeys method iterates through the collection and passes each model to the given lambda expression. It returns the std::unordered_map<K, V> and the lambda expression should return the std::pair<K, V> containing a single column / value pair:

ModelsCollection<User> users {
{{"id", 1}, {"name", "John"}, {"email", "john@example.com"}},
{{"id", 2}, {"name", "Jill"}, {"email", "jill@example.com"}},
};

auto usersMap = users.mapWithKeys<quint64, QString>(
[](User *const user) -> std::pair<quint64, QString>
{
return {user->getKeyCasted(), user->getAttribute<QString>("name")};
});

// {{1, 'John'}, {2, 'Jill'}}

You can also map IDs to the model pointers:

auto usersMap = users.mapWithKeys<quint64, User *>(
[](User *const user) -> std::pair<quint64, User *>
{
return {user->getKeyCasted(), user};
});

mapWithModelKeys()

The mapWithModelKeys maps the primary keys to the Model *, it returns the std::unordered_map<Model::KeyType, Model *>:

auto usersMap = users.mapWithModelKeys();

modelKeys()

The modelKeys method returns the primary keys for all models in the collection:

ModelsCollection<User> users {
{{"id", 1}, {"name", "John"}},
{{"id", 2}, {"name", "Jill"}},
{{"id", 3}, {"name", "Kate"}},
{{"id", 5}, {"name", "Rose"}},
};

users.modelKeys(); // Returns QVector<QVariant>
users.modelKeys<quint64>();

// {1, 2, 3, 5}

only()

The only method returns all of the models that have the given primary keys:

ModelsCollection<User *> usersResult = users.only({1, 2, 3});

An empty collection is returned if the ids argument is empty only({}).

The order of models in the collection is preserved.

For the inverse of only, see the except method.

pluck()

The pluck method retrieves all of the values for a given column, the following overload returns the QVector<QVariant>:

ModelsCollection<Product> products {
{{"id", 1}, {"name", "Desk"}},
{{"id", 2}, {"name", "Chair"}},
};

auto plucked = products.pluck("name");

// {Desk, Chair}

The second overload allows returning the custom type QVector<T>:

auto plucked = products.pluck<QString>("name");

You may also specify how you wish the resulting collection to be keyed, this overload returns the std::map<T, QVariant>:

auto plucked = products.pluck<quint64>("name", "id");

// {{1, "Desk"}, {2, "Chair"}}

If duplicate keys exist, the last matching attribute will be inserted into the plucked collection:

ModelsCollection<Product> collection {
{{"brand", "Tesla"}, {"color", "red"}},
{{"brand", "Pagani"}, {"color", "white"}},
{{"brand", "Tesla"}, {"color", "black"}},
{{"brand", "Pagani"}, {"color", "orange"}},
};

auto plucked = collection.pluck<QString>("color", "brand");

// {{'Tesla', 'black'}, {'Pagani', 'orange"}}

reject()

The reject method filters the collection using the given lambda expression. The lambda should return true if the model should be removed from the resulting collection:

auto usersWithNote = users.reject([](const User *const user)
{
return user->getAttribute("note").isNull();
});

You may also pass the lambda expression with two arguments, whereas the second argument can be an index of the std::size_t type.

For the inverse of the reject method, see the filter method.

sort()

The sort method sorts the models collection by primary keys:

ModelsCollection<User> users {
{{"id", 2}, {"name", "Kate"}},
{{"id", 3}, {"name", "John"}},
{{"id", 1}, {"name", "Jack"}},
};

auto sorted = users.sort();

/*
{
{{"id", 1}, {"name", "Jack"}},
{{"id", 2}, {"name", "Kate"}},
{{"id", 3}, {"name", "John"}},
}
*/

You may pass a predicate and projection callbacks to the sort method with your own algorithms. Refer to the CPP reference documentation on ranges::sort, which is what the sort method calls internally.

You can eg. sort by multiple columns, for an alternative method of multi-column sorting look at sortBy:

ModelsCollection<User> users {
{{"name", "Kate"}, {"votes", 350}},
{{"name", "John"}, {"votes", 200}},
{{"name", "John"}, {"votes", 150}},
{{"name", "Kate"}, {"votes", 200}},
};

auto sorted = users.sort([](const User *const left,
const User *const right)
{
const auto leftValue = left->getAttribute<QString>("name");
const auto rightValue = right->getAttribute<QString>("name");

if (leftValue == rightValue)
return left->getAttribute<quint64>("votes") <
right->getAttribute<quint64>("votes");

return leftValue < rightValue;
});

/*
{
{{"name", "John"}, {"votes", 150}},
{{"name", "John"}, {"votes", 200}},
{{"name", "Kate"}, {"votes", 200}},
{{"name", "Kate"}, {"votes", 350}},
}
*/

The order of equal elements is not guaranteed to be preserved.

info

You can use the stable sort method variants to preserve the order of equal models.

sortBy()

The sortBy method sorts the collection by the given column, this overload needs the template argument so it can cast the attribute value before comparing:

ModelsCollection<User> users {
{{"name", "Kate"}, {"votes", 150}},
{{"name", "John"}, {"votes", 200}},
{{"name", "Jack"}, {"votes", 400}},
};

auto sorted = users.sortBy<QString>("name");

/*
{
{{"name", "Jack"}, {"votes", 400}},
{{"name", "John"}, {"votes", 200}},
{{"name", "Kate"}, {"votes", 150}},
}
*/

You may pass the projection callback to determine how to sort the collection's models:

auto sorted = users.sortBy([](User *const user)
{
return user->getAttribute<quint64>("votes");
});

/*
{
{{"name", "Kate"}, {"votes", 150}},
{{"name", "John"}, {"votes", 200}},
{{"name", "Jack"}, {"votes", 400}},
}
*/

If you would like to sort your collection by multiple columns, you may pass a vector of comparison lambda expressions that define each sort operation to the sortBy method, in the following example is the name column sorted in ascending order and the second votes column is sorted in descending order:

using AttributeUtils = Orm::Tiny::Utils::Attribute;

ModelsCollection<User> users {
{{"name", "Kate"}, {"votes", 350}},
{{"name", "John"}, {"votes", 200}},
{{"name", "John"}, {"votes", 150}},
{{"name", "Kate"}, {"votes", 200}},
};

auto sorted = users.sortBy({
[](const User *const left, const User *const right)
{
return AttributeUtils::compareForSortBy(
left->getAttribute<QString>("name"),
right->getAttribute<QString>("name"));
},
[](const User *const left, const User *const right)
{
return AttributeUtils::compareForSortByDesc(
left->getAttribute<quint64>("votes"),
right->getAttribute<quint64>("votes"));
},
});

/*
{
{{"name", "John"}, {"votes", 200}},
{{"name", "John"}, {"votes", 150}},
{{"name", "Kate"}, {"votes", 350}},
{{"name", "Kate"}, {"votes", 200}},
}
*/

The AttributeUtils::compareForSortBy and compareForSortByDesc methods are helper methods, they are needed because the Qt framework doesn't define <=> spaceship operator on its types, it doesn't support the three-way comparison.

The order of equal elements is not guaranteed to be preserved.

sortByDesc()

This method has the same signature as the sortBy method but will sort the collection in the opposite order.

The order of equal elements is not guaranteed to be preserved.

sortDesc()

This method will sort the collection in the opposite order as the sort method:

ModelsCollection<User> users {
{{"id", 2}, {"name", "Kate"}},
{{"id", 3}, {"name", "John"}},
{{"id", 1}, {"name", "Jack"}},
};

auto sorted = users.sortDesc();

/*
{
{{"id", 3}, {"name", "John"}},
{{"id", 2}, {"name", "Kate"}},
{{"id", 1}, {"name", "Jack"}},
}
*/

The order of equal elements is not guaranteed to be preserved.

stableSort()

This method has the same signature as the sort method but will preserve the order of equal elements (guaranteed to be preserved).

stableSortBy()

This method has the same signature as the sortBy method but will preserve the order of equal elements (guaranteed to be preserved).

stableSortByDesc()

This method has the same signature as the sortByDesc method but will sort the collection in the opposite order and preserve the order of equal elements (guaranteed to be preserved).

stableSortDesc()

This method has the same signature as the sortDesc method but will sort the collection in the opposite order and preserve the order of equal elements (guaranteed to be preserved).

tap()

The tap method passes a collection to the given lambda expression, allowing you to "tap" into the collection at a specific point and do something with the models while not affecting the collection itself:

ModelsCollection<User> users {
{{"id", 2}, {"name", "Kate"}},
{{"id", 3}, {"name", "John"}},
{{"id", 1}, {"name", "Jack"}},
};

users.sort()
.tap([](/*const */ModelsCollection<User *> &usersRef)
{
qDebug() << "IDs after sorting:"
<< usersRef.template modelKeys<quint64>();
})
.value<quint64>("id");

// 1

The tap method returns an lvalue reference to the currently processed collection.

It can be also called on ModelsCollection rvalues, it returns an rvalue reference in this case.

toBase()

The toBase method returns a copy of the underlying vector represented by the collection:

QVector<User> = users.toBase();
note

The toBase is an alias to the all method.

toJson()

The toJson method converts the collection of models with all nested relations into a JSON serialized QByteArray.

It returns an empty array for empty many type relations and null for empty one type relations.

info

The toJson method accepts the QJsonDocument::JsonFormat, possible values are QJsonDocument::Indented or QJsonDocument::Compact.

toJsonArray()

The toJsonArray method converts the collection of models with all nested relations into a QJsonArray.

toJsonDocument()

The toJsonDocument method converts the collection of models with all nested relations into a QJsonDocument.

toMap()

The toMap method converts the collection of models with all nested relations into an attributes map QVector<QVariantMap>.

It returns an empty QVariantList for empty many type relations and a null QVariant for empty one type relations.

toMapVariantList()

The toMapVariantList method converts the collection of models with all nested relations into an attributes map, but it returns the QVariantList instead of the QVector<QVariantMap>.

It returns an empty QVariantList for empty many type relations and a null QVariant for empty one type relations.

note

The toMapVariantList method is internally needed by the toJson related methods.

toQuery()

The toQuery method returns the TinyBuilder instance containing a whereIn constraint with the collection of models' primary keys:

using Models::User;

ModelsCollection<User> users = User::whereEq("status", "VIP")->get();

users.toQuery()->update({
{"status", "Administrator"},
});

toVector()

The toVector method converts the collection of models with all nested relations into an attributes vector QVector<QVector<AttributeItem>>.

It returns an empty QVariantList for empty many type relations and a null QVariant for empty one type relations.

toVectorVariantList()

The toVectorVariantList method converts the collection of models with all nested relations into an attributes vector, but it returns the QVariantList instead of the QVector<QVector<AttributeItem>>.

It returns an empty QVariantList for empty many type relations and a null QVariant for empty one type relations.

note

The toVectorVariantList method is internally needed by the toJson related methods.

unique()

The unique method returns all of the unique models in the sorted collection. Any models with the same primary key as another model in the collection are removed:

ModelsCollection<User> users {
{{"id", 2}, {"name", "Kate"}},
{{"id", 1}, {"name", "Jack"}},
{{"id", 3}, {"name", "John"}},
{{"id", 1}, {"name", "Jack"}},
};

auto unique = users.unique();

/*
{
{{"id", 1}, {"name", "Jack"}},
{{"id", 2}, {"name", "Kate"}},
{{"id", 3}, {"name", "John"}},
}
*/

It sorts the collection internally because the ranges::unique can correctly operate only on the sorted container. You can disable it by passing false using the first sort parameter:

auto unique = users.sort().unique(false);

/*
{
{{"id", 1}, {"name", "Jack"}},
{{"id", 2}, {"name", "Kate"}},
{{"id", 3}, {"name", "John"}},
}
*/

uniqueBy()

The uniqueBy method returns all of the unique models in the sorted collection by the given column. Any models with the same column value as another model in the collection are removed. It needs the template argument, so it can cast the attribute value before comparing:

ModelsCollection<User> users {
{{"name", "Kate"}},
{{"name", "Jack"}},
{{"name", "John"}},
{{"name", "Jack"}},
};

auto unique = users.uniqueBy<QString>("name");

/*
{
{{"name", "Jack"}},
{{"name", "John"}},
{{"name", "Kate"}},
}
*/

It sorts the collection internally because the ranges::unique can correctly operate only on the sorted container. You can disable it by passing false using the second sort parameter:

auto unique = users.sortBy<QString>("name")
.uniqueBy<QString>("name", false);

/*
{
{{"name", "Jack"}},
{{"name", "John"}},
{{"name", "Kate"}},
}
*/

uniqueRelaxed()

The uniqueRelaxed method returns all of the unique models in the collection, it doesn't need a sorted collection. Any models with the same primary key as another model in the collection are removed:

ModelsCollection<User> users {
{{"id", 2}, {"name", "Kate"}},
{{"id", 1}, {"name", "Jack"}},
{{"id", 3}, {"name", "John"}},
{{"id", 1}, {"name", "Jack"}},
};

auto unique = users.uniqueRelaxed();

/*
{
{{"id", 2}, {"name", "Kate"}},
{{"id", 1}, {"name", "Jack"}},
{{"id", 3}, {"name", "John"}},
}
*/

uniqueRelaxedBy()

The uniqueRelaxedBy method returns all of the unique models in the collection by the given column, it doesn't need a sorted collection, but it needs the template argument, so it can cast the attribute value before comparing:

ModelsCollection<User> users {
{{"name", "Kate"}},
{{"name", "Jack"}},
{{"name", "John"}},
{{"name", "Jack"}},
};

auto unique = users.uniqueRelaxedBy<QString>("name");

/*
{
{{"name", "Kate"}},
{{"name", "Jack"}},
{{"name", "John"}},
}
*/

value()

The value method retrieves a given value from the first model of the collection:

ModelsCollection<User> users {
{{"name", "John"}, {"votes", 200}},
{{"name", "Jack"}, {"votes", 400}},
};

QVariant votes = users.value("votes");

// 200

Alternatively, you can cast an obtained QVariant value to the given type by the second value overload:

quint64 votes = users.value<quint64>("votes");

The value method also accepts the second defaultValue argument, which will be returned if a collection is empty, the first model is nullptr, or a model doesn't contain the given column:

auto votes = ModelsCollection<User>().value("votes", 0);

// 0

You can also call all value overloads provided by the QList::value.

where()

The where method filters the collection by a given column / value pair:

ModelsCollection<Product> products {
{{"product", "Desk"}, {"price", 200}},
{{"product", "Chair"}, {"price", 100}},
{{"product", "Bookcase"}, {"price", 150}},
{{"product", "Door"}, {"price", 100}},
};

auto filtered = products.where("price", "=", 100);

/*
{
{{"product", "Chair"}, {"price", 100}},
{{"product", "Door"}, {"price", 100}},
}
*/

For convenience, if you want to verify that a column is = to a given value, you may call whereEq method. Similar XxxEq methods are also defined for other commands:

auto filtered = products.whereEq("price", 100);

Optionally, you may pass a comparison operator as the second argument.
Supported operators are =, !=, <, >, <=, and >=:

ModelsCollection<Product> products {
{{"product", "Desk"}, {"price", 200}},
{{"product", "Chair"}, {"price", 100}},
{{"product", "Bookcase"}, {"price", 150}},
{{"product", "Door"}, {"price", 250}},
};

auto filtered = products.where("price", ">", 150);

/*
{
{{"product", "Desk"}, {"price", 200}},
{{"product", "Door"}, {"price", 250}},
}
*/

whereBetween()

The whereBetween method filters the collection by determining if a specified models' attribute value is within a given range:

ModelsCollection<Product> products {
{{"product", "Desk"}, {"price", 200}},
{{"product", "Chair"}, {"price", 80}},
{{"product", "Bookcase"}, {"price", 150}},
{{"product", "Pencil"}, {"price", 30}},
{{"product", "Door"}, {"price", 100}},
};

auto filtered = products.whereBetween<quint64>("price", {100, 200});

/*
{
{{"product", "Desk"}, {"price", 200}},
{{"product", "Bookcase"}, {"price", 150}},
{{"product", "Door"}, {"price", 100}},
}
*/

whereIn()

The whereIn method filters models from the collection that have a specified attribute value that is contained within the given unordered set:

ModelsCollection<Product> products {
{{"product", "Desk"}, {"price", 200}},
{{"product", "Chair"}, {"price", 100}},
{{"product", "Bookcase"}, {"price", 150}},
{{"product", "Door"}, {"price", 250}},
};

auto filtered = products.whereIn<quint64>("price", {100, 200});

/*
{
{{"product", "Desk"}, {"price", 200}},
{{"product", "Chair"}, {"price", 100}},
}
*/

An empty collection is returned if the values argument is empty whereIn("price", {}).

The order of models in the collection is preserved.

whereNotBetween()

The whereNotBetween method filters the collection by determining if a specified models' attribute value is outside of a given range:

ModelsCollection<Product> products {
{{"product", "Desk"}, {"price", 200}},
{{"product", "Chair"}, {"price", 80}},
{{"product", "Bookcase"}, {"price", 150}},
{{"product", "Pencil"}, {"price", 30}},
{{"product", "Door"}, {"price", 100}},
};

auto filtered = products.whereNotBetween<quint64>("price", {100, 200});

/*
{
{{"product", "Chair"}, {"price", 80}},
{{"product", "Pencil"}, {"price", 30}},
}
*/

whereNotIn()

The whereNotIn method removes models from the collection that have a specified attribute value that is contained within the given unordered set:

ModelsCollection<Product> products {
{{"product", "Desk"}, {"price", 200}},
{{"product", "Chair"}, {"price", 100}},
{{"product", "Bookcase"}, {"price", 150}},
{{"product", "Door"}, {"price", 250}},
};

auto filtered = products.whereNotIn<quint64>("price", {100, 200});

/*
{
{{"product", "Bookcase"}, {"price", 150}},
{{"product", "Door"}, {"price", 250}},
}
*/

All of the models are returned if the values argument is empty whereNotIn("price", {}).

The order of models in the collection is preserved.

whereNotNull()

The whereNotNull method returns models from the collection where the given column is not null QVariant:

#include <orm/utils/nullvariant.hpp>

using NullVariant = Orm::Utils::NullVariant;

ModelsCollection<User> users {
{{"name", "John"}},
{{"name", NullVariant::QString()}},
{{"name", "Jack"}},
};

auto filtered = users.whereNotNull("name");

/*
{
{{"name", "John"}},
{{"name", "Jack"}},
}
*/
note

The NullVariant class returns the correct null QVariant for both Qt 5 QVariant(QVariant::String) and also Qt 6 QVariant(QMetaType(QMetaType::QString)).

whereNull()

The whereNull method returns models from the collection where the given column is null QVariant:

#include <orm/utils/nullvariant.hpp>

using NullVariant = Orm::Utils::NullVariant;

ModelsCollection<User> users {
{{"name", "John"}},
{{"name", NullVariant::QString()}},
{{"name", "Jack"}},
};

auto filtered = users.whereNotNull("name");

// {{"name", NullVariant::QString()}}
note

The NullVariant class returns the correct null QVariant for both Qt 5 QVariant(QVariant::String) and also Qt 6 QVariant(QMetaType(QMetaType::QString)).

+

all()

The all method returns a copy of the underlying vector represented by the collection:

QVector<User> = users.all();
note

The toBase is an alias to the all method.

contains()

The contains method may be used to determine if a given model instance is contained by the collection. This method accepts a primary key or a model instance:

users.contains(1);

users.contains(User::find(1));

Alternatively, you may pass a lambda expression to the contains method to determine if a model exists in the collection matching a given truth test:

users.contains([](const User *const user)
{
return user->getKeyCasted() == 2;
});

For the inverse of contains, see the doesntContain method.

doesntContain()

The doesntContain method determines whether the collection does not contain a given item. This method accepts a primary key or a model instance:

users.doesntContain(1);

users.doesntContain(User::find(1));

Alternatively, you may pass a lambda expression to the doesntContain method to determine if a model does not exist in the collection matching a given truth test:

users.doesntContain([](const User *const user)
{
return user->getKeyCasted() == 2;
});

For the inverse of doesntContain, see the contains method.

each()

The each method iterates over the models in the collection and passes each model to the lambda expression:

ModelsCollection<User> users = Post::whereEq("user_id", 1)->get();

users.each([](User *const user)
{
// ...
});

If you would like to stop iterating through the models, you may return false from your lambda expression:

users.each([](User *const user)
{
if (/* condition */)
return false;

// Some logic

return true;
});

You may also pass the lambda expression with two parameters, whereas the second one is an index:

users.each([](User *const user, const std::size_t index)
{
// ...
});

The each method returns an lvalue reference to the currently processed collection.

It can be also called on ModelsCollection rvalues, it returns an rvalue reference in this case.

except()

The except method returns all of the models that do not have the given primary keys:

ModelsCollection<User *> usersResult = users.except({1, 2, 3});

All of the models are returned if the ids argument is empty except({}).

The order of models in the collection is preserved.

For the inverse of except, see the only method.

filter()

The filter method filters the collection using the lambda expression, keeping only those models that pass a given truth test:

auto usersBanned = users.filter([](const User *const user)
{
return user->getAttribute<bool>("is_banned");
});

You may also pass the lambda expression with two parameters, whereas the second one is an index:

auto usersBanned = users.filter([](const User *const user,
const std::size_t index)
{
return index < 10 && user->getAttribute<bool>("is_banned");
});

If no lambda expression is supplied, all models of the collection that are equivalent to the nullptr will be removed:

ModelsCollection<User> usersRaw = User::findMany({1, 2});
ModelsCollection<User *> users {&usersRaw[0], nullptr, &usersRaw[1]};

ModelsCollection<User *> filtered = users.filter();

// {1, 2}

For the inverse of filter, see the reject method.

find()

The find method returns the model that has a primary key matching the given key:

User *const user = users.find(1);

If you pass a model instance, find will attempt to return a model matching the primary key:

User *user = users.find(anotherUser);

The two overloads above also accept the second defaultModel model argument, which will be returned if a model was not found in the collection, its default value is the nullptr.

Alternatively, may pass more IDs and find will return all models which have a primary key within the given unordered set:

ModelsCollection<User *> usersMany = users.find({1, 2});

This overload internally calls the only method.

first()

The first method returns the first model in the collection that passes a given truth test:

ModelsCollection<User> users {
{{"name", "Kate"}, {"votes", 150}},
{{"name", "John"}, {"votes", 200}},
{{"name", "Jack"}, {"votes", 400}},
};

User *user = users.first([](User *const user)
{
return user->getAttribute<quint64>("votes") > 150;
});

// {{"name", "John"}, {"votes", 200}}

If no model passes a given truth test then the value of the second defaultModel argument will be returned, its default value is the nullptr.

using NullVariant = Orm::Utils::NullVariant;

User defaultUser {{"name", NullVariant::QString()},
{"votes", NullVariant::ULongLong()}};

User *user = users.first([](User *const user)
{
return user->getAttribute<quint64>("votes") > 500;
},
&defaultUser);

/*
{{"name", NullVariant::QString()},
{"votes", NullVariant::ULongLong()}}
*/

You can also call all first overloads provided by the QList::first.

firstWhere()

The firstWhere method returns the first model in the collection with the given column / value pair:

using NullVariant = Orm::Utils::NullVariant;

ModelsCollection<User> users {
{{"name", "Leon"}, {"age", NullVariant::UShort()}},
{{"name", "Jill"}, {"age", 14}},
{{"name", "Jack"}, {"age", 23}},
{{"name", "Jill"}, {"age", 84}},
};

auto user = users.firstWhereEq("name", "Linda");

// {{"name", "Jill"}, {"age", 14}}

You may also call the firstWhere method with a comparison operator:

users.firstWhere("age", ">=", 18);

// {{"name", "Jack"}, {"age", 23}}

fresh()

The fresh method retrieves a fresh instance of each model in the collection from the database. In addition, any specified relationships will be eager loaded:

auto usersFresh = users.fresh();

auto usersFresh = users.fresh("comments");

auto usersFresh = users.fresh("posts:id,name");

auto usersFresh = users.fresh({"comments", "posts:id,name"});

The relations argument format is the same as for TinyBuilder's load method.

implode()

The implode method joins attributes by the given column and the "glue" string you wish to place between the values:

ModelsCollection<Product> products {
{{"product", "Desk"}, {"price", 200}},
{{"product", "Chair"}, {"price", 100}},
};

products.implode("product", ", ");

// {Desk, Chair}

The default "glue" value is an empty string "".

isEmpty()

The isEmpty method returns true if the collection is empty; otherwise, false is returned:

ModelsCollection<User>().isEmpty();

// true

isNotEmpty()

The isNotEmpty method returns true if the collection is not empty; otherwise, false is returned:

ModelsCollection<User>().isNotEmpty();

// false

last()

The last method returns the last model in the collection that passes a given truth test:

ModelsCollection<User> users {
{{"name", "Kate"}, {"votes", 150}},
{{"name", "John"}, {"votes", 200}},
{{"name", "Jack"}, {"votes", 400}},
{{"name", "Rose"}, {"votes", 350}},
};

User *user = users.last([](User *const user)
{
return user->getAttribute<quint64>("votes") < 300;
});

// {{"name", "John"}, {"votes", 200}}

If no model passes a given truth test then the value of the second defaultModel argument will be returned, its default value is the nullptr.

using NullVariant = Orm::Utils::NullVariant;

User defaultUser {{"name", NullVariant::QString()},
{"votes", NullVariant::ULongLong()}};

User *user = users.last([](User *const user)
{
return user->getAttribute<quint64>("votes") < 100;
},
&defaultUser);

/*
{{"name", NullVariant::QString()},
{"votes", NullVariant::ULongLong()}}
*/

You can also call all last overloads provided by the QList::last.

load()

The load method eager loads the given relationships for all models in the collection:

users.load({"comments", "posts"});

users.load("comments.author");

users.load({{"comments"}, {"posts", [](auto &query)
{
query.whereEq("active", true);
}}});

The relations argument format is the same as for TinyBuilder's load method.

map()

The map method iterates through the collection and passes a copy of each model to the given lambda expression. The lambda expression is free to modify the model and return it, thus forming a new collection of modified models:

ModelsCollection<User> users {
{{"name", "John"}, {"votes", 200}},
{{"name", "Jack"}, {"votes", 400}},
};

auto usersAdded = users.map([](User &&userCopy)
{
if (userCopy.getAttribute<QString>("name") == "John")
userCopy["votes"] = userCopy.getAttribute<quint64>("votes") + 1;

return std::move(userCopy);
});

/*
{
{{"name", "John"}, {"price", 201}},
{{"name", "Jack"}, {"price", 400}},
}
*/

The second map overload allows to return the QVector<T>:

QVector<quint64> usersAdded = users.map<quint64>([](User &&userCopy)
{
const auto votesRef = userCopy["votes"];

if (userCopy.getAttribute<QString>("name") == "John")
votesRef = userCopy.getAttribute<quint64>("votes") + 1;

return votesRef->value<quint64>();
});

// {201, 400}

Both overloads allow to pass the lambda expression with two arguments, whereas the second argument can be an index of the std::size_t type.

caution

Like most other collection methods, map returns a new collection instance; it does not modify the collection it is called on. If you want to modify the original collection in place, use the each method.

info

The model copy is passed to the lambda expression even if the map iterates over a collection of model pointers ModelsCollection<Model *>. The models are dereferenced behind the scene.

mapWithKeys()

The mapWithKeys method iterates through the collection and passes each model to the given lambda expression. It returns the std::unordered_map<K, V> and the lambda expression should return the std::pair<K, V> containing a single column / value pair:

ModelsCollection<User> users {
{{"id", 1}, {"name", "John"}, {"email", "john@example.com"}},
{{"id", 2}, {"name", "Jill"}, {"email", "jill@example.com"}},
};

auto usersMap = users.mapWithKeys<quint64, QString>(
[](User *const user) -> std::pair<quint64, QString>
{
return {user->getKeyCasted(), user->getAttribute<QString>("name")};
});

// {{1, 'John'}, {2, 'Jill'}}

You can also map IDs to the model pointers:

auto usersMap = users.mapWithKeys<quint64, User *>(
[](User *const user) -> std::pair<quint64, User *>
{
return {user->getKeyCasted(), user};
});

mapWithModelKeys()

The mapWithModelKeys maps the primary keys to the Model *, it returns the std::unordered_map<Model::KeyType, Model *>:

auto usersMap = users.mapWithModelKeys();

modelKeys()

The modelKeys method returns the primary keys for all models in the collection:

ModelsCollection<User> users {
{{"id", 1}, {"name", "John"}},
{{"id", 2}, {"name", "Jill"}},
{{"id", 3}, {"name", "Kate"}},
{{"id", 5}, {"name", "Rose"}},
};

users.modelKeys(); // Returns QVector<QVariant>
users.modelKeys<quint64>();

// {1, 2, 3, 5}

only()

The only method returns all of the models that have the given primary keys:

ModelsCollection<User *> usersResult = users.only({1, 2, 3});

An empty collection is returned if the ids argument is empty only({}).

The order of models in the collection is preserved.

For the inverse of only, see the except method.

pluck()

The pluck method retrieves all of the values for a given column, the following overload returns the QVector<QVariant>:

ModelsCollection<Product> products {
{{"id", 1}, {"name", "Desk"}},
{{"id", 2}, {"name", "Chair"}},
};

auto plucked = products.pluck("name");

// {Desk, Chair}

The second overload allows returning the custom type QVector<T>:

auto plucked = products.pluck<QString>("name");

You may also specify how you wish the resulting collection to be keyed, this overload returns the std::map<T, QVariant>:

auto plucked = products.pluck<quint64>("name", "id");

// {{1, "Desk"}, {2, "Chair"}}

If duplicate keys exist, the last matching attribute will be inserted into the plucked collection:

ModelsCollection<Product> collection {
{{"brand", "Tesla"}, {"color", "red"}},
{{"brand", "Pagani"}, {"color", "white"}},
{{"brand", "Tesla"}, {"color", "black"}},
{{"brand", "Pagani"}, {"color", "orange"}},
};

auto plucked = collection.pluck<QString>("color", "brand");

// {{'Tesla', 'black'}, {'Pagani', 'orange"}}

reject()

The reject method filters the collection using the given lambda expression. The lambda should return true if the model should be removed from the resulting collection:

auto usersWithNote = users.reject([](const User *const user)
{
return user->getAttribute("note").isNull();
});

You may also pass the lambda expression with two arguments, whereas the second argument can be an index of the std::size_t type.

For the inverse of the reject method, see the filter method.

sort()

The sort method sorts the models collection by primary keys:

ModelsCollection<User> users {
{{"id", 2}, {"name", "Kate"}},
{{"id", 3}, {"name", "John"}},
{{"id", 1}, {"name", "Jack"}},
};

auto sorted = users.sort();

/*
{
{{"id", 1}, {"name", "Jack"}},
{{"id", 2}, {"name", "Kate"}},
{{"id", 3}, {"name", "John"}},
}
*/

You may pass a predicate and projection callbacks to the sort method with your own algorithms. Refer to the CPP reference documentation on ranges::sort, which is what the sort method calls internally.

You can eg. sort by multiple columns, for an alternative method of multi-column sorting look at sortBy:

ModelsCollection<User> users {
{{"name", "Kate"}, {"votes", 350}},
{{"name", "John"}, {"votes", 200}},
{{"name", "John"}, {"votes", 150}},
{{"name", "Kate"}, {"votes", 200}},
};

auto sorted = users.sort([](const User *const left,
const User *const right)
{
const auto leftValue = left->getAttribute<QString>("name");
const auto rightValue = right->getAttribute<QString>("name");

if (leftValue == rightValue)
return left->getAttribute<quint64>("votes") <
right->getAttribute<quint64>("votes");

return leftValue < rightValue;
});

/*
{
{{"name", "John"}, {"votes", 150}},
{{"name", "John"}, {"votes", 200}},
{{"name", "Kate"}, {"votes", 200}},
{{"name", "Kate"}, {"votes", 350}},
}
*/

The order of equal elements is not guaranteed to be preserved.

info

You can use the stable sort method variants to preserve the order of equal models.

sortBy()

The sortBy method sorts the collection by the given column, this overload needs the template argument so it can cast the attribute value before comparing:

ModelsCollection<User> users {
{{"name", "Kate"}, {"votes", 150}},
{{"name", "John"}, {"votes", 200}},
{{"name", "Jack"}, {"votes", 400}},
};

auto sorted = users.sortBy<QString>("name");

/*
{
{{"name", "Jack"}, {"votes", 400}},
{{"name", "John"}, {"votes", 200}},
{{"name", "Kate"}, {"votes", 150}},
}
*/

You may pass the projection callback to determine how to sort the collection's models:

auto sorted = users.sortBy([](User *const user)
{
return user->getAttribute<quint64>("votes");
});

/*
{
{{"name", "Kate"}, {"votes", 150}},
{{"name", "John"}, {"votes", 200}},
{{"name", "Jack"}, {"votes", 400}},
}
*/

If you would like to sort your collection by multiple columns, you may pass a vector of comparison lambda expressions that define each sort operation to the sortBy method, in the following example is the name column sorted in ascending order and the second votes column is sorted in descending order:

using AttributeUtils = Orm::Tiny::Utils::Attribute;

ModelsCollection<User> users {
{{"name", "Kate"}, {"votes", 350}},
{{"name", "John"}, {"votes", 200}},
{{"name", "John"}, {"votes", 150}},
{{"name", "Kate"}, {"votes", 200}},
};

auto sorted = users.sortBy({
[](const User *const left, const User *const right)
{
return AttributeUtils::compareForSortBy(
left->getAttribute<QString>("name"),
right->getAttribute<QString>("name"));
},
[](const User *const left, const User *const right)
{
return AttributeUtils::compareForSortByDesc(
left->getAttribute<quint64>("votes"),
right->getAttribute<quint64>("votes"));
},
});

/*
{
{{"name", "John"}, {"votes", 200}},
{{"name", "John"}, {"votes", 150}},
{{"name", "Kate"}, {"votes", 350}},
{{"name", "Kate"}, {"votes", 200}},
}
*/

The AttributeUtils::compareForSortBy and compareForSortByDesc methods are helper methods, they are needed because the Qt framework doesn't define <=> spaceship operator on its types, it doesn't support the three-way comparison.

The order of equal elements is not guaranteed to be preserved.

sortByDesc()

This method has the same signature as the sortBy method but will sort the collection in the opposite order.

The order of equal elements is not guaranteed to be preserved.

sortDesc()

This method will sort the collection in the opposite order as the sort method:

ModelsCollection<User> users {
{{"id", 2}, {"name", "Kate"}},
{{"id", 3}, {"name", "John"}},
{{"id", 1}, {"name", "Jack"}},
};

auto sorted = users.sortDesc();

/*
{
{{"id", 3}, {"name", "John"}},
{{"id", 2}, {"name", "Kate"}},
{{"id", 1}, {"name", "Jack"}},
}
*/

The order of equal elements is not guaranteed to be preserved.

stableSort()

This method has the same signature as the sort method but will preserve the order of equal elements (guaranteed to be preserved).

stableSortBy()

This method has the same signature as the sortBy method but will preserve the order of equal elements (guaranteed to be preserved).

stableSortByDesc()

This method has the same signature as the sortByDesc method but will sort the collection in the opposite order and preserve the order of equal elements (guaranteed to be preserved).

stableSortDesc()

This method has the same signature as the sortDesc method but will sort the collection in the opposite order and preserve the order of equal elements (guaranteed to be preserved).

tap()

The tap method passes a collection to the given lambda expression, allowing you to "tap" into the collection at a specific point and do something with the models while not affecting the collection itself:

ModelsCollection<User> users {
{{"id", 2}, {"name", "Kate"}},
{{"id", 3}, {"name", "John"}},
{{"id", 1}, {"name", "Jack"}},
};

users.sort()
.tap([](/*const */ModelsCollection<User *> &usersRef)
{
qDebug() << "IDs after sorting:"
<< usersRef.template modelKeys<quint64>();
})
.value<quint64>("id");

// 1

The tap method returns an lvalue reference to the currently processed collection.

It can be also called on ModelsCollection rvalues, it returns an rvalue reference in this case.

toBase()

The toBase method returns a copy of the underlying vector represented by the collection:

QVector<User> = users.toBase();
note

The toBase is an alias to the all method.

toJson()

The toJson method converts the collection of models with all nested relations into a JSON serialized QByteArray.

It returns an empty array for empty many type relations and null for empty one type relations.

info

The toJson method accepts the QJsonDocument::JsonFormat, possible values are QJsonDocument::Indented or QJsonDocument::Compact.

toJsonArray()

The toJsonArray method converts the collection of models with all nested relations into a QJsonArray.

toJsonDocument()

The toJsonDocument method converts the collection of models with all nested relations into a QJsonDocument.

toMap()

The toMap method converts the collection of models with all nested relations into an attributes map QVector<QVariantMap>.

It returns an empty QVariantList for empty many type relations and a null QVariant for empty one type relations.

toMapVariantList()

The toMapVariantList method converts the collection of models with all nested relations into an attributes map, but it returns the QVariantList instead of the QVector<QVariantMap>.

It returns an empty QVariantList for empty many type relations and a null QVariant for empty one type relations.

note

The toMapVariantList method is internally needed by the toJson related methods.

toQuery()

The toQuery method returns the TinyBuilder instance containing a whereIn constraint with the collection of models' primary keys:

using Models::User;

ModelsCollection<User> users = User::whereEq("status", "VIP")->get();

users.toQuery()->update({
{"status", "Administrator"},
});

toVector()

The toVector method converts the collection of models with all nested relations into an attributes vector QVector<QVector<AttributeItem>>.

It returns an empty QVariantList for empty many type relations and a null QVariant for empty one type relations.

toVectorVariantList()

The toVectorVariantList method converts the collection of models with all nested relations into an attributes vector, but it returns the QVariantList instead of the QVector<QVector<AttributeItem>>.

It returns an empty QVariantList for empty many type relations and a null QVariant for empty one type relations.

note

The toVectorVariantList method is internally needed by the toJson related methods.

unique()

The unique method returns all of the unique models in the sorted collection. Any models with the same primary key as another model in the collection are removed:

ModelsCollection<User> users {
{{"id", 2}, {"name", "Kate"}},
{{"id", 1}, {"name", "Jack"}},
{{"id", 3}, {"name", "John"}},
{{"id", 1}, {"name", "Jack"}},
};

auto unique = users.unique();

/*
{
{{"id", 1}, {"name", "Jack"}},
{{"id", 2}, {"name", "Kate"}},
{{"id", 3}, {"name", "John"}},
}
*/

It sorts the collection internally because the ranges::unique can correctly operate only on the sorted container. You can disable it by passing false using the first sort parameter:

auto unique = users.sort().unique(false);

/*
{
{{"id", 1}, {"name", "Jack"}},
{{"id", 2}, {"name", "Kate"}},
{{"id", 3}, {"name", "John"}},
}
*/

uniqueBy()

The uniqueBy method returns all of the unique models in the sorted collection by the given column. Any models with the same column value as another model in the collection are removed. It needs the template argument, so it can cast the attribute value before comparing:

ModelsCollection<User> users {
{{"name", "Kate"}},
{{"name", "Jack"}},
{{"name", "John"}},
{{"name", "Jack"}},
};

auto unique = users.uniqueBy<QString>("name");

/*
{
{{"name", "Jack"}},
{{"name", "John"}},
{{"name", "Kate"}},
}
*/

It sorts the collection internally because the ranges::unique can correctly operate only on the sorted container. You can disable it by passing false using the second sort parameter:

auto unique = users.sortBy<QString>("name")
.uniqueBy<QString>("name", false);

/*
{
{{"name", "Jack"}},
{{"name", "John"}},
{{"name", "Kate"}},
}
*/

uniqueRelaxed()

The uniqueRelaxed method returns all of the unique models in the collection, it doesn't need a sorted collection. Any models with the same primary key as another model in the collection are removed:

ModelsCollection<User> users {
{{"id", 2}, {"name", "Kate"}},
{{"id", 1}, {"name", "Jack"}},
{{"id", 3}, {"name", "John"}},
{{"id", 1}, {"name", "Jack"}},
};

auto unique = users.uniqueRelaxed();

/*
{
{{"id", 2}, {"name", "Kate"}},
{{"id", 1}, {"name", "Jack"}},
{{"id", 3}, {"name", "John"}},
}
*/

uniqueRelaxedBy()

The uniqueRelaxedBy method returns all of the unique models in the collection by the given column, it doesn't need a sorted collection, but it needs the template argument, so it can cast the attribute value before comparing:

ModelsCollection<User> users {
{{"name", "Kate"}},
{{"name", "Jack"}},
{{"name", "John"}},
{{"name", "Jack"}},
};

auto unique = users.uniqueRelaxedBy<QString>("name");

/*
{
{{"name", "Kate"}},
{{"name", "Jack"}},
{{"name", "John"}},
}
*/

value()

The value method retrieves a given value from the first model of the collection:

ModelsCollection<User> users {
{{"name", "John"}, {"votes", 200}},
{{"name", "Jack"}, {"votes", 400}},
};

QVariant votes = users.value("votes");

// 200

Alternatively, you can cast an obtained QVariant value to the given type by the second value overload:

quint64 votes = users.value<quint64>("votes");

The value method also accepts the second defaultValue argument, which will be returned if a collection is empty, the first model is nullptr, or a model doesn't contain the given column:

auto votes = ModelsCollection<User>().value("votes", 0);

// 0

You can also call all value overloads provided by the QList::value.

where()

The where method filters the collection by a given column / value pair:

ModelsCollection<Product> products {
{{"product", "Desk"}, {"price", 200}},
{{"product", "Chair"}, {"price", 100}},
{{"product", "Bookcase"}, {"price", 150}},
{{"product", "Door"}, {"price", 100}},
};

auto filtered = products.where("price", "=", 100);

/*
{
{{"product", "Chair"}, {"price", 100}},
{{"product", "Door"}, {"price", 100}},
}
*/

For convenience, if you want to verify that a column is = to a given value, you may call whereEq method. Similar XxxEq methods are also defined for other commands:

auto filtered = products.whereEq("price", 100);

Optionally, you may pass a comparison operator as the second argument.
Supported operators are =, !=, <, >, <=, and >=:

ModelsCollection<Product> products {
{{"product", "Desk"}, {"price", 200}},
{{"product", "Chair"}, {"price", 100}},
{{"product", "Bookcase"}, {"price", 150}},
{{"product", "Door"}, {"price", 250}},
};

auto filtered = products.where("price", ">", 150);

/*
{
{{"product", "Desk"}, {"price", 200}},
{{"product", "Door"}, {"price", 250}},
}
*/

whereBetween()

The whereBetween method filters the collection by determining if a specified models' attribute value is within a given range:

ModelsCollection<Product> products {
{{"product", "Desk"}, {"price", 200}},
{{"product", "Chair"}, {"price", 80}},
{{"product", "Bookcase"}, {"price", 150}},
{{"product", "Pencil"}, {"price", 30}},
{{"product", "Door"}, {"price", 100}},
};

auto filtered = products.whereBetween<quint64>("price", {100, 200});

/*
{
{{"product", "Desk"}, {"price", 200}},
{{"product", "Bookcase"}, {"price", 150}},
{{"product", "Door"}, {"price", 100}},
}
*/

whereIn()

The whereIn method filters models from the collection that have a specified attribute value that is contained within the given unordered set:

ModelsCollection<Product> products {
{{"product", "Desk"}, {"price", 200}},
{{"product", "Chair"}, {"price", 100}},
{{"product", "Bookcase"}, {"price", 150}},
{{"product", "Door"}, {"price", 250}},
};

auto filtered = products.whereIn<quint64>("price", {100, 200});

/*
{
{{"product", "Desk"}, {"price", 200}},
{{"product", "Chair"}, {"price", 100}},
}
*/

An empty collection is returned if the values argument is empty whereIn("price", {}).

The order of models in the collection is preserved.

whereNotBetween()

The whereNotBetween method filters the collection by determining if a specified models' attribute value is outside of a given range:

ModelsCollection<Product> products {
{{"product", "Desk"}, {"price", 200}},
{{"product", "Chair"}, {"price", 80}},
{{"product", "Bookcase"}, {"price", 150}},
{{"product", "Pencil"}, {"price", 30}},
{{"product", "Door"}, {"price", 100}},
};

auto filtered = products.whereNotBetween<quint64>("price", {100, 200});

/*
{
{{"product", "Chair"}, {"price", 80}},
{{"product", "Pencil"}, {"price", 30}},
}
*/

whereNotIn()

The whereNotIn method removes models from the collection that have a specified attribute value that is contained within the given unordered set:

ModelsCollection<Product> products {
{{"product", "Desk"}, {"price", 200}},
{{"product", "Chair"}, {"price", 100}},
{{"product", "Bookcase"}, {"price", 150}},
{{"product", "Door"}, {"price", 250}},
};

auto filtered = products.whereNotIn<quint64>("price", {100, 200});

/*
{
{{"product", "Bookcase"}, {"price", 150}},
{{"product", "Door"}, {"price", 250}},
}
*/

All of the models are returned if the values argument is empty whereNotIn("price", {}).

The order of models in the collection is preserved.

whereNotNull()

The whereNotNull method returns models from the collection where the given column is not null QVariant:

#include <orm/utils/nullvariant.hpp>

using NullVariant = Orm::Utils::NullVariant;

ModelsCollection<User> users {
{{"name", "John"}},
{{"name", NullVariant::QString()}},
{{"name", "Jack"}},
};

auto filtered = users.whereNotNull("name");

/*
{
{{"name", "John"}},
{{"name", "Jack"}},
}
*/
note

The NullVariant class returns the correct null QVariant for both Qt 5 QVariant(QVariant::String) and also Qt 6 QVariant(QMetaType(QMetaType::QString)).

whereNull()

The whereNull method returns models from the collection where the given column is null QVariant:

#include <orm/utils/nullvariant.hpp>

using NullVariant = Orm::Utils::NullVariant;

ModelsCollection<User> users {
{{"name", "John"}},
{{"name", NullVariant::QString()}},
{{"name", "Jack"}},
};

auto filtered = users.whereNotNull("name");

// {{"name", NullVariant::QString()}}
note

The NullVariant class returns the correct null QVariant for both Qt 5 QVariant(QVariant::String) and also Qt 6 QVariant(QMetaType(QMetaType::QString)).

\ No newline at end of file diff --git a/tinyorm/getting-started.html b/tinyorm/getting-started.html index 6fc8a498f..57a53bb9b 100644 --- a/tinyorm/getting-started.html +++ b/tinyorm/getting-started.html @@ -14,7 +14,7 @@ - + diff --git a/tinyorm/relationships.html b/tinyorm/relationships.html index 39b0b45f3..e68a0c598 100644 --- a/tinyorm/relationships.html +++ b/tinyorm/relationships.html @@ -14,7 +14,7 @@ - + diff --git a/tinyorm/serialization.html b/tinyorm/serialization.html index 1d234ce8b..ced611758 100644 --- a/tinyorm/serialization.html +++ b/tinyorm/serialization.html @@ -14,7 +14,7 @@ - +