É muitas vezes útil ou mesmo essencial que as bases de dados sejam adaptadas de uma forma flexível e evolutiva aos utilizadores e às suas empresas no mundo informático. O controlo de dados acessíveis é também um assunto recorrente e sensível. Deste ponto de vista, os programadores utilizam métodos e fórmulas por vezes complexos para dar ou restringir o acesso à informação, dependendo do contexto ou dos direitos de acesso dos utilizadores.
Vejamos um exemplo simples. Na sua aplicação, é por vezes necessário apresentar uma lista de pessoas. Uma das colunas mostra os seus nomes completos, mas na sua base de dados, tem um campo de primeiro nome e um campo de último nome. Actualmente, escreve uma fórmula na coluna da caixa de listagem, e tem de ser o próprio a gerir a ordenação na coluna. Não seria óptimo ter um campo calculado onde possa definir a sua fórmula de cálculo e método de ordenação, e ter toda a lógica de negócio dentro da classe e não em cada interface?
Bem, começando com 4D v19 R3, 4D fornece uma solução para isto, com atributos computorizados.
Atributos computorizados HDI ORDA
DEMO ORDA Atributos computados
IMAGINE…
Tem uma lista de pessoas cujos sobrenomes, nomes próprios, datas de nascimento, etc.
Imagine que quer exibir, de forma a que seja apresentada, uma lista de pessoas com o seu nome completo (com base no primeiro nome + apelido), a sua idade (com base na data de nascimento), a sua fotografia actual (com base na sua idade)…
Depois, assim que uma pessoa é seleccionada, pode exibir informações sobre os seus pais, avós, filhos, irmãos e irmãs, tios e tias, e não nos esqueçamos dos primos!
Será isto possível? Sim, é claro.
Sem linhas complicadas de código na forma? Sim!
Toda esta magia é possível graças aos atributos computorizados, nos quais também é possível pesquisar e classificar, tal como com os outros atributos! Vamos dar uma vista de olhos mais atenta!
Definição e cálculos
Para explicar o poder deste conceito, vamos começar com um exemplo concreto. Um dataclass “pessoas” contém classicamente atributos tais como “último nome”, “primeiro nome”, “endereço”, “código postal”, “cidade”, “país”, “data de nascimento”, etc.
Desta classe, podemos precisar do nome completo (sobrenome + nome próprio), idade (com base na data de nascimento), ou o endereço completo (como um objecto).
E podemos mesmo ir além disso. Se as relações o permitirem, os atributos computados podem até ser do tipo de entidade (por exemplo, pai ou mãe) ou selecção de entidade (filhos, pais, avós, irmãos, etc.).
O acesso a estes atributos será possível através da classe “peopleEntity”, na qual novas funções devem ser definidas para este fim.
Serão chamados quando 4D necessitar de acesso a estes atributos, quer para os ler (por exemplo, simples visualização), durante uma pesquisa ou uma espécie, e finalmente durante uma possível salvaguarda após modificação.
O resultado dos atributos computados, como o endereço completo, pode necessitar de outro atributo computado, tal como o nome completo. Este tipo de recursividade também é possível; voltaremos a este ponto.
Estes functions (get, set assim como query e orderBy que serão discutidos mais tarde) serão definidos dentro da própria classe da entidade (ex: peopleEntity)
Acesso a atributos computorizados
Obter
A primeira função, “get”, permite definir a forma como o atributo é computado. Para este efeito, deve retornar o resultado do cálculo.
Esta função é a única que é obrigatória para a utilização dos atributos computados. Na verdade, é apenas a sua presença que determina a própria existência deste atributo.
Function get fullName($event : Object) -> $result : Text
If (This.firstname=Null)
$result :=This.lastname
Else
If (This.lastname=Null)
$result :=This.firstname
Else
$result :=This.firstname+" "+This.lastname
End if
End if
Exposto, Local … ou não?
Como é que este atributo será acessível, e em que condições será calculado?
A palavra-chave “exposto” que pode ser utilizada para definir a função é o equivalente à caixa de verificação “Exposto” no inspector do editor da estrutura.
A palavra-chave “local” utilizada no modo Cliente-Servidor determina se o cálculo deve ser efectuado sistematicamente no servidor ou se deve ser efectuado localmente para limitar o acesso à rede.
SET
A segunda função, “set”, permite a operação oposta para modificar atributos reais de um atributo computado.
Se tomarmos o exemplo do nome completo, a regra simples será procurar um espaço no atributo para redistribuir o valor no primeiro e último nome. Evidentemente, este é apenas um exemplo simples. Se o nome completo contiver vários espaços, terá de aplicar uma regra de negócio mais complexa, como a utilização de uma barra (/), e tratá-la como uma prioridade.
Por exemplo “Pablo Miguel/de la Casa del Mar”.
A vantagem é que, uma vez definida, esta regra será aplicada sistematicamente sem que tenha de a redefinir de acordo com o contexto de entrada.
Function set fullName($value: Text)
var $p : Integer
$p :=Position("/"; $value)
If ($p>0)
$p :=Position(" "; $value)
End if
If ($p>0)
This .firstname:=Substring("$value; 1; $p-1)
This .lastname:=Substring("$value; $p+1)
Else
This .firstname:=""
This .lastname:=$value
End if
Um get sem um conjunto? E o oposto…
Uma função Get é obrigatória para ter acesso a um atributo computorizado. Uma função Set é obrigatória para tornar este atributo modificável, mas e se apenas uma ou outra destas funções existir?
Quando apenas Get está disponível, o atributo pode ser considerado“read-only ” e não pode ser modificado.
Quando apenas o Set está presente, o atributo pode ser escrito mas não pode ser relido. Este é o princípio de uma caixa de correio ou senha.
O que se segue?
A terceira e quarta funções de consulta e ordenaçãoBy são bastante semelhantes e são tratadas da mesma forma.
Estas funções não são obrigatórias, mas a sua utilização irá aumentar drasticamente os desempenhos, como veremos. De facto, se estas funções não forem definidas, 4D terá de executar a função “obter ” para cada entidade e depois executar uma ordenação sequencial. É provável que isto pertença e estará longe de ser optimizado.
É essencial compreender e lembrar que os atributos computados não têm índices. Ainda assim, se o seu cálculo for baseado em atributos, então esses atributos podem ser (ou devem ser) indexados!
Por esta razão, é oferecida a possibilidade de definir como a pesquisa deve realmente ser feita quando uma consulta ou uma espécie é lançada sobre um atributo computado.
Se tomarmos o exemplo de fullname, com base nos atributos firstname e lastname (ambos indexados), uma pesquisa do tipo <<fullname = “Paul Smith” >> significará provavelmente que <<firstname = “Paul”>> e <<lastname = “Smith”>>. É livre de estender a pesquisa e decidir que uma pesquisa <<<fullname = “Pa Sm” >> significa que o <<firstname começa com “Pa”>> e o <<lastname começa com “Sm”>>. Ou que <<< = “Martin” >> significa que ou o apelido ou o primeiro nome começa com “Martin”…
Consulta
Ao executar a função “consulta” definida para qualquer atributo computado, o parâmetro recebido conterá duas propriedades necessárias para compreender a pesquisa desejada. Sabendo isto, a consulta pode ser reescrita para utilizar um (ou mais) atributo(s) indexado(s) e assim evitar uma pesquisa sequencial.
$event.operator contém o operador em forma de cadeia (“==”, “>=”, “<“, etc.)
$event. Value contém o valor a ser pesquisado ou comparado, também como uma cadeia de caracteres.
O resultado de uma função de consulta pode ser ou uma cadeia de caracteres ou um objecto.
Se a função devolver uma cadeia de caracteres, a cadeia de caracteres deve ser uma consulta válida.
$result:="último nome = A@ e primeiro nome = B@".
Se a função devolver um objecto, este deve conter duas propriedades:
- .query: uma cadeia de consulta válida que pode conter marcadores de lugar (:1,:2, etc.)
- .parameters: uma colecção de valores a serem utilizados dentro de marcadores de lugar
$result:=New object("consulta"; $query; "parâmetros"; $parameters)
Vejamos agora como gerir a seguinte consulta de classe de dados dentro da função de consulta!
$es:=ds.people.query("fullname = :1"; "Paul Smith")
Aqui está o código completo da função de consulta:
Function query fullname ($event: Object) -> $result: Object
$fullname :=$event.valor
$operator :=$event.
operador
$p :=Position(" "; $fullname)
If ($p>0)
$firstname :=Substring($fullname; 1; $p-1)+"@"
$lastname :=Substring($fullname; $p+1)+"@"
$parameters :=New collection($firstname; $lastname)
Else $fullname
:=$fullname+"@" $parameters :=New collection($fullname)
End if
Case of
: ($operator="==") | ($operator="===")
If ($p>0)
$query :="primeiro nome = :1 e último nome = :2"
Else
$query :="primeiro nome = :1 ou último nome = :1"
End if
: ($operator="!=")
If ($p>0)
$query :="sobrenome != :1 e sobrenome != :2"
Else
$query :="sobrenome != :1 e sobrenome != :1"
End if
End case
$result :=New object("consulta"; $query; "parâmetros"; $parameters)
Encomendar por
No que diz respeito à triagem, é bastante semelhante. Neste caso, ordenar as pessoas pela sua idade é precisamente o mesmo que ordenar pela sua data de nascimento revertida. Assim, quando uma ordenação por idade é solicitada, será inteligente utilizar a data de nascimento, que é indexada como o critério.
- Ordenar por amostras:
$es:=ds.people.all().orderBy("descrição por idade")
$es :=ds.people.all().orderBy("age asc")
- OrderBy function:
Function orderBy age($event: Object) -> $result: String
If($event.operator = "desc")
$result:="birthday asc"
Else
$result :="birthday desc"
End if
Utilização de índices Composite
Os índices compostos podem ser utilizados de uma forma altamente positiva em alguns casos. Lembrete: Uma espécie por “apelido” e “primeiro nome” não utiliza o apelido ou o índice do primeiro nome. Para que este tipo de ordenação seja optimizado, deve haver um índice de nome anterior + primeiro nome, ou primeiro nome + último nome… ou ambos.
No caso de um atributo computado como nome completo, 4D utilizará este índice composto se estiver ciente de que é de facto um tipo nome próprio + último nome! É mesmo possível forçar uma ordenação por apelido + nome próprio, mesmo que o nome completo exiba o primeiro nome antes do último nome!
- Encomendar por amostra:
Form.people:=ds.people.all().orderBy("fullname asc")
- OrderBy function:
Function orderBy fullname($event : Objecto)->$result : Text
If ($event.descending)
$result :="lastname desc, firstname desc"
Else
$result :="lastname asc, firstname asc"
End if
Outros tipos possíveis de atributos COMPUTADOS
Nas secções acima, cobrimos atributos computorizados de tipo escalar (texto, numérico…), mas os atributos computorizados também podem ser objectos, entidades, ou selecções de entidades!
Alguns exemplos possíveis:
- fullAddress: objecto com tantos atributos necessários (nome completo, rua, código postal, cidade, país, etc.)
Function get fullAddressThis($event: Object) -> $result: Object
$result :=New object
$result .fullName:=This.fullName // Another computed attribute can be used!
$result .address:=This.address
$result .zipCode:=This.zipCode
$result.
city:=This.city
$result .
state:=This.state
$result .
country:country
- bigBoss: entidade
Function get bigBoss($event: Object) -> $result: cs.peopleEntity
$result :=this.manager.manager
- colegas de trabalho : entidadeSelecção
Function get coworkers($event: Object) -> $result: cs.peopleEntitySelection
$result :=this.manager.directReports. .minus(this)
Conclusão
Osatributos computorizados nascem e nascem bem! Trazem tanto flexibilidade e poder como um maior controlo sobre o que é acessível ou não. O acesso a estes atributos é optimizado de modo a não penalizar nem a memória nem o acesso à rede. São a solução simples para as exigências das empresas e satisfazem as exigências crescentes da programação moderna.
Para mais detalhes, leia esta documentação.