mirror of
https://github.com/danieleteti/delphimvcframework.git
synced 2024-11-15 07:45:54 +01:00
articles_crud migrated to services container
This commit is contained in:
parent
6142c2d929
commit
b41e245bbc
@ -3,46 +3,51 @@ unit BusinessObjects;
|
||||
interface
|
||||
|
||||
uses
|
||||
MVCFramework.Serializer.Commons, MVCFramework.Nullables;
|
||||
MVCFramework.Serializer.Commons, MVCFramework.Nullables,
|
||||
MVCFramework.ActiveRecord;
|
||||
|
||||
type
|
||||
TBaseBO = class
|
||||
TBaseBO = class(TMVCActiveRecord)
|
||||
private
|
||||
[MVCTableField('ID', [foPrimaryKey])]
|
||||
FID: Integer;
|
||||
procedure SetID(const Value: Integer);
|
||||
public
|
||||
procedure CheckInsert; virtual;
|
||||
procedure CheckUpdate; virtual;
|
||||
procedure CheckDelete; virtual;
|
||||
property ID: Integer read FID write SetID;
|
||||
end;
|
||||
|
||||
[MVCNameCase(ncLowerCase)]
|
||||
[MVCTable('ARTICOLI')]
|
||||
[MVCNamedSQLQuery(
|
||||
'search_by_text',
|
||||
'SELECT * FROM ARTICOLI WHERE DESCRIZIONE CONTAINING ? ORDER BY ID')
|
||||
]
|
||||
TArticle = class(TBaseBO)
|
||||
private
|
||||
FPrice: Currency;
|
||||
[MVCTableField('CODICE')]
|
||||
FCode: string;
|
||||
[MVCTableField('DESCRIZIONE')]
|
||||
FDescription: String;
|
||||
[MVCTableField('PREZZO')]
|
||||
FPrice: Currency;
|
||||
[MVCTableField('UPDATED_AT')]
|
||||
FUpdatedAt: TDateTime;
|
||||
[MVCTableField('CREATED_AT')]
|
||||
FCreatedAt: TDateTime;
|
||||
procedure SetCode(const Value: string);
|
||||
procedure SetDescription(const Value: String);
|
||||
procedure SetPrice(const Value: Currency);
|
||||
procedure SetCreatedAt(const Value: TDateTime);
|
||||
procedure SetUpdatedAt(const Value: TDateTime);
|
||||
protected
|
||||
procedure OnBeforeInsertOrUpdate; override;
|
||||
procedure OnBeforeUpdate; override;
|
||||
procedure OnBeforeDelete; override;
|
||||
public
|
||||
procedure CheckInsert; override;
|
||||
procedure CheckUpdate; override;
|
||||
procedure CheckDelete; override;
|
||||
[MVCColumn('CODICE')]
|
||||
property Code: string read FCode write SetCode;
|
||||
[MVCColumn('DESCRIZIONE')]
|
||||
property Description: String read FDescription write SetDescription;
|
||||
[MVCColumn('PREZZO')]
|
||||
property Price: Currency read FPrice write SetPrice;
|
||||
[MVCColumn('CREATED_AT')]
|
||||
property CreatedAt: TDateTime read FCreatedAt write SetCreatedAt;
|
||||
[MVCColumn('UPDATED_AT')]
|
||||
property UpdatedAt: TDateTime read FUpdatedAt write SetUpdatedAt;
|
||||
end;
|
||||
|
||||
@ -53,46 +58,28 @@ uses
|
||||
|
||||
{ TBaseBO }
|
||||
|
||||
procedure TBaseBO.CheckDelete;
|
||||
begin
|
||||
|
||||
end;
|
||||
|
||||
procedure TBaseBO.CheckInsert;
|
||||
begin
|
||||
|
||||
end;
|
||||
|
||||
procedure TBaseBO.CheckUpdate;
|
||||
begin
|
||||
|
||||
end;
|
||||
|
||||
procedure TBaseBO.SetID(const Value: Integer);
|
||||
begin
|
||||
FID := Value;
|
||||
end;
|
||||
|
||||
{ TArticolo }
|
||||
|
||||
procedure TArticle.CheckDelete;
|
||||
procedure TArticle.OnBeforeDelete;
|
||||
begin
|
||||
inherited;
|
||||
if Price <= 5 then
|
||||
raise Exception.Create('Cannot delete an article with a price below 5 euros (yes, it is a silly check)');
|
||||
end;
|
||||
|
||||
procedure TArticle.CheckInsert;
|
||||
procedure TArticle.OnBeforeInsertOrUpdate;
|
||||
begin
|
||||
inherited;
|
||||
if not TRegEx.IsMatch(Code, '^C[0-9]{2,4}') then
|
||||
raise Exception.Create('Article code must be in the format "CXX or CXXX or CXXXX"');
|
||||
end;
|
||||
|
||||
procedure TArticle.CheckUpdate;
|
||||
procedure TArticle.OnBeforeUpdate;
|
||||
begin
|
||||
inherited;
|
||||
CheckInsert;
|
||||
if Price <= 2 then
|
||||
raise Exception.Create('We cannot sell so low cost pizzas!');
|
||||
end;
|
||||
|
@ -10,6 +10,9 @@ type
|
||||
|
||||
end;
|
||||
|
||||
const
|
||||
CON_DEF_NAME = 'MyConnX';
|
||||
|
||||
implementation
|
||||
|
||||
end.
|
||||
|
@ -7,14 +7,19 @@ uses
|
||||
mvcframework.Commons,
|
||||
mvcframework.Serializer.Commons,
|
||||
System.Generics.Collections,
|
||||
Controllers.Base, BusinessObjects;
|
||||
Controllers.Base, BusinessObjects, Services;
|
||||
|
||||
type
|
||||
|
||||
[MVCDoc('Resource that manages articles CRUD')]
|
||||
[MVCPath('/articles')]
|
||||
TArticlesController = class(TBaseController)
|
||||
private
|
||||
fArticlesService: IArticlesService;
|
||||
public
|
||||
[MVCInject]
|
||||
constructor Create(ArticlesService: IArticlesService); reintroduce;
|
||||
|
||||
[MVCDoc('Returns the list of articles')]
|
||||
[MVCPath]
|
||||
[MVCHTTPMethod([httpGET])]
|
||||
@ -23,17 +28,17 @@ type
|
||||
[MVCDoc('Returns the list of articles')]
|
||||
[MVCPath('/searches')]
|
||||
[MVCHTTPMethod([httpGET])]
|
||||
procedure GetArticlesByDescription(const [MVCFromQueryString('q', '')] Search: String);
|
||||
function GetArticlesByDescription(const [MVCFromQueryString('q', '')] Search: String): IMVCObjectDictionary;
|
||||
|
||||
[MVCDoc('Returns the article with the specified id')]
|
||||
[MVCPath('/meta')]
|
||||
[MVCHTTPMethod([httpGET])]
|
||||
procedure GetArticleMeta;
|
||||
function GetArticleMeta: IMVCObjectDictionary;
|
||||
|
||||
[MVCDoc('Returns the article with the specified id')]
|
||||
[MVCPath('/($id)')]
|
||||
[MVCHTTPMethod([httpGET])]
|
||||
procedure GetArticleByID(id: Integer);
|
||||
function GetArticleByID(id: Integer): IMVCObjectDictionary;
|
||||
|
||||
[MVCDoc('Deletes the article with the specified id')]
|
||||
[MVCPath('/($id)')]
|
||||
@ -61,14 +66,19 @@ implementation
|
||||
{ TArticlesController }
|
||||
|
||||
uses
|
||||
Services,
|
||||
Commons,
|
||||
mvcframework.Serializer.Intf,
|
||||
System.SysUtils;
|
||||
|
||||
constructor TArticlesController.Create(ArticlesService: IArticlesService);
|
||||
begin
|
||||
inherited Create;
|
||||
fArticlesService := ArticlesService;
|
||||
end;
|
||||
|
||||
procedure TArticlesController.CreateArticle(const Article: TArticle);
|
||||
begin
|
||||
GetArticlesService.Add(Article);
|
||||
fArticlesService.Add(Article);
|
||||
Render201Created('/articles/' + Article.id.ToString, 'Article Created');
|
||||
end;
|
||||
|
||||
@ -76,101 +86,60 @@ procedure TArticlesController.CreateArticles(const ArticleList: TObjectList<TArt
|
||||
var
|
||||
lArticle: TArticle;
|
||||
begin
|
||||
GetArticlesService.StartTransaction;
|
||||
try
|
||||
for lArticle in ArticleList do
|
||||
begin
|
||||
GetArticlesService.Add(lArticle);
|
||||
end;
|
||||
GetArticlesService.Commit;
|
||||
except
|
||||
GetArticlesService.Rollback;
|
||||
raise;
|
||||
end;
|
||||
Render(201, 'Articles Created');
|
||||
// fArticlesService.StartTransaction;
|
||||
// try
|
||||
// for lArticle in ArticleList do
|
||||
// begin
|
||||
// fArticlesService.Add(lArticle);
|
||||
// end;
|
||||
// fArticlesService.Commit;
|
||||
// except
|
||||
// fArticlesService.Rollback;
|
||||
// raise;
|
||||
// end;
|
||||
// Render(201, 'Articles Created');
|
||||
end;
|
||||
|
||||
procedure TArticlesController.DeleteArticleByID(id: Integer);
|
||||
var
|
||||
Article: TArticle;
|
||||
lArticle: TArticle;
|
||||
begin
|
||||
GetArticlesService.StartTransaction;
|
||||
try
|
||||
Article := GetArticlesService.GetByID(id);
|
||||
try
|
||||
GetArticlesService.Delete(Article);
|
||||
finally
|
||||
Article.Free;
|
||||
end;
|
||||
GetArticlesService.Commit;
|
||||
except
|
||||
GetArticlesService.Rollback;
|
||||
raise;
|
||||
end;
|
||||
lArticle := fArticlesService.GetByID(id);
|
||||
fArticlesService.Delete(lArticle);
|
||||
end;
|
||||
|
||||
procedure TArticlesController.GetArticles;
|
||||
begin
|
||||
Render(ObjectDict().Add('data', GetArticlesService.GetAll));
|
||||
Render(ObjectDict().Add('data', fArticlesService.GetAll));
|
||||
end;
|
||||
|
||||
procedure TArticlesController.GetArticlesByDescription(const Search: String);
|
||||
var
|
||||
lDict: IMVCObjectDictionary;
|
||||
function TArticlesController.GetArticlesByDescription(const Search: String): IMVCObjectDictionary;
|
||||
begin
|
||||
try
|
||||
if Search = '' then
|
||||
begin
|
||||
lDict := ObjectDict().Add('data', GetArticlesService.GetAll);
|
||||
end
|
||||
else
|
||||
begin
|
||||
lDict := ObjectDict().Add('data', GetArticlesService.GetArticles(Search));
|
||||
end;
|
||||
Render(lDict);
|
||||
except
|
||||
on E: EServiceException do
|
||||
begin
|
||||
raise EMVCException.Create(E.Message, '', 0, 404);
|
||||
end
|
||||
else
|
||||
raise;
|
||||
if Search = '' then
|
||||
begin
|
||||
Result := ObjectDict().Add('data', fArticlesService.GetAll);
|
||||
end
|
||||
else
|
||||
begin
|
||||
Result := ObjectDict().Add('data', fArticlesService.GetArticles(Search));
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TArticlesController.UpdateArticleByID(const Article: TArticle; const id: Integer);
|
||||
begin
|
||||
Article.id := id;
|
||||
GetArticlesService.Update(Article);
|
||||
fArticlesService.Update(Article);
|
||||
Render(200, 'Article Updated');
|
||||
end;
|
||||
|
||||
procedure TArticlesController.GetArticleByID(id: Integer);
|
||||
function TArticlesController.GetArticleByID(id: Integer): IMVCObjectDictionary;
|
||||
begin
|
||||
try
|
||||
Render(ObjectDict().Add('data', GetArticlesService.GetByID(id)));
|
||||
except
|
||||
on E: EServiceException do
|
||||
begin
|
||||
raise EMVCException.Create(E.Message, '', 0, 404);
|
||||
end
|
||||
else
|
||||
raise;
|
||||
end;
|
||||
Result := ObjectDict().Add('data', fArticlesService.GetByID(id));
|
||||
end;
|
||||
|
||||
procedure TArticlesController.GetArticleMeta;
|
||||
function TArticlesController.GetArticleMeta: IMVCObjectDictionary;
|
||||
begin
|
||||
try
|
||||
Render(ObjectDict().Add('data', GetArticlesService.GetMeta));
|
||||
except
|
||||
on E: EServiceException do
|
||||
begin
|
||||
raise EMVCException.Create(E.Message, '', 0, 404);
|
||||
end
|
||||
else
|
||||
raise;
|
||||
end;
|
||||
Result := ObjectDict().Add('data', fArticlesService.GetMeta);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
@ -3,19 +3,10 @@ unit Controllers.Base;
|
||||
interface
|
||||
|
||||
uses
|
||||
MVCFramework, MVCFramework.Commons, Services, MainDM;
|
||||
MVCFramework, MVCFramework.Commons, Services;
|
||||
|
||||
type
|
||||
TBaseController = class abstract(TMVCController)
|
||||
strict private
|
||||
FDM: TdmMain;
|
||||
FArticlesService: TArticlesService;
|
||||
function GetDataModule: TdmMain;
|
||||
strict protected
|
||||
function GetArticlesService: TArticlesService;
|
||||
public
|
||||
destructor Destroy; override;
|
||||
|
||||
end;
|
||||
|
||||
[MVCPath('/private')]
|
||||
@ -23,7 +14,7 @@ type
|
||||
public
|
||||
[MVCPath('/articles')]
|
||||
[MVCHTTPMethods([httpDELETE])]
|
||||
procedure DeleteAllArticles;
|
||||
procedure DeleteAllArticles(ArticlesService: IArticlesService);
|
||||
end;
|
||||
|
||||
implementation
|
||||
@ -31,34 +22,11 @@ implementation
|
||||
uses
|
||||
System.SysUtils;
|
||||
|
||||
{ TBaseController }
|
||||
|
||||
destructor TBaseController.Destroy;
|
||||
begin
|
||||
FArticlesService.Free;
|
||||
FDM.Free;
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TBaseController.GetArticlesService: TArticlesService;
|
||||
begin
|
||||
if not Assigned(FArticlesService) then
|
||||
FArticlesService := TArticlesService.Create(GetDataModule);
|
||||
Result := FArticlesService;
|
||||
end;
|
||||
|
||||
function TBaseController.GetDataModule: TdmMain;
|
||||
begin
|
||||
if not Assigned(FDM) then
|
||||
FDM := TdmMain.Create(nil);
|
||||
Result := FDM;
|
||||
end;
|
||||
|
||||
{ TPrivateController }
|
||||
|
||||
procedure TPrivateController.DeleteAllArticles;
|
||||
procedure TPrivateController.DeleteAllArticles(ArticlesService: IArticlesService);
|
||||
begin
|
||||
GetArticlesService.DeleteAllArticles();
|
||||
ArticlesService.DeleteAllArticles();
|
||||
end;
|
||||
|
||||
end.
|
||||
|
225
samples/articles_crud_server/FDConnectionConfigU.pas
Normal file
225
samples/articles_crud_server/FDConnectionConfigU.pas
Normal file
@ -0,0 +1,225 @@
|
||||
unit FDConnectionConfigU;
|
||||
|
||||
interface
|
||||
|
||||
|
||||
|
||||
procedure CreateFirebirdPrivateConnDef(AIsPooled: boolean);
|
||||
procedure CreateInterbasePrivateConnDef(AIsPooled: boolean);
|
||||
procedure CreateMySQLPrivateConnDef(AIsPooled: boolean);
|
||||
procedure CreateMSSQLServerPrivateConnDef(AIsPooled: boolean);
|
||||
procedure CreatePostgresqlPrivateConnDef(AIsPooled: boolean);
|
||||
procedure CreateSqlitePrivateConnDef(AIsPooled: boolean);
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
MVCFramework.Commons,
|
||||
System.Classes,
|
||||
System.IOUtils,
|
||||
FireDAC.Comp.Client,
|
||||
FireDAC.Moni.Base,
|
||||
FireDAC.Moni.FlatFile,
|
||||
FireDAC.Stan.Intf
|
||||
, Commons;
|
||||
|
||||
|
||||
var
|
||||
gFlatFileMonitor: TFDMoniFlatFileClientLink = nil;
|
||||
|
||||
procedure CreateMySQLPrivateConnDef(AIsPooled: boolean);
|
||||
var
|
||||
LParams: TStringList;
|
||||
begin
|
||||
{
|
||||
docker run --detach --env MARIADB_USER=example-user --env MARIADB_PASSWORD=my_cool_secret --env MARIADB_ROOT_PASSWORD=root -p 3306:3306 mariadb:latest
|
||||
}
|
||||
|
||||
LParams := TStringList.Create;
|
||||
try
|
||||
LParams.Add('Database=activerecorddb');
|
||||
LParams.Add('Protocol=TCPIP');
|
||||
LParams.Add('Server=localhost');
|
||||
LParams.Add('User_Name=root');
|
||||
LParams.Add('Password=root');
|
||||
LParams.Add('TinyIntFormat=Boolean'); { it's the default }
|
||||
LParams.Add('CharacterSet=utf8mb4'); // not utf8!!
|
||||
LParams.Add('MonitorBy=FlatFile');
|
||||
if AIsPooled then
|
||||
begin
|
||||
LParams.Add('Pooled=True');
|
||||
LParams.Add('POOL_MaximumItems=100');
|
||||
end
|
||||
else
|
||||
begin
|
||||
LParams.Add('Pooled=False');
|
||||
end;
|
||||
FDManager.AddConnectionDef(CON_DEF_NAME, 'MySQL', LParams);
|
||||
finally
|
||||
LParams.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure CreateMSSQLServerPrivateConnDef(AIsPooled: boolean);
|
||||
var
|
||||
LParams: TStringList;
|
||||
begin
|
||||
{
|
||||
docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=!SA_password!" -p 1433:1433 -d mcr.microsoft.com/mssql/server:2019-latest
|
||||
}
|
||||
|
||||
// [ACTIVERECORDB_SQLSERVER]
|
||||
// Database=activerecorddb
|
||||
// OSAuthent=Yes
|
||||
// Server=DANIELETETI\SQLEXPRESS
|
||||
// DriverID=MSSQL
|
||||
//
|
||||
|
||||
LParams := TStringList.Create;
|
||||
try
|
||||
LParams.Add('Database=activerecorddb');
|
||||
LParams.Add('OSAuthent=Yes');
|
||||
LParams.Add('Server=DANIELETETI\SQLEXPRESS');
|
||||
if AIsPooled then
|
||||
begin
|
||||
LParams.Add('Pooled=True');
|
||||
LParams.Add('POOL_MaximumItems=100');
|
||||
end
|
||||
else
|
||||
begin
|
||||
LParams.Add('Pooled=False');
|
||||
end;
|
||||
FDManager.AddConnectionDef(CON_DEF_NAME, 'MSSQL', LParams);
|
||||
finally
|
||||
LParams.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure CreateFirebirdPrivateConnDef(AIsPooled: boolean);
|
||||
var
|
||||
LParams: TStringList;
|
||||
begin
|
||||
LParams := TStringList.Create;
|
||||
try
|
||||
LParams.Add('Database=' + dotEnv.Env('database.path'));
|
||||
LParams.Add('Protocol=TCPIP');
|
||||
LParams.Add('Server=localhost');
|
||||
LParams.Add('User_Name=sysdba');
|
||||
LParams.Add('Password=masterkey');
|
||||
LParams.Add('CharacterSet=UTF8');
|
||||
if AIsPooled then
|
||||
begin
|
||||
LParams.Add('Pooled=True');
|
||||
LParams.Add('POOL_MaximumItems=100');
|
||||
end
|
||||
else
|
||||
begin
|
||||
LParams.Add('Pooled=False');
|
||||
end;
|
||||
FDManager.AddConnectionDef(CON_DEF_NAME, 'FB', LParams);
|
||||
finally
|
||||
LParams.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure CreateInterbasePrivateConnDef(AIsPooled: boolean);
|
||||
var
|
||||
LParams: TStringList;
|
||||
begin
|
||||
LParams := TStringList.Create;
|
||||
try
|
||||
LParams.Add('Database=' + TPath.GetFullPath(TPath.Combine('..\..',
|
||||
'data\ACTIVERECORDDB.IB')));
|
||||
LParams.Add('Protocol=TCPIP');
|
||||
LParams.Add('Server=localhost');
|
||||
LParams.Add('User_Name=sysdba');
|
||||
LParams.Add('Password=masterkey');
|
||||
LParams.Add('CharacterSet=UTF8');
|
||||
if AIsPooled then
|
||||
begin
|
||||
LParams.Add('Pooled=True');
|
||||
LParams.Add('POOL_MaximumItems=100');
|
||||
end
|
||||
else
|
||||
begin
|
||||
LParams.Add('Pooled=False');
|
||||
end;
|
||||
FDManager.AddConnectionDef(CON_DEF_NAME, 'IB', LParams);
|
||||
finally
|
||||
LParams.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure CreatePostgresqlPrivateConnDef(AIsPooled: boolean);
|
||||
var
|
||||
LParams: TStringList;
|
||||
begin
|
||||
LParams := TStringList.Create;
|
||||
try
|
||||
LParams.Add('Database=activerecorddb');
|
||||
LParams.Add('Protocol=TCPIP');
|
||||
LParams.Add('Server=localhost');
|
||||
LParams.Add('User_Name=postgres');
|
||||
LParams.Add('Password=postgres');
|
||||
// LParams.Add('MonitorBy=FlatFile');
|
||||
|
||||
// https://quality.embarcadero.com/browse/RSP-19755?jql=text%20~%20%22firedac%20guid%22
|
||||
LParams.Add('GUIDEndian=Big');
|
||||
if AIsPooled then
|
||||
begin
|
||||
LParams.Add('Pooled=True');
|
||||
LParams.Add('POOL_MaximumItems=100');
|
||||
end
|
||||
else
|
||||
begin
|
||||
LParams.Add('Pooled=False');
|
||||
end;
|
||||
FDManager.AddConnectionDef(CON_DEF_NAME, 'PG', LParams);
|
||||
finally
|
||||
LParams.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure CreateSqlitePrivateConnDef(AIsPooled: boolean);
|
||||
var
|
||||
LParams: TStringList;
|
||||
lFName: string;
|
||||
begin
|
||||
LParams := TStringList.Create;
|
||||
try
|
||||
lFName := TPath.Combine(TPath.GetDirectoryName(ParamStr(0)),
|
||||
'..\..\data\activerecorddb.db');
|
||||
LParams.Add('Database=' + lFName);
|
||||
LParams.Add('StringFormat=Unicode');
|
||||
if AIsPooled then
|
||||
begin
|
||||
LParams.Add('Pooled=True');
|
||||
LParams.Add('POOL_MaximumItems=100');
|
||||
end
|
||||
else
|
||||
begin
|
||||
LParams.Add('Pooled=False');
|
||||
end;
|
||||
FDManager.AddConnectionDef(CON_DEF_NAME, 'SQLite', LParams);
|
||||
finally
|
||||
LParams.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
initialization
|
||||
|
||||
gFlatFileMonitor := TFDMoniFlatFileClientLink.Create(nil);
|
||||
gFlatFileMonitor.FileColumns := [tiRefNo, tiTime, tiThreadID, tiClassName, tiObjID, tiMsgText];
|
||||
gFlatFileMonitor.EventKinds := [
|
||||
ekVendor, ekConnConnect, ekLiveCycle, ekError, ekConnTransact,
|
||||
ekCmdPrepare, ekCmdExecute, ekCmdDataIn, ekCmdDataOut];
|
||||
gFlatFileMonitor.ShowTraces := False;
|
||||
gFlatFileMonitor.FileAppend := False;
|
||||
gFlatFileMonitor.FileName := TPath.ChangeExtension(ParamStr(0), '.trace.log');
|
||||
gFlatFileMonitor.Tracing := True;
|
||||
|
||||
finalization
|
||||
|
||||
gFlatFileMonitor.Free;
|
||||
|
||||
end.
|
@ -23,10 +23,10 @@ uses
|
||||
FireDAC.DApt,
|
||||
FireDAC.Comp.DataSet,
|
||||
FireDAC.Phys.FBDef,
|
||||
FireDAC.VCLUI.Wait;
|
||||
FireDAC.VCLUI.Wait, Services;
|
||||
|
||||
type
|
||||
TdmMain = class(TDataModule)
|
||||
TdmMain = class(TDataModule, IArticlesDataModule)
|
||||
Connection: TFDConnection;
|
||||
dsArticles: TFDQuery;
|
||||
updArticles: TFDUpdateSQL;
|
||||
@ -34,7 +34,9 @@ type
|
||||
private
|
||||
{ Private declarations }
|
||||
public
|
||||
constructor Create;
|
||||
function SearchProducts(const SearchText: string): TDataSet;
|
||||
|
||||
end;
|
||||
|
||||
implementation
|
||||
@ -52,6 +54,11 @@ begin
|
||||
Connection.Params.Values['Database'] := dotEnv.Env('database.path');
|
||||
end;
|
||||
|
||||
constructor TdmMain.Create;
|
||||
begin
|
||||
inherited Create(nil);
|
||||
end;
|
||||
|
||||
function TdmMain.SearchProducts(const SearchText: string): TDataSet;
|
||||
begin
|
||||
Result := TFDMemTable.Create(nil);
|
||||
|
@ -5,23 +5,23 @@ interface
|
||||
uses
|
||||
System.Generics.Collections,
|
||||
BusinessObjects,
|
||||
MainDM,
|
||||
System.SysUtils,
|
||||
Commons, JsonDataObjects;
|
||||
|
||||
type
|
||||
|
||||
TServiceBase = class abstract
|
||||
strict protected
|
||||
FDM: TdmMain;
|
||||
public
|
||||
constructor Create(AdmMain: TdmMain); virtual;
|
||||
procedure Commit;
|
||||
procedure Rollback;
|
||||
procedure StartTransaction;
|
||||
IArticlesService = interface
|
||||
['{D6843E17-1B98-435C-9EBC-8E76DCEF9A3B}']
|
||||
function GetAll: TObjectList<TArticle>;
|
||||
function GetArticles(const aTextSearch: string): TObjectList<TArticle>;
|
||||
function GetByID(const AID: Integer): TArticle;
|
||||
procedure Delete(AArticolo: TArticle);
|
||||
procedure DeleteAllArticles;
|
||||
procedure Add(AArticolo: TArticle);
|
||||
procedure Update(AArticolo: TArticle);
|
||||
function GetMeta: TJSONObject;
|
||||
end;
|
||||
|
||||
TArticlesService = class(TServiceBase)
|
||||
TArticlesService = class(TInterfacedObject, IArticlesService)
|
||||
public
|
||||
function GetAll: TObjectList<TArticle>;
|
||||
function GetArticles(const aTextSearch: string): TObjectList<TArticle>;
|
||||
@ -36,112 +36,59 @@ type
|
||||
implementation
|
||||
|
||||
uses
|
||||
FireDAC.Stan.Option,
|
||||
FireDAC.Comp.Client,
|
||||
FireDAC.Stan.Param,
|
||||
MVCFramework.ActiveRecord,
|
||||
MVCFramework.FireDAC.Utils,
|
||||
MVCFramework.DataSet.Utils,
|
||||
MVCFramework.Serializer.Commons;
|
||||
MVCFramework.Serializer.Commons, Data.DB;
|
||||
|
||||
|
||||
{ TArticoliService }
|
||||
|
||||
procedure TArticlesService.Add(AArticolo: TArticle);
|
||||
var
|
||||
Cmd: TFDCustomCommand;
|
||||
begin
|
||||
AArticolo.CheckInsert;
|
||||
Cmd := FDM.updArticles.Commands[arInsert];
|
||||
TFireDACUtils.ObjectToParameters(Cmd.Params, AArticolo, 'NEW_');
|
||||
Cmd.Execute;
|
||||
AArticolo.ID := Cmd.ParamByName('ID').AsInteger;
|
||||
AArticolo.Insert;
|
||||
end;
|
||||
|
||||
procedure TArticlesService.Delete(AArticolo: TArticle);
|
||||
var
|
||||
Cmd: TFDCustomCommand;
|
||||
begin
|
||||
AArticolo.CheckDelete;
|
||||
Cmd := FDM.updArticles.Commands[arDelete];
|
||||
TFireDACUtils.ObjectToParameters(Cmd.Params, AArticolo, 'OLD_');
|
||||
Cmd.Execute;
|
||||
AArticolo.Delete();
|
||||
end;
|
||||
|
||||
procedure TArticlesService.DeleteAllArticles;
|
||||
begin
|
||||
FDM.Connection.ExecSQL('delete from articoli');
|
||||
TMVCActiveRecord.DeleteAll(TArticle);
|
||||
end;
|
||||
|
||||
function TArticlesService.GetAll: TObjectList<TArticle>;
|
||||
begin
|
||||
FDM.dsArticles.Open('SELECT * FROM ARTICOLI ORDER BY ID', []);
|
||||
Result := FDM.dsArticles.AsObjectList<TArticle>;
|
||||
FDM.dsArticles.Close;
|
||||
Result := TMVCActiveRecord.SelectRQL<TArticle>('sort(+id)', 1000);
|
||||
end;
|
||||
|
||||
function TArticlesService.GetArticles(
|
||||
const aTextSearch: string): TObjectList<TArticle>;
|
||||
begin
|
||||
FDM.dsArticles.Open('SELECT * FROM ARTICOLI WHERE DESCRIZIONE CONTAINING ? ORDER BY ID', [aTextSearch]);
|
||||
try
|
||||
Result := FDM.dsArticles.AsObjectList<TArticle>()
|
||||
finally
|
||||
FDM.dsArticles.Close;
|
||||
end;
|
||||
Result := TMVCActiveRecord.SelectByNamedQuery<TArticle>('search_by_text',[aTextSearch],[ftString]);
|
||||
end;
|
||||
|
||||
function TArticlesService.GetByID(const AID: Integer): TArticle;
|
||||
begin
|
||||
FDM.dsArticles.Open('SELECT * FROM ARTICOLI WHERE ID = :ID', [AID]);
|
||||
try
|
||||
if not FDM.dsArticles.Eof then
|
||||
Result := FDM.dsArticles.AsObject<TArticle>
|
||||
else
|
||||
raise EServiceException.Create('Article not found');
|
||||
finally
|
||||
FDM.dsArticles.Close;
|
||||
end;
|
||||
Result := TMVCActiveRecord.GetByPK<TArticle>(AID);
|
||||
end;
|
||||
|
||||
function TArticlesService.GetMeta: TJSONObject;
|
||||
begin
|
||||
FDM.dsArticles.Open('SELECT ID, CODICE as CODE, DESCRIZIONE as DESCRIPTION, PREZZO as PRICE, CREATED_AT as CREATEDAT, UPDATED_AT as UPDATEDAT FROM ARTICOLI WHERE TRUE = FALSE');
|
||||
Result := FDM.dsArticles.MetadataAsJSONObject();
|
||||
var lDS := TMVCActiveRecord.SelectDataSet('SELECT ID, CODICE as CODE, DESCRIZIONE as DESCRIPTION, PREZZO as PRICE, CREATED_AT as CREATEDAT, UPDATED_AT as UPDATEDAT FROM ARTICOLI WHERE TRUE = FALSE',[]);
|
||||
try
|
||||
Result := lDS.MetadataAsJSONObject()
|
||||
finally
|
||||
lDS.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TArticlesService.Update(AArticolo: TArticle);
|
||||
var
|
||||
Cmd: TFDCustomCommand;
|
||||
begin
|
||||
AArticolo.CheckUpdate;
|
||||
Cmd := FDM.updArticles.Commands[arUpdate];
|
||||
TFireDACUtils.ObjectToParameters(Cmd.Params, AArticolo, 'NEW_');
|
||||
Cmd.ParamByName('OLD_ID').AsInteger := AArticolo.ID;
|
||||
Cmd.Execute;
|
||||
if Cmd.RowsAffected <> 1 then
|
||||
raise Exception.Create('Article not found');
|
||||
end;
|
||||
|
||||
{ TServiceBase }
|
||||
|
||||
procedure TServiceBase.Commit;
|
||||
begin
|
||||
FDM.Connection.Commit;
|
||||
end;
|
||||
|
||||
constructor TServiceBase.Create(AdmMain: TdmMain);
|
||||
begin
|
||||
inherited Create;
|
||||
FDM := AdmMain;
|
||||
end;
|
||||
|
||||
procedure TServiceBase.Rollback;
|
||||
begin
|
||||
FDM.Connection.Rollback;
|
||||
end;
|
||||
|
||||
procedure TServiceBase.StartTransaction;
|
||||
begin
|
||||
FDM.Connection.StartTransaction;
|
||||
AArticolo.Update();
|
||||
end;
|
||||
|
||||
end.
|
||||
|
@ -2,7 +2,8 @@ unit WebModuleUnit1;
|
||||
|
||||
interface
|
||||
|
||||
uses System.SysUtils, System.Classes, Web.HTTPApp, mvcframework;
|
||||
uses System.SysUtils, System.Classes, Web.HTTPApp, mvcframework, FireDAC.Phys.FBDef, FireDAC.Stan.Intf, FireDAC.Phys,
|
||||
FireDAC.Phys.IBBase, FireDAC.Phys.FB;
|
||||
|
||||
type
|
||||
TWebModule1 = class(TWebModule)
|
||||
@ -23,10 +24,13 @@ implementation
|
||||
uses
|
||||
Controllers.Articles,
|
||||
MVCFramework.Middleware.CORS,
|
||||
MVCFramework.Middleware.ActiveRecord,
|
||||
MVCFramework.Middleware.Compression,
|
||||
MVCFramework.Middleware.Trace,
|
||||
MVCFramework.SQLGenerators.Firebird,
|
||||
MVCFramework.Commons,
|
||||
Controllers.Base;
|
||||
Controllers.Base,
|
||||
Commons;
|
||||
|
||||
{$R *.dfm}
|
||||
|
||||
@ -45,6 +49,8 @@ begin
|
||||
{$ENDIF}
|
||||
FEngine.AddMiddleware(TCORSMiddleware.Create);
|
||||
FEngine.AddMiddleware(TMVCCompressionMiddleware.Create(256));
|
||||
FEngine.AddMiddleware(TMVCActiveRecordMiddleware.Create(CON_DEF_NAME));
|
||||
|
||||
// FEngine.AddMiddleware(TMVCTraceMiddleware.Create);
|
||||
end;
|
||||
|
||||
|
@ -10,6 +10,7 @@ uses
|
||||
MVCFramework.Commons,
|
||||
MVCFramework.Signal,
|
||||
MVCFramework.Logger,
|
||||
MVCFramework.Container,
|
||||
MVCFramework.dotEnv,
|
||||
Web.WebReq,
|
||||
Web.WebBroker,
|
||||
@ -18,10 +19,10 @@ uses
|
||||
Controllers.Articles in 'Controllers.Articles.pas',
|
||||
Services in 'Services.pas',
|
||||
BusinessObjects in 'BusinessObjects.pas',
|
||||
MainDM in 'MainDM.pas' {dmMain: TDataModule},
|
||||
Commons in 'Commons.pas',
|
||||
MVCFramework.ActiveRecord in '..\..\sources\MVCFramework.ActiveRecord.pas',
|
||||
MVCFramework.Serializer.JsonDataObjects in '..\..\sources\MVCFramework.Serializer.JsonDataObjects.pas';
|
||||
MVCFramework.Serializer.JsonDataObjects in '..\..\sources\MVCFramework.Serializer.JsonDataObjects.pas',
|
||||
FDConnectionConfigU in 'FDConnectionConfigU.pas';
|
||||
|
||||
{$R *.res}
|
||||
|
||||
@ -68,6 +69,12 @@ begin
|
||||
.Build(); //uses the executable folder to look for .env* files
|
||||
end);
|
||||
|
||||
CreateFirebirdPrivateConnDef(True);
|
||||
|
||||
DefaultMVCServiceContainer
|
||||
.RegisterType(TArticlesService, IArticlesService, '', TRegistrationType.SingletonPerRequest)
|
||||
.Build;
|
||||
|
||||
WebRequestHandlerProc.MaxConnections := dotEnv.Env('dmvc.handler.max_connections', 1024);
|
||||
RunServer(dotEnv.Env('dmvc.server.port', 8080));
|
||||
except
|
||||
|
@ -97,13 +97,10 @@
|
||||
<DCCReference Include="Controllers.Articles.pas"/>
|
||||
<DCCReference Include="Services.pas"/>
|
||||
<DCCReference Include="BusinessObjects.pas"/>
|
||||
<DCCReference Include="MainDM.pas">
|
||||
<Form>dmMain</Form>
|
||||
<DesignClass>TDataModule</DesignClass>
|
||||
</DCCReference>
|
||||
<DCCReference Include="Commons.pas"/>
|
||||
<DCCReference Include="..\..\sources\MVCFramework.ActiveRecord.pas"/>
|
||||
<DCCReference Include="..\..\sources\MVCFramework.Serializer.JsonDataObjects.pas"/>
|
||||
<DCCReference Include="FDConnectionConfigU.pas"/>
|
||||
<BuildConfiguration Include="Base">
|
||||
<Key>Base</Key>
|
||||
</BuildConfiguration>
|
||||
|
@ -23,14 +23,6 @@ type
|
||||
procedure Build();
|
||||
end;
|
||||
|
||||
MVCInjectAttribute = class(TCustomAttribute)
|
||||
private
|
||||
fServiceName: String;
|
||||
public
|
||||
constructor Create(ServiceName: String = '');
|
||||
property ServiceName: String read fServiceName;
|
||||
end;
|
||||
|
||||
EMVCContainerError = class(Exception) end;
|
||||
EMVCContainerErrorUnknownService = class(EMVCContainerError) end;
|
||||
EMVCContainerErrorInterfaceNotSupported = class(EMVCContainerError) end;
|
||||
@ -45,7 +37,7 @@ type
|
||||
implementation
|
||||
|
||||
uses
|
||||
MVCFramework.Rtti.Utils;
|
||||
MVCFramework.Rtti.Utils, MVCFramework;
|
||||
|
||||
type
|
||||
IMVCServiceInternalResolver = interface
|
||||
@ -314,15 +306,6 @@ begin
|
||||
Result := TMVCServiceContainer.Create;
|
||||
end;
|
||||
|
||||
|
||||
{ MVCInjectAttribute }
|
||||
|
||||
constructor MVCInjectAttribute.Create(ServiceName: String);
|
||||
begin
|
||||
inherited Create;
|
||||
fServiceName := ServiceName;
|
||||
end;
|
||||
|
||||
{ TMVCServiceContainerAdapter }
|
||||
|
||||
constructor TMVCServiceContainerAdapter.Create(Container: IMVCServiceContainer);
|
||||
|
@ -315,7 +315,7 @@ begin
|
||||
end;
|
||||
if lMsg.Trim.IsEmpty then
|
||||
lMsg := '<EOF>';
|
||||
raise ERQLException.CreateFmt('[Error] %s (column %d - found %s)', [message, fCurIdx, lMsg]);
|
||||
raise ERQLException.CreateFmt('[Error] %s (column %d - found %s)', [message, fCurIdx, lMsg]) at AddressOfReturnAddress;
|
||||
end;
|
||||
|
||||
procedure TRQL2SQL.Execute(
|
||||
|
@ -338,6 +338,14 @@ type
|
||||
|
||||
end;
|
||||
|
||||
MVCInjectAttribute = class(TCustomAttribute)
|
||||
private
|
||||
fServiceName: String;
|
||||
public
|
||||
constructor Create(ServiceName: String = '');
|
||||
property ServiceName: String read fServiceName;
|
||||
end;
|
||||
|
||||
// test
|
||||
// TMVCHackHTTPAppRequest = class(TIdHTTPAppRequest)
|
||||
// private
|
||||
@ -5051,6 +5059,16 @@ begin
|
||||
inherited;
|
||||
end;
|
||||
|
||||
{ MVCInjectAttribute }
|
||||
|
||||
{ MVCInjectAttribute }
|
||||
|
||||
constructor MVCInjectAttribute.Create(ServiceName: String);
|
||||
begin
|
||||
inherited Create;
|
||||
fServiceName := ServiceName;
|
||||
end;
|
||||
|
||||
initialization
|
||||
|
||||
// https://quality.embarcadero.com/browse/RSP-38281
|
||||
|
Loading…
Reference in New Issue
Block a user