Хотелось бы уточнить вопрос, как отделить бизнес логику от базы данных в проектах Lazarus?
Я знаю, что есть патnерн MVC но не видел его применения в Delphi/Lazarus. Может есть какие то практики, советы и т.д
Пример.
Есть таблица заказов в базе, данную информацию нужно выводить менеджеру. Первый вариант работал так.
Накидал на форму кнопочки и stringgrid и при инициализации делал запрос и заполнял таблицу. Но мне по задаче понадобилось, что бы эту таблицу могли просматривать другие сотрудники.
Второй вариант. Создал класс Manager Table которая в конструкторе настраивает кол-во колонок и именую их + делает запрос к базе. Теперь я просто размещаю stringgrid на форме и передаю ее в класс ManagerTable.
Теперь хочу отделить логику базы от интерфейса.
Как я сделал.
- Код: Выделить всё
unit ucore;
{$mode objfpc}{$H+}
interface
uses
classes, udb;
type
PProduct = ^TProduct;
TProduct = object
private
public
Id : string;
Title: string;
end;
PTask = ^TTask;
TTask = object
private
public
Id : string;
Width : string;
Height : string;
Product : TProduct;
end;
TTasks = array of TTask;
type
PCore = ^TCore;
TCore = object
private
public
Ident: string;
Title: string;
function GetProductNameById(id: longword): string;
procedure EditProductNameById(id: longword; Name: string);
function GetTasks(): TTasks;
end;
implementation
function TCore.GetProductNameById(id: longword): string;
begin
with Db.Query do
begin
SQL.Clear;
SQL.Add('SELECT title FROM products WHERE id = :id');
ParamByName('id').AsInteger := id;
Open;
Result := FieldByName('title').AsString;
Close;
end;
end;
procedure TCore.EditProductNameById(id: longword; Name: string);
begin
Db.Query.SQL.Clear;
Db.Query.SQL.Add('UPDATE products SET title = :title WHERE id = :id');
Db.Query.ParamByName('id').AsInteger := id;
Db.Query.ParamByName('title').AsString := Name;
Db.Query.ExecSQL;
Db.Transact.Commit;
Db.Query.Close;
end;
function TCore.GetTasks(): TTasks;
var
Tasks: TTasks;
i : longword;
begin
with Db.Query do
begin
SQL.Clear;
SQL.Add('SELECT tasks.id as task_id, width, height, products.title as product_name, products.id as product_id FROM tasks JOIN products ON tasks.product_id = products.id');
Open;
SetLength(Tasks, RecordCount);
i := 0;
while Eof <> true do
begin
Tasks[i].Id := FieldByName('task_id').AsString;
Tasks[i].Width := FieldByName('width').AsString;
Tasks[i].Height := FieldByName('height').AsString;
Tasks[i].Product.Title := FieldByName('product_name').AsString;
Tasks[i].Product.Id := FieldByName('product_id').AsString;
Next;
inc(i);
end;
Close;
end;
Result := Tasks;
end;
end.
И вызываю уже в программе данный класс. Данное ядро пишу на фрипаскале в консольном варианте, с последующей интеграцией модулей.
- Код: Выделить всё
{$mode objfpc}
program test;
uses
classes, ucore;
var
Source: TCore;
Table : TTasks;
i : longword;
begin
SetMultiByteConversionCodePage(CP_UTF8);
SetMultiByteRTLFileSystemCodePage(CP_UTF8);
Table := Source.GetTasks();
i := 0;
while (i < Length(Table)) do
begin
WriteLn(Table[i].id + ':' + Table[i].Width + ':' + Table[i].Product.Title);
inc(i);
end;
end.
Код подключения к базе.
- Код: Выделить всё
unit udb;
{$mode objfpc}{$H+}
interface
uses
sqldb, sqlite3conn;
type
TDataBase = class
private
public
Connect : TSQLite3Connection;
Transact: TSQLTransaction;
Query : TSQLQuery;
constructor Create(Name: string);
destructor Free();
end;
var
Db: TDataBase;
implementation
constructor TDataBase.Create(Name: string);
begin
Connect := TSQLite3Connection.Create(nil);
Connect.DatabaseName := Name;
Connect.Transaction := Transact;
Connect.Connected := True;
Transact := TSQLTransaction.Create(nil);
Transact.DataBase := Connect;
Transact.Active := true;
Query := TSQLQuery.Create(nil);
Query.Database := Connect;
Query.Transaction := Transact;
end;
destructor TDataBase.Free();
begin
Connect.Free;
Transact.Free;
Query.Free;
end;
initialization
Db := TDataBase.Create('test.db');
finalization
Db.Free();
end.
Правильно я делаю, есть ли более гибкое решение?