Olá

Este seria mais um post da série Dia-a-dia do Desenvolvedor de Software, porém, como penso voltar a discutir sobre o assunto no futuro, irei tratá-lo como avulso, pelo menos por enquanto.

Do que se trata

Utilizar Delphi para interagir com Banco de Dados MongoDB utilizando Parse, que no caso será via www.back4app.com.

Motivação

O sistema que disponibilizo aos meus clientes tem um controle de liberação. Este controle acessa um banco de dados online, verifica se o cliente tem pendências e se está habilitado com base numa data de liberação.

Antes, eu vinha utilizando uma hospedagem normal, com banco de dados MySQL. Porém, recentemente surgiu a necessidade de ampliar a interação com o banco de dados, que seria: implementar um sistema de envio de mensagens (financeiro, informações gerais, atualizações, correções, etc.).

Utilizar a atual estrutura era inviável, mesmo porque, eu precisaria de mais acesso aos recursos do SO (apache/linux), e numa hospedagem normal não teria essa possibilidade (pelo menos não na atual hospedagem que utilizo aqui).

Primeiramente, pensei utilizar o AWS – Amazon Web Services. Cheguei a efetuar alguns testes e confesso que fiquei animado com os primeiros resultados. Mas eu precisava de algo mais simples visto que, mesmo ampliando o sistema com o controle de mensagens, não se justificaria contratar toda a estrutura oferecida pela Amazon.

Passei a testar os “bancos de dados em tempo real” como o Firebase e o Parse. E estes demonstraram ser uma ótima alternativa e, com simplicidade, supria tranquilamente minhas necessidades. Após vários testes, escolhi Back4app.

Eu pequei

Alguns desenvolvedores tratam linguagens de programação como tratam de religião (me refiro aos extremistas de plantão). E com estes, é comum desentendimentos em redes sociais, fóruns e até mesmo no ambiente de trabalho. E para eles eu digo: eu pequei.

Nunca me apeguei a qualquer tipo de tecnologia, seja ela qual for. Nunca me prendi a discussões do tipo “minha linguagem é melhor do que a sua!”, “Mac é melhor do que PC”, “Android rules, IOS is bad”, “Windows fede, Linux reina”, etc. Considero este tipo de discussão um atraso tremendo. Mesmo porque, o que vale pra mim no final das contas é que meu problema seja resolvido.

Feito esta explicação, retomo. No controle de liberação (habilitando ou não a utilização do sistema), construí uma pequena API utilizado Slim FrameWork (PHP). Para liberar um cliente, eu utilizo sistema feito no Delphi no desktop e no Android, primeiramente utilizei Xamarin(Visual Studio C#) e posteriormente, Android Studio.

É agora que a porca torce o rabo! Sei que muitos desenvolvedores Delphi me diriam: “Mas como pode?!? o Delphi já lhe permitiria desenvolver um único sistema, compilando para as três plataformas: desktop, Android e IOS!”.

Sim, sim, eu sei. Porém, a versão que comprei do Delphi (XE2 professional) não me permitiu utilizá-lo para este fim. E como sendo um micro, micro, miiiiiiicro empresário, atualizar para uma versão mais completa é complicado. Então, a escolha do Android Studio foi algo natural para mim. E aí você pergunta: e o IOS?! Simples, não desenvolvo para esta plataforma. Não no momento (e se necessário, que venha SWift!!!).

Chega de blá, blá, blá

Escolhido o serviço, Back4app, torna-se necessário colocar mãos na massa, ou seria no código?!

Procurei algo pronto no Github, porém achei apenas para Firebase. Como não sou preguiçoso (pelo menos não neste caso! Experimente me pedir para lavar uma louça, não é mesmo patroa?!), parti para a documentação da API disponibilizada no Dashboard do serviço: Parse – Rest API Guide.

Tudo muito simples, mas “chatim” às vezes. No Android Studio, já temos uma vasta biblioteca que torna tudo muito simples de implementar (fiz um chat simples com apenas alguns minutos de trabalho), porém para Delphi, teremos que desenvolver a partir do zero.

Ao acessar o Guia conseguimos já de início identificar possíveis objetos:

parserestapi

Após alguns minutos lendo a API, cheguei à seguinte conclusão/interface:

unit DelphiParse.Interfaces;

interface

uses System.JSON, System.SysUtils, System.Generics.Collections;

type
IResponseParse = interface
['{D356B879-8FAC-47BC-8946-7418497C1047}']
function ResponseAsString(const Encoding: TEncoding = nil): string;
end;

IDelphiParse = interface
['{87E940B9-D2F8-4197-84D8-A84A426049BA}']
function Post(const UrlParams: array of string;
ObjectJson: TJSONValue = nil;
QueryParams: string = ''): IResponseParse;

function Get(const UrlParams: array of string;
ObjectJson: TJSONValue = nil;
QueryParams: string = ''): IResponseParse;

function Put(const UrlParams: array of string;
ObjectJson: TJSONValue = nil;
QueryParams: string = ''): IResponseParse;

function Delete(const UrlParams: array of string;
ObjectJson: TJSONValue = nil;
QueryParams: string = ''): IResponseParse;
end;

IParseQuery = interface
['{A00E5771-50D0-44C2-86BC-2F0ED2418CF9}']

function Count: Integer;

//where
procedure WhereEqualTo(Key, Value: string);
procedure WhereStartsWith(Key, Value: string);
procedure WhereContains(Key, Value: string);

//others
procedure SetLimit(Value: Integer);
procedure SetSkip(Value: Integer);

//formatted
function GetParamsFormatted: string;
end;

IParseObject = interface
['{A6616D36-B794-46DC-BBC9-51CF0AC18E5F}']
procedure WhereEqualTo(Key, Value: string);
procedure WhereStartsWith(Key, Value: string);
procedure WhereContains(Key, Value: string);
procedure Limit(Value: Integer);
procedure Skip(Value: Integer);

procedure Add(Key, Value: Variant);

function SaveInBackGround: string;
function GetInBackGround: string;
function GetAllInBackGround: string;
function DeleteInBackGround(ObjectId: string): string;
end;
  • IDelphiParse: classes que implementem esta interface será a responsável por enviar e receber nossos JSONObjects.
  • IQueryParse: será onde montaremos os parâmetros no formato JSON. Exemplo: {“nome”:”joão”&limit:100&Skip=1}.
  • IParseObject: terá a incumbência de processar os comandos da API (inserir, atualizar, buscar e deletar).

A Classe TDelphiParse implementa IDelphiParse:

TDelphiParse = class(TInterfacedObject, IDelphiParse)
private
function Send(const UrlParams: array of string;
const Command: TParseVerbs; ObjectJson: TJSONValue = nil;
QueryParams: string = ''): IResponseParse;

function FormatUrlParams(Params: array of string): string;
public
function Post(const UrlParams: array of string;
ObjectJson: TJSONValue = nil;
QueryParams: string = ''): IResponseParse;

function Get(const UrlParams: array of string;
ObjectJson: TJSONValue = nil;
QueryParams: string = ''): IResponseParse;

function Put(const UrlParams: array of string;
ObjectJson: TJSONValue = nil;
QueryParams: string = ''): IResponseParse;

function Delete(const UrlParams: array of string;
ObjectJson: TJSONValue = nil;
QueryParams: string = ''): IResponseParse;
end;

Para exemplificar, vejamos o método Post:

function TDelphiParse.Post(const UrlParams: array of string;
ObjectJson: TJSONValue; QueryParams: string): IResponseParse;
begin
Result := Send(UrlParams, TParseVerbs.pvPost, ObjectJson, QueryParams);
end;

E o método Send:

function TDelphiParse.Send(const UrlParams: array of string;
const Command: TParseVerbs; ObjectJson: TJSONValue;
QueryParams: string): IResponseParse;
var
HttpCliente: THTTPClient;
HttpResponse: IHTTPResponse;
CompletURL: string;
ObjectStream: TStringStream;
begin
HttpCliente := THTTPClient.Create;
try
HttpCliente.ContentType := 'application/json';
HttpCliente.CustomHeaders['X-Parse-Application-Id'] := APP_ID;
HttpCliente.CustomHeaders['X-Parse-REST-API-Key'] := REST_KEY;
ObjectStream := nil;
if ObjectJson <> nil then
ObjectStream := TStringStream.Create(ObjectJson.ToJSON);
try
CompletURL := BASE_URL + FormatUrlParams(UrlParams) +
IfThen(QueryParams='','', '?' + QueryParams);
case Command of
pvPost:
HttpResponse := HttpCliente.Post(CompletURL, ObjectStream);
pvGet:
HttpResponse := HttpCliente.Get(CompletURL);
pvPut:
HttpResponse := HttpCliente.Put(CompletURL, ObjectStream);
pvDelete:
HttpResponse := HttpCliente.Delete(CompletURL);
end;
Result := TResponseParse.Create(HttpResponse);
finally
if Assigned(ObjectStream) then
ObjectStream.Free;
end;
finally
HttpCliente.Free;
end;
end;

O Post recebe a URL completa do resource e o objeto JSON, e repassa ao método Send. Este sim, terá a responsabilidade de enviar os dados para a API. Passamos no header a APP ID e o REST API Key.

Com relação a IParseObject, vejamos a classe que implementa esta interface:

TParseObjects = class(TInterfacedObject, IParseObject)
private
FClassName: string;
Obj: TJSonObject;
Parse: IDelphiParse;
Query: IParseQuery;
public
constructor Create(ClassName: string);
destructor Destroy; override;

procedure WhereEqualTo(Key, Value: string);
procedure WhereStartsWith(Key, Value: string);
procedure WhereContains(Key, Value: string);
procedure Limit(Value: Integer);
procedure Skip(Value: Integer);

procedure Add(Key, Value: Variant);

function SaveInBackGround: string;
function GetInBackGround: string;
function GetAllInBackGround: string;
function DeleteInBackGround(ObjectId: string): string;
end;

Como este projeto, assim como foi informado no Github (link no final), é um Alfa do Alfa ;), então destaco o método SaveInBackGround, que foi criado apenas para efetuarmos os testes abaixo. Inspirado no que temos no Android, este serviria tanto para inserir quanto para salvar registros na base de dados. Porém, como vemos abaixo, ele contém apenas a primeira opção, ou seja, a de inserir um novo registro. A função de salvar ainda será implementada.

function TParseObjects.SaveInBackGround: string;
begin
if (Obj.Count = 0) then
raise Exception.Create('Objeto JSON não preenchido!');
Result := Parse.Post(['classes', FClassName], Obj).ResponseAsString();
end;

Outro ponto que merece atenção, SaveInBackGround sugere o uso de threads, o que ainda não foi implementado.

Não irei passar por todas as classes e nem explicar cada método que compõe este projeto, visto que o código restante é bastante intuitivo e poderá ser visto na íntegra ao final deste post.

Primeiros Testes

Finalizado o processo de codificação, vamos ao teste. Iremos simular um sistema de envio de mensagens contendo: username, mensagem.

Para isto, criei um form:

form1

Vejamos o código do botão de enviar mensagem:

var
Parse: IParseObject;
Resultado: string;
begin
Parse := TParseObjects.Create('Mensagens');
Parse.Add('username', editUser.Text);
Parse.Add('mensagem', editMessage.Text);
Resultado := Parse.SaveInBackGround();
memResult.Lines.Clear;
memResult.Lines.Add(Resultado);
end;

É tudo que precisamos para enviar uma mensagem.

Nele, instanciamos um objeto do tipo TParseObject. Adicionamos o campo username e mensagem. Acionamos o método SaveInBackGround e o resultado adicionamos ao memo. Clicando no botão de enviar, obtemos:

enviomensagem

A resposta do envia mensagem informa a ID e data da criação.

Botão GetAll:

var
Parse: IParseObject;
Resultado: string;
begin
Parse := TParseObjects.Create('Mensagens');
Resultado := Parse.GetAllInBackGround();
memResult.Lines.Clear;
memResult.Lines.Add(Resultado);
end;

Clicando no botão, o Memo receberá todas as mensagens já enviadas no formato JSON:

{"results":[{"objectId":"FH9Am8MJOe","username":"joão","mensagem":"acabo de enviar mensagem para o Back4app","createdAt":"2016-09-10T18:23:12.148Z","updatedAt":"2016-09-10T18:23:12.148Z"}]}

Para finalizar, um código interessante é o do botão JsonToObj, que converte a resposta do servidor em objeto:

var
Parse: IParseObject;
Response: string;
JsonStr: TJSONObject;
JsonArray: TJSONArray;
MensagemJson: TJSONValue;
MensagemObj: TMensagem;
begin
Parse := TParseObjects.Create('Mensagens');
Response := Parse.GetAllInBackGround();
JsonStr := TJSONObject.ParseJSONValue(Response) as TJSONObject;
try
memResult.Lines.Clear;
JsonArray := JsonStr.GetValue('results') as TJSONArray;
for MensagemJson in JsonArray do
begin
MensagemObj := TJson.JsonToObject<TMensagem>(MensagemJson.ToString);
ObjetoToMemo(MensagemObj);
MensagemObj.Free;
end;
finally
JsonStr.Free;
end;
end;

Classe Mensagem:

TMensagem = class
private
FObjectId: string;
FCreatedAt: TDateTime;
FUpdatedAt: TDateTime;
FUsername: string;
FMensagem: string;
public
property ObjectId: string read FObjectId write FObjectId;
property CreatedAt: TDateTime read FCreatedAt write FCreatedAt;
property UpdatedAt: TDateTime read FUpdatedAt write FUpdatedAt;
property Username: string read FUsername write FUsername;
property Mensagem: string read FMensagem write FMensagem;
end;

Quando clicamos no botão, o resultado será:

================================
ObjectId: FH9Am8MJOe
Username: joão
Mensagem: acabo de enviar mensagem para o Back4app
CreatedAt: 10/09/2016 18:23:12
UpdatedAt: 10/09/2016 18:23:12
================================

Github

Link para o projeto: DelphiParse

Quando forem compilar, não se esqueçam de colocar a DLL do FastMM4 na pasta do executável.

Lembro mais uma vez, é apenas o começo. Muita coisa será alterada e/ou implementada ainda. Em posts futuros, irei mostrar a implementação de uma nova classe: TParseUser, para a questão do login. E também irei tratar das threads.

Isso é tudo por enquanto. Abraços.

Desenvolvedor de software desde 1995. Em 1998, abriu sua própria empresa, a Lukas Sistemas, desde então passou a atender diversas empresas, principalmente autopeças. Apaixonado por Delphi, porém não o impede de flertar com outras linguagens sempre que possível. Mora na cidade de Balsas/MA com sua esposa e dois filhos.

4 thoughts on “Delphi e Parse: Início dos trabalhos”

  1. OI Luiz,

    Para que ser essa declaração
    IParseQuery = interface
    [‘{A00E5771-50D0-44C2-86BC-2F0ED2418CF9}’]

    De onde você tirou esses numeros ?

    1. Oteniel

      Este é o GUID (Globally Unique Identifier), ou seja, Identificador Global Único. É um identificador da interface ou objeto de servidor COM. Para gerar o número, basta usar o atalho Ctrl+Shift+G.

      Serve para você saber se determinado objeto dá suporte a uma determinada interface, esta tem um identificador único, o GUID. É mais ou menos isso.

  2. Parabéns Luiz Carlos
    Gostaria de parabenizar está muito bom
    Concordo contigo, sobre a tecnologia, o problema é que as próprias empresas dão esse divisor de águas entre Delphi / Java etc.
    É possível com um pouco de esforço ter facilidades no delphi, basta arregaçar as mangas e ir a luta.

    Parabéns e que venha os próximos.

Deixe um comentário para ernani Cancelar resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.