Delphi e Parse: Novas implementações

Olá

No post anterior, demos início ao trabalho de fazer com que o Delphi interagisse com o Parse (Back4app).

Classe TParseUser

Agora, iremos implementar novas funcionalidades. E a primeira tarefa será a de disponibilizar acesso às funções de usuários (login, logout…). Para isso, uma nova classe será incluída no projeto, a TParseUser:

  TParseUser = class(TInterfacedObject, IParseUser)
  private
    FUserName: string;
    FEmail: string;
    FPassword: string;

    Obj: TJSonObject;
    CustomField: TDictionary<string,string>;
    Parse: IDelphiParse;
    Query: IParseQuery;

    procedure ValidatesNewUser;
    procedure FormatParams;
    procedure ProcessResponse(AResponse: string);
  public
    constructor Create;
    destructor Destroy; override;

    procedure SetUserName(Value: string);
    procedure SetEmail(Value: string);
    procedure SetPassword(Value: string);

    function GetSessionToken: string;

    procedure Add(Key, Value: Variant);

    function Login(UserName, Password: string): string;
    function LogOut: string;
    function GetCurrencyUser: string;
    function SignUpInBackground: string;
  end;

...

function TParseUser.Login(UserName, Password: string): string;
var
  Params: string;
begin
  Parse.SetRevocableSession(True);
  Params := Format('%s=%s&%s=%s', [
    TURI.URLEncode('username'),
    TURI.URLEncode(UserName),
    TURI.URLEncode('password'),
    TURI.URLEncode(Password)]);
  Result := Parse.Get(['login'], nil, Params).ResponseAsString();
  ProcessResponse(Result);
end;

function TParseUser.LogOut: string;
begin
  Parse.SetRevocableSession(False);
  Result := Parse.Post(['logout']).ResponseAsString();
  ProcessResponse(Result);
end;

function TParseUser.GetCurrencyUser: string;
begin
  if Parse.GetSessionToken.IsEmpty then
    Exit;
  Parse.SetRevocableSession(False);
  Result := Parse.Get(['users', 'me']).ResponseAsString();
  ProcessResponse(Result);
end;

...
// inserir novo user
function TParseUser.SignUpInBackground: string;
begin
  ValidatesNewUser;
  FormatParams;
  Parse.SetRevocableSession(True);
  Result := Parse.Post(['users'], Obj).ResponseAsString();
  ProcessResponse(Result);
end;

...

Perceba que agora temos os métodos necessários para logar, deslogar e inserir um novo usuário. Por enquanto, no que se refere aos usuários, é o que será entregue. Futuramente, voltaremos a esta parte do código para implementar demais recursos disponibilizados pela API.

Alterei nosso formulário de testes:

form2

No botão de inserir usuários temos:

var
  User: IParseUser;
  Resultado: string;
begin
  User := TParseUser.Create;
  User.SetUserName(edUserName.Text);
  User.SetPassword(edPassword.Text);
  User.SetEmail(edEmail.Text);
  Resultado := User.SignUpInBackground;
  memResult.Lines.Clear;
  memResult.Lines.Add(Resultado);
  memResult.Lines.Add('Token: ' + User.GetSessionToken);
end;

Simples, não?! É possível até mesmo pegar a sessão atual do usuário logado (linha 12).

Abaixo, código dos botões de login e logout:

//login
var
  User: IParseUser;
  Resultado: string;
begin
  User := TParseUser.Create;
  Resultado := User.Login(edUsername.Text, edPassword.Text);
  memResult.Lines.Clear;
  memResult.Lines.Add(Resultado);
  memResult.Lines.Add('Token: ' + User.GetSessionToken);
end;

//logout
var
  User: IParseUser;
  Resultado: string;
begin
  User := TParseUser.Create;
  Resultado := User.LogOut;
  memResult.Lines.Clear;
  memResult.Lines.Add(Resultado);
  memResult.Lines.Add('Token: ' + User.GetSessionToken);
end;

Sem segredo até aqui.

Refatorando TParseQuery

Nesta classe, trabalhamos as querys da API. Eu vinha criando uma lista (TList) para cada tipo de where (startswith, contains, equalTo, etc.). À medida que eu criava um novo “where”, era necessário criar uma nova lista. E neste processo, devia-se ter o cuidado com a instanciação da mesma e a sua liberação da memória quando esta não mais se fizesse necessária. Não fiquei satisfeito com a direção que isso tomara, visto que, ainda viriam outras constraints ($lte, $gte, etc.) que são disponibilizadas pela API.

Para contornar este problema, resolvi então, eliminar todas estas listas e criar uma classe para receber estas opções, ou seja, foi criada a TConstraints:

  TConstraints = class
  private
    List: TObjectDictionary<TConstraintType, TList<TParams>>;
    procedure ValidatesKey(Key: string; Params: TList<TParams>);
  public
    constructor Create;
    destructor Destroy; override;

    procedure AddParams(Key, Value: string; ConstraintType: TConstraintType;
      FieldType: TFieldType = ftString);

    procedure AddConstraint(ConstraintType: TConstraintType);
    function Items(Key: TConstraintType): TList<TParams>;
    function CountWhere: Integer;
  end;

O destaque fica por conta da lista (List) do tipo TObjectDictionary. Sendo deste tipo, no método Create, já adicionamos todas as querys que desejamos trabalhar:

constructor TConstraints.Create;
begin
  inherited;
  List := TObjectDictionary<TConstraintType, TList<TParams>>.Create([doOwnsValues]);
  Self.AddConstraint(ctEqualTo);
  Self.AddConstraint(ctStartsWith);
  Self.AddConstraint(ctContains);
  Self.AddConstraint(ctLessThan);
  Self.AddConstraint(ctGreaterThan);
  Self.AddConstraint(ctOthers);
end;

Na classe TParseQuery, basta criar um objeto do tipo TConstraints:

constructor TParseQuery.Create;
begin
  inherited;
  Constraints := TConstraints.Create;

A partir de agora, acionaremos o objeto criado para manipular cada query como, por exemplo, ao adicionar um novo parâmetro no EqualTo:

procedure TParseQuery.WhereEqualTo(Key, Value: string);
begin
  Constraints.AddParams(Key, Value, ctEqualTo);
end;

Voltando ao formulário de teste:

form3

Dando uma conferida no botão LessThan, temos:

var
  Parse: IParseObject;
  Response: string;
begin
  Text := InputBox('LessThan test in the Level field', 'Text:','');
  Parse := TParseObjects.Create('Mensagens');
  Parse.WhereLessThan('level', Text, ftNumber);
  Response := Parse.GetInBackGround;
  LerResponse(Response);
end;

Foi necessário inserir um terceiro parâmetro, FieldType, na requisição. Visto que alguns tipos, para funcionarem, necessitam desta informação.

Resumo

Foi dado início à implementação da classe responsável pelo controle dos usuários. Por enquanto, apenas adicionar, logar e deslogar foram implementadas.

Diversas alterações tornaram-se necessárias com a implementação de novas funcionalidades. Como por exemplo, a necessidade de algumas querys exigirem o tipo de dado que é passado no parâmetro.

A Classe TConstraints veio para facilitar o processo de adicionar uma nova query. Não temos mais a preocupação com instanciação e liberação de memória das listas criadas para cada função. Ainda não estou 100% satisfeito, e por isso, irei voltar para rever a questão das fórmulas de cada query. Talvez movê-las de TParseQuery para TConstraints, ou até mesmo, criar uma nova classe.

Para conferir o código completo, baixe os fontes no Github.

Abraços.

Sobre o autor: Luiz Carlos (60 Posts)

Desenvolvedor de software Balsas/MA


Compartilhe:

2 Replies to “Delphi e Parse: Novas implementações”

  1. Olá Luiz,
    Muito legal este teu exemplo de utilização no Delphi do back4app, parabéns!

    Queria saber se você tem algum exemplo de coluna do tipo POINTER, queria construir uma classe que tivesse um apontamento para outras, por exemplo um item venda que tem um produto, entendeu?

    1. Olá Edson,

      Antes de mais nada, obrigado pela visita.

      A necessidade que me levou a ir para o Parse exigia apenas trabalho com dados primitivos (int, double, etc.), então não cheguei a explorar colunas do tipo que você mencionou. Sugiro que dê uma olhada no manual do serviço. Lá poderá encontrar o que procura.

Deixe uma resposta

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