ORM no PHP5 utilizando o SQLReactor
Agora vamos mapear o seguinte diagrama de classes:
Este diagrama nos possibilitará a compreensão de vários conceitos de ORM e a utilização de diversos tipos de dados diferentes, com o objetivo de guiar os desenvolvedores num início completo ao SQLReactor.
Faça esse mapeamento no arquivo "mapping.php". No SQLReactor o mapeamento é todo construído na forma de classes que estendem a classe SQLReactor, dentro da função mágica __map.
mapping.php
Agora vamos detalhar o que foi feito no mapeamento. Cada classe representa uma tabela no banco de dados. Cada atributo definido dentro da função mágica __map, representa uma coluna no banco. E todas as classes, automaticamente ganham o atributo id, como primary key, a menos que se especifique o contrário.
Portanto, a linha:
Define na tabela "user", a coluna "login" do tipo VARCHAR(100) NOT NULL. Porém vamos pensar nisso como um atributo do tipo string. Colunas do tipo string sem o parâmetro 'length' serão consideradas como string longa (em banco de dados, o tipo normalmente é conhecido como TEXT, LONG ou CHAR LOB);
Os tipos de coluna possíveis são:
Mais dois tipos especiais:
O campo do tipo SQLReactor::ForeignKey recebem como parâmetro a classe alvo do relacionamento, e criam de forma implícita um atributo numérico para armazenar o id extrangeiro. Por exemplo, uma instância da classe UserGroup, teria os atributos:
A linha:
Define que a classe UserGroup, não conterá o atributo automático "id" como primary key. Ao contrário, terá uma chave primária composta pelos atributos 'userId' e 'groupId'.
Um campo do tipo SQLReactor::Backref não será criado no banco de de dados. Ele existe apenas no mapeamento, funciona como um "relacionamento reverso", e tem a finalidade de permitir a navegação no sentido contrário ao do relacionamento.
No nosso modelo, como o relacionamento de User com Group é N..N, temos a classe de ligação que contém os ids de User e de Group. Essa classe, como tem o relacionamento, permite navegação para os objetos extrangeiros. Mas a navegação no sentido User -> UserGroup não existe. Para permitir isso, utilizamos o Backref, que é definido apontando pra classe "alvo" e para o atributo do tipo ForeignKey que aponta para si.
Por exemplo, na classe User, o atributo groups é mapeado como:
Pois o attributo "user" da classe "UserGroup", é que "aponta" para a classe "User"
E por fim, temos a função mágica __setPassword:
Esta função faz com que a senha seja criptografada automaticamente quando for definida. Este setter mágico (prefixo __set) pode ser definido para qualquer atributo da classe. Também podem ser definidos getters mágicos com o prefixo __get;
Este diagrama nos possibilitará a compreensão de vários conceitos de ORM e a utilização de diversos tipos de dados diferentes, com o objetivo de guiar os desenvolvedores num início completo ao SQLReactor.
Faça esse mapeamento no arquivo "mapping.php". No SQLReactor o mapeamento é todo construído na forma de classes que estendem a classe SQLReactor, dentro da função mágica __map.
mapping.php
<?php class User extends SQLReactor{ function __map(){ $this->login = SQLReactor::StringCol( array( 'length' => 100, 'notNull' => true ) ); $this->password = SQLReactor::StringCol( array( 'length' => 100, 'notNull' => true ) ); $this->birthday = SQLReactor::DateCol(); $this->isActive = SQLReactor::BoolCol( array( 'default' => true ) ); $this->weight = SQLReactor::FloatCol(); $this->height = SQLReactor::IntCol(); $this->unique( 'login' ); $this->groups = SQLReactor::Backref( array( 'target' => array( 'UserGroup', 'user' ) ) ); } function __setPassword( $value ){ return sha1( $value ); } } class Group extends SQLReactor{ function __map(){ $this->login = SQLReactor::StringCol( array( 'length' => 100, 'notNull' => true ) ); $this->users = SQLReactor::Backref( array( 'target' => array( 'UserGroup', 'group' ) ) ); } } class UserGroup extends SQLReactor{ function __map(){ $this->user = SQLReactor::ForeignKey( array( 'target' => 'User' ) ); $this->group = SQLReactor::ForeignKey( array( 'target' => 'Group' ) ); $this->primaryKey( 'userId', 'groupId' ); } } ?>
Agora vamos detalhar o que foi feito no mapeamento. Cada classe representa uma tabela no banco de dados. Cada atributo definido dentro da função mágica __map, representa uma coluna no banco. E todas as classes, automaticamente ganham o atributo id, como primary key, a menos que se especifique o contrário.
Portanto, a linha:
$this->login = SQLReactor::StringCol( array( 'length' => 100, 'notNull' => true ) );
Os tipos de coluna possíveis são:
SQLReactor::IntCol SQLReactor::FloatCol SQLReactor::StringCol SQLReactor::DateCol SQLReactor::DateTimeCol SQLReactor::TimeCol SQLReactor::BoolCol
Mais dois tipos especiais:
SQLReactor::ForeignKey SQLReactor::Backref
O campo do tipo SQLReactor::ForeignKey recebem como parâmetro a classe alvo do relacionamento, e criam de forma implícita um atributo numérico para armazenar o id extrangeiro. Por exemplo, uma instância da classe UserGroup, teria os atributos:
$ug->group; //Retorna a instância da classe Group, relacionada $ug->groupId; //Retorna o id do grupo relacionado
A linha:
$this->primaryKey( 'userId', 'groupId' );
Um campo do tipo SQLReactor::Backref não será criado no banco de de dados. Ele existe apenas no mapeamento, funciona como um "relacionamento reverso", e tem a finalidade de permitir a navegação no sentido contrário ao do relacionamento.
No nosso modelo, como o relacionamento de User com Group é N..N, temos a classe de ligação que contém os ids de User e de Group. Essa classe, como tem o relacionamento, permite navegação para os objetos extrangeiros. Mas a navegação no sentido User -> UserGroup não existe. Para permitir isso, utilizamos o Backref, que é definido apontando pra classe "alvo" e para o atributo do tipo ForeignKey que aponta para si.
Por exemplo, na classe User, o atributo groups é mapeado como:
$this->groups = SQLReactor::Backref( array( 'target' => array( 'UserGroup', 'user' ) ) );
E por fim, temos a função mágica __setPassword:
function __setPassword( $value ){ return sha1( $value ); }
Esta função faz com que a senha seja criptografada automaticamente quando for definida. Este setter mágico (prefixo __set) pode ser definido para qualquer atributo da classe. Também podem ser definidos getters mágicos com o prefixo __get;
Pra quem interessar, também existe um grupo para discussão de dúvidas sobre o projeto.
http://groups.google.com.br/group/sqlreactor-brasil
Abraços
http://groups.google.com.br/group/sqlreactor-brasil
Abraços
29/05/2009 2:57pm
(~15 anos atrás)
Marcos,
Você está completamente correto ao dizer da quantidade de recusos. Como eu mesmo disse no artigo, é um projeto bem novo (versao 0.3) e precisa mesmo de muitas coisas ainda. Conheci o Doctrine recentemente e concordo com você que ele vale muito a pena.
Porém, discordo quanto ao propel. O mapeamento de objetos não é tão simples e, mesmo o SQLReactor sendo novo, ja faz, de forma simples, coisas chatas de se fazer no propel, por exemplo. (Como fazer eagerload de múltiplos níveis)
Se você conhecer python, vai notar que a forma de mapeamento do SQLReactor se basea na forma do SQLObject, que é muito simples... Já a forma de busca e acesso aos dados, assemelha-se ao SQLAlchemy, que é considerado um dos mais completos pra python.
O reactor nasceu pra ser diferente dos outros ORMs em PHP. Eu o considero mais prático. Mas cada um prefere o seu. O objetivo do artigo é divulgar mais uma ferramenta de ORM e mostrar como funciona.
Fica por conta de cada desenvolvedor a escolha do seu.
Abraços,
Rafael
Você está completamente correto ao dizer da quantidade de recusos. Como eu mesmo disse no artigo, é um projeto bem novo (versao 0.3) e precisa mesmo de muitas coisas ainda. Conheci o Doctrine recentemente e concordo com você que ele vale muito a pena.
Porém, discordo quanto ao propel. O mapeamento de objetos não é tão simples e, mesmo o SQLReactor sendo novo, ja faz, de forma simples, coisas chatas de se fazer no propel, por exemplo. (Como fazer eagerload de múltiplos níveis)
Se você conhecer python, vai notar que a forma de mapeamento do SQLReactor se basea na forma do SQLObject, que é muito simples... Já a forma de busca e acesso aos dados, assemelha-se ao SQLAlchemy, que é considerado um dos mais completos pra python.
O reactor nasceu pra ser diferente dos outros ORMs em PHP. Eu o considero mais prático. Mas cada um prefere o seu. O objetivo do artigo é divulgar mais uma ferramenta de ORM e mostrar como funciona.
Fica por conta de cada desenvolvedor a escolha do seu.
Abraços,
Rafael
23/05/2009 5:39pm
(~15 anos atrás)
O artigo é realmente válido. Usar ORM tende a se tornar padrão em qualquer priojeto e em qualquer linguagem.
Quanto ao SQLReactor só o que tenho a dizer é que ele está muito longe das funcionalidades de outros ORMs mais maduros como o Propel <http://propel.phpdb.org/trac/> e o Doctrine <http://www.doctrine-project.org/>. Esses sim valem muito a pena.
Eu mesmo construi um ORM que ainda uso em alguns projetos mais antigos. Dieferente desses mencionados que utilizam o Pattern Active Record <http://pt.wikipedia.org/wiki/Active_record> eu usei o Pattern DAO <http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html>.
Outro ponto é a classe de abstração. Eu já utilizava PDO no meu ORM desde o início (quando foi lançado junto com a versão 5 do PHP) e agora todos tendem a migrar para ele assim como fez o Propel que utilizava Creole.
Alias o modelo de ORM que acho interessante e que inclusive "copie" diversas idéias é o JPA do Java.
Quanto ao SQLReactor só o que tenho a dizer é que ele está muito longe das funcionalidades de outros ORMs mais maduros como o Propel <http://propel.phpdb.org/trac/> e o Doctrine <http://www.doctrine-project.org/>. Esses sim valem muito a pena.
Eu mesmo construi um ORM que ainda uso em alguns projetos mais antigos. Dieferente desses mencionados que utilizam o Pattern Active Record <http://pt.wikipedia.org/wiki/Active_record> eu usei o Pattern DAO <http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html>.
Outro ponto é a classe de abstração. Eu já utilizava PDO no meu ORM desde o início (quando foi lançado junto com a versão 5 do PHP) e agora todos tendem a migrar para ele assim como fez o Propel que utilizava Creole.
Alias o modelo de ORM que acho interessante e que inclusive "copie" diversas idéias é o JPA do Java.
23/05/2009 4:33pm
(~15 anos atrás)
Fala ae Rapaz....
Parabens pelo artigo, ficou muito bom....
abraço
fui
Parabens pelo artigo, ficou muito bom....
abraço
fui
22/05/2009 12:45pm
(~15 anos atrás)
O SQLReactor é genial. Amei o artigo :D