Software Development Kit
to the Delphi-Win32 and Free Pascal compilers
Home > Wiki > br/Metadata

br/Metadata

english (en) | português (br)

Metadata

Metadata é responsável por fornecer informação sobre as classes de negócio para o framework em tempo de execução. Exemplos deste tipo de informação: uma lista com os atributos da classe, tipos de atributos, atributos calculados, valores padrão e assim por diante.

O metadata do PressObjects pode ser comparado com tabelas e campos de um RDBMS. Para um RDBMS não é possível armazenar informação, num banco de dados, sem tabelas e campos. Da mesma forma, para um projeto usando PressObjects, uma classe de negócio não pode ser instanciada, para receber dados, sem seu metadata.

Contents

Declaração de atributos

O método InternalMetadataStr da classe de negócio (TPressObject) fornece o metadata em forma de string para o framework. A sintaxe do metadata é:

'ClassName <options> (attribute1: attrtype <options>; ...)'

O modelo de exemplo a seguir declara duas classes, cada uma com seus atributos.

TClient = class(TPressObject)
  _Name: TPressString;
  _City: TPressReference;
protected
  class function InternalMetadataStr: string; override;
end;

TCity = class(TPressObject)
  _Name: TPressString;
protected
  class function InternalMetadataStr: string; override;
end;

...

class function TClient.InternalMetadataStr: string;
begin
  Result := 'TClient IsPersistent (' +
   'Name: String(40);' +
   'City: Reference(TCity);' +
   ')';
end;

class function TCity.InternalMetadataStr: string;
begin
  Result := 'TCity IsPersistent (' +
   'Name: String(30);' +
   ')';
end;

...

initialization
  TClient.RegisterClass;
  TCity.RegisterClass;

A opção IsPersistent, declarada depois do nome da classe, informa ao Press OPF que esta classe e seus descendentes devem ser persistidos (salvos), ex: o banco de dados deve fornecer tabelas e campos para salvar seus dados. Esta opção pode ser omitida quando estiver sincronizando dois modelos de negócio, ex: usando um broker do InstantObjects.

Mesmo em classes persistentes, você pode ter atributos não persistentes. Como em alguns casos de #Atributos calculados, ou atributos que obtém seu valor apenas em tempo de execução. Para tanto, use a opção 'IsPersistent=False' no metadata do atributo, veja um exemplo:

class function TClient.InternalMetadataStr: string;
begin
  Result := 'TClient IsPersistent (' +
   'FirstName: String(20);' +
   'LastName: String(20);' +
   'FullName: String(40) IsPersistent=False Calc(FirstName, LastName);' +
   ')';
end;

Valores padrão e máscara de edição

O exemplo a seguir cria um atributo booleano com string personalizado para true e false, e um atributo tipo datetime formatado, que é preenchido com a data e hora correntes, sempre que a classe for instanciada:

TInvoice = class(TPressObject)
  _Registered: TPressDateTime;
  _Delivered: TPressBoolean;
protected
  class function InternalMetadataStr: string; override;
end;

...

class function TInvoice.InternalMetadataStr: string;
begin
  Result := 'TInvoice (' +
   'Registered: DateTime DefaultValue="now" EdiMask="dd/MM/yyyy";' +
   'Delivered: Boolean EditMask="yes;no";' +
   ')';
end;

Você também pode usar valores padrão para atributos do tipo #Enumerações. Como nesta classe:

TInvoiceStatus = (isNew, isRegistered, isDelivered);

TInvoice = class(TPressObject)
  _Status: TPressEnum;
...

E seu metadata:

TInvoice (
  Status: Enum(TInvoiceStatus) DefaultValue=isNew;
...
  )

Atributos calculados

Um atributo calculado obtém seu valor a partir do valor de outros atributos. Seu valor muda, portanto, sempre que o valor dos outros atributos mudarem. Veja o exemplo a seguir.

TInvoice = class(TPressObject)
  _Items: TPressParts;
  _SumOfItems: TPressCurrency;
private
  // SumOfItems getter and setter
protected
  procedure InternalCalcAttribute(AAttribute: TPressAttribute); override;
  class function InternalMetadataStr: string; override;
published
  property SumOfItems: Currency read GetSumOfItems write SetSumOfItems;
end;

...

procedure TInvoice.InternalCalcAttribute(AAttribute: TPressAttribute);
var
  VSum: Currency;
  I: Integer;
begin
  if AAttribute = _SumOfItems then
  begin
    VSum := 0;
    for I := 0 to Pred(Items.Count) do
      VSum := VSum + (Items[I] as TInvoiceItem).TotalItem;
    SumOfItems := VSum;
  end;
end;

class function TInvoice.InternalMetadataStr: string;
begin
  Result := 'TInvoice (' +
   'Items: Parts(TInvoiceItem);' +
   'SumOfItems: Currency Calc(Items);' +
   ')';
end;

O metadata da classe TInvoice declara que SumOfItems é calculado usando o atributo Items. O próprio cálculo é definido no método InternalCalcAttribute, que é chamado para atualizar o valor do campo, sempre que necessário.

O exemplo a seguir atualiza um atributo calculado, baseado no valor de outros dois atributos:

TInvoiceItem = class(TPressObject)
  _Price: TPressCurrency;
  _Discount: TPressCurrency;
  _FinalPrice: TPressCurrency;
private
  // getters and setters
protected
  procedure InternalCalcAttribute(AAttribute: TPressAttribute); override;
  class function InternalMetadataStr: string; override;
published
  // attribute accessors
end;

...

procedure TInvoiceItem.InternalCalcAttribute(AAttribute: TPressAttribute);
begin
  if AAttribute = _FinalPrice then
    FinalPrice := Price * (100 - Discount) / 100;
end;

class function TInvoiceItem.InternalMetadataStr: string;
begin
  Result := 'TInvoiceItem (' +
   'Price: Currency;' +
   'Discount: Currency;' +
   'FinalPrice: Currency Calc(Price, Discount);' +
   ')';
end;

Agregações, composições e coleções

Um relacionamento é definido no metadata da classe, colocando o nome da classe alvo entre parênteses, depois do nome do tipo de relacionamento. O PressObjects usa 'Part' e 'Parts' para especificar composições (o objeto alvo é possuído), 'Reference' e 'References' para especificar agregações (o objeto alvo não é possuído). Veja o exemplo a seguir.

TInvoice = class(TPressObject)
  _Client: TPressReference;
  _Items: TPressParts;

TInvoiceItem = class(TPressObject)
  _Product: TPressReference;
  _Colors: TPressReferences;

TClient = class(TPressObject)
  _Name: TPressString;
  _Address: TPressPart;

TAddress = class(TPressObject)
  _Street: TPressString;
  _City: TPressReference;

TCity = class(TPressObject)
  _Name: TPressString;

e os strings de metadata:

TInvoice (
 Client: Reference(TClient);
 Items: Parts(TInvoiceItem);
)

TInvoiceItem (
 Product: Reference(TProduct);
 Colors: References(TColors);
)

TClient (
 Name: String(40);
 Address: Part(TAddress);
)

TAddress (
 Street: String(40);
 City: Reference(TCity);
)

TCity (
 Name: String(40);
)

Classe contêiner personalizada

Também é possível criar contêineres personalizados para atributos de coleção. Eis um exemplo de como criar um novo atributo do tipo parts.

Primeiro declare um novo descendente, do tipo de atributo apropriado:

TInvoiceItemParts = class(TPressParts)
public
  function Add: TInvoiceItem;
  ...
  class function ValidObjectClass: TPressObjectClass; override;
end;

...sua implementação (ValidObjectClass é obrigatório)

function TInvoiceItemParts.Add: TInvoiceItem;
begin
  Result := inherited Add as TInvoiceItem;
end;
...
class function TInvoiceItemParts.ValidObjectClass: TPressObjectClass;
begin
  Result := TInvoiceItem;
end;

...registre o novo tipo de atributo:

initialization
  TInvoiceItemParts.RegisterAttribute;

...e finalmente use este tipo de atributo no metadata do Pedido:

TInvoice (
 Client: Reference(TClient);
 Items: TInvoiceItemParts;
)

Enumerações

O framework tem suporte à enumerações, contanto que seja registrado. Veja o exemplo a seguir.

TPhoneType = (ptPhone, ptFax, ptMobile);

TPhoneItem = class(TPressObject)
  _Number: TPressString;
  _PhoneType: TPressEnum;

Metadata:

TPhoneItem (
 Number: String(20);
 PhoneType: Enum(TPhoneType);
)

Registrando a enumeração:

initialization
  PressModel.RegisterEnumMetadata(TypeInfo(TPhoneType), 'TPhoneType');

...uma alternativa ao exemplo do registro da enumeração, mudando as representações string dos tipos dos items, a seguir:

initialization
  PressModel.RegisterEnumMetadata(TypeInfo(TPhoneType), 'TPhoneType', [
   'Home Phone', 'Work Fax', 'Mobile Phone']);

A representação string também suporta valores dinâmicos. Se o valor da variável mudar, o valor da representação também muda.

initialization
  PressModel.RegisterEnumMetadata(TypeInfo(TPhoneType), 'TPhoneType', [
   @SHomePhoneStr, @SWorkFaxStr, @SMobilePhoneStr]);

Metadata de consulta

A consulta a seguir procura por clientes cujo nome comece com a informação que estiver no atributo Name:

TClientQuery = class(TPressQuery)
  _Name: TPressString;

Metadata:

TClientQuery(TClient) (
 Name: String MatchType=mtStarting;
)

O parâmetro (TClient) indica a classe dos objetos que serão recuperados. A propriedade MatchType configura o tipo de comparação que será efetuada com os valores dos atributos durante a consulta.

O exemplo a seguir procura pedidos cujo atributo data esteja entre duas datas:

TInvoiceQuery = class(TPressQuery)
  _MinDate: TPressDate;
  _MaxDate: TPressDate;

Metadata:

TInvoiceQuery(TInvoice) (
 MinDate: Date DataName=Date MatchType=mtGreaterThanOrEqual;
 MaxDate: Date DataName=Date MatchType=mtLesserThanOrEqual;
)

A propriedade DataName indica o nome do atributo persistente (campo). Use esta sintaxe quando dois ou mais atributos fizerem referência ao mesmo atributo persistente.

Estas são as opções para MatchType:

Nota: Distinção entre caixa ALTA e baixa e outros problemas de conjunto de caracteres (character set) devem ser responsabilidade do banco de dados. O OPF criará apenas as declarações como "<Campo> = 'Valor'", "<Campo> like 'Valor%'", baseado na informação fornecida pela Query.

  • mtEqual: o valor do atributo tem de ser igual ao valor persistido (valor salvo).
  • mtStarting: para atributos string, o valor persistido deve começar com valor do atributo.
  • mtFinishing: para atributos string, o valor persistido deve terminar com o valor do atributo.
  • mtContains: para atributos string, o valor persistido contém o valor do atributo em alguma parte de seu valor string.
  • mtGreaterThan: o valor do atributo tem de ser maior que o valor persistido.
  • mtGreaterThanOrEqual: o valor do atributo tem de ser maior ou igual ao valor persistido.
  • mtLesserThan: o valor do atributo tem de ser menor que o valor persistido.
  • mtLesserThanOrEqual: o valor do atributo tem de ser menor ou igual ao valor persistido.