Thiago Cantero

Tecnologia e Entretenimento

Active RecordArquitetura de SoftwareBanco de DadosPadrões de ProjetoPHPProgramação

ORM, Active Record – Parte I

Olá, mundo! Tudo bem?
Aqui estou novamente para postar um conteúdo para vocês, para levar o conhecimento de conceito do que se trata de um ORM (Object Relational Mapper), na tradução: Mapeador de Objetos Relacionais, e o que são os padrões que os compõe, os mais usados eu diria, que são: o Active Record, e o Data Mapper.

Quando usamos um Framework, certamente iremos usar um ORM, seja qual for a linguagem.

No paradigma da Programação Orientada a Objetos há uma uma adaptação do modelo relacional, que é representado no modelo matemático da Teoria de Conjuntos, proposto por Frank Edgar Codd. Para que se tenha a abstração e persistência da camada de dados no modelo proposto, se faz necessário transformá-lo em objeto. Diante desta premissa, adotamos um padrão ao qual fará a abstração, manipulação dos dados presentes no Banco de Dados. O esquema abaixo, simplista, demonstra como funciona o funcionamento do ORM.

No diagrama acima, vemos uma solicitação de um cliente, que manda a consulta para um Modelo de Escrita, este atua em várias frentes, ou popularmente conhecimento como CRUD (Create, Record, Update, Delete), em que são transformados em objetos, e repassados para o Modelo de Leitura para a consumo destas informações. Para entendermos melhor o conceito ORM, ele é um método, que se vale de padrões de projetos para transformação das informações, estas podendo ou não serem relacionais, para sua manipulação.

O Padrão Active Record

Este padrão, que será utilizado com a técnica ORM, baseia-se na criação de Classes que representam a tabela do Banco de Dados Relacional, que será baseada em uma Classe Abstrata (Caso não conheça os conceitos de OOP, recomendo a leitura), em que nela iremos criar, através de Herança (Outro Conceito de OOP),  outras tabelas se forem necessárias.

Martin Fowler, em seu livro Padrões de Arquitetura de Aplicações Corporativas (2002), ele descreve com a seguinte síntese:

Um objeto que envolve uma linha em uma tabela ou exibição de banco de dados, encapsula o acesso ao banco de dados e adiciona lógica de domínio a esses dados.

Ou Seja, como falamos em linguagem com paradigma de Orientação a Objetos, e trabalhamos com Dados, devemos pensar nos Dados como objetos, isto ficará mais claro ao decorrer do artigo, além da explanação dos dois padrões.

No exemplo, criarei apenas uma tabela cuja a representatividade será de uma pessoa. Vejamos o diagrama abaixo:

 

O padrão Active Record, como podemos observar o diagrama UML acima, possui uma Classe Abstrata a qual possui todos os métodos de manipulação dos dados com o Banco, com os métodos: load(), reload(), store(), delete(), fromArray() e toArray(). Enquanto cada tabela de seu banco de dados, será representada por uma classe, no exemplo, nossa tabela Pessoa possui os seguintes campos: id, nome, endereco, telefone e os métodos: salvaDado() e pegaDado().

Vamos, de maneira didática exemplificar isto em código PHP, da criação do Modelo Pessoa.

<?php
/**
 * Pessoa Model - Exemplo Didático, na hipótese de herdar de uma classe abstrata
 *
 * @version    1.0
 * @package    ActiveRecord
 * @author     Thiago Cantero Mari Monteiro
 * @copyright  Copyright (c) 2022 Thiago Cantero Mari Monteiro
 * @license    http://www.thiagocantero.com.br/sobre
 */

class Pessoa extends ActiveRecord
{
    const NOMETABELA    = 'pessoa';
    const CHAVEPRIMARIA = 'id';
    

    public function __construct($id = NULL)
    {
        parent::__construct($id);

        parent::adcCampo('id');
        parent::adcCampo('nome');
        parent::adcCampo('endereco');
        parent::adcCampo('telefone');

    }
}

No código acima, vislumbramos que a classe Pessoa (O modelo que simboliza como objeto a tabela Pessoa de nosso Banco), herda a Super Classe ActiveRecord (linha 12), posteriormente nas linhas 14 e 15 definimos o nome da tabela e a chave primária através de uma constante, respectivamente. Chamamos um método construtor para definir os campos da tabela, nele inserimos os campos: id, nome, endereco e telefone.

Agora vamos manipular essa informação.

$pessoa = new Pessoa();
$pessoa->nome('Thiago');
$pessoa->endereco('Rua das Pedras, 12');
$pessoa->telefone('11-1111-1111');

$pessoa->store();

Usamos a palavra reservada New do PHP para instanciar um novo objeto, e através dele atribuímos para a variável $pessoa os atributos: nome, endereco e telefone, e posteriormente salvamos no Banco com o método store().

Para carregar o objeto faríamos o seguinte comando:

$pessoa = new Pessoa(1);
echo 'Nome:         ' . $pessoa->nome       ."<br>\n";
echo 'Endereço:     ' . $pessoa->endereco   ."<br>\n";
echo 'Telefone:     ' . $pessoa->telefone   ."<br>\n";

Carregamos o id no Objeto pessoa e passamos os atributos nas linhas abaixo. Isto seria o suficiente para Retornar algo assim:

Nome: Thiago

Endereço: Rua das Pedras, 12

Telefone: 11-1111-1111

Para atualizar, necessitaríamos executar a instância, referenciando o registro, neste exemplo o primeiro registro na tabela, e repassar os atributos ou somente um caso queira realizar apenas uma correção ao nome, adicionando um sobrenome, por exemplo:

$pessoa = new Pessoa(1);

$pessoa->nome('Thiago Cantero');

$pessoa->store();

Carregamos o objeto e alteramos e executamos o método store, que neste caso, atuaria como um update do registro.

 

E para excluir usaríamos apenas uma única linha de código:

$pessoa->delete(1);

Simples, não?
No entanto há muita polêmica sobre este padrão, alguns desenvolvedores o acha como um anti-padrão na verdade, como forte acoplamento da aplicação ao banco de dados, a classe de modelo mais complexa com as possíveis relações que por ventura surjam, violação da responsabilidade única, e a dificuldade de se realizar teste unitários. Porém grandes Frameworks utilizam este padrão, como o Laravel com o Eloquent (PHP), e o Ruby on Rails (Ruby). Creio que seja mais uma procrastinação/criticidade por parte dos desenvolvedores em querer causar problemas, pois se assim não fosse, ambos Frameworks citados não seriam famosos e amplamente utilizados em diversos segmentos do mundo afora.

Claro é que possível melhorar o padrão, atribuindo a ele repositório, critérios, dentre outras minucias que podem ser agregadas para suprir todas as necessidades de uma boa e moderna aplicação.

O importante é saber usar a ferramenta para atender as demandas que eventualmente necessite, desde que preencha as boas práticas de segurança, testes e documentação.

No próximo artigo, trarei mais a fundo o padrão Data Mapper.

Por hoje é só, até mais! : )