completed merge - working on the master_details sample

This commit is contained in:
Daniele Teti 2021-05-03 19:29:01 +02:00
parent a0dbbc272b
commit 662d86e57a
11 changed files with 2503 additions and 199 deletions

View File

@ -482,8 +482,7 @@ begin
//all the other customers will be deleted
//calculate the unitofwork to merge the lists
//calculate the unit-of-work to merge the lists
TMVCActiveRecord.Merge<TCustomer>(lCustomers, lCustomersChanges).Apply(
procedure (const Customer: TCustomer; const EntityAction: TMVCEntityAction; var Handled: Boolean)
begin
@ -495,13 +494,12 @@ begin
end;
end);
finally
lCustomers.Free;
lCustomersChanges.Free;
end;
finally
lCustomersChanges.Free;
lCustomers.Free;
end;
lCustomers := TMVCActiveRecord.SelectRQL<TCustomer>('eq(rating,1)', 1000);
try
Assert(lCustomers.Count = 4, 'Expected 4 customers, got ' + lCustomers.Count.ToString);

View File

@ -0,0 +1,165 @@
unit BusinessObjects;
// *************************************************************************** }
//
// Delphi MVC Framework
//
// Copyright (c) 2010-2021 Daniele Teti and the DMVCFramework Team
//
// https://github.com/danieleteti/delphimvcframework
//
// ***************************************************************************
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// ***************************************************************************
interface
uses
MVCFramework.Serializer.Commons,
MVCFramework.ActiveRecord,
System.Generics.Collections,
System.Classes;
type
[MVCNameCase(ncCamelCase)]
[MVCTable('articles')]
TArticles = class(TMVCActiveRecord)
private
[MVCTableField('id', [foPrimaryKey, foAutoGenerated])]
fID: Int64;
[MVCTableField('description')]
fDescription: String;
[MVCTableField('price')]
fPrice: Integer;
public
constructor Create; override;
destructor Destroy; override;
property ID: Int64 read fID write fID;
property Description: String read fDescription write fDescription;
property Price: Integer read fPrice write fPrice;
end;
[MVCNameCase(ncCamelCase)]
[MVCTable('order_details')]
TOrderDetail = class(TMVCActiveRecord)
private
[MVCTableField('id', [foPrimaryKey, foAutoGenerated])]
fID: Int64;
[MVCTableField('id_order')]
fIDOrder: Int64;
[MVCTableField('id_article')]
fIDArticle: Int64;
[MVCTableField('unit_price')]
fUnitPrice: Currency;
[MVCTableField('discount')]
fDiscount: Integer;
[MVCTableField('quantity')]
fQuantity: Integer;
[MVCTableField('description')]
fDescription: String;
[MVCTableField('total')]
fTotal: Currency;
public
constructor Create; override;
destructor Destroy; override;
property ID: Int64 read fID write fID;
property IDOrder: Int64 read fIDOrder write fIDOrder;
property IDArticle: Int64 read fIDArticle write fIDArticle;
property UnitPrice: Currency read fUnitPrice write fUnitPrice;
property Discount: Integer read fDiscount write fDiscount;
property Quantity: Integer read fQuantity write fQuantity;
property Description: String read fDescription write fDescription;
property Total: Currency read fTotal write fTotal;
end;
[MVCNameCase(ncCamelCase)]
[MVCTable('orders')]
TOrder = class(TMVCActiveRecord)
private
[MVCTableField('id', [foPrimaryKey, foAutoGenerated])]
fID: Int64;
[MVCTableField('id_customer')]
fIDCustomer: Integer;
[MVCTableField('order_date')]
fOrderDate: TDate;
[MVCTableField('total')]
fTotal: Currency;
fDetails: TObjectList<TOrderDetail>;
protected
procedure OnAfterLoad; override;
public
constructor Create; override;
destructor Destroy; override;
property ID: Int64 read fID write fID;
property IDCustomer: Integer read fIDCustomer write fIDCustomer;
property OrderDate: TDate read fOrderDate write fOrderDate;
property Total: Currency read fTotal write fTotal;
property OrderItems: TObjectList<TOrderDetail> read fDetails;
end;
implementation
uses
System.SysUtils;
constructor TArticles.Create;
begin
inherited Create;
end;
destructor TArticles.Destroy;
begin
inherited;
end;
constructor TOrderDetail.Create;
begin
inherited Create;
end;
destructor TOrderDetail.Destroy;
begin
inherited;
end;
constructor TOrder.Create;
begin
inherited Create;
fDetails := TObjectList<TOrderDetail>.Create(true);
end;
destructor TOrder.Destroy;
begin
fDetails.Free;
inherited;
end;
procedure TOrder.OnAfterLoad;
var
lList: TObjectList<TOrderDetail>;
begin
inherited;
lList := TMVCActiveRecord.SelectRQL<TOrderDetail>(Format('eq(order_id,%d)',[ID]), 1000);
try
fDetails.Clear;
fDetails.AddRange(lList);
lList.OwnsObjects := False;
finally
lList.Free;
end;
end;
end.

View File

@ -0,0 +1,15 @@
unit Commons;
interface
uses
System.SysUtils;
type
EServiceException = class(Exception)
end;
implementation
end.

View File

@ -0,0 +1,43 @@
unit Controllers.Base;
interface
uses
MVCFramework, MVCFramework.Commons;
type
TBaseController = class abstract(TMVCController)
strict protected
public
destructor Destroy; override;
end;
[MVCPath('/private')]
TPrivateController = class(TBaseController)
public
[MVCPath('/articles')]
[MVCHTTPMethods([httpDELETE])]
procedure DeleteAllArticles;
end;
implementation
uses
System.SysUtils;
{ TBaseController }
destructor TBaseController.Destroy;
begin
inherited;
end;
{ TPrivateController }
procedure TPrivateController.DeleteAllArticles;
begin
// GetArticlesService.DeleteAllArticles();
end;
end.

View File

@ -0,0 +1,171 @@
unit Controllers.Orders;
interface
uses
mvcframework,
mvcframework.Commons,
mvcframework.Serializer.Commons,
System.Generics.Collections,
Controllers.Base, BusinessObjects;
type
[MVCDoc('Resource that manages Orders CRUD')]
[MVCPath('/orders')]
TOrdersController = class(TBaseController)
public
[MVCDoc('Returns the list of Orders')]
[MVCPath]
[MVCHTTPMethod([httpGET])]
procedure GetOrders;
[MVCDoc('Returns the list of Orders')]
[MVCPath('/searches')]
[MVCHTTPMethod([httpGET])]
procedure GetOrdersByDescription(const [MVCFromQueryString('q', '')] Search: String);
[MVCDoc('Returns the Order with the specified id')]
[MVCPath('/meta')]
[MVCHTTPMethod([httpGET])]
procedure GetOrderMeta;
[MVCDoc('Returns the Order with the specified id')]
[MVCPath('/($id)')]
[MVCHTTPMethod([httpGET])]
procedure GetOrderByID(id: Integer);
[MVCDoc('Deletes the Order with the specified id')]
[MVCPath('/($id)')]
[MVCHTTPMethod([httpDelete])]
procedure DeleteOrderByID(id: Integer);
[MVCDoc('Updates the Order with the specified id and return "200: OK"')]
[MVCPath('/($id)')]
[MVCHTTPMethod([httpPUT])]
procedure UpdateOrderByID(const [MVCFromBody] Order: TOrder; const id: Integer);
[MVCDoc('Creates a new Order and returns "201: Created"')]
[MVCPath]
[MVCHTTPMethod([httpPOST])]
procedure CreateOrder(const [MVCFromBody] Order: TOrder);
[MVCDoc('Creates new Orders from a list and returns "201: Created"')]
[MVCPath('/bulk')]
[MVCHTTPMethod([httpPOST])]
procedure CreateOrders(const [MVCFromBody] OrderList: TObjectList<TOrder>);
end;
implementation
{ TOrdersController }
uses
Commons,
mvcframework.Serializer.Intf,
System.SysUtils, mvcframework.ActiveRecord;
procedure TOrdersController.CreateOrder(const [MVCFromBody] Order: TOrder);
begin
// GetOrdersService.Add(Order);
// Render201Created('/Orders/' + Order.id.ToString, 'Order Created');
end;
procedure TOrdersController.CreateOrders(const OrderList: TObjectList<TOrder>);
begin
// GetOrdersService.StartTransaction;
// try
// for lOrder in OrderList do
// begin
// GetOrdersService.Add(lOrder);
// end;
// GetOrdersService.Commit;
// except
// GetOrdersService.Rollback;
// raise;
// end;
Render(201, 'Orders Created');
end;
procedure TOrdersController.DeleteOrderByID(id: Integer);
var
Order: TOrder;
begin
TMVCActiveRecord.CurrentConnection.StartTransaction;
try
Order := TMVCActiveRecord.GetByPk<TOrder>(id);
try
Order.Delete;
finally
Order.Free;
end;
TMVCActiveRecord.CurrentConnection.Commit;
except
TMVCActiveRecord.CurrentConnection.Rollback;
raise;
end;
end;
procedure TOrdersController.GetOrders;
begin
// Render(ObjectDict().Add('data', GetOrdersService.GetAll));
end;
procedure TOrdersController.GetOrdersByDescription(const Search: String);
var
lDict: IMVCObjectDictionary;
begin
// try
// if Search = '' then
// begin
// lDict := ObjectDict().Add('data', GetOrdersService.GetAll);
// end
// else
// begin
// lDict := ObjectDict().Add('data', GetOrdersService.GetOrders(Search));
// end;
// Render(lDict);
// except
// on E: EServiceException do
// begin
// raise EMVCException.Create(E.Message, '', 0, 404);
// end
// else
// raise;
// end;
end;
procedure TOrdersController.UpdateOrderByID(const Order: TOrder; const id: Integer);
begin
// Order.id := id;
// GetOrdersService.Update(Order);
// Render(200, 'Order Updated');
end;
procedure TOrdersController.GetOrderByID(id: Integer);
begin
try
// Render(ObjectDict().Add('data', GetOrdersService.GetByID(id)));
except
on E: EServiceException do
begin
raise EMVCException.Create(E.Message, '', 0, 404);
end
end;
end;
procedure TOrdersController.GetOrderMeta;
begin
try
// Render(ObjectDict().Add('data', GetOrdersService.GetMeta));
except
on E: EServiceException do
begin
raise EMVCException.Create(E.Message, '', 0, 404);
end
else
raise;
end;
end;
end.

View File

@ -0,0 +1,187 @@
unit FDConnectionConfigU;
interface
const
CON_DEF_NAME = 'MyConnX';
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
System.Classes,
System.IOUtils,
FireDAC.Comp.Client;
procedure CreateMySQLPrivateConnDef(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=root');
LParams.Add('Password=root');
LParams.Add('TinyIntFormat=Boolean'); { it's the default }
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, 'MySQL', LParams);
finally
LParams.Free;
end;
end;
procedure CreateMSSQLServerPrivateConnDef(AIsPooled: boolean);
var
LParams: TStringList;
begin
// [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');
// LParams.Add('TinyIntFormat=Boolean'); { it's the default }
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=' + TPath.GetFullPath(TPath.Combine('..\..', 'data\ACTIVERECORDDB.FDB')));
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');
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;
end.

View File

@ -0,0 +1,12 @@
object WebModule1: TWebModule1
OldCreateOrder = False
OnCreate = WebModuleCreate
Actions = <
item
Default = True
Name = 'DefaultHandler'
PathInfo = '/'
end>
Height = 230
Width = 415
end

View File

@ -0,0 +1,46 @@
unit WebModuleUnit1;
interface
uses System.SysUtils, System.Classes, Web.HTTPApp, mvcframework;
type
TWebModule1 = class(TWebModule)
procedure WebModuleCreate(Sender: TObject);
private
FEngine: TMVCEngine;
public
{ Public declarations }
end;
var
WebModuleClass: TComponentClass = TWebModule1;
implementation
{ %CLASSGROUP 'Vcl.Controls.TControl' }
uses Controllers.Orders, mvcframework.Middleware.CORS, mvcframework.Middleware.Compression,
Controllers.Base, MVCFramework.Commons;
{$R *.dfm}
procedure TWebModule1.WebModuleCreate(Sender: TObject);
begin
FEngine := TMVCEngine.Create(self,
procedure(Config: TMVCConfig)
begin
//Enabling the following line, the API will start to respond from "/api/v1"
//So "/articles/1" becomes "/api/v1/articles/1"
//Config[TMVCConfigKey.PathPrefix] := '/api/v1';
end);
FEngine.AddController(TOrdersController);
{$IFDEF TESTINSTANCE}
FEngine.AddController(TPrivateController);
{$ENDIF}
FEngine.AddMiddleware(TCORSMiddleware.Create);
FEngine.AddMiddleware(TMVCCompressionMiddleware.Create(256));
end;
end.

View File

@ -0,0 +1,51 @@
program masterdetailssample;
{$APPTYPE CONSOLE}
uses
System.SysUtils,
IdHTTPWebBrokerBridge,
Web.WebReq,
Web.WebBroker,
WebModuleUnit1 in 'WebModuleUnit1.pas' {WebModule1: TWebModule},
Controllers.Base in 'Controllers.Base.pas',
Controllers.Orders in 'Controllers.Orders.pas',
BusinessObjects in 'BusinessObjects.pas',
Commons in 'Commons.pas',
MVCFramework.ActiveRecord in '..\..\sources\MVCFramework.ActiveRecord.pas',
MVCFramework.Serializer.JsonDataObjects in '..\..\sources\MVCFramework.Serializer.JsonDataObjects.pas',
FDConnectionConfigU in 'FDConnectionConfigU.pas';
{$R *.res}
procedure RunServer(APort: Integer);
var
LServer: TIdHTTPWebBrokerBridge;
begin
WriteLn('ARTICLES CRUD Sample. Use articles_crud_vcl_client.dproj to manage data');
WriteLn(Format('Starting HTTP Server on port %d', [APort]));
LServer := TIdHTTPWebBrokerBridge.Create(nil);
try
LServer.DefaultPort := APort;
LServer.Active := True;
WriteLn('Press RETURN to stop the server');
ReadLn;
finally
LServer.Free;
end;
end;
begin
ReportMemoryLeaksOnShutdown := True;
try
if WebRequestHandler <> nil then
WebRequestHandler.WebModuleClass := WebModuleClass;
RunServer(8080);
except
on E: Exception do
WriteLn(E.ClassName, ': ', E.Message);
end
end.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff