[Rio-pm] Fey::ORM alguém já usou ou viu, tem futuro?

breno breno em rio.pm.org
Segunda Dezembro 13 16:03:27 PST 2010


2010/12/12 German Valenzuela <german.valenzuela em gmail.com>:
> Prezados
>
> hoje ouvi falar do Fey::ORM alguém já usou ou viu, tem futuro?
>

Resposta curta
ou "preguiça de ler os emails enormes do garu"
=======================================

 * Considerando as datas/quantidade de releases, a qualidade do
código, a documentação e o histórico/comprometimento do autor, eu
diria que tem futuro sim =)
 * O projeto Fey é relativamente antigo (2006~2010) e bastante
estável, mas o Fey::ORM ainda é muito novo e a API pode mudar de uma
versão pra outra, então use com cuidado (ou "olhe o Changelog antes de
atualizar").
 * Uma solução ORM estável e alternativa ao Fey::ORM (porém mais
inchada e que não usa Fey por baixo) é o famoso DBIx::Class (DBIC para
os íntimos).


Resposta longa
ou "o Fey mais Bunitin do pedaço :P"
ou "prévia do meu artigo para o equinócio de março/2011"
================================================

"Fey" é uma distribuição contendo uma série de módulos para
representar de forma simples e flexível os componentes de um schema
DBMS, bem como gerar queries SQL dinamicamente conforme esse schema.

Digamos que vc tenha um banco SQLite em "banco.db". Vc pode obter o
schema assim (strict e warnings omitidos apenas por brevidade):

--------------8<--------------
use DBI;
use Fey::Loader;

my $dbh = DBI->connect( 'dbi:SQLite:dbname=banco.db' )
    or die "erro conectando-se ao banco";

my $schema = Fey::Loader->new( dbh => $dbh )->make_schema;
-------------->8--------------

A partir daí vc pode manipular seu $schema como quiser, através dos
métodos oferecidos pelo Fey::Schema - tables(), add_table(),
remove_table(), add_foreign_key(), etc. Fey também oferece o
Fey::DBIManager, uma interface única para um ou mais fontes de dados,
permitindo o gerenciamento deles em um só lugar, além de garantir, por
exemplo, que os handles DBI sejam recriados corretamente após uma
operação de fork.

Para obter/manipular as tabelas 'user' e 'group' do seu banco (supondo
que elas existem, claro), bastaria fazer:

--------------8<--------------
my $user  = $schema->table('user');

my $group = $schema->table('group');
-------------->8--------------

São retornados objetos Fey::Table, com uma série de métodos
auxiliares. As operações em SQL (select, insert, update, delete ) são
manipuladas através de objetos também. Por exemplo:

--------------8<--------------
use Fey::SQL;

my $query = Fey::SQL->new_select;

$query->select( $user->columns( 'user_id', 'username' ) )
         ->from( $user, $group )
         ->where( $group->column('group_id'), 'IN', 1, 2, 3 )
         ->order_by( $user->column('username'), 'DESC' )
         ->limit( 10 );

-------------->8--------------

O código acima é equivalente (e argumentativamente mais fácil de
entender/manter) do que escrever uma string com o código SQL nativo
para o DBI:

--------------8<--------------
my $statement = <<'EOSQL';
SELECT user.user_id, User.username
   FROM user JOIN Group
        ON user.user_id = Group.user_id
  WHERE group.group_id IN (?, ?, ?)
 ORDER BY user.username DESC
 LIMIT 10
EOSQL
-------------->8--------------

Como mostrado no exemplo mais acima, seu select é construído através
de métodos do Fey::SQL (no caso, Fey::SQL::Select). Você pode até
manipular funções com ele:

--------------8<--------------
my $func = Fey::Literal::Function->new( 'LCASE', $user->column('username') );
-------------->8--------------

e depois fazer algo como:

--------------8<--------------
$query->select( ...
                ...
                ->and( $func, 'LIKE', 'breno%' )
                ...
-------------->8--------------

Fey lida apenas com a geração de SQL. Ao terminar de escrever sua
query, vc pode executá-la como uma invocação DBI tradicional:

--------------8<--------------
my $rv = $dbh->prepare( $query->sql($dbh) )
                    ->execute( $query->bind_params )
-------------->8--------------

A extensão FeyX::Active permite a integração do Fey com o seu DBI
handler. Assim, se $query fosse um "FeyX::Active::SQL" em vez de um
"Fey::SQL", poderiamos fazer a execução diretamente:

--------------8<--------------
my @linha = $query->execute->fetchrow
-------------->8--------------

Finalmente, para aqueles que procuram usar o Fey em um mapeamento
objeto-relacional, foi criado o Fey::ORM. Vejamos um exemplo
extremamente simples, a partir de um banco SQLite3 armazenado em
/var/myapp.db e com o seguinte schema:

CREATE TABLE User (
        user_id integer not null primary key autoincrement,
        username text not null,
        email text null,
        UNIQUE(username)
);

O primeiro passo é criar uma classe pra representar o schema do nosso
modelo, Digamos, MyApp::Model::Schema:

--------------8<--------------
package MyApp::Model::Schema;
use strict;
use warnings;

use Fey::ORM::Schema;
use Fey::Loader;
use Fey::DBIManager::Source;

my $source = Fey::DBIManager::Source->new(
        dsn => 'dbi:SQLite:dbname=/var/myapp.db'
);

has_schema( Fey::Loader->new( dbh => $source->dbh )->make_schema );

__PACKAGE__->DBIManager->add_source( $source );

-------------->8--------------

No código acima definimos um $source - um wrapper para handles DBI
como já explicado - e dizemos que nossa classe possui o schema
carregado pelo Fey::Loader. Basta isso. Como vimos, o Fey se vira pra
criar uma representação do schema pra vc.

Agora vamos criar as classes das nossas tabelas, nesse exemplo apenas 'User':

--------------8<--------------
package MyApp::Model::User;
use strict;
use warnings;

use MyApp::Model::Schema;
use Fey::ORM::Table;

has_table( MyApp::Model::Schema->Schema->table('User') );

1;
-------------->8--------------

Quando definimos que essa classe deve usar a tabela "User" extraida do
nosso schema MyApp::Model::Schema->Schema(), o Fey::ORM cria pra vc
todos os accessors para as colunas da tabela em questão, além de
oferecer outros métodos bastante convenientes.

A partir daí, podemos usar nosso modelo como quisermos! Por exemplo,
veja o programa myapp.pl:


--------------8<--------------
#!/usr/bin/perl
use strict;
use warnings;
use 5.10.0;

use MyApp::Model::User;

# adicionando um usuário
my $user = MyApp::Model::User->insert(
        username => 'german',
        email    => 'german.valenzuela em gmail.com',
);

# exibindo informações do usuário
say $user->username;  # 'german'

# buscando usuários
my $result = MyApp::Model::User->new( username => 'german' );

# atualizando entradas
$result->update( username => 'valenzuela' );

# removendo entradas
$user->delete;
-------------->8--------------

Os atributos (username, email, etc) são carregados apenas uma vez (e
todos de uma vez, por questões de eficiência), e depois servidos via
cache.

Você pode definir relacionamentos entre tabelas através de chamadas a
has_one() e has_many(). Por exemplo, se tivéssemos uma tabela
"Messages" com posts de cada usuário, poderiamos escrever em nosso
User.pm algo como:

--------------8<--------------
has_many 'messages', table => Forum::Model::Schema->Schema->table('Message');
-------------->8--------------

e a partir daí poderíamos acessar diretamente todas as mensagens de
nosso usuário:

--------------8<--------------
my $messages = $user->messages;

while ( my $message = $messages->next ) {
      say $message->message_id;
}
-------------->8--------------

Outra coisa bacana é a criação de inflators/deflators para suas
colunas. Isso é muito usado quando vc quer transformar os dados de uma
coluna em um objeto para manipulação (inflar) e reverter o processo
para armazenar novamente no banco (desinflar). Por exemplo, vamos
voltar a nossa classe User.pm e transformar o campo "email" em um
objeto Email::Address:

--------------8<--------------
transform 'email'
      => inflate { defined $_[1] ? Email::Address->new( $_[1] ) : undef }
      => deflate { defined $_[1] && blessed $_[1] ? $_[1]->address() : $_[1] }
;
-------------->8--------------


É isso. Tanto Fey quanto suas extensões (entre elas o Fey::ORM)
possuem uma série de opções pra facilitar a vida de quem manipula
queries, muito mais do que cabem neste (já bem longo) email. A
documentação está cheia de exemplos e o suporte ao módulo oferecido
por autarch e companhia em #fey no irc.perl.org e no RT é digno de
respeito. Fey está na minha lista pessoal de módulos modernos para
Perl, e outras pessoas parecem concordar comigo. Dito isso, manipular
bancos de dados é algo muito particular às suas preferências e
necessidades. Escolha seu veneno e seja feliz =)


[]s

garu


Mais detalhes sobre a lista de discussão Rio-pm