Introdução
Este artigo pretende ajudar na transição da framework de PHC CS para a framework de addons de PHC GO, nomeadamente trabalhar com valores que estão armazenados na base de
dados.
Existem vários artigos da série Usa PHC CS com o intuito de facilitar a transição da
programação da framework PHC CS em Visual FoxPro para a framework PHC GO em .NET
Framework, utilizando Visual Basic.
Recomenda-se a leitura dos artigos pela ordem indicada, de forma a apresentar as diversas
tecnologias e técnicas de programação em sequência crescente de complexidade e
funcionalidade.
Cursores
No PHC CS, os cursores são representações em memória de registos já existentes na base de
dados, ao executar uma instrução na base de dados, é criada uma área de trabalho que irá
conter esses registos.
No seguinte exemplo, é definido uma expressão em Transact-SQL com um filtro para retornar
os álbuns cuja data de lançamento seja superior ou igual a 1 de janeiro de 2023
Em seguida, utilizamos a função u_sqlexec para executar essa expressão na base de dados,
especificando uma nova área de trabalho chamada newAlbums, que conterá os álbuns de
música que obedecem ao filtro indicado.
Por fim utilizamos um bloco Scan/EndScan para mover o ponteiro interno de posicionamento
da área de trabalho e processamos os álbuns sequencialmente.
* Código PHC CS – Visual FoxPro
local cTsql
m.cTsql = "Select * from u_album (nolock) where u_album.releaseDate >= '20230101'"
u_sqlexec(m.cTsql,"newAlbums")
Select newAlbums
goto top
if eof()
mensagem("Albums not found.")
return .f.
endif
Scan
Do Case
…
EndCase
EndScan
* it will return empty because Scan/EndScan left the cursor at EOF()
Return newAlbums.nome
No PHC GO, o termo cursor foi substituído por entidade. A representação de um registo ou
coleção de registos é feito por uma variável e não uma área de trabalho, ou seja, são existe
necessidade de posicionamento externo.
No seguinte exemplo, é definido uma expressão de filtro para retornar os álbuns cuja data de
lançamento seja superior ou igual a 1 de janeiro de 2023.
Em seguida, utilizamos a função SDK.Query.GetEntityData para executar essa expressão na
base de dados, especificando que a variável newAlbums, irá conterá os álbuns de música que
obedecem ao filtro indicado.
Por fim utilizamos um bloco ForEach para percorrer todos os álbuns contidos na variável que agora representa uma coleção de registos, e processamos os álbuns
sequencialmente.
' Código PHC GO – VB.NET
Dim newAlbums as New List(Of u0000_AlbumVO)
Dim myFilter as New FilterItem("u0000_album.releaseDate", Comparison.GreaterOrEqual,
"20230101".ToDate())
newAlbums = SDK.Query.GetEntityData(Of u0000_AlbumVO)(myFilter)
If newAlbums.IsVoid() Then
Return New MsgInfo("Albums not found.")
End If
For Each albumItem In newAlbums
Select Case
…
End Select
Next
Posicionamento
No PHC CS, é necessário mover o ponteiro de posicionamento interno da área de trabalho,
utilizando, por exemplo, uma instrução Skip ou um bloco Scan/EndScan, antes de aceder aos
valores do registo nessa posição.
No PHC GO, essa noção de posicionamento não existe. Em vez de uma instrução como Skip,
basta indicar o valor da posição que queremos aceder ou usar instruções de bloco como
ForEach.
Posicionamento de Inicio
* Código PHC CS – Visual FoxPro
Select newAlbums
goto top
mensagem("The title of the first album is: " + newAlbums.nome)
' Código PHC GO – VB.NET
' option 1
Return New MsgInfo("The title of the first album is: " + newAlbums(0).nome)
' option 2
Return New MsgInfo("The title of the first album is: " + newAlbums.First().nome)
Posicionamento de Fim
* Código PHC CS – Visual FoxPro
Select newAlbums
goto bottom
mensagem("The title of the last album is: " + newAlbums.nome)
' Código PHC GO – VB.NET
' option 1
Return New MsgInfo("The title of the last album is: " + newAlbums(newAlbums.count-1).nome)
' option 2
Return New MsgInfo("The title of the last album is: " + newAlbums.Last().nome)
Percorrer a lista
* Código PHC CS – Visual FoxPro
Select newAlbums
Scan
Do Case
Case newAlbums.country = "pt_PT"
newAlbums.obs = "Musicas Portuguesas"
Case newAlbums.country = "pt_BR"
newAlbums.obs = "Musicas Brasileiras"
Otherwise
newAlbums.obs = "Musicas do Mundo"
EndCase
EndScan
* it will return empty because Scan/EndScan left the cursor at EOF()
Return newAlbums.nome
' Código PHC GO – VB.NET
' option 1
For Each albumItem In newAlbums
Select Case albumItem.country
Case "pt_PT"
albumItem.obs = "Musicas Portuguesas"
Case "pt_BR"
albumItem.obs = "Musicas Brasileiras"
Case Else
albumItem.obs = "Musicas do Mundo"
End Select
Next
' option 2
For index as Integer = 0 To newAlbums.count()-1
Dim albumItem as u0000_AlbumVO = newAlbums(index)
Next
' option 3
For Each albumItem In (From pk In newAlbums Where pk.country="pt_PT")
albumItem.obs = "Musicas Portuguesas"
Next
' it will return not empty because ForEach/Next doesn't change the list position
Return newAlbums.nome
Só um registo
Outra diferença no PHC GO é a possibilidade de utilizar variáveis de coleção ou variáveis
associadas a uma posição específica dentro da coleção. Isto pode ser feito como no exemplo
anterior, onde a variável albumItem é criada em cada ciclo do ForEach, ou aplicando
diretamente um comando ao retorno da função utilizada.
' Código PHC GO – VB.NET
Dim myFilter as New FilterItem("u0000_album.releaseDate", Comparison.GreaterOrEqual,
"20230101".ToDate())
Dim albumItem As u0000_AlbumVO
albumItem = SDK.Query.GetEntityData(Of u0000_AlbumVO)(myFilter).FirstOrDefault()
If albumItem Is Nothing Then
Return New MsgInfo("Albums not found.")
End If
Return New MsgInfo("The title of the first album is: " + albumItem.nome)
Verificação de vazio
Nos exemplos anteriores, apresentámos duas técnicas para verificar se a variável que recebe os
dados da base de dados está vazia — ou seja, se não existem registos na tabela ou se os dados
não cumprem a expressão de filtro definida.
A primeira técnica aplica-se a coleções ou a tipos de dados básicos do .NET, como textos, datas,
valores lógicos e números (inteiros ou decimais). Esta verificação é realizada utilizando a função
.IsVoid()
' Código PHC GO – VB.NET
Dim myText As String = ""
If myText.IsVoid() Then
' The text is empty
End If
Dim myNumber As Integer
If myNumber.IsVoid() Then
' The number is empty
End If
Dim myDate As Date
If myDate.IsVoid() Then
' The date is empty
End If
Dim myBool As Boolean
If myBool.IsVoid() Then
' The boolean is empty
End If
Dim myDecimal As Decimal
If myDecimal.IsVoid() Then
' The decimal is empty
End If
Dim myCollection As New List(Of u0000_AlbumVO)
If myCollection.IsVoid() Then
' The collection is empty
End If
Em código de frontend não existe a extensão IsVoid() por isso temos que usar o operador de
igualdade === ou o operador de diferente !== ou simplesmente ! aplicado á variável.
// Código PHC GO – Typescrip
let myText: string = '';
if (myText === '') {
// The text is empty
}
if (myText !== '') {
// The text is not empty
}
let myNumber: number = 0;
if (myNumber === 0) {
// The integer ou decimal is zero (empty)
}
let emptyDate: Date = new Date('1900-01-01T00:00:00Z')
let myDate: Date = new Date();
if (myDate <= emptyDate) {
// The calendar date is empty
}
if (myDate >= emptyDate) {
// The calendar date is not empty
}
let myBool: boolean = false;
if (!myBool) {
// The boolean is false
}
if (myBool) {
// The boolean is true
}
if (myBool === true) {
// The boolean is true
}
const myCollection: u0000_AlbumVO[] = [];
if (myCollection.length === 0) {
// The collection is empty
}
A segunda técnica aplica-se a variáveis que contêm um objeto e não uma coleção de objetos,
nesta situação usamos o próprio comando do .net Is Nothing ou IsNot Nothing
' Código PHC GO – VB.NET
Dim albumItem As u0000_AlbumVO
albumItem = SDK.Query.GetEntityData(Of u0000_AlbumVO)(myFilter).FirstOrDefault()
If albumItem Is Nothing Then
Return New MsgInfo("Albums not found.")
End If
Em código de frontend o valor de Is Nothing passa a ser === null e IsNot Nothing passa a ser
!== null
// Código PHC GO – Typescrip
let myText: string;
if (myText === null) {
// The text is nothing
}
let myNumber: number;
if (myNumber === null) {
// The integer ou decimal is nothing
}
let myDate: Date;
if (myDate === null) {
// The calendar date is nothing
}
let myBool: boolean;
if (myBool === null) {
// The boolean is nothing
}
const myCollection: u0000_AlbumVO[];
if (myCollection.length === 0) {
// The collection is nothing
}
const myRecord: u0000_AlbumVO;
if (myRecord === null) {
// The object is nothing
}
const myObject: any;
if (myObject === null) {
// The object is nothing
}
Mensagens
No código PHC CS, quando queremos notificar o utilizador sobre uma determinada situação,
utilizamos a função mensagem para exibir essa notificação no ecrã do dispositivo onde a
aplicação está a ser executada.
' Código PHC CS – Visual FoxPro
Select newAlbums
goto top
if eof()
mensagem("Albums not found.")
return .f.
endif
A função mensagem é exibida imediatamente no ecrã do utilizador. Apenas depois de o utilizador
a fechar, ou de esta desaparecer após alguns segundos, a aplicação continua o processamento
na instrução return .f.
No PHC GO, é importante compreender que a aplicação é composta por um ambiente de frontend — a parte visual da aplicação, que funciona num navegador no computador do utilizador —
e um back-end, onde residem a lógica de negócio e o armazenamento de dados, operando na
nuvem.
Como a maior parte do código dos add-ons é executada no back-end, não é possível utilizar a
instrução mensagem para que esta seja imediatamente exibida no navegador (front-end) e, só
depois de fechada, retornar ao back-end para executar a instrução return .f.
Para contornar esta limitação, o PHC GO adota uma técnica que consiste em adicionar a
mensagem a uma lista, executar as linhas de código necessárias e, apenas no final do processo,
retornar essa lista para que as mensagens sejam exibidas no navegador (front-end).
' Código PHC GO – VB.NET
Public Function CheckNumber(myNumber as Integer) As List(Of MessageVO)
Dim listMsg as New List(Of MessageVO)
If myNumber.IsVoid() Then
listMsg.Add(New MsgError("The number is 0."))
End If
If myNumber < 1 Then
listMsg.Add(New MsgError("The number is less than 1."))
End If
Return listMsg
End Function
No exemplo acima, se o número enviado for igual a zero, o utilizador irá ver duas mensagens,
podemos também optar por retornar uma só mensagem em vez de uma lista de mensagens.
' Código PHC GO – VB.NET
Public Function CheckNumber(myNumber as Integer) As MessageVO
If myNumber.IsVoid() Then
Return New MsgError("The number is 0.")
End If
If myNumber < 1 Then
Return New MsgError("The number is less than 1.")
End If
Return Nothing
End Function
No PHC GO, existem várias funções para criar mensagens: MsgInfo() para mensagens
informativas, que aparecem em cinzento; MsgError() para mensagens de erro, exibidas em
vermelho; e MsgWarning() para mensagens de aviso, apresentadas em amarelo.
No caso da função MsgWarning(), existe uma situação especial no momento AoGravar, em
que, em vez de exibir o texto como uma mensagem de aviso, é apresentada uma pergunta ao
utilizador com o texto fornecido, solicitando uma resposta de Sim ou Não. Esta funcionalidade
será abordada em maior detalhe no manual: Usa PHC CS – Regras de Negócio.
Nome de Tabelas
' Código PHC CS – Visual FoxPro
local cTsql
m.cTsql = "Select * from u_album (nolock) where u_album.releaseDate >= '20230101'"
u_sqlexec(m.cTsql,"newAlbums")
Na expressão Transact-SQL, indicamos que queremos retornar registos de uma tabela existente
na base de dados com o nome u_album.
O prefixo u_ indica que se trata de uma tabela de utilizador. Ao customizar uma tabela na
opção Supervisor - Framework PHC - Tabelas do Utilizador, a aplicação adiciona
automaticamente o prefixo ao início do nome da tabela.
Isto assegura que o nome da tabela não entre em conflito com outras tabelas do sistema, ou
seja, com tabelas nativas da aplicação que possam ter o mesmo nome.
Em PHC GO as tabelas de utilizador também usam o prefixo u_ acrescentado de um número.
Isto porque as tabelas de utilizador são criadas por um determinado add-on, desenvolvido por
um parceiro da PHC, e cada parceiro possui um ID único, nos exemplos apresentados vamos
utilizar o número 0000.
' Código PHC GO – VB.NET
Dim newAlbums As New List(Of u0000_AlbumVO)
Dim myFilter as New FilterItem("u0000_album.releaseDate", Comparison.GreaterOrEqual,
"20230101".ToDate())
newAlbums = SDK.Query.GetEntityData(Of u0000_AlbumVO)(myFilter)
No entanto, em memória, ou seja, no código da aplicação, essa tabela adquire o sufixo VO no
final do nome, resultando em u0000_AlbumVO. Esse sufixo VO, que significa value object.
Tipificação
No PHC GO, existe uma classe específica para cada tabela da base de dados. Assim, quando
precisamos retornar registos de uma tabela, utilizamos a classe correspondente para
representar o cursor em memória, um conceito conhecido como tipificação.
A tabela de álbuns existente na base de dados, u0000_album, é representada por uma classe
específica na aplicação, u0000_AlbumVO, que faz o mapeamento entre a tabela na base de
dados e os dados em memória na aplicação.
' Código PHC GO – VB.NET
Dim newAlbums As New List(Of u0000_AlbumVO)
Dim myFilter as New FilterItem("u0000_album.releaseDate", Comparison.GreaterOrEqual,
"20230101".ToDate())
O sufixo VO no final do nome é uma tipologia adotada pela PHC que significa Value Object.
Este termo refere-se a uma classe ou objeto que apenas contém dados, sem regras de negócio
associadas, e cuja finalidade é representar os valores da base de dados que serão utilizados nas
operações de serialização e desserialização durante as comunicações entre o back-end e o
front-end.
Esta classe é criada automaticamente pela aplicação quando se instala um add-on e obedece
ás configurações feitas na opção GO Studio - Toolbox - Ecrãs e Entidades, em PHC GO uma
tabela é definida quando se constrói o desenho do seu ecrã.
' Código PHC GO – VB.NET
Public Class u0000_albumVO
<AttLenght(25), AttFieldStamp()>
Public Property [u0000_albumstamp]() As String
<AttLenght(80),AttList(),AttLogInfo()>
Public Property [nome]() As String
<AttLenght(40),AttList(),AttLogInfo()>
Public Property [singername]() As String
End Class
Coleções em PHC CS
No PHC CS, quando uma tabela é composta, como no caso da tabela u_album, em que cada
álbum pode ter várias músicas associadas, estas são armazenadas numa tabela separada,
u_musica, com uma relação entre ambas as tabelas. Assim, temos duas tabelas físicas na base
de dados e dois cursores no código da aplicação.
Por exemplo, o cursor u_album pode conter um registo específico, como o álbum "Cantigas de
Portugal". O cursor u_musica, filtrado pela chave estrangeira u_albumstamp, exibirá apenas as
músicas associadas a esse álbum específico.
* Código PHC CS – Visual FoxPro
local uniqueID,msel,musicList
m.uniqueID = "Z_20240505155423284756456"
m.msel = "Select * from u_album (nolock) where u_album.u_albumstamp='"+m.uniqueID+"'"
if not u_sqlexec(m.msel,"u_album")
mensagem("Unable to open the album.")
return .f.
endif
select u_album
goto top
if eof()
mensagem("Album not found.")
return .f.
endif
m.msel = "Select * from u_musica (nolock) where u_musica.u_albumstamp='"+m.uniqueID+"'"
if not u_sqlexec(m.msel,"u_musica")
mensagem("Unable to open the musics.")
return .f.
endif
select u_musica
goto top
if eof()
mensagem("The musics for the album " + albumItem.nome + " is empty.")
return .t.
endif
m.musicList = "The album " + albumItem.nome + " have this list of musics:"
select u_musica
goto top
scan
m.musicList = m.musicList + u_musica.nome + chr(13)
endscan
mensagem(m.musicList)
return .t.
Coleções em PHC GO
No PHC GO, a situação é semelhante ao PHC CS: existem duas tabelas físicas na base de dados,
uma para os álbuns e outra para as músicas.
A grande diferença é que a coleção de músicas é representada dentro da estrutura da tabela
álbum. Ou seja, cada registo da entidade u0000_AlbumVO contém uma propriedade do tipo
coleção com o nome u0000_musica, que armazena todas as músicas de um álbum específico.
' Código PHC GO – VB.NET
Public Class u0000_albumVO
<AttLenght(25), AttFieldStamp()>
Public Property [u0000_albumstamp]() As String
<AttCollection(GetType(u0000_musicaVO))>
Public Property u0000_musica As List(Of u0000_musicaVO)
End Class
A tabela de músicas existente na base de dados, u0000_musica, é também representada por
uma classe específica na aplicação, u0000_MusicaVO, que faz o mapeamento entre a tabela na
base de dados e os dados em memória na aplicação.
' Código PHC GO – VB.NET
Public Class u0000_musicaVO
<AttLenght(25,0),AttFieldStamp()>
Public Property [u0000_musicastamp]() As String
<AttLenght(25,0),AttForeignKey(GetType(u0000_albumVO)),AttIndex()>
Public Property [u0000_albumstamp]() As String
<AttLenght(80,0)>
Public Property [nome]() As String
End Class
Ao obter um álbum da base de dados, a aplicação traz automaticamente as músicas associadas
a esse álbum, eliminando a necessidade de código adicional para essa função, como era
necessário anteriormente no PHC CS.
É importante destacar que a propriedade de coleção u0000_musica utiliza o tipo Lazy Loading.
Isso significa que, após solicitar um determinado álbum, as músicas não são carregadas
imediatamente. Somente quando se faz referência à propriedade u0000_música, as músicas são
então solicitadas e carregadas da base de dados.
' Código PHC GO – VB.NET
Dim uniqueID As String = "Z_20240505155423284756456"
' in this instruction we only obtain the album data without the songs.
Dim albumItem As u0000_AlbumVO = SDK.Query.GetEntityByStamp(Of u0000_AlbumVO)(uniqueID)
If albumItem Is Nothing Then
Return New MsgError("Album not found.")
End If
' the songs are returned from the database in this instruction.
If albumItem.u0000_musica.IsVoid() Then
Return New MsgInfo(String.Format("No musics in album {0}.", albumItem.nome))
End If
Dim musicList As New Text.StringBuilder()
musicList.AppendLine(String.Format("Musics of album {0}", albumItem.nome))
For Each musicaItem As u0000_MusicaVO In albumItem.u0000_musica
musicList.AppendLine(musicaItem.Nome)
Next
Return New MsgInfo(musicList.ToString())
Views
No PHC CS, ao criar, modificar ou eliminar registos na base de dados, em vez de utilizarmos um
cursor obtido, por exemplo, através da função u_sqlexec, recorremos a uma técnica chamada
view.
Ao utilizar a função dbfusetabusrnome, usada para tabelas de utilizador, o cursor criado possui
propriedades ativadas que simplificam o processo de persistência das alterações na base de
dados.
* Código PHC CS – Visual FoxPro
Do dbfusetabusrnome with "album"
if not Used("u_album")
mensagem("Unable to open the album.")
return .f.
endif
No PHC GO, como as entidades estão tipificadas, cada tabela da base de dados é sempre
representada por uma classe específica na aplicação. A obtenção dos valores é realizada da
mesma forma explicada anteriormente nos exemplos de cursores.
' Código PHC GO – VB.NET
' get some record
Dim uniqueID As String = "Z_20240505155423284756456"
Dim albumItem As u0000_AlbumVO = SDK.Query.GetEntityByStamp(Of u0000_AlbumVO)(uniqueID)
If albumItem Is Nothing Then
Return New MsgError("Album not found.")
End If
Views – Funcionalidades
No PHC GO, as entidades herdam de uma classe de negócio base que contém diversas
funcionalidades comuns a todas as entidades do sistema, sejam estas entidades principais,
secundárias ou específicas de utilizador.
- IsEmptyInstance() as Boolean
Quando se cria manualmente uma nova instância de uma entidade, o valor desta
propriedade assume True. No momento em que se atribui um valor a qualquer campo
dessa entidade, a propriedade muda automaticamente para False.
- Operation as Integer
Indica a operação a ser efetuada na base de dados para um registo, pode assumir um dos
seguintes valores:
0 = None, nada será efetuado na base de dados.
1 = Inserted, o registo vai ser criado na base de dados.
2 = Update, o registo já existe na base de dados e vai ser modificado.
3 = Delete, o registo existe na base de dados e vai ser removido.
- IsChanged() as Boolean
As entidades conseguem rastrear o valor original e o valor atual de cada campo. Esta
função retorna True caso algum campo tenha sido alterado, ou False caso contrário.
Quando a entidade contém coleções de outras entidades secundárias, a verificação é feita
em todos os registos e em todos os campos dessas coleções. Por exemplo, se a fatura 53
contiver 20 linhas de artigos e apenas o campo quantidade da linha 19 for alterado, a
função retornará True quando aplicada à fatura, e não apenas à linha 19.
Devido a este comportamento, é importante considerar questões de desempenho ao
utilizar esta função em entidades compostas que incluam um grande número de registos
nas suas coleções.
- IsFieldChanged(fieldName As String) as Boolean
- IsAnyFieldChanged(ParamArray fieldsName() As String) as Boolean
- IsAnyFieldChanged(fieldsName As IEnumerable(Of String)) as Boolean
- IsOnlyAnyThisFieldsChanged(ParamArray fieldsName() As String) as Boolean
Estas funções permitem verificar se um campo específico ou um conjunto de campos foi
modificado.
- OldValue(Of T)(fieldName As String) as T
- OldValue(fieldName As String) as Object
Estas funções permitem verificar se um campo específico ou um conjunto de campos foi
alterado.
- ousrinis As String
- ousrdata As DateTime
- ousrhora As String
Este são campos de sistema, preenchidos automaticamente, que indicam qual foi o utilizador
que criou o registo, bem como a data e hora dessa operação.
- usrinis As String
- usrdata As DateTime
- usrhora As String
São campos de sistema, preenchidos automaticamente, que indicam qual foi o último
utilizador que alterou o registo, bem como a data e hora dessa operação.
Views – Criar registos
No PHC CS utilizamos a instrução append blank associada a uma view, quando queremos criar
um novo registo na base de dados.
* Código PHC CS – Visual FoxPro
Do dbfusetabusrnome with "album"
if not Used("u_album")
mensagem("Unable to open the album.")
return .f.
endif
* create
select u_album
append blank
* fill the values
u_album.u_albumstamp = "Z_20240505155423284756456"
u_album.nome = "The Best Portuguese Pimba"
u_album.percious = .t.
* persist changes to the database.
if not u_tabupdate(.t., .t., "u_album")
mensagem("Unable to create the album: " + u_album.nome)
return .f.
endif
return .t.
No PHC GO, existe uma classe gestora da entidade, que contém todas as regras de negócio e
ações disponíveis para essa entidade. Uma dessas ações é a gravação Save, que deve ser
utilizada para executar alterações de valores na base de dados.
' Código PHC GO – VB.NET
Dim listMsg as New List(Of MessageVO)
' entity business controller
Dim albumBiz As SDKBiz = SDK.Business.CreateBiz("u0000_Album")
If albumBiz Is Nothing Then
listMsg.Add(New MsgError("The album business is not available."))
Return listMsg
End If
' create
Dim albumItem As u0000_AlbumVO = u0000_AlbumVO.GetNewInstance(Of u0000_AlbumVO)()
' Note:
' albumItem.IsEmptyInstance = True
' albumItem.Operation = OperationEnum.Inserted = 1
' albumItem.u0000_albumstamp = Some unique value, ex: Z_20240505155423284756456
' fill the values
albumItem.nome = "The Best Portuguese Pimba"
albumItem.percious = .t.
' Note:
' albumItem.IsEmptyInstance = False
' albumItem.IsFieldChanged("percious") = True
' albumItem.OldValue(Of Boolean)("percious") = False
' create record on the database.
listMsg.AddRange(albumBiz.Save(newAlbums))
Return listMsg
Ao executar a ação Save, são aplicadas as regras de sistema, assim como quaisquer regras
adicionadas através dos add-ons instalados na aplicação. Adicionalmente, todos os triggers
associados são executados, garantindo a coerência dos dados.
No PHC GO, não existem triggers na base de dados; por isso a criação, alteração ou apagar de
registos da base de dados deve passar sempre pela classe gestora da entidade. Qualquer
atualização direta irá comprometer a consistência dos dados.
Views – Alterar registos
No PHC CS, para alterar um registo, podemos usar a função c_requery para retornar os valores
da base de dados, proceder à alteração dos valores desejados e, em seguida, gravar as
modificações com u_tabupdate
* Código PHC CS – Visual FoxPro
Do dbfusetabusrnome with "album"
if not Used("u_album")
mensagem("Unable to open the album.")
return .f.
endif
* get some record
m.v_u_albumstamp = "Z_20240505155423284756456"
c_requery("u_album")
select u_album
goto top
if eof()
mensagem("Album not found.")
return .f.
endif
* changing something in the album, for example, now becomes my precious album.
u_album.percious = .t.
* persist changes to the database.
if not u_tabupdate(.t., .t., "u_album")
mensagem("Unable to update the album: " + u_album.nome)
return .f.
endif
return .t.
No PHC GO, obtemos o registo de forma habitual, alteramos os seus valores e gravamos as
modificações através da respetiva classe de negócio.
' Código PHC GO – VB.NET
Dim listMsg as New List(Of MessageVO)
' get some record
Dim uniqueID As String = "Z_20240505155423284756456"
Dim albumItem As u0000_AlbumVO = SDK.Query.GetEntityByStamp(Of u0000_AlbumVO)(uniqueID)
If albumItem Is Nothing Then
Return New MsgError("Album not found.")
End If
' Note:
' albumItem.IsEmptyInstance = False
' albumItem.Operation = OperationEnum.None = 0
' albumItem.u0000_albumstamp = Z_20240505155423284756456
' entity business controller
Dim albumBiz As SDKBiz = SDK.Business.CreateBiz("u0000_Album")
If albumBiz Is Nothing Then
listMsg.Add(New MsgError("The album business is not available."))
Return listMsg
End If
' changing something in the album, for example, now becomes my precious album.
albumItem.percious = .f.
' Note:
' albumItem.Operation = OperationEnum.Update = 2
' albumItem.IsFieldChanged("percious") = True
' albumItem.OldValue(Of Boolean)("percious") = True
' persist changes to the database.
listMsg.AddRange(albumBiz.Save(newAlbums))
Return listMsg
Views – Apagar registos
No PHC CS utilizamos a instrução delete associada a uma view, quando queremos apagar um
registo na base de dados.
* Código PHC CS – Visual FoxPro
Do dbfusetabusrnome with "album"
if not Used("u_album")
mensagem("Unable to open the album.")
return .f.
endif
* get some record
m.v_u_albumstamp = "Z_20240505155423284756456"
c_requery("u_album")
select u_album
goto top
if eof()
mensagem("Album not found.")
return .f.
endif
* mark as deleted
delete
* persist changes to the database.
if not u_tabupdate(.t., .t., "u_album")
mensagem("Unable to delete the album: " + u_album.nome)
return .f.
endif
return .t.
No PHC GO, obtemos o registo de forma habitual, modificamos o valor da propriedade
operation e apagamos o registo através da respetiva classe de negócio.
' Código PHC GO – VB.NET
Dim listMsg as New List(Of MessageVO)
' get some record
Dim uniqueID As String = "Z_20240505155423284756456"
Dim albumItem As u0000_AlbumVO = SDK.Query.GetEntityByStamp(Of u0000_AlbumVO)(uniqueID)
If albumItem Is Nothing Then
Return New MsgError("Album not found.")
End If
' entity business controller
Dim albumBiz As SDKBiz = SDK.Business.CreateBiz("u0000_Album")
If albumBiz Is Nothing Then
listMsg.Add(New MsgError("The album business is not available."))
Return listMsg
End If
' mark as deleted
albumItem.Operation = OperationEnum.Delete
' delete the record from the database
listMsg.AddRange(albumBiz.Save(newAlbums))
Return listMsg
Uma nota sobre a operação de apagar: se a entidade principal contiver coleções, ao marcar a
entidade principal como Delete, todas as coleções são automaticamente marcadas da mesma
forma. Assim, na base de dados, toda a estrutura é removida, evitando que registos fiquem
perdidos.
Views – Criar, Alterar e Apagar registos de Coleções
No PHC GO, ao aplicar operações nas coleções de uma entidade principal, é necessário
primeiro obter a entidade principal e, em seguida, realizar a operação desejada na coleção.
A gravação deve sempre ser feita através da classe gestora da entidade, sendo que apenas as
entidades principais dispõem desse tipo de classes.
Por exemplo, o método Save da classe gestora da entidade álbuns aceita apenas objetos do
tipo u0000_AlbumVO, e não possui uma assinatura que permita receber uma coleção de
objetos u0000_MusicaVO.
Nos exemplos a seguir, vamos realizar estas três operações na coleção de músicas de um álbum
específico, utilizando a respetiva classe gestora.
' Código PHC GO – VB.NET
Dim listMsg as New List(Of MessageVO)
' entity business controller
Dim albumBiz As SDKBiz = SDK.Business.CreateBiz("u0000_Album")
If albumBiz Is Nothing Then
listMsg.Add(New MsgError("The album business is not available."))
Return listMsg
End If
Criar um álbum com uma música.
' Código PHC GO – VB.NET
Dim albumItem As u0000_AlbumVO = u0000_AlbumVO.GetNewInstance(Of u0000_AlbumVO)()
' fill the values in the album
albumItem.nome = "The Best Portuguese Pimba"
albumItem.percious = .t.
' add a song
Dim musicItem as u0000_MusicaVO = albumItem.AddChild(Of u0000_MusicaVO)
' fill the values in the song
musicItem.nome = "O que elas querem é pimba"
' create the album with a song on the database
listMsg.AddRange(albumBiz.Save(newAlbums))
Return listMsg
Criar um álbum com uma música para quem adora de escrever código.
' Código PHC GO – VB.NET
Dim albumItem As u0000_AlbumVO = u0000_AlbumVO.GetNewInstance(Of u0000_AlbumVO)()
' fill the values in the album
albumItem.nome = "The Best Portuguese Pimba"
albumItem.percious = .t.
' add a song
Dim musicItem as u0000_MusicaVO = u0000_MusicaVO.GetNewInstance(Of u0000_MusicaVO)()
' relation
musicItem.u0000_albumstamp = albumItem.u0000_albumstamp
musicItem.parentVO = albumItem
' fill the values in the song
musicItem.nome = "O que elas querem é pimba"
' add to collection
albumItem.u0000_musica.add(musicItem)
' create the album with a song on the database.
listMsg.AddRange(albumBiz.Save(newAlbums))
Return listMsg
Alterar uma música e adicionar uma nova.
' Código PHC GO – VB.NET
' get some record
Dim uniqueID As String = "Z_20240505155423284756456"
Dim albumItem As u0000_AlbumVO = SDK.Query.GetEntityByStamp(Of u0000_AlbumVO)(uniqueID)
If albumItem Is Nothing Then
Return New MsgError("Album not found.")
End If
' the song
Dim musicItem as u0000_MusicaVO = Nothing
' find the song, option 1
musicItem = (From pk in albumItem.u0000_musica
Where pk.nome="O que elas querem é pimba").FirstOrDefault
' find the song, option 2
If albumItem.u0000_musica.any
musicItem = albumItem.u0000_musica(0)
End If
' find the song, option 3
If albumItem.u0000_musica.count >= 1
musicItem = albumItem.u0000_musica(0)
End If
' changing the song name
If musicItem IsNot Nothing Then
musicItem.nome = "O que elas querem ás vezes é pimba"
End If
' add a new song
'
' musicItem is a is a pointer, changing it in the next instruction
' does not affect the first song at all
musicItem = albumItem.AddChild(Of u0000_MusicaVO)
' fill the values in the new song
musicItem.nome = "O bacalhau quer …"
' persist changes to the database.
listMsg.AddRange(albumBiz.Save(newAlbums))
Return listMsg
Apagar uma música
' Código PHC GO – VB.NET
' get some record
Dim uniqueID As String = "Z_20240505155423284756456"
Dim albumItem As u0000_AlbumVO = SDK.Query.GetEntityByStamp(Of u0000_AlbumVO)(uniqueID)
If albumItem Is Nothing Then
Return New MsgError("Album not found.")
End If
' find the song
Dim musicItem as u0000_MusicaVO = (From pk in albumItem.u0000_musica
Where pk.nome="O bacalhau quer …").FirstOrDefault
' mark as deleted.
If musicItem IsNot Nothing Then
musicItem.Operation = OperationEnum.Delete
End If
' persist changes to the database.
listMsg.AddRange(albumBiz.Save(newAlbums))
Return listMsg
Apagar todas as músicas
' Código PHC GO – VB.NET
' get some record
Dim uniqueID As String = "Z_20240505155423284756456"
Dim albumItem As u0000_AlbumVO = SDK.Query.GetEntityByStamp(Of u0000_AlbumVO)(uniqueID)
If albumItem Is Nothing Then
Return New MsgError("Album not found.")
End If
' delete all
albumItem.u0000_musica.Clear()
' persist changes to the database.
listMsg.AddRange(albumBiz.Save(newAlbums))
Return listMsg
SDK – QUERY
https://helpcenter.phcgo.net/pt/sug/ptxview.aspx?stamp=d5183bc7eb7f7e%3a%3agd1562
Nos manuais disponíveis no Developers Help Center, há um tópico dedicado às operações de
retorno de valores da base de dados. As funções desta categoria, podem receber desde algo
simples, como o valor de stamp de um determinado registo, até estruturas mais avançadas,
como filtros e definições de pesquisa (QueryVO).
Neste contexto, iremos abordar essas estruturas auxiliares, algumas das quais já foram
incluídas nos exemplos apresentados nos manuais desta série Usa PHC CS.
Uma nota importante: todas as funções desta categoria estão agrupadas em dois tipos de
funcionalidades:
- Funções que obedecem aos filtros de sistema e utilizador implementados para a
entidade em questão.
- Funções que ignoram esses filtros, considerando apenas os filtros fornecidos no
momento da chamada da função. Caso nenhum filtro seja passado, a função realiza
uma pesquisa livre na base de dados
SDK – QUERY – Com Filtros Automáticos
Estas funções são amplamente utilizadas na rotina comum de acesso à base de dados. Por
exemplo, se um Add-On implementou um filtro que define que, na entidade de documentos de
faturação, cada vendedor só pode visualizar as suas próprias faturas.
Espera-se que, ao realizar queries para essa entidade, seja a partir do frontend ou do backend,
esse filtro seja automaticamente aplicado, sem a necessidade de o definir repetidamente em
cada consulta.
Get Values
- GetValues(Of T) (entityName As String, field As String) as List(Of T)
- GetValues(Of T) (entityName As String, field As String, filter As FilterItem) as List(Of T)
- GetValues(Of T) (entityName As String, field As String, filter As FilterItems) as List(Of T)
O exemplo seguinte mostra como retornar todos os números internos (ndoc) das séries de
documentos de faturação (td) que estejam configuradas para lançar em conta corrente
(lancacc).
Além do filtro aqui especificado, é automaticamente incluído os filtros de acesso por
utilizador que possam estar definidos na aplicação.
' Código PHC GO – VB.NET
Dim allConfigNumbers as New List(Of Decimal)
allConfigNumbers = SDK.Query.GetValues(Of Decimal)("td", "ndoc",
New FilterItem("lancacc", Comparison.Equal, True))
Neste exemplo como queremos adicionar mais um filtro para considerar apenas as séries
configuradas para controlar plafond de crédito (nocredit), optamos por definir primeiro
uma coleção de filtros e enviar essa coleção para a função.
' Código PHC GO – VB.NET
Dim myFilter As FilterItems = New FilterItems()
myFilter.Add(New FilterItem("lancacc", Comparison.Equal, True))
myFilter.Add(New FilterItem("nocredit", Comparison.Equal, True))
Dim allConfigNumbers as New List(Of Decimal)
allConfigNumbers = SDK.Query.GetValues(Of Decimal)("td", "ndoc", myFilter)
Get Entity
- GetEntityData(Of T) () As List(Of T)
- GetEntityData(Of T) (filter As FilterItem) As List(Of T)
- GetEntityData(Of T) (filter As FilterItems) As List(Of T)
Importante: Os registos obtidos por estas funções, podem ser usados na função
SDKBIZ.Save, para gravar possíveis alterações efetuadas após a sua obtenção da base
de dados.
No exemplo seguinte retornamos da base de dados um registo da entidade de clientes
usando um filtro pelo nome do cliente (nome).
' Código PHC GO – VB.NET
Dim client as ClVO = SDK.Query.GetEntityData(Of ClVO)(New FilterItem("nome",
Comparison.Equal, "'Bernardo Santiago'")).FirstOrDefault()
- GetEntityData(Of T) (queryItem As QueryVO) As List(Of T)
Importante: Os registos obtidos por esta função, só podem ser usados na função
SDKBIZ.Save, para gravar possíveis alterações efetuadas após a sua obtenção da base
de dados, caso a propriedade SelectItems esteja vazia.
- GetEntityData(queryItem As QueryVO) as IList
Importante: Os registos retornados por esta função não podem ser utilizados na
função SDKBIZ.Save. Tentar fazê-lo resultará em erro de compilação no Add-On.
- GetEntityByStamp(Of T)(stamp As String) As T
Importante: Os registos obtidos por esta função, podem ser usados na função
SDKBIZ.Save, para gravar possíveis alterações efetuadas após a sua obtenção da base
de dados.
- GetEntityDynamic(queryItem as QueryVO) as IList
Os registos retornados por esta função não são tipificados, por isso, o retorno é uma
lista de objetos, sendo necessário usar uma interface para definir o tipo de retorno.
Importante: Os registos retornados por esta função não podem ser utilizados na
função SDKBIZ.Save. Tentar fazê-lo resultará em erro de compilação no Add-On.
Esta função é especialmente útil quando precisamos executar uma query na base de
dados que retorna valores pertencentes a mais de uma entidade.
No exemplo seguinte, retornamos apenas alguns campos da entidade de artigos e
serviços (st), juntamente com campos da entidade de stock por armazém (sa). Para
isso, definimos uma ligação entre as duas entidades.
' Código PHC GO – VB.NET
' main table
Dim query As New QueryVO("sa")
' select the columns we want
query.SelectItems.Add("sa.armazem")
query.SelectItems.Add("sa.ref")
query.SelectItems.Add("sa.stock")
query.SelectItems.Add("st.design")
' create the join
Dim join as New JoinEntity("st")
' join expression
join.joinExp.Add(New FilterItem("st.ref",Comparison.Equal,"sa.ref"))
' add
query.joinEntities.Add(join)
' order
query.OrderByItems.Add(New OrderByItem("sa.armazem", OrderTypes.Descending))
' result
Dim result as Ilist = SDK.Query.GetEntityData(query)
For each item as Object in result
Dim parseTxt as String
parseTxt = String.Format("The item: {0} has {1} units",
item.design,item.stock)
Next
SDK – QUERY – Sem Filtros Automáticos
Estas funções são mais específicas e destinam-se a operações concretas, muitas vezes
relacionadas com a criação de novos registos, onde é necessário cumprir determinadas regras
de negócio.
Por exemplo, no caso dos filtros automáticos mencionados anteriormente, como o filtro de
vendedor, esse comportamento não é desejável em situações como a determinação do
próximo número a atribuir a uma fatura em criação.
De acordo com as regras de certificação, o número deve ser sequencial e sem falhas,
independentemente dos acessos configurados para os utilizadores do sistema.
Get Values
- GetOneValue(Of T) (entityName As String, field As String) As T
- GetOneValue(Of T) (entityName As String, field As String, filter As FilterItem) As T
- GetOneValue(Of T)(entityName As String, field As String, filter As FilterItems) As T
- GetOneValue(Of T)(entityName As String, field As String, aggregateType As Aggregate) As T
- GetOneValue(Of T)(entityName As String, field As String, aggregateType As Aggregate, filter As FilterItem) As T
- GetOneValue(Of T)(entityName As String, field As String, aggregateType As Aggregate, filter As FilterItems) As T
- GetNumRecords (entityName As String) As Int32
- GetNumRecords(entityName As String, filter As FilterItem) As Int32
- GetNumRecords(entityName As String, filter As FilterItems) As Int32
Get Entity
- GetEntityData(queryItem as String) as IList
Os registos retornados por esta função não são tipificados, por isso, o retorno é uma
lista de objetos, sendo necessário usar uma interface para definir o tipo de retorno.
Importante: Os registos retornados por esta função não podem ser utilizados na
função SDKBIZ.Save. Tentar fazê-lo resultará em erro de compilação no Add-On.
O exemplo a seguir é semelhante ao apresentado para a função
GetEntityDynamic(queryItem As QueryVO) As IList. A principal diferença é que, como
esta função recebe um comando Transact-SQL em formato de texto, não há uma
forma segura de injetar os filtros definidos na aplicação para a entidade em questão.
Por esse motivo, esta função está categorizada no grupo SDK – QUERY – Sem Filtros
Automáticos.
' Código PHC GO – VB.NET
Dim result as IList = SDK.Query.GetEntityData("select sa.armazem, sa.ref,
st.design, sa.stock from sa inner join st on sa.ref = st.ref order by sa.armazem")
For each item as Object in result
Dim parseTxt as String
parseTxt = String.Format("The item: {0} has {1} units",
item.design,item.stock)
Next
Utils
- ExistRecord(entityName As String) As Boolean
- ExistRecord(entityName As String, filtro As FilterItem) As Boolean
- ExistRecord(entityName As String, filtros As FilterItems) As Boolean
No exemplo seguinte, verificamos se existe algum registo na tabela de clientes que no
campo (zona) tenham o valor “Centro”.
' Código PHC GO – VB.NET
Dim fromCenter as Boolean = SDK.Query.ExistRecord("cl",New FilterItem("zona",
Comparison.Equal, "'Centro'"))
SDK – QUERY – QueryVO
Esta classe descreve um comando Transact-SQL. Permite definir as propriedades que devem
ser incluídas no comando enviado para a base de dados. Após configurar as propriedades,
basta chamar uma das funções da categoria SDK – Query que aceitem um QueryVO nos seus
parâmetros de entrada.
' Código PHC GO – VB.NET
Class QueryVO
#Region "Properties"
Property entityName As String = ""
Property limit As Integer
Property distinct As Boolean
#End Region
#Region "Groups"
Property SelectItems As New List(Of String)
Property filterItems As New List(Of FilterItem)
Property orderByItems As New List(Of OrderByItem)
Property joinEntities As New List(Of JoinEntity)
Property groupByItems As New List(Of GroupByItem)
#End Region
#Region "Filter"
Property ndoc As Integer
ApplyCoreFilters As Boolean = True
#End Region
End Class
A propriedade ApplyCoreFilters, permite controlar a característica da função chamada
respeitar ou não os filtros de sistema e de utilizador.
Importante: Caso os registos obtidos sejam destinados a alterações e posterior gravação na
base de dados NUNCA adicione valores à propriedade SelectItems.
SDK – QUERY – FilterItem
' Código PHC GO – VB.NET
Class FilterItem
#Region "Factory"
New()
New(filterItem As String, comparison As Comparison, valueItem As Object)
New(filterItem As String, valueItem As Object)
New(filterItem As String, comparison As Comparison,
valueItem As Object, groupItem As FilterGroupItem)
New(filterItem As String, valueItem As Object, groupItem As FilterGroupItem)
New(filterItem As String)
New(groupItem As FilterGroupItem)
#End Region
#Region "Properties"
Property filterItem As String = ""
Property comparison As Comparison = Comparison.Equal
Property valueItem As Object
Property groupItem As FilterGroupItem = FilterGroupItem.And
#End Region
End Class
SDK – QUERY – Comparison
' Código PHC GO – VB.NET
Enum Comparison As Integer
None = -1
Equal = 0
NotEqual = 1
Greater = 2
GreaterOrEqual = 3
Less = 4
LessOrEqual = 5
StartWith = 6
NotStartWith = 7
EndWith = 8
NotEndWith = 9
Contain = 10
NotContain = 11
Exist = 12
EqualLike = 13
NotEqualLike = 14
In = 15
NotExist = 16
NotIn = 17
Blank = 18
IsNULL = 19
IsNotNULL = 20
End Enum
SDK – QUERY – FilterGroupItem
' Código PHC GO – VB.NET
Enum FilterGroupItem As Integer
None = 0
And = 1
AndNot = 2
AndBkt = 3
AndNotBkt = 4
BktAndBkt = 5
BktAndNotBkt = 6
BktAnd = 7
BktAndNot = 8
Or = 9
OrNot = 10
OrBkt = 11
OrNotBkt = 12
BktOrBkt = 13
BktOrNotBkt = 14
BktOr = 15
BktOrNot = 16
CloseBkt = 17
OpenBkt = 18
End Enum
SDK – QUERY – JoinEntity
' Código PHC GO – VB.NET
Class JoinEntity
#Region "Factory"
New()
New(tableName As String)
New(tableName As String, plainJoin As String)
New(tableName As String, joinType As JoinsType)
New(tableName As String, joinType As JoinsType, plainExp As String)
New(tableName As String, joinType As JoinsType, joinExpression As FilterItem)
#End Region
#Region "Properties"
Property tableName As String = ""
Property entityAlias As String = ""
Public Property subselect As String = ""
Property joinType As JoinsType = JoinsType.Inner
Property joinExp As New List(Of FilterItem)
Property plainJoin As String = ""
#End Region
End Class
SDK – QUERY – GroupByItem
' Código PHC GO – VB.NET
Class GroupByItem
#Region "Factory"
New()
New(groupByItem As String)
#End Region
#Region "Properties"
Property GroupByItem As String = ""
#End Region
End Class
SDK – QUERY – OrderByItem
' Código PHC GO – VB.NET
Class OrderByItem
#Region "Factory"
New()
New(orderItem As String)
New(orderItem As String, orderType As OrderTypes)
New(orderItem As String, orderType As String)
New(orderItem As String, aggregateType As Aggregate)
New(orderItem As String, orderType As OrderTypes, aggregateType As Aggregate)
New(orderItem As String, orderType As String, aggregateType As Aggregate)
#End Region
#Region "Properties"
Property OrderItem As String = ""
Property OrderType As OrderTypes = OrderTypes.Ascending
Property AggregateType As Aggregate = Aggregate.None
#End Region
End Class
SDK – QUERY – OrderTypes
' Código PHC GO – VB.NET
Enum OrderTypes As Integer
None = -1
Ascending = 0
Descending = 1
End Enum
SDK – QUERY – Aggregate
' Código PHC GO – VB.NET
Enum Aggregate As Integer
None = 0
Avg = 1
Count = 2
Min = 3
Max = 4
Sum = 5
NullMin = 6
Accumulated = 7
InvertedAccumulated = 8
End Enum