Entidades

"A plataforma inteira é desenvolvida em torno de seis entidades principais: Agentes, Espaços, Eventos, Projetos, Oportunidades e Inscrições"

O Mapas Culturais utiliza o Doctrine como ORM. Para o Doctrine, as entidades representam um mapeamento entre o modelo orientado a objetos e o modelo relacional do banco de dados.

Uma classe de entidade representa uma tabela. Enquanto uma instância dessa classe representa uma entrada nesta tabela.

O Mapas Culturais implementa na classe MapasCulturais\Entity uma abstração em cima das entidades do Doctrine.

Essa implementação ocorre de modo a facilitar a validação da entidade, as operações CRUD e, ao mesmo tempo, cuidar das permissões, impedindo que um usuário faça operações e/ou visualize informações que ele não tem permissão. Todas as entidades do mapas estendem esta classe.

Para se aprofundar nas entidades vale a leitura da documentação Working With Objects do Doctrine. Lembrando que o Mapas faz uma abstração em cima do Doctrine.

Trabalhando com Entidades (CRUD e os Repositórios)

Operação de Inserção

Para inserir uma nova entrada em uma tabela, ou em outras palavras, criar um novo objeto de uma determinada entidade, deve-se instanciar um novo objeto da classe desejada, definir ao menos os atributos obrigatórios e chamar o método save do objeto.

Você pode também, antes de salvar, verificar se há erros de validação no objeto.

$agent = new \MapasCulturais\Entities\Agent;
$agent->name = 'Fulano';
$agent->type = 1; // agente do tipo individual
$agent->shortDescription = 'Descrição do fulano';

if (!($errors = $agent->getValidationErrors())) {
   $agent->save(true); // passar true para o método save faz o flush do objeto no banco de dados
}

Operação de Leitura (Recuperando Entidade do Banco de Dados)

Para recuperar um objeto do banco de dados você deve utilizar o repositório da entidade.

O repositório da entidade pode ser acessado pelo método repo da aplicação.

$app = MapasCulturais\App::i();

// pega o agente de id 1
$agent = $app->repo('Agent')->find(1);

// pega um agente de nome 'Fulano'
$agent = $app->repo('Agent')->findOneBy(['name' => 'Fulano']);

// pega todos os agentes de nome 'Fulano'
$agents = $app->repo('Agent')->findBy(['name' => 'Fulano']);

Operação de Atualização

Para modificar um objeto após recuperá-lo do banco de dados, você deve modificar suas propriedades e utilizar o método save.

$app = MapasCulturais\App::i();

// pega o agente de id 1
$agent = $app->repo('Agent')->find(1);
$agent->name = 'Ciclano';
$agent->save(true);

Operação de Deleção

Para deletar um objeto você deve utilizar o método delete, do mesmo:

$app = MapasCulturais\App::i();

// pega o agente de id 1
$agent = $app->repo('Agent')->find(1);
$agent->delete(true);

Permissões no Nível do Modelo e Diagrama de Permissões

O sistema de permissões do Mapas Culturais é implementado no nível do modelo. Esta abordagem auxilia na prevenção de bugs, uma vez que o controle do que um usuário pode ver, criar, modificar, ou deletar, acontece automaticamente, disparando uma exceção PermissionDenied quando uma operação não permitida for executada.

Considerando que usuário não está autenticado, o exemplo abaixo disparará a exceção no momento da execução do save(true):

$app = MapasCulturais\App::i();

// pega o agente de id 1
$agent = $app->repo('Agent')->find(1);

if ($app->user->is('guest')) {
   $agent->name = 'Ciclano';
   $agent->save(true); 
}

Ou do delete:

$agent = $app->repo('Agent')->find(1);
if ($app->user->is('guest')) {
   $agent->delete(true);
}

O mesmo acontece quando um usuário tenta modificar ou apagar um objeto que não lhe pertence e ao qual ele não tem controle.

As permissões são implementadas em funções com o prefixo canUser (por exemplo, canUserModify), e podem ser verificadas através do método canUser da entidade.

// se o usuário autenticado pode modificar o objeto $agent, imprime um botão editar
if ($agent->canUser('modify')) {
   echo "<button>editar</button>";
}

Os métodos canUser recebem o usuário ao qual a permissão está sendo testada e devem ser implementados como protegidos:

class Agent {
   protected function canUserDoSomething($user) {
      if($user->is('admin')) {
         return true;
      } else {
         return false;
      }
   }
}

$agent->canUser('doSomething'); // retorna true se o usuário logado for admin, false de outra forma

Pode-se verificar a permissão antes de efetuar uma operação utilizando o método checkPermission, que dispara uma exceção de PermissionDenied quando o método canUser da permissão retornar false. Esta função é muito útil nos controladores, como veremos no próximo capítulo.

$agent->checkPermission('doSomething'); // dispara a exceção se o usuário logado não for um admin

Agente Relacionado com Controle (permissão @control)

@todo

Diagrama de Permissões

Metadados, Taxonomias e Termos, e Arquivos

Metadados

Como falado anteriormente, cada entidade principal, agentes, espaços, eventos, projetos, oportunidades e inscrições, conta com uma entidade secundária auxiliar para salvamento de metadados. Diferentemente das outras entidades auxiliares, que veremos em seguida.

Para cada entidade principal temos uma tabela própria para a entidade secundária, por exemplo, temos uma tabela agent_meta para salvar os metadados dos agentes. A estrutura dessas entidades é a seguinte:

  • id: int;

  • key: string;

  • value: string;

  • owner: tipo da entidade principal, ou seja Agent, Space, Event, Project, Opportunity ou Registration (mapeado para a coluna object_id).

Os metadados das entidades precisam ser registrados antes de serem utilizados. No método register de plugins ou temas, podemos registrar metadados da seguinte maneira:

function register() {
   $this->registerAgentMetadata('numeroDeFilhos', [
      'label' => 'Metadado para salvar a informação de quantos filhos o agente tem',
      'type' => 'number',
      // campo privado, somente quem puder ver dados privados ($agent->canUser('viewPrivateData')) poderá ver esta informação
      'private' => true,
      'default' => 0,
      'validations' => [
            'required' => 'O número de filhos é obrigatório',
            // sintaxe do Respect/Validation
            'v::intVal()->positive()' => 'O número de filhos deve ser um número inteiro positivo'
      ],
      'serialize' => function ($val) { 
         return (string) (int) $val;
      },
      'unserialize' => function ($val) {
         return (int) $val;
      }
   ])
}

Os seguintes métodos estão disponíveis para utilização em Plugins, Módulos e Temas:

  • registerAgentMetadata;

  • registerEventMetadata;

  • registerOpportunityMetadata;

  • registerProjectMetadata;

  • registerRegistrationMetadata;

  • registerSealMetadata;

  • registerSpaceMetadata;

  • registerUserMetadata.

Após registrar o metadado, você pode utilizá-lo como se fosse um atributo do objeto, usando a chave registrada diretamente:

$agent->numeroDeFilhos = 4;
$agent->save();

echo $agent->numeroDeFilhos;

É importante utilizar os metadados dessa maneira, pois é nesta implementação que é feita a verificação das permissões para visualização dos metadados privados.

Ou seja, acessar os metadados através do getter getMetadata NÃO é uma boa prática

Taxonomias e Termos

A implementação das taxonomias é feita em torno das entidades Term e TermRelation, e do trait EntityTaxonomies.

Uma vez registrada uma taxonomia, você pode acessar e definir os termos que uma entidade principal utiliza desta taxonomia da seguinte maneira, utilizando como exemplo a taxonomia tag:

$tags = $agent->terms['tag'];
$tags[] = 'mapas'; 
$agent->terms['tag'] = $tags;

$agent->save();

A operação do exemplo acima adicionará uma entrada na tabela term para o termo mapas da taxonomia tag, isso ocorre somente se ainda não houver o termo. E, adicionará outra entrada na tabela term_relation relacionando o termo criado ao agente.

Arquivos

A funcionalidade de anexar arquivos nas entidades é implementada pela entidade secundária File e pelos traits EntityFiles, que implementa a parte do modelo. E, pelo ControllerUploads que automatiza os uploads dos arquivos.

Cada entidade que utiliza o trait EntityFiles tem uma entidade auxiliar, por exemplo, a entidade Agent tem a entidade auxiliar AgentFile que tem a propriedade owner do tipo Agent.

Além disso, a entidade principal recebe do trait alguns métodos gets que auxiliam a obtenção dos arquivos anexos. O principal deles é o getFiles, acessível pelo magic getter por ->files.

>>> $agent->files
   [
      "avatar" => MapasCulturais\Entities\AgentFile {#9645
         +__isInitialized__: true,
         …2
      },
      "downloads" => [
         MapasCulturais\Entities\AgentFile {#9654},
         MapasCulturais\Entities\AgentFile {#9656},
      ]
   ]

Traits de Entidades

Os seguintes traits estão disponíveis e são utilizados nas diversas entidades:

  • EntityAgentRelation: Implementa a funcionalidade de relacionar agentes na entidade. É utilizado por todas as entidades onde é preciso definir administradores para conceder permissão de controle sobre a entidade;

  • EntityArchive: Implementa a funcionalidade de arquivar e desarquivar uma entidade (Entity::STATUS_ARCHIVE = -2). É utilizado nas entidades principais;

  • EntityAvatar: Implementa um getter para o grupo de arquivos avatar. É utilizado pelas entidades que têm a funcionalidade de avatar;

  • EntityDraft: Implementa a funcionalidade de entidades em rascunho. É utilizado pelas entidades principais;

  • EntityFiles: Implementa a funcionalidade de arquivos anexos às entidades;

  • EntityGeoLocation: Implementa a funcionalidade de geolocalização. É utilizado nas entidades Agent e Space;

  • EntityMetaLists: Implementa a funcionalidade de listas (links e vídeos);

  • EntityMetadata: Implementa os metadados para os agentes. É utilizado pelas entidades que possuem a entidade secundária auxiliar para salvamento de metadados;

  • EntityNested: Implementa a funcionalidade de entidades hierárquicas. É utilizado pelas entidades com a propriedade parent, ou seja, Agent, Space, Project e Opportunity;

  • EntityOpportunities: Implementa um getter para retornar as oportunidades vinculadas a entidade. Utilizado nas entidades Agent, Space, Project e Event;

  • EntityOriginSubsite: Implementa nas entidades métodos relacionados ao SaaS. É utilizado nas entidades que têm a propriedade subsite, mapeamento para a coluna subsite_id;

  • EntityOwnerAgent: Implementa a funcionalidade de agente proprietário da entidade. É utilizado pelas entidades que têm um agente owner ("Assinado por", no rodapé da página);

  • EntityPermissionCache: Implementa a funcionalidade de cache de permissão para a entidade. Como esse cache só é utilizado pela API de busca do Mapas Culturais, este trait é utilizado pelas entidades que disponibilizam a API ou que são utilizadas nas queries da API, a saber, Agent, EvaluationMethodConfiguration, Event, Notification, Opportunity, Project, Registration, Request, Seal e Space;

  • EntityRevision: Implementa a funcionalidade de revisões para as entidades. É utilizada nas entidades Agent, Space, Registration e RegistrationEvaluation;

  • EntitySealRelation: Implementa a funcionalidade da aplicação de selos nas entidades. Utilizado em Agent, Event, Opportunity, Project e Space;

  • EntitySoftDelete: Implementa o soft delete (lixeira) para as entidades. Utilizado em Agent, Event, Opportunity, Project, Seal, Space, Subsite e UserApp;

  • EntityTaxonomies: Implementa a funcionalidade de aplicar termos de taxonomias (tags, linguagem e área de atuação) nas entidades. Utilizado nas entidades Agent, Event, Opportunity, Project e Space;

  • EntityTypes: Implementa o campo type nas entidades. Utilizado em Agent, EvaluationMethodConfiguration, Event, Opportunity, Project e Space.

Last updated

Was this helpful?