Artigo
· Maio 26, 2023 5min de leitura

Acessando as propriedades de uma relação em uma classe persistente habilitada para JSON

Definindo o contexto

Suponha que você tenha as 2 classes persistentes a seguir habilitadas para JSON (ou seja, estende %JSON.Adaptor ou %pkg.isc.rest.model.adaptor)

Class Test.Employee Extends (%Persistent, %pkg.isc.rest.model.adaptor) { 
  Parameter RESOURCENAME = "employee";
  Parameter firstName As %String;
  Parameter lastName As %String; 
  Relationship projects As Test.Project [ Cardinality = many, Inverse = employee) ];
}

Class Test.Project Extends (%Persistent, %pkg.isc.rest.model.adaptor) {
  Parameter RESOURCENAME = "project"; 
  Parameter name As %String; 
  Relationship employee As Test.Employee [ Cardinality = one, Inverse = projects, OnDelete = setnull ];
}

Nesse exemplo, um Employee (funcionário) pode ter vários projetos, mas cada projeto só pode ser delegado a um funcionário. 

Agora, imagine que os seguintes objetos estão armazenados no banco de dados

Tabela de funcionários:

ID firstName lastName
1 John Doe
2 Jane Fonda

Tabela de projetos:

ID name employee
1 Project Alpha 1
2 Project Beta 2
3 Project Charlie 2

Nesse exemplo, John Doe é o funcionário designado para o Project Alpha e Jane Fonda é a funcionária designada para o Project Beta e Project Charlie.

 

O problema: não é possível acessar a propriedade Relationship por solicitações REST

Se você executar uma solicitação GET básica para obter todos os objetos Test.Project armazenados no banco de dados, perceberá que as propriedades Relationship não estão incluídas no JSON retornado.

O array JSON retornado será algo do tipo:

[
  {"_id": "1", "name": "Project Alpha" },
  {"_id": "2", "name": "Project Beta" },
  {"_id": "3", "name": "Project Charlie" },
]

 

Como obtemos os detalhes sobre o funcionário designado de um projeto?

Devemos substituir o valor padrão escrito no console quando %JSONExport() for chamado em um objeto.

Para fazer isso, atualizamos as definições Relationship nas classes:

Test.Employee 

Relationship projects As Test.Project(%JSONINCLUDE= "none" ) [ Cardinality = many, Inverse = employee) ];

XData BasicReference [ XMLNamespace = "http://www.intersystems.com/apps/jsonmapping" ]
{
  <Mapping xmlns="http://www.intersystems.com/apps/jsonmapping">
    <Property Name = "firstName" />
    <Property Name = "lastName" />
  </Mapping>
}

Veja que adicionamos set %JSONINCLUDE = "none" na Relationship dos projetos em Test.Employee. Isso evita a criação de um loop infinito (por exemplo, ao escrever os detalhes JSON de um Test.Project, são escritos os detalhes JSON do Test.Employee designado, que escreverá os detalhes JSON do Test.Project de um funcionário designado etc.)

Test.Project 

Relationship employee As Test.Employee(%JSONINCLUDE = "INOUT", %JSONMAPPING = "BasicReference", %JSONREFERENCE = "OBJECT") [ Cardinality = one, Inverse = projects, OnDelete = setnull ]; 

Definimos %JSONINCLUDE = "INOUT" na relação do funcionário em Test.Project. Isso diz que a Relationship do funcionário deve ser incluída na entrada e saída JSON.

Também definimos %JSONMAPPING = "BasicReference" e %JSONREFERENCE = "OBJECT".

Sem definir %JSONREFERENCE, a exportação de um Project Alpha para um objeto JSON retornará o ID do funcionário:

{"_id": "1", "name": "Project Alpha", "employee": "1" },

Se mais propriedades forem adicionadas a Test.Employee depois (por exemplo, "dob"), elas serão incluídas no JSON retornado. Com a definição de %JSONMAPPING = "BasicReference", podemos especificar quais propriedades queremos incluir no JSON retornado para o funcionário.

{"_id": "1", "name": "Project Alpha", "employee": {"_id": "1", "firstName": "John", "lastName": "Doe"} }

 

Conclusão

Agora que executamos uma solicitação GET básica para todos os objetos Test.Project no banco de dados armazenado, o array do JSON retornado incluirá as propriedades da relação

[   
  {"_id": "1", "name": "Project Alpha", "employee": {"_id": "1", "firstName": "John", "lastName": "Doe" },   
  {"_id": "2", "name": "Project Beta", "employee": {"_id": "2", "firstName": "Jane", "lastName": "Fonda" },  
  {"_id": "3", "name": "Project Charlie", "employee": {"_id": "2", "firstName": "Jane", "lastName": "Fonda" },  
]

Código final

Class Test.Employee Extends (%Persistent, %pkg.isc.rest.model.adaptor) {   
  Parameter RESOURCENAME = "employee";   
  Parameter firstName As %String;   
  Parameter lastName As %String;   
  Relationship projects As Test.Project(%JSONINCLUDE= "none" ) [ Cardinality = many, Inverse = employee) ];

  XData BasicReference [ XMLNamespace = "http://www.intersystems.com/apps/jsonmapping" ]
  {
    <Mapping xmlns="http://www.intersystems.com/apps/jsonmapping">
      <Property Name = "firstName" />
      <Property Name = "lastName" />
    </Mapping>
  }
} 

Class Test.Project Extends (%Persistent, %pkg.isc.rest.model.adaptor) {   
  Parameter RESOURCENAME = "project";   
  Parameter name As %String;   
  Relationship employee As Test.Employee(%JSONINCLUDE = "INOUT", %JSONMAPPING = "BasicReference", %JSONREFERENCE = "OBJECT") [ Cardinality = one, Inverse = projects, OnDelete = setnull ]; 
}

 

Um passo além: atualizando uma propriedade Relationship por solicitação POST/PUT

Para definir o Employee designado a um Project por solicitação POST/PUT (ao criar ou editar um projeto), basta incluir um par de chave-valor no corpo da solicitação em que a chave é a classe RESOURCENAME e o valor é um JSON que segue o mapeamento BasicReference para um Test.Employee 

Veja o exemplo de corpo de solicitação abaixo para atualizar o funcionário atribuído para o Project 3 de Jane Fonda para John Doe:

{
  "_id": "3",
  "name": "Project Charlie",
  "employee": { "_id": "1", "firstName": "John", "lastName": "Doe" }
}
Discussão (0)1
Entre ou crie uma conta para continuar