diff --git a/README.md b/README.md index e8f92326..e8e83105 100644 --- a/README.md +++ b/README.md @@ -245,6 +245,8 @@ Congratulations to Daniele Teti and all the staff for the excellent work!" -- Ma - ⚡ Wizard updated to be dotEnv aware +- ⚡ Added "Load Style" methods to `TMVCActiveRecord` as suggested by https://github.com/danieleteti/delphimvcframework/issues/675 + - ⚡ Better error message in case of serialization of `TArray` - ⚡ Improved serialization of `TObjectList` (however `ObjectDict` is still the preferred way to serialize multiple datasets). diff --git a/samples/activerecord_showcase/MainFormU.dfm b/samples/activerecord_showcase/MainFormU.dfm index 5a2196ec..5d1aeb54 100644 --- a/samples/activerecord_showcase/MainFormU.dfm +++ b/samples/activerecord_showcase/MainFormU.dfm @@ -2,62 +2,49 @@ object MainForm: TMainForm Left = 0 Top = 0 Caption = 'TMVCActiveRecord - ShowCase' - ClientHeight = 1423 - ClientWidth = 2760 + ClientHeight = 569 + ClientWidth = 1104 Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText - Font.Height = -28 + Font.Height = -11 Font.Name = 'Tahoma' Font.Style = [] OnDestroy = FormDestroy OnShow = FormShow - PixelsPerInch = 240 DesignSize = ( - 2760 - 1423) - TextHeight = 34 + 1104 + 569) + TextHeight = 13 object btnCRUD: TButton - Left = 20 - Top = 20 - Width = 303 - Height = 83 - Margins.Left = 8 - Margins.Top = 8 - Margins.Right = 8 - Margins.Bottom = 8 + Left = 8 + Top = 8 + Width = 121 + Height = 33 Caption = 'CRUD' TabOrder = 0 OnClick = btnCRUDClick end object btnSelect: TButton - Left = 20 - Top = 605 - Width = 303 - Height = 83 - Margins.Left = 8 - Margins.Top = 8 - Margins.Right = 8 - Margins.Bottom = 8 + Left = 8 + Top = 242 + Width = 121 + Height = 33 Caption = 'Queries' TabOrder = 1 OnClick = btnSelectClick end object Memo1: TMemo - Left = 700 - Top = 20 - Width = 2040 - Height = 1383 - Margins.Left = 8 - Margins.Top = 8 - Margins.Right = 8 - Margins.Bottom = 8 + Left = 280 + Top = 8 + Width = 816 + Height = 553 Anchors = [akLeft, akTop, akRight, akBottom] Ctl3D = True DoubleBuffered = True Font.Charset = ANSI_CHARSET Font.Color = clWindowText - Font.Height = -33 + Font.Height = -13 Font.Name = 'Consolas' Font.Style = [] ParentCtl3D = False @@ -68,306 +55,214 @@ object MainForm: TMainForm TabOrder = 2 WantReturns = False WordWrap = False - ExplicitWidth = 2020 - ExplicitHeight = 1381 + ExplicitWidth = 812 + ExplicitHeight = 552 end object btnRelations: TButton - Left = 20 - Top = 703 - Width = 303 - Height = 87 - Margins.Left = 8 - Margins.Top = 8 - Margins.Right = 8 - Margins.Bottom = 8 + Left = 8 + Top = 281 + Width = 121 + Height = 35 Caption = 'Relations' TabOrder = 3 OnClick = btnRelationsClick end object btnInheritance: TButton - Left = 20 - Top = 805 - Width = 303 - Height = 85 - Margins.Left = 8 - Margins.Top = 8 - Margins.Right = 8 - Margins.Bottom = 8 + Left = 8 + Top = 322 + Width = 121 + Height = 34 Caption = 'Inheritance' TabOrder = 4 OnClick = btnInheritanceClick end object btnValidation: TButton - Left = 20 - Top = 905 - Width = 303 - Height = 85 - Margins.Left = 8 - Margins.Top = 8 - Margins.Right = 8 - Margins.Bottom = 8 + Left = 8 + Top = 362 + Width = 121 + Height = 34 Caption = 'Validation' TabOrder = 5 OnClick = btnValidationClick end object btnMultiThreading: TButton - Left = 360 - Top = 20 - Width = 303 - Height = 83 - Margins.Left = 8 - Margins.Top = 8 - Margins.Right = 8 - Margins.Bottom = 8 + Left = 144 + Top = 8 + Width = 121 + Height = 33 Caption = 'Multi Threading' TabOrder = 6 OnClick = btnMultiThreadingClick end object btnRQL: TButton - Left = 20 - Top = 1005 - Width = 303 - Height = 85 - Margins.Left = 8 - Margins.Top = 8 - Margins.Right = 8 - Margins.Bottom = 8 + Left = 8 + Top = 402 + Width = 121 + Height = 34 Caption = 'RQL Query' TabOrder = 7 OnClick = btnRQLClick end object btnReadOnlyFields: TButton - Left = 20 - Top = 508 - Width = 303 - Height = 82 - Margins.Left = 8 - Margins.Top = 8 - Margins.Right = 8 - Margins.Bottom = 8 + Left = 8 + Top = 203 + Width = 121 + Height = 33 Caption = 'CRUD With R/O Field' TabOrder = 8 OnClick = btnReadOnlyFieldsClick end object btnNullTest: TButton - Left = 360 - Top = 118 - Width = 303 - Height = 82 - Margins.Left = 8 - Margins.Top = 8 - Margins.Right = 8 - Margins.Bottom = 8 + Left = 144 + Top = 47 + Width = 121 + Height = 33 Caption = 'Nullables' TabOrder = 9 OnClick = btnNullTestClick end object btnCRUDNoAutoInc: TButton - Left = 20 - Top = 215 - Width = 303 - Height = 83 - Margins.Left = 8 - Margins.Top = 8 - Margins.Right = 8 - Margins.Bottom = 8 + Left = 8 + Top = 86 + Width = 121 + Height = 33 Caption = 'CRUD (no autoinc)' TabOrder = 10 OnClick = btnCRUDNoAutoIncClick end object btnCRUDWithStringPKs: TButton - Left = 20 - Top = 313 - Width = 303 - Height = 82 - Margins.Left = 8 - Margins.Top = 8 - Margins.Right = 8 - Margins.Bottom = 8 + Left = 8 + Top = 125 + Width = 121 + Height = 33 Caption = 'CRUD (string pks)' TabOrder = 11 OnClick = btnCRUDWithStringPKsClick end object btnWithSpaces: TButton - Left = 20 - Top = 410 - Width = 303 - Height = 83 - Margins.Left = 8 - Margins.Top = 8 - Margins.Right = 8 - Margins.Bottom = 8 + Left = 8 + Top = 164 + Width = 121 + Height = 33 Caption = 'CRUD (entity with spaces)' TabOrder = 12 WordWrap = True OnClick = btnWithSpacesClick end object btnCountWithRQL: TButton - Left = 360 - Top = 215 - Width = 303 - Height = 83 - Margins.Left = 8 - Margins.Top = 8 - Margins.Right = 8 - Margins.Bottom = 8 + Left = 144 + Top = 86 + Width = 121 + Height = 33 Caption = 'Count with RQL' TabOrder = 13 OnClick = btnCountWithRQLClick end object btnReadAndWriteOnly: TButton - Left = 360 - Top = 313 - Width = 303 - Height = 82 - Margins.Left = 8 - Margins.Top = 8 - Margins.Right = 8 - Margins.Bottom = 8 + Left = 144 + Top = 125 + Width = 121 + Height = 33 Caption = 'R/O, R/W' TabOrder = 14 OnClick = btnReadAndWriteOnlyClick end object btnClientGeneratedPK: TButton - Left = 360 - Top = 410 - Width = 303 - Height = 83 - Margins.Left = 8 - Margins.Top = 8 - Margins.Right = 8 - Margins.Bottom = 8 + Left = 144 + Top = 164 + Width = 121 + Height = 33 Caption = 'Client Generated PKs' TabOrder = 15 OnClick = btnClientGeneratedPKClick end object btnAttributes: TButton - Left = 360 - Top = 508 - Width = 303 - Height = 82 - Margins.Left = 8 - Margins.Top = 8 - Margins.Right = 8 - Margins.Bottom = 8 + Left = 144 + Top = 203 + Width = 121 + Height = 33 Caption = 'Attributes' TabOrder = 16 OnClick = btnAttributesClick end object btnJSON_XML_Types: TButton - Left = 360 - Top = 605 - Width = 303 - Height = 88 - Margins.Left = 8 - Margins.Top = 8 - Margins.Right = 8 - Margins.Bottom = 8 + Left = 144 + Top = 242 + Width = 121 + Height = 35 Caption = 'JSON && XML' TabOrder = 17 OnClick = btnJSON_XML_TypesClick end object btnMerge: TButton - Left = 360 - Top = 708 - Width = 303 - Height = 85 - Margins.Left = 8 - Margins.Top = 8 - Margins.Right = 8 - Margins.Bottom = 8 + Left = 144 + Top = 283 + Width = 121 + Height = 34 Caption = 'Merge' TabOrder = 18 OnClick = btnMergeClick end object btnTableFilter: TButton - Left = 360 - Top = 808 - Width = 303 - Height = 85 - Margins.Left = 8 - Margins.Top = 8 - Margins.Right = 8 - Margins.Bottom = 8 + Left = 144 + Top = 323 + Width = 121 + Height = 34 Caption = 'Table Filter' TabOrder = 19 OnClick = btnTableFilterClick end object btnPartitioning: TButton - Left = 360 - Top = 908 - Width = 303 - Height = 82 - Margins.Left = 8 - Margins.Top = 8 - Margins.Right = 8 - Margins.Bottom = 8 + Left = 144 + Top = 363 + Width = 121 + Height = 33 Caption = 'Table Partitioning' TabOrder = 20 OnClick = btnPartitioningClick end object btnCRUDWithGUID: TButton - Left = 20 - Top = 118 - Width = 303 - Height = 82 - Margins.Left = 8 - Margins.Top = 8 - Margins.Right = 8 - Margins.Bottom = 8 + Left = 8 + Top = 47 + Width = 121 + Height = 33 Caption = 'CRUD (with GUID PK)' TabOrder = 21 OnClick = btnCRUDWithGUIDClick end object btnOOP: TButton - Left = 360 - Top = 1005 - Width = 303 - Height = 85 - Margins.Left = 8 - Margins.Top = 8 - Margins.Right = 8 - Margins.Bottom = 8 + Left = 144 + Top = 402 + Width = 121 + Height = 34 Caption = 'OOP with Partitioning and Filtering' TabOrder = 22 WordWrap = True OnClick = btnOOPClick end object btnReadOnly: TButton - Left = 20 - Top = 1105 - Width = 303 - Height = 85 - Margins.Left = 8 - Margins.Top = 8 - Margins.Right = 8 - Margins.Bottom = 8 + Left = 8 + Top = 442 + Width = 121 + Height = 34 Caption = 'Read/Only Entities' TabOrder = 23 OnClick = btnReadOnlyClick end object btnSpeed: TButton - Left = 20 - Top = 1205 - Width = 303 - Height = 85 - Margins.Left = 8 - Margins.Top = 8 - Margins.Right = 8 - Margins.Bottom = 8 + Left = 8 + Top = 482 + Width = 121 + Height = 34 Caption = 'Metadata Speed Test' TabOrder = 24 OnClick = btnSpeedClick end object btnRefresh: TButton - Left = 360 - Top = 1105 - Width = 303 - Height = 85 - Margins.Left = 8 - Margins.Top = 8 - Margins.Right = 8 - Margins.Bottom = 8 + Left = 144 + Top = 442 + Width = 121 + Height = 34 Caption = 'Manual Refresh' TabOrder = 25 OnClick = btnRefreshClick diff --git a/samples/activerecord_showcase/MainFormU.pas b/samples/activerecord_showcase/MainFormU.pas index 2dd5fdf2..f82b4dba 100644 --- a/samples/activerecord_showcase/MainFormU.pas +++ b/samples/activerecord_showcase/MainFormU.pas @@ -1040,6 +1040,7 @@ var lItem: TMVCActiveRecord; lCustomer: TCustomer; lCustList: TObjectList; + lRecCount: Integer; const cRQL1 = 'in(City,["Rome","London"]);sort(+code);limit(0,50)'; cRQL2 = 'and(eq(City,"Rome"),or(contains(CompanyName,"GAS"),contains(CompanyName,"Motors")))'; @@ -1133,6 +1134,114 @@ begin lList.Free; end; + + //****************************************************** + // Using "Load" methods ******************************** + //****************************************************** + Log('*************************************************'); + Log('** RQL Queries Test (using "Load" style methods)'); + Log('*************************************************'); + Log('>> RQL Query (1) - ' + cRQL1); + lList := TMVCActiveRecordList.Create; + try + TMVCActiveRecord.SelectRQL(TCustomer, cRQL1, 20, lList); + Log(lList.Count.ToString + ' record/s found'); + for lItem in lList do + begin + lCustomer := TCustomer(lItem); + Log(Format('%5s - %s (%s)', [lCustomer.Code.ValueOrDefault, + lCustomer.CompanyName.ValueOrDefault, lCustomer.City])); + end; + finally + lList.Free; + end; + + Log('>> RQL Query (2) - ' + cRQL2); + lCustList := TObjectList.Create; + try + lRecCount := TMVCActiveRecord.SelectRQL(cRQL2, 20, lCustList); + Log(lRecCount.ToString + ' record/s found'); + for lCustomer in lCustList do + begin + Log(Format('%5s - %s (%s)', [lCustomer.Code.ValueOrDefault, + lCustomer.CompanyName.ValueOrDefault, lCustomer.City])); + end; + finally + lCustList.Free; + end; + + Log('**RQL Query (3) - ' + cRQL2); + lList := TMVCActiveRecordList.Create; + try + lRecCount := TMVCActiveRecord.SelectRQL(TCustomer, cRQL2, 20, lList); + Log(lRecCount.ToString + ' record/s found'); + for lItem in lList do + begin + lCustomer := TCustomer(lItem); + Log(Format('%5s - %s (%s)', [lCustomer.Code.ValueOrDefault, + lCustomer.CompanyName.ValueOrDefault, lCustomer.City])); + end; + finally + lList.Free; + end; + + Log('**RQL Query (4) - with limit 20'); + lList := TMVCActiveRecordList.Create; + try + lRecCount := TMVCActiveRecord.SelectRQL(TCustomer, '', 20, lList); + Log(lRecCount.ToString + ' record/s found'); + Assert(lRecCount = 20); + Assert(lList.Count = lRecCount); + finally + lList.Free; + end; + + Log('**RQL Query (5) - sort by code with limit 20'); + lList := TMVCActiveRecordList.Create; + try + lRecCount := TMVCActiveRecord.SelectRQL(TCustomer, 'sort(+code)', 20, lList); + Log(lRecCount.ToString + ' record/s found'); + Assert(lRecCount = lList.Count); + Assert(lList.Count = 20); + finally + lList.Free; + end; + + Log('**RQL Query (6) - with limit 10'); + lList := TMVCActiveRecordList.Create; + try + lRecCount := TMVCActiveRecord.SelectRQL(TCustomer, '', 10, lList); + Log(lList.Count.ToString + ' record/s found'); + Assert(lRecCount = lList.Count); + Assert(lList.Count = 10); + finally + lList.Free; + end; + + Log('**RQL Query (7) - with limit 1'); + lList := TMVCActiveRecordList.Create; + try + lRecCount := TMVCActiveRecord.SelectRQL(TCustomer, '', 1, lList); + Log(lList.Count.ToString + ' record/s found'); + Assert(lList.Count = 1); + Assert(lRecCount = lList.Count); + finally + lList.Free; + end; + + Log('**RQL Query (8) - with limit 0'); + lList := TMVCActiveRecordList.Create; + try + lRecCount := TMVCActiveRecord.SelectRQL(TCustomer, '', 0, lList); + Log(lList.Count.ToString + ' record/s found'); + Assert(lList.Count = 0); + Assert(lRecCount = lList.Count); + finally + lList.Free; + end; + + + end; procedure TMainForm.btnSelectClick(Sender: TObject); diff --git a/sources/MVCFramework.ActiveRecord.pas b/sources/MVCFramework.ActiveRecord.pas index 877b7a76..59e18fb6 100644 --- a/sources/MVCFramework.ActiveRecord.pas +++ b/sources/MVCFramework.ActiveRecord.pas @@ -333,7 +333,9 @@ type function InternalCount(const RQL: string): int64; function InternalSelectRQL(const RQL: string; const MaxRecordCount: Integer) - : TMVCActiveRecordList; + : TMVCActiveRecordList; overload; + function InternalSelectRQL(const RQL: string; const MaxRecordCount: Integer; + const OutList: TMVCActiveRecordList): UInt32; overload; public constructor Create(aLazyLoadConnection: Boolean); overload; { cannot be virtual! } @@ -392,18 +394,33 @@ type class function Select(const aClass: TMVCActiveRecordClass; const SQL: string; const Params: array of Variant; const Connection: TFDConnection): TMVCActiveRecordList; overload; + class function Select(const aClass: TMVCActiveRecordClass; const SQL: string; + const Params: array of Variant; + const Connection: TFDConnection; const OutList: TMVCActiveRecordList): UInt32; overload; class function SelectRQL(const aClass: TMVCActiveRecordClass; const RQL: string; const MaxRecordCount: Integer) : TMVCActiveRecordList; overload; + class function SelectRQL(const aClass: TMVCActiveRecordClass; const RQL: string; + const MaxRecordCount: Integer; const OutList: TMVCActiveRecordList): UInt32; overload; class function DeleteRQL(const aClass: TMVCActiveRecordClass; const RQL: string): int64; function SelectRQL(const RQL: string; const MaxRecordCount: Integer) : TMVCActiveRecordList; overload; - class function Where(const aClass: TMVCActiveRecordClass; const SQLWhere: string; + class function Where( + const aClass: TMVCActiveRecordClass; + const SQLWhere: string; const Params: array of Variant) : TMVCActiveRecordList; overload; - class function Where(const aClass: TMVCActiveRecordClass; const SQLWhere: string; + class function Where( + const aClass: TMVCActiveRecordClass; + const SQLWhere: string; const Params: array of Variant; const Connection: TFDConnection): TMVCActiveRecordList; overload; + class function Where( + const aClass: TMVCActiveRecordClass; + const SQLWhere: string; + const Params: array of Variant; + const Connection: TFDConnection; + const OutList: TMVCActiveRecordList): UInt32; overload; class function All(const aClass: TMVCActiveRecordClass): TObjectList; overload; class function DeleteAll(const aClass: TMVCActiveRecordClass): int64; overload; @@ -451,13 +468,27 @@ type const RaiseExceptionIfNotFound: Boolean = True): T; overload; class function GetByPK(const aValue: TGuid; const RaiseExceptionIfNotFound: Boolean = True): T; overload; + /// + /// Returns a TObjectList from a SQL using variant params + /// class function Select(const SQL: string; const Params: array of Variant; const Options: TMVCActiveRecordLoadOptions = []): TObjectList; overload; + /// + /// Returns a TObjectList from a SQL using typed params + /// class function Select(const SQL: string; const Params: array of Variant; const ParamTypes: array of TFieldType; const Options: TMVCActiveRecordLoadOptions = []) : TObjectList; overload; + /// + /// Fills a TObjectList from a SQL using typed params. + /// Returns number of the records in the list (not only the selected records, but the current .Count of the list) + /// + class function Select(const SQL: string; const Params: array of Variant; + const ParamTypes: array of TFieldType; const Options: TMVCActiveRecordLoadOptions; + const OutList: TObjectList): UInt32; overload; + class function SelectOne(const SQL: string; const Params: array of Variant; const ParamTypes: array of TFieldType; const Options: TMVCActiveRecordLoadOptions = []; @@ -468,6 +499,8 @@ type class function SelectRQL(const RQL: string; const MaxRecordCount: Integer) : TObjectList; overload; + class function SelectRQL(const RQL: string; + const MaxRecordCount: Integer; const OutList: TObjectList): UInt32; overload; class function SelectOneByRQL(const RQL: string; const RaiseExceptionIfNotFound: Boolean = True): T; overload; class function All: TObjectList; overload; @@ -483,6 +516,10 @@ type class function Where(const SQLWhere: string; const Params: array of Variant; const ParamTypes: array of TFieldType): TObjectList; overload; + class function Where(const SQLWhere: string; + const Params: array of Variant; + const ParamTypes: array of TFieldType; + const OutList: TObjectList): UInt32; overload; class function GetOneByWhere(const SQLWhere: string; const Params: array of Variant; const RaiseExceptionIfNotFound: Boolean = True): T; overload; class function GetOneByWhere(const SQLWhere: string; @@ -1419,6 +1456,16 @@ begin Result := GetScalar(lSQL, []); end; +function TMVCActiveRecord.InternalSelectRQL(const RQL: string; + const MaxRecordCount: Integer; const OutList: TMVCActiveRecordList): UInt32; +var + lSQL: string; +begin + lSQL := SQLGenerator.CreateSQLWhereByRQL(RQL, GetMapping, True, false, MaxRecordCount); + LogD(Format('RQL [%s] => SQL [%s]', [RQL, lSQL])); + Result := Where(TMVCActiveRecordClass(Self.ClassType), lSQL, [], nil, OutList); +end; + function TMVCActiveRecord.InternalSelectRQL(const RQL: string; const MaxRecordCount: Integer): TMVCActiveRecordList; var lSQL: string; @@ -1657,6 +1704,52 @@ begin end; end; +class function TMVCActiveRecordHelper.SelectRQL(const RQL: string; + const MaxRecordCount: Integer; const OutList: TObjectList): UInt32; +var + lAR: TMVCActiveRecord; + lSQL: string; +begin + lAR := T.Create; + try + lSQL := lAR.SQLGenerator.CreateSQLWhereByRQL(RQL, lAR.GetMapping, MaxRecordCount > -1, false, MaxRecordCount).Trim; + lSQL := TMVCSQLGenerator.RemoveInitialWhereKeyword(lSQL); + Result := Where(lSQL, [], [], OutList); + finally + lAR.Free; + end; +end; + +class function TMVCActiveRecordHelper.Where(const SQLWhere: string; + const Params: array of Variant; const ParamTypes: array of TFieldType; + const OutList: TObjectList): UInt32; +var + lAR: TMVCActiveRecord; + lFilter: string; +begin + lAR := T.Create; + try + lFilter := lAR.SQLGenerator.GetDefaultSQLFilter(True); + if SQLWhere.Trim.IsEmpty() or SQLWhere.Trim.StartsWith('/*limit*/') or SQLWhere.Trim.StartsWith('/*sort*/') then + begin + Result := Select(lAR.GenerateSelectSQL + lFilter + SQLWhere, Params, ParamTypes, [], OutList); + end + else + begin + if lFilter.IsEmpty then + begin + Result := Select(lAR.GenerateSelectSQL + ' WHERE ' + SQLWhere, Params, ParamTypes, [], OutList); + end + else + begin + Result := Select(lAR.GenerateSelectSQL + lFilter + ' AND ' + SQLWhere, Params, ParamTypes, [], OutList); + end; + end; + finally + lAR.Free; + end; +end; + function TMVCActiveRecord.GetPartitionInfo: TPartitionInfo; var lRQLCompilerClass: TRQLCompilerClass; @@ -2454,29 +2547,14 @@ end; class function TMVCActiveRecord.Select(const aClass: TMVCActiveRecordClass; const SQL: string; const Params: array of Variant; const Connection: TFDConnection): TMVCActiveRecordList; -var - lDataSet: TDataSet; - lAR: TMVCActiveRecord; begin Result := TMVCActiveRecordList.Create; try - lDataSet := ExecQuery(SQL, Params, Connection, True, False); - try - while not lDataSet.Eof do - begin - lAR := aClass.Create; - Result.Add(lAR); - lAR.LoadByDataset(lDataSet); - lDataSet.Next; - end; - finally - lDataSet.Free; - end; + Select(aClass, SQL, Params, Connection, Result); except Result.Free; raise; end; - end; class function TMVCActiveRecord.SelectDataSet(const SQL: string; const Params: array of Variant; @@ -2502,6 +2580,41 @@ begin Result := InternalSelectRQL(RQL, MaxRecordCount); end; +class function TMVCActiveRecord.SelectRQL(const aClass: TMVCActiveRecordClass; + const RQL: string; const MaxRecordCount: Integer; + const OutList: TMVCActiveRecordList): UInt32; +var + lAR: TMVCActiveRecord; +begin + lAR := aClass.Create(True); + try + Result := lAR.InternalSelectRQL(RQL, MaxRecordCount, OutList); + finally + lAR.Free; + end; +end; + +class function TMVCActiveRecordHelper.Select(const SQL: string; const Params: array of Variant; + const ParamTypes: array of TFieldType; const Options: TMVCActiveRecordLoadOptions; const OutList: TObjectList): UInt32; +var + lDataSet: TDataSet; + lAR: TMVCActiveRecord; +begin + lDataSet := ExecQuery(SQL, Params, ParamTypes, True, False); + try + while not lDataSet.Eof do + begin + lAR := T.Create; + OutList.Add(lAR); + lAR.LoadByDataset(lDataSet, Options); + lDataSet.Next; + end; + Result := OutList.Count; + finally + lDataSet.Free; + end; +end; + class function TMVCActiveRecordHelper.Select(const SQL: string; const Params: array of Variant; const ParamTypes: array of TFieldType; const Options: TMVCActiveRecordLoadOptions): TObjectList; var @@ -2511,18 +2624,7 @@ var begin Result := TObjectList.Create(True); try - lDataSet := ExecQuery(SQL, Params, ParamTypes, True, False); - try - while not lDataSet.Eof do - begin - lAR := T.Create; - Result.Add(lAR); - lAR.LoadByDataset(lDataSet, Options); - lDataSet.Next; - end; - finally - lDataSet.Free; - end; + Select(SQL, Params, ParamTypes, Options, Result); except Result.Free; raise; @@ -2581,28 +2683,13 @@ end; class function TMVCActiveRecordHelper.Where(const SQLWhere: string; const Params: array of Variant; const ParamTypes: array of TFieldType): TObjectList; -var - lAR: TMVCActiveRecord; - lFilter: string; begin - lAR := T.Create; + Result := TObjectList.Create(True); try - lFilter := lAR.SQLGenerator.GetDefaultSQLFilter(True); - if SQLWhere.Trim.IsEmpty() or SQLWhere.Trim.StartsWith('/*limit*/') or SQLWhere.Trim.StartsWith('/*sort*/') then - begin - Result := Select(lAR.GenerateSelectSQL + lFilter + SQLWhere, Params, ParamTypes) - end - else - begin - if lFilter.IsEmpty then - Result := Select(lAR.GenerateSelectSQL + ' WHERE ' + SQLWhere, Params, ParamTypes) - else - begin - Result := Select(lAR.GenerateSelectSQL + lFilter + ' AND ' + SQLWhere, Params, ParamTypes); - end; - end; - finally - lAR.Free; + Where(SQLWhere, Params, ParamTypes, Result); + except + Result.Free; + raise; end; end; @@ -3000,6 +3087,20 @@ begin OnAfterInsertOrUpdate; end; +class function TMVCActiveRecord.Where(const aClass: TMVCActiveRecordClass; + const SQLWhere: string; const Params: array of Variant; + const Connection: TFDConnection; const OutList: TMVCActiveRecordList): UInt32; +var + lAR: TMVCActiveRecord; +begin + lAR := aClass.Create; + try + Result := Select(aClass, lAR.GenerateSelectSQL + SQLWhere, Params, Connection, OutList); + finally + lAR.Free; + end; +end; + procedure TMVCActiveRecord.AddChildren(const ChildObject: TObject); begin if fChildren = nil then @@ -3051,14 +3152,13 @@ end; class function TMVCActiveRecord.Where(const aClass: TMVCActiveRecordClass; const SQLWhere: string; const Params: array of Variant; const Connection: TFDConnection): TMVCActiveRecordList; -var - lAR: TMVCActiveRecord; begin - lAR := aClass.Create; + Result := TMVCActiveRecordList.Create; try - Result := Select(aClass, lAR.GenerateSelectSQL + SQLWhere, Params, Connection); - finally - lAR.Free; + Where(aClass, SQLWhere, Params, Connection, Result); + except + Result.Free; + raise; end; end; @@ -4063,6 +4163,28 @@ begin inherited; end; +class function TMVCActiveRecord.Select(const aClass: TMVCActiveRecordClass; + const SQL: string; const Params: array of Variant; + const Connection: TFDConnection; const OutList: TMVCActiveRecordList): UInt32; +var + lDataSet: TDataSet; + lAR: TMVCActiveRecord; +begin + lDataSet := ExecQuery(SQL, Params, Connection, True, False); + try + while not lDataSet.Eof do + begin + lAR := aClass.Create; + OutList.Add(lAR); + lAR.LoadByDataset(lDataSet); + lDataSet.Next; + end; + Result := OutList.Count; + finally + lDataSet.Free; + end; +end; + initialization gConnectionsLock := TObject.Create;