mirror of
https://github.com/danieleteti/delphimvcframework.git
synced 2024-11-15 07:45:54 +01:00
First implementation for foDoNotInsert and foDoNotUpdate
This commit is contained in:
parent
09ecb5f5a1
commit
ab92225d12
@ -207,7 +207,6 @@ var
|
||||
lModuleName: string;
|
||||
lPath: string;
|
||||
lFormat: string;
|
||||
lNow: TDateTime;
|
||||
begin
|
||||
{$IF Defined(Android)}
|
||||
lModuleName := TAndroidHelper.ApplicationTitle.Replace(' ', '_', [rfReplaceAll]);
|
||||
@ -221,7 +220,6 @@ begin
|
||||
lFormat := fLogFileNameFormat;
|
||||
|
||||
lPath := fLogsFolder;
|
||||
lNow := Now();
|
||||
lFormat := lFormat
|
||||
.Replace('{module}', lModuleName, [rfReplaceAll])
|
||||
.Replace('{number}', aFileNumber.ToString.PadLeft(2,'0') , [rfReplaceAll])
|
||||
|
@ -231,6 +231,32 @@ type
|
||||
property City: string read fCity write fCity;
|
||||
end;
|
||||
|
||||
|
||||
[MVCNameCase(ncLowerCase)]
|
||||
[MVCTable('customers')]
|
||||
TCustomerWithOptions = class(TCustomEntity)
|
||||
private
|
||||
{$IFNDEF USE_SEQUENCES}
|
||||
[MVCTableField('id', [foPrimaryKey, foAutoGenerated])]
|
||||
{$ELSE}
|
||||
[MVCTableField('id', [foPrimaryKey, foAutoGenerated],
|
||||
'SEQ_CUSTOMERS_ID' { required for interbase } )]
|
||||
{$ENDIF}
|
||||
fID: Integer;
|
||||
[MVCTableField('code', [foDoNotInsert, foDoNotUpdate])]
|
||||
fCode: NullableString;
|
||||
[MVCTableField('description', [foDoNotInsert])]
|
||||
fCompanyName: string;
|
||||
[MVCTableField('city', [foDoNotUpdate])]
|
||||
fCity: string;
|
||||
public
|
||||
property ID: Integer read fID write fID;
|
||||
property Code: NullableString read fCode write fCode;
|
||||
property CompanyName: string read fCompanyName write fCompanyName;
|
||||
property City: string read fCity write fCity;
|
||||
end;
|
||||
|
||||
|
||||
[MVCNameCase(ncLowerCase)]
|
||||
[MVCTable('order_details')]
|
||||
TOrderDetail = class(TCustomEntity)
|
||||
|
@ -2,7 +2,7 @@ object MainForm: TMainForm
|
||||
Left = 0
|
||||
Top = 0
|
||||
Caption = 'TMVCActiveRecord - ShowCase'
|
||||
ClientHeight = 626
|
||||
ClientHeight = 660
|
||||
ClientWidth = 1094
|
||||
Color = clBtnFace
|
||||
Font.Charset = DEFAULT_CHARSET
|
||||
@ -14,7 +14,7 @@ object MainForm: TMainForm
|
||||
OnShow = FormShow
|
||||
DesignSize = (
|
||||
1094
|
||||
626)
|
||||
660)
|
||||
TextHeight = 13
|
||||
object btnCRUD: TButton
|
||||
Left = 8
|
||||
@ -27,7 +27,7 @@ object MainForm: TMainForm
|
||||
end
|
||||
object btnSelect: TButton
|
||||
Left = 8
|
||||
Top = 242
|
||||
Top = 283
|
||||
Width = 121
|
||||
Height = 33
|
||||
Caption = 'Queries'
|
||||
@ -38,7 +38,7 @@ object MainForm: TMainForm
|
||||
Left = 280
|
||||
Top = 8
|
||||
Width = 806
|
||||
Height = 610
|
||||
Height = 644
|
||||
Anchors = [akLeft, akTop, akRight, akBottom]
|
||||
Ctl3D = True
|
||||
DoubleBuffered = True
|
||||
@ -55,10 +55,11 @@ object MainForm: TMainForm
|
||||
TabOrder = 2
|
||||
WantReturns = False
|
||||
WordWrap = False
|
||||
ExplicitHeight = 610
|
||||
end
|
||||
object btnRelations: TButton
|
||||
Left = 8
|
||||
Top = 281
|
||||
Top = 322
|
||||
Width = 121
|
||||
Height = 35
|
||||
Caption = 'Relations'
|
||||
@ -67,7 +68,7 @@ object MainForm: TMainForm
|
||||
end
|
||||
object btnInheritance: TButton
|
||||
Left = 8
|
||||
Top = 322
|
||||
Top = 363
|
||||
Width = 121
|
||||
Height = 34
|
||||
Caption = 'Inheritance'
|
||||
@ -76,7 +77,7 @@ object MainForm: TMainForm
|
||||
end
|
||||
object btnValidation: TButton
|
||||
Left = 8
|
||||
Top = 362
|
||||
Top = 403
|
||||
Width = 121
|
||||
Height = 34
|
||||
Caption = 'Validation'
|
||||
@ -94,7 +95,7 @@ object MainForm: TMainForm
|
||||
end
|
||||
object btnRQL: TButton
|
||||
Left = 8
|
||||
Top = 402
|
||||
Top = 443
|
||||
Width = 121
|
||||
Height = 34
|
||||
Caption = 'RQL Query'
|
||||
@ -240,7 +241,7 @@ object MainForm: TMainForm
|
||||
end
|
||||
object btnReadOnly: TButton
|
||||
Left = 8
|
||||
Top = 442
|
||||
Top = 483
|
||||
Width = 121
|
||||
Height = 34
|
||||
Caption = 'Read/Only Entities'
|
||||
@ -249,7 +250,7 @@ object MainForm: TMainForm
|
||||
end
|
||||
object btnSpeed: TButton
|
||||
Left = 8
|
||||
Top = 482
|
||||
Top = 523
|
||||
Width = 121
|
||||
Height = 34
|
||||
Caption = 'Metadata Speed Test'
|
||||
@ -285,7 +286,7 @@ object MainForm: TMainForm
|
||||
end
|
||||
object btnIntegersAsBool: TButton
|
||||
Left = 8
|
||||
Top = 522
|
||||
Top = 563
|
||||
Width = 121
|
||||
Height = 34
|
||||
Caption = 'Integers As Booleans'
|
||||
@ -294,7 +295,7 @@ object MainForm: TMainForm
|
||||
end
|
||||
object btnObjectVersion: TButton
|
||||
Left = 8
|
||||
Top = 562
|
||||
Top = 603
|
||||
Width = 121
|
||||
Height = 34
|
||||
Caption = 'Object Version'
|
||||
@ -310,6 +311,15 @@ object MainForm: TMainForm
|
||||
TabOrder = 30
|
||||
OnClick = btnCustomTableClick
|
||||
end
|
||||
object btnCRUDWithOptions: TButton
|
||||
Left = 8
|
||||
Top = 242
|
||||
Width = 121
|
||||
Height = 33
|
||||
Caption = 'CRUD With Fields Opts'
|
||||
TabOrder = 31
|
||||
OnClick = btnCRUDWithOptionsClick
|
||||
end
|
||||
object FDConnection1: TFDConnection
|
||||
Left = 312
|
||||
Top = 40
|
||||
|
@ -63,6 +63,7 @@ type
|
||||
btnIntegersAsBool: TButton;
|
||||
btnObjectVersion: TButton;
|
||||
btnCustomTable: TButton;
|
||||
btnCRUDWithOptions: TButton;
|
||||
procedure btnCRUDClick(Sender: TObject);
|
||||
procedure btnInheritanceClick(Sender: TObject);
|
||||
procedure btnMultiThreadingClick(Sender: TObject);
|
||||
@ -96,6 +97,7 @@ type
|
||||
procedure btnIntegersAsBoolClick(Sender: TObject);
|
||||
procedure btnObjectVersionClick(Sender: TObject);
|
||||
procedure btnCustomTableClick(Sender: TObject);
|
||||
procedure btnCRUDWithOptionsClick(Sender: TObject);
|
||||
private
|
||||
procedure Log(const Value: string);
|
||||
procedure LoadCustomers(const HowManyCustomers: Integer = 50);
|
||||
@ -412,6 +414,78 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMainForm.btnCRUDWithOptionsClick(Sender: TObject);
|
||||
var
|
||||
lCustomer: TCustomerWithOptions;
|
||||
lID: Integer;
|
||||
begin
|
||||
Log('** CRUD test with fields options');
|
||||
lCustomer := TCustomerWithOptions.Create;
|
||||
try
|
||||
{
|
||||
'Code' will not be persisted on table because defined as 'foReadOnly'
|
||||
}
|
||||
lCustomer.Code := '1234'; // "Code" will be skipped in insert and in update as well
|
||||
lCustomer.CompanyName := 'Google Inc.'; // "CompanyName" will be skipped in insert
|
||||
lCustomer.City := 'Montain View, CA'; // "City" will be skipped in update
|
||||
lCustomer.Insert;
|
||||
lID := lCustomer.ID;
|
||||
Log('Just inserted Customer ' + lID.ToString + ' with fields options');
|
||||
finally
|
||||
lCustomer.Free;
|
||||
end;
|
||||
|
||||
//let's check that code is empty
|
||||
lCustomer := TMVCActiveRecord.GetByPK<TCustomerWithOptions>(lID);
|
||||
try
|
||||
Assert(lCustomer.Code.IsNull); // it's null
|
||||
Assert(lCustomer.CompanyName.IsEmpty); //empty string
|
||||
Assert(lCustomer.City = 'Montain View, CA'); //inserted
|
||||
|
||||
lCustomer.Code := '1234'; // "Code" will be skipped in insert and in update as well
|
||||
lCustomer.CompanyName := 'Google Inc.'; // "CompanyName" will be saved
|
||||
lCustomer.City := 'Via Roma 10, ITALY'; // "City" will be skipped in update
|
||||
lCustomer.Update;
|
||||
finally
|
||||
lCustomer.Free;
|
||||
end;
|
||||
|
||||
//let's check
|
||||
lCustomer := TMVCActiveRecord.GetByPK<TCustomerWithOptions>(lID);
|
||||
try
|
||||
Assert(lCustomer.Code.IsNull); // it's null
|
||||
Assert(lCustomer.CompanyName = 'Google Inc.'); //correctly updated
|
||||
Assert(lCustomer.City = 'Montain View, CA'); // not updated, mantains old value
|
||||
finally
|
||||
lCustomer.Free;
|
||||
end;
|
||||
|
||||
{
|
||||
//if underlying field is not null, it is loaded as usual
|
||||
TMVCActiveRecord.CurrentConnection.ExecSQL('update customers set code = ''XYZ'' where id = ?', [lID]);
|
||||
lCustomer := TMVCActiveRecord.GetByPK<TCustomerWithReadOnlyFields>(lID);
|
||||
try
|
||||
Assert('XYZ' = lCustomer.Code);
|
||||
lCustomer.CompanyName := lCustomer.CompanyName + ' changed!';
|
||||
lCustomer.Code := 'this code will not be saved';
|
||||
lCustomer.Update; //do not save field "code"
|
||||
Log('Just updated Customer ' + lID.ToString);
|
||||
finally
|
||||
lCustomer.Free;
|
||||
end;
|
||||
|
||||
//but being foReadOnly is not updated
|
||||
lCustomer := TMVCActiveRecord.GetByPK<TCustomerWithReadOnlyFields>(lID);
|
||||
try
|
||||
Assert('XYZ' = lCustomer.Code);
|
||||
lCustomer.Delete;
|
||||
Log('Just deleted Customer ' + lID.ToString + ' with a R/O field');
|
||||
finally
|
||||
lCustomer.Free;
|
||||
end;
|
||||
}
|
||||
end;
|
||||
|
||||
procedure TMainForm.btnCRUDWithStringPKsClick(Sender: TObject);
|
||||
var
|
||||
lCustomer: TCustomerWithCode;
|
||||
|
@ -66,9 +66,11 @@ type
|
||||
TMVCActiveRecord = class;
|
||||
TMVCActiveRecordFieldOption = (foPrimaryKey, { it's the primary key of the mapped table }
|
||||
foAutoGenerated, { not written, read - similar to readonly }
|
||||
foReadOnly, { not written, read }
|
||||
foReadOnly, { not written, read - like foDoNotInsert+foDoNotUpdate }
|
||||
foWriteOnly, { written, not read }
|
||||
foVersion {used for versioning, only one field with foVersion is allowed in class}
|
||||
foVersion, {used for versioning, only one field with foVersion is allowed in class}
|
||||
foDoNotInsert, { this field is not included in SQL INSERT commands }
|
||||
foDoNotUpdate { this field is not included in SQL UPDATE commands }
|
||||
);
|
||||
TMVCActiveRecordFieldOptions = set of TMVCActiveRecordFieldOption;
|
||||
TMVCEntityAction = (eaCreate, eaRetrieve, eaUpdate, eaDelete);
|
||||
@ -104,7 +106,7 @@ type
|
||||
FieldName: string;
|
||||
FieldOptions: TMVCActiveRecordFieldOptions;
|
||||
DataTypeName: string;
|
||||
Writeable, Readable, IsVersion: Boolean;
|
||||
Writeable, Readable, Insertable, Updatable, IsVersion: Boolean;
|
||||
procedure EndUpdates;
|
||||
end;
|
||||
|
||||
@ -3899,7 +3901,7 @@ begin
|
||||
begin
|
||||
Result := Result + GetFieldNameForSQL(lPair.Value.FieldName) + ' = ' +
|
||||
GetParamNameForSQL(lPair.Value.FieldName) + ' + 1,';
|
||||
end else if lPair.Value.Writeable then
|
||||
end else if lPair.Value.Updatable then
|
||||
begin
|
||||
Result := Result + GetFieldNameForSQL(lPair.Value.FieldName) + ' = :' +
|
||||
GetParamNameForSQL(lPair.Value.FieldName) + ',';
|
||||
@ -4236,13 +4238,27 @@ begin
|
||||
begin
|
||||
Writeable := false;
|
||||
Readable := false;
|
||||
Insertable := False;
|
||||
Updatable := False;
|
||||
end
|
||||
else
|
||||
begin
|
||||
Writeable := ((FieldOptions * [foReadOnly, foAutoGenerated]) = []);
|
||||
Readable := (FieldOptions * [foWriteOnly]) = [];
|
||||
Readable := not (foWriteOnly in FieldOptions);
|
||||
Insertable := not (foDoNotInsert in FieldOptions);
|
||||
Updatable := not (foDoNotUpdate in FieldOptions);
|
||||
end;
|
||||
IsVersion := foVersion in FieldOptions;
|
||||
|
||||
// field options consistency check
|
||||
// if Writeable and (not Updatable) then
|
||||
// begin
|
||||
// raise EMVCActiveRecord.CreateFmt('Field "%s" cannot be Writeable but not Updateable', [FieldName]);
|
||||
// end;
|
||||
// if (not Writeable) and (Readable) and (Updatable or Insertable) then
|
||||
// begin
|
||||
// raise EMVCActiveRecord.CreateFmt('Field "%s" cannot be ReadOnly but (Updateable or Insertable)', [FieldName]);
|
||||
// end;
|
||||
end;
|
||||
|
||||
{ TMVCUnitOfWork<T> }
|
||||
|
@ -85,7 +85,7 @@ begin
|
||||
|
||||
for lKeyValue in TableMap.fMap do
|
||||
begin
|
||||
if lKeyValue.Value.Writeable then
|
||||
if lKeyValue.Value.Insertable then
|
||||
begin
|
||||
lSB.Append(GetFieldNameForSQL(lKeyValue.Value.FieldName) + ',');
|
||||
end;
|
||||
@ -109,7 +109,7 @@ begin
|
||||
if lKeyValue.Value.IsVersion then
|
||||
begin
|
||||
lSB.Append(OBJECT_VERSION_STARTING_VALUE + ',');
|
||||
end else if lKeyValue.Value.Writeable then
|
||||
end else if lKeyValue.Value.Insertable then
|
||||
begin
|
||||
lSB.Append(':' + GetParamNameForSQL(lKeyValue.Value.FieldName) + ',');
|
||||
end;
|
||||
|
Loading…
Reference in New Issue
Block a user