From ec9140f2a731cacf5f2698dccc5b57ce505e6d32 Mon Sep 17 00:00:00 2001 From: Daniele Teti Date: Sun, 1 Oct 2023 15:22:43 +0200 Subject: [PATCH] Added MVCActiveRecord feature which allows to map a boolean object attribute to integer field on the actual table. --- samples/activerecord_showcase/EntitiesU.pas | 36 +++++++++++ samples/activerecord_showcase/MainFormU.dfm | 17 ++++-- samples/activerecord_showcase/MainFormU.pas | 56 ++++++++++++++++++ samples/data/activerecorddb.db | Bin 131072 -> 131072 bytes .../data/activerecorddb_firebird_script.sql | 6 ++ .../activerecorddb_mssqlserver_script.sql | 5 ++ samples/data/activerecorddb_mysql_script.sql | 8 ++- .../data/activerecorddb_postgresql_script.sql | 8 +++ sources/MVCFramework.Serializer.Commons.pas | 7 ++- 9 files changed, 135 insertions(+), 8 deletions(-) diff --git a/samples/activerecord_showcase/EntitiesU.pas b/samples/activerecord_showcase/EntitiesU.pas index 7750bd1d..84afaf31 100644 --- a/samples/activerecord_showcase/EntitiesU.pas +++ b/samples/activerecord_showcase/EntitiesU.pas @@ -601,6 +601,25 @@ type end; + [MVCTable('integers_as_booleans')] + TIntegersAsBooleans = class(TMVCActiveRecord) + private + [MVCTableField('id', [foPrimaryKey, foAutoGenerated])] + FID: NullableInt64; + [MVCTableField('done_int', 'int2')] + FDoneAsInteger: Integer; + [MVCTableField('done_bool')] + FDoneAsBoolean: Boolean; + procedure SetDoneAsBoolean(const Value: Boolean); + procedure SetDoneAsInteger(const Value: Integer); + procedure SetID(const Value: NullableInt64); + public + property ID: NullableInt64 read FID write SetID; + property DoneAsBoolean: Boolean read FDoneAsBoolean write SetDoneAsBoolean; + property DoneAsInteger: Integer read FDoneAsInteger write SetDoneAsInteger; + end; + + implementation @@ -785,4 +804,21 @@ begin fFullName := GetFullName; end; +{ TIntegersAsBooleans } + +procedure TIntegersAsBooleans.SetDoneAsBoolean(const Value: Boolean); +begin + FDoneAsBoolean := Value; +end; + +procedure TIntegersAsBooleans.SetDoneAsInteger(const Value: Integer); +begin + FDoneAsInteger := Value; +end; + +procedure TIntegersAsBooleans.SetID(const Value: NullableInt64); +begin + FID := Value; +end; + end. diff --git a/samples/activerecord_showcase/MainFormU.dfm b/samples/activerecord_showcase/MainFormU.dfm index 9b914f49..c0fc608f 100644 --- a/samples/activerecord_showcase/MainFormU.dfm +++ b/samples/activerecord_showcase/MainFormU.dfm @@ -2,7 +2,7 @@ object MainForm: TMainForm Left = 0 Top = 0 Caption = 'TMVCActiveRecord - ShowCase' - ClientHeight = 569 + ClientHeight = 593 ClientWidth = 1104 Color = clBtnFace Font.Charset = DEFAULT_CHARSET @@ -14,7 +14,7 @@ object MainForm: TMainForm OnShow = FormShow DesignSize = ( 1104 - 569) + 593) TextHeight = 13 object btnCRUD: TButton Left = 8 @@ -38,7 +38,7 @@ object MainForm: TMainForm Left = 280 Top = 8 Width = 816 - Height = 553 + Height = 577 Anchors = [akLeft, akTop, akRight, akBottom] Ctl3D = True DoubleBuffered = True @@ -56,7 +56,7 @@ object MainForm: TMainForm WantReturns = False WordWrap = False ExplicitWidth = 812 - ExplicitHeight = 552 + ExplicitHeight = 576 end object btnRelations: TButton Left = 8 @@ -285,6 +285,15 @@ object MainForm: TMainForm TabOrder = 27 OnClick = btnVirtualEntitiesClick end + object btnIntegersAsBool: TButton + Left = 8 + Top = 522 + Width = 121 + Height = 34 + Caption = 'Integers As Booleans' + TabOrder = 28 + OnClick = btnIntegersAsBoolClick + end object FDConnection1: TFDConnection Left = 312 Top = 40 diff --git a/samples/activerecord_showcase/MainFormU.pas b/samples/activerecord_showcase/MainFormU.pas index 7f6bdc05..32e042ee 100644 --- a/samples/activerecord_showcase/MainFormU.pas +++ b/samples/activerecord_showcase/MainFormU.pas @@ -60,6 +60,7 @@ type btnRefresh: TButton; btnNamedQuery: TButton; btnVirtualEntities: TButton; + btnIntegersAsBool: TButton; procedure btnCRUDClick(Sender: TObject); procedure btnInheritanceClick(Sender: TObject); procedure btnMultiThreadingClick(Sender: TObject); @@ -90,6 +91,7 @@ type procedure btnRefreshClick(Sender: TObject); procedure btnNamedQueryClick(Sender: TObject); procedure btnVirtualEntitiesClick(Sender: TObject); + procedure btnIntegersAsBoolClick(Sender: TObject); private procedure Log(const Value: string); procedure LoadCustomers; @@ -388,10 +390,15 @@ begin lCustWithGUID.LoadByPK(lIDGUID); lCustWithGUID.Code.Value := '😉9012🙂'; lCustWithGUID.Update; + + lCustWithGUID.GUID := TGUID.NewGuid; + lCustWithGUID.Insert; finally lCustWithGUID.Free; end; + + lCustWithGUID := TMVCActiveRecord.GetByPK(lIDGUID); try lCustWithGUID.Delete; @@ -481,6 +488,55 @@ begin end; end; +procedure TMainForm.btnIntegersAsBoolClick(Sender: TObject); +begin + Log('** Bool as Integer'); + Log(' Only in the mapping layer it is possibile to map an integer field used '); + Log(' as boolean with values (0,1) as a boolean property'); + Log(' --> (False is stored as 0, True is stored as 1) <--'); + TMVCActiveRecord.DeleteAll(TIntegersAsBooleans); + + for var I := 0 to 1 do + begin + for var b := False to True do + begin + var lTest1 := TIntegersAsBooleans.Create; + try + lTest1.DoneAsBoolean := b; + lTest1.DoneAsInteger := I; + lTest1.Store; + finally + lTest1.Free; + end; + end; + end; + + { ** WARNING ** + While mapping layer recognize a boolean stored as integer, queries must still + use the actual type (integer) instead of the mapped types} + Assert(2 = TMVCActiveRecord.Count('eq(doneasboolean,true)')); + Assert(2 = TMVCActiveRecord.Count('eq(doneasinteger,1)')); {the boolean attribute as integer} + Assert(1 = TMVCActiveRecord.Count('and(eq(doneasboolean,true),eq(doneasinteger,1))')); + Assert(1 = TMVCActiveRecord.Count('and(eq(doneasboolean,false),eq(doneasinteger,0))')); + + var lList := TMVCActiveRecord.SelectRQL('sort(+id)', 10); + try + Assert(lList.Count = 4); + var lIdx := 0; + for var I := 0 to 1 do + begin + for var b := False to True do + begin + Assert(b = lList[lIdx].DoneAsBoolean); + Assert(I = lList[lIdx].DoneAsInteger); + Inc(lIdx); + end; + end; + finally + lList.Free; + end; +end; + procedure TMainForm.btnJSON_XML_TypesClick(Sender: TObject); var lCTypes: TComplexTypes; diff --git a/samples/data/activerecorddb.db b/samples/data/activerecorddb.db index a4cf9a6fa98945378810f2adfe02fbda541df549..95194fcf6b854ee61761e1d8a47d834bee2a1d4a 100644 GIT binary patch delta 496 zcmZo@;Am*zm>@0qj)8$e0g8EnwD&|EW1e>my3e?H+14-!FnLYPmSdD|SH}=ng%C$4 zA6ErzDikz$xfB#KQxrV?LR{TlgA@XSJbfL5A{D${BNZG&L;OAcfMz)Ry84CafK;aB z=cR(JRDjy6ke6SgkXM?MgOCQ=qfnfin3Dryz@&LM^Dv)fX4Kn!i%o-#&0U{OJlb^f zIW7rpY_5to#3m}d*_(H#5EnE5UIzZ_{7?D!ZWa_+&o3px%xubtO})(I>-t)3!0=@d zVA#xV@Hd}_h5rQu|1bV8{O|Z*Y!?(@Jjy?Dfv^ON3=0D%CyO)-0|zII6p+oqA_-)3 zEZR`O#s$_6Q9D^dz;v4dBl`hS9-yc+5K95E2oOsEvD|c-4~){R!ayeXcAF23NB9Ax CG>y^# delta 2462 zcmZXVeQaCR6~Nzl{{DFPHBH>&x{e=g(6~+N=WjcvNnK+yK5l&8(R7pJIH~>+{C)q4cE9x|*KyM<=zq_N8ggQvOKaT0 zU7BcG5kG(F2(mD{`%kDFa%LkA0mMjnyf zn2@hz_vZ?O(w5%dt;zT{B*jv7P^u0duHxqd!=-9T+OoBGOS^Pnuv{q>v(?c9^{=H$ zu8=E_O69>^p?5V(!AcF8O+Vd{%EWgJNa>`0Kx)hHanD#aA84Rs<`aFJ zequ;)OKn)e9w}d}Mx<@ar^xTgmdbku%Tj(OExftX$ctAv@rwAxOw;MVh=LU~PRNz3 z`9}+bm5D2!^?K73=hsfo807z!#vhSBHB5FzQi5&4~c^~`Jvy2+*= zBXiq(mRJCM!jEptl&Pm5!p7})Aq%^HHt~bJiq57%N9QsilgyongU^6_koWjTUmg;m-7bYNPxm&i1JA%O&1}_r`J-K57yGwN$`+v z2L^g6ib2kf8;U-_=jg-+&f+>1jNs=MZcp;b!FPZ1H*_p5w5=+Pe~2&^5pqefw8< zxP`~?aPg*wRqy-|Lc$6b$u3E)Zs8SN``TvcAaSuFc6vLO9VK*h4y%g__?_F9s3%RQ zFuvQ0Y=eF2KK?Whev+^tW3Soolfyn5I|pIXV@Bq;Z|D57-xr{ZQxGFD6U((FId9l2 zN9Ye{@qT-Z4RKc=f_nu+J*<{GvKM4QkBGg`8pX!xC>&mih&^+)s_x*NJzb$OjzdrkYYwqI-4e5!dtlj6VU&+}Qn87{*UkcMXN zQ{1rjbK)mNOe<~m!hOC#K&Dd{vGAa>4&mLlZjM1(?T7Wqn0dF@!%>wn^%_}5Vd`PG z)pDi1u!U?=n0paOE4xKB(fs&;5qtvqIx zCZo9M;yhaNS^@P>DXMtO3w4PJ#ffXP_Niqt*GWuSi;(q-y8PacLDs|+2fca}Vw!}q zhQ3^ZHT3;3xUpV4hO+UYhJKz0pO{jZjjTE3aK!7Q9_$W5S&gjmTw#St$*f{=bmMVo z$8BuXJ&)U@looob69>WUUg*%Im1es7I;6$8!mMMa`+_JFJ@Wzt@sv$;vL8?R$0S(j znIsCUFy)r5cWViI4AGJ65JSl1^EjD6p-_Nc+l>Mx z6lR~gCN?uAqj;l9h1thO8EahH^)PfIWA@!*Pw#?Gd^ya%C5F4a1W|-czYb?DIYhT_ zgHAzV{;~6^3raU2qUyKohFW!4Vg9kJs|mvf5*Ek|yj9@t0f^w4RReFCrdhf?ye$Z)3wIFKEVFlROsTn*je}VL5Rf4EP#zr zb&lT{!b6VyU(U(Rpu3=xfq%=!rj`SrHTYQYxdWeOx^@8^x>o$ya^c