Neste artigo, dou várias dicas de como fazer determinadas coisas com dbix class. Este não vai ser fácil para iniciantes que não leram a documentação. No entanto, espero que sirva como referência.
Faça o download do pacote e acesse o diretório 'app' para obter o código fonte do exemplo utilizado.
DBIx::Class é um extensível e flexível objeto <-> mapeamento relacional Na minha opinião é o mais fantástico mapeamento objeto relacional. Com ele você consegue acessar tabelas relacionadas a partir de qualquer ponto. Ou seja, vamos supor que temos um db assim: ______ _______ _______ ________ | | | | | | | | | pai |----------<| filho |-----------<| amigo |--------------|namorada| |______| 1 ou + |_______| 1 ou + |_______| apenas 1 |________| (pai tem 1 ou + filhos, cada filho pode ter 1 ou + amigos e cada amigo pode ter 1 namorada ) Agora vamos supor que estamos usando dbix class e queremos adicionar: - 1 pai, - 1 filho, - 2 amigos - 2 namordas para cada amigo inserido É bastante simples... veja como na seção de apêndice logo após as dicas de A a Z.
$ script/myapp_create.pl model DB DBIC::Schema MyApp::DB create=static dbi:Pg:dbname=myapp USER pass $ script/myapp_create.pl model DB DBIC::Schema MyApp::DB create=static dbi:mysql:db=myapp USER pass
dbicdump -o dump_directory=./lib \ -o components='["InflateColumn::DateTime"]' \ <-- *** nao obrigatorio -o debug=1 \ My::Schema \ 'dbi:Pg:dbname=foo' \ myuser \ mypassword
1. fazer um schema dump com dbix class schema loader (letra B) 2. colocar seu projeto em pastas assim: /myapp /myapp/programa.pl /myapp/lib /myapp/lib/DBSchema 3.editar seu programa.pl e adicionar estas linhas para ele poder utilizar o DBSchema: use lib ( "./lib" ); use DBSchema; my $schema = DBSchema->connect('dbi:Pg:dbname=saude', 'hernan', '123'); my $medico = $schema->resultset('Medico')->find({ id => 1}); print $medico->name;
http://search.cpan.org/~rkitover/Catalyst-Model-DBIC-Schema-0.41/lib/Catalyst/TraitFor/Model/DBIC/Schema/Caching.pm __PACKAGE__->config({ traits => ['Caching'], connect_info => ['dbi:mysql:db', 'user', 'pass'], }); $c->model('DB::Table')->search({ foo => 'bar' }, { cache_for => 18000 });
Depois é só acessar como se fosse um metodo. infos: http://beta.metacpan.org/module/Catalyst::Model::DBIxResultSet sub is_my_img { my ($self, $c, $img_gallery_id) = @_; return $self->search({ id => $img_gallery_id, user_id => $c->user->id, }); No controller: my $test = $c->model('DBICSchemamodel::ImgGallery')->is_my_img($c, $pks[0])->single();
$schema->deploy
dbic dbix class count search({}, { order_by => { -desc => \'count(tracks.trackid)' }, join => 'tracks', distinct => 1, });
my $schema = DB->connect(...); my $stmt = 'create table foo ( id int );'; $schema->storage->dbh->prepare( $stmt )->execute()
my $uniqueKey = md5_hex($schemamodel); my $cached_data; unless ( $cached_data = $c->cache->get($uniqueKey) ) { my $result = $c->model($schemamodel) ->search({ is_deleted => 0, },{ }); $result->result_class('DBIx::Class::ResultClass::HashRefInflator'); #sets the result to be hashreinflator my @items_list = $result->all; #inflates the whole resultset into a array hash use Data::Dumper; $c->log->debug('Dumper ' . Dumper( \@items_list )); $cached_data = \@items_list; $c->cache->set( $uniqueKey, $cached_data ); }
$cd->artistname(shift @{$c->req->params->{artistname}}); $cd->update();
sub preco_fmt #retorna preço formatado { my $self = shift @_; return 'R$ ' . sub { my $price_fmt = $self->preco() ; $price_fmt =~ s/\./,/g; return $price_fmt; }->(); }
->search({... id => { 'not in' => [1,2], }, ...},{});
order_by => \''COUNT(\'story_id\') AS count' order_by => \'COUNT(\'story_id\') DESC'
$c->user->obj->discard_changes
package myapp::DBSchema::ResultSet::Company; use strict; use warnings; use base 'DBIx::Class::ResultSet'; sub all_companys { my ($self) = @_; my @companys = $self-> search( { 'me.is_deleted' => 0 }, { order_by => ['me.name ASC',] } )->all; return @companys; } 1;
sem cores: $ export DBIC_TRACE=1 && script/imobiliaria_software_server.pl -r -d -p 3089 com cores: $ export DBIC_TRACE_PROFILE=console && export DBIC_TRACE=1 && script/imobiliaria_software_server.pl -r -d -p 3089
$item = $schema->resultset('Medico')->find(); #retorna uma row, pode ser acessado ex. $item->id, $item->nome, $item->nome('novo nome') $item->update @res = $schema->resultset('Medico')->search({},{})->all #Retorna array $res = $schema->resultset('Medico')->search({...},{}) #Retorna varias rows e para fazer loop tem que fazer: while ( my $item = $res->next ) { ... }
Veja como é bacana e elegante trabalhar com DBIx::Class
Eu recomento a utilização do postgres para ensinar dbix class pois o postgres salva os relacionamentos dentro do banco de dados e isso facilita pois o dbix class consegue detectar esses relacionamentos e já cria todos os models para nós incluindo esses relacionamentos.
CREATE TABLE amigo ( id integer NOT NULL, nome text, amigo_id integer ); ALTER TABLE public.amigo OWNER TO hernan; CREATE SEQUENCE amigo_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE public.amigo_id_seq OWNER TO hernan; ALTER SEQUENCE amigo_id_seq OWNED BY amigo.id; SELECT pg_catalog.setval('amigo_id_seq', 1, false); CREATE TABLE filho ( id integer NOT NULL, nome text, pai_id integer ); ALTER TABLE public.filho OWNER TO hernan; CREATE SEQUENCE filho_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE public.filho_id_seq OWNER TO hernan; ALTER SEQUENCE filho_id_seq OWNED BY filho.id; SELECT pg_catalog.setval('filho_id_seq', 1, false); CREATE TABLE namorada ( id integer NOT NULL, nome text, amigo_id integer ); ALTER TABLE public.namorada OWNER TO hernan; CREATE SEQUENCE namorada_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE public.namorada_id_seq OWNER TO hernan; ALTER SEQUENCE namorada_id_seq OWNED BY namorada.id; SELECT pg_catalog.setval('namorada_id_seq', 1, false); CREATE TABLE pai ( id integer NOT NULL, nome text ); ALTER TABLE public.pai OWNER TO hernan; CREATE SEQUENCE pai_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE public.pai_id_seq OWNER TO hernan; ALTER SEQUENCE pai_id_seq OWNED BY pai.id; SELECT pg_catalog.setval('pai_id_seq', 1, false); ALTER TABLE ONLY amigo ALTER COLUMN id SET DEFAULT nextval('amigo_id_seq'::regclass); ALTER TABLE ONLY filho ALTER COLUMN id SET DEFAULT nextval('filho_id_seq'::regclass); ALTER TABLE ONLY namorada ALTER COLUMN id SET DEFAULT nextval('namorada_id_seq'::regclass); ALTER TABLE ONLY pai ALTER COLUMN id SET DEFAULT nextval('pai_id_seq'::regclass); COPY amigo (id, nome, amigo_id) FROM stdin; \. COPY filho (id, nome, pai_id) FROM stdin; \. COPY namorada (id, nome, amigo_id) FROM stdin; \. COPY pai (id, nome) FROM stdin; \. ALTER TABLE ONLY amigo ADD CONSTRAINT amigo_pkey PRIMARY KEY (id); ALTER TABLE ONLY filho ADD CONSTRAINT filho_pkey PRIMARY KEY (id); ALTER TABLE ONLY namorada ADD CONSTRAINT namorada_pkey PRIMARY KEY (id); ALTER TABLE ONLY pai ADD CONSTRAINT pai_pkey PRIMARY KEY (id); ALTER TABLE ONLY amigo ADD CONSTRAINT amigo_amigo_id_fkey FOREIGN KEY (amigo_id) REFERENCES filho(id); ALTER TABLE ONLY filho ADD CONSTRAINT filho_pai_id_fkey FOREIGN KEY (pai_id) REFERENCES pai(id); ALTER TABLE ONLY namorada ADD CONSTRAINT namorada_amigo_id_fkey FOREIGN KEY (amigo_id) REFERENCES amigo(id); REVOKE ALL ON SCHEMA public FROM PUBLIC; REVOKE ALL ON SCHEMA public FROM postgres; GRANT ALL ON SCHEMA public TO postgres; GRANT ALL ON SCHEMA public TO nixus; GRANT ALL ON SCHEMA public TO PUBLIC;
Agora que já criamos o banco de dados, devemos criar os models em nossa app para que ela se conecte no banco de dados. Então vamos lá... usaremos a dica "B. Como gerar os models se você já tem o banco de dados, sem catalyst" que diz:
Se você não tiver permissão, pode conectar no db com
psql tut_dbixclass_perl_orm
e depois executar um grant all em todas as tabelas pra um usuario, no meu casi 'webdev'
select 'grant all on '||schemaname||'.'||tablename||' to webdev;' from pg_tables where schemaname in ('public') order by schemaname, tablename;
faça um select em pg_tables antes para ver quais itens você vai precisar... neste caso é só public e agora vamos criar um diretório para a aplicação
mkdir /home/catalyst/tutorial-dbix-class-perl-orm-pt-br/app cd /home/catalyst/tutorial-dbix-class-perl-orm-pt-br/app vim /home/catalyst/tutorial-dbix-class-perl-orm-pt-br/app/app.pl
Instale o módulos do cpan:
DBIx::Class::Schema::Loader #dbicdump (gera os models pra você, com todos os relacionamentos) DBD::Pg #para conectar no banco de dados postgres
Talvez você precise daquelas libs de -dev para poder criar o DBD::Pg... ex.
postgresql-server-dev-all #ferramentas para desenvolvimento postgres
$ dbicdump The program 'dbicdump' is currently not installed. To run 'dbicdump' please ask your administrator to install the package 'libdbix-class-schema-loader-perl'
Primeiro tentei rodar o comando dbicdump diretamente no meu terminal para ver se o mesmo está instalado. Apareceu essa mensagem dizendo que o mesmo não está instalado, porem eu posso instalar pois ele está no repositório do ubuntu (que legal em) Mas, acontece que eu estou utilizando minha versão de perl 5.17.1 (a mais nova) que eu instalei usando "perlbrew" (procure perlbrew) e eu utilizo junto o "cpanm" e assim eu posso instalar módulos sem root no perl. legal pois é mais seguro!!
cpanm DBIx::Class::Schema::Loader
esse é o módulo que fornece o dbicdump
Após instalar o DBIx::Class::Schema::Loader, Tentei executar o comando dbicdump mas veja o que aconteceu:
$ dbicdump -o dump_directory=./lib -o debug=1 DB::Tutorial::DBIx::Class::PT::BR 'dbi:Pg:dbname=tut_dbixclass_perl_orm' username password DBIx::Class::Schema::Loader::make_schema_at(): DBI Connection failed: install_driver(Pg) failed: Can't locate DBD/Pg.pm in @INC (@INC contains: /home/webdev/perl5/perlbrew/perls/perl-5.15.9/lib/site_perl/5.15.9/x86_64-linux /home/webdev/perl5/perlbrew/perls/perl-5.15.9/lib/site_perl/5.15.9 /home/webdev/perl5/perlbrew/perls/perl-5.15.9/lib/5.15.9/x86_64-linux /home/webdev/perl5/perlbrew/perls/perl-5.15.9/lib/5.15.9 .) at (eval 95) line 3. Perhaps the DBD::Pg perl module hasn't been fully installed, or perhaps the capitalisation of 'Pg' isn't right. Available drivers: DBM, ExampleP, File, Gofer, Proxy, SQLite, Sponge. at /home/webdev/perl5/perlbrew/perls/perl-5.15.9/lib/site_perl/5.15.9/DBIx/Class/Storage/DBI.pm line 1249. at /home/webdev/perl5/perlbrew/perls/perl-5.15.9/bin/dbicdump line 178
Isto quer dizer que não tem instalado o DBD::Pg, que é o módulo que vai permitir nossa conexão com o banco de dados Postgres. O erro aparece na linha acima, nesta parte:
Can't locate DBD/Pg.pm in @INC
Quer dizer que não localizou o módulo DBD/Pg e esse módulo é uma dependência necessária para essa ação:
DBIx::Class::Schema::Loader::make_schema_at(): DBI Connection failed
Ou seja, tentou executar metodo:
make_schema
no módulo
DBIx::Class::Schema::Loader
e resultou em:
DBI Connection failed
pelo motivo
install_driver(Pg) failed: Can't locate DBD/Pg.pm in @INC
Quando tentei rodar o dbicdump, eu havia instalado minha versão perl com perlbrew recentemente e nem tinha instalado o DBD::Pg ainda... por isso resultou no erro #2. Então vou instalar o módulo DBD::Pg assim:
$ cpanm DBD::Pg --> Working on DBD::Pg Fetching http://www.cpan.org/authors/id/T/TU/TURNSTEP/DBD-Pg-2.19.2.tar.gz ... OK Configuring DBD-Pg-2.19.2 ... OK Building and testing DBD-Pg-2.19.2 ... OK Successfully installed DBD-Pg-2.19.2 1 distribution installed
$ dbicdump -o dump_directory=./lib -o debug=1 DB::Tutorial::DBIx::Class::PT::BR 'dbi:Pg:dbname=tut_dbixclass_perl_orm' username password DBIx::Class::Schema::Loader::make_schema_at(): DBI Connection failed: DBI connect('dbname=tut_dbixclass_perl_orm','username',...) failed: FATAL: Peer authentication failed for user "username" at /home/webdev/perl5/perlbrew/perls/perl-5.15.9/lib/site_perl/5.15.9/DBIx/Class/Storage/DBI.pm line 1249. at /home/webdev/perl5/perlbrew/perls/perl-5.15.9/bin/dbicdump line 178
Dê permissão ao seu usuário no banco de dados atraves do comando grant
grant all on database tut_dbixclass_perl_orm to username;
$ dbicdump -o dump_directory=./lib -o debug=1 DB::Tutorial::DBIx::Class::PT::BR 'dbi:Pg:dbname=tut_dbixclass_perl_orm' username webdev123 Bad table or view 'amigo', ignoring: DBIx::Class::Schema::Loader::make_schema_at(): DBI Exception: DBD::Pg::st execute failed: ERROR: permission denied for relation amigo [for Statement "SELECT * FROM "public"."amigo" WHERE ( 1 = 0 )"] at /home/webdev/perl5/perlbrew/perls/perl-5.15.9/bin/dbicdump line 178
dê permissão de select nas tabelas para seu usuário:
grant all on public.pai to username;
$ dbicdump -o dump_directory=./lib -o debug=1 DB::Tutorial::DBIx::Class::PT::BR 'dbi:Pg:dbname=tut_dbixclass_perl_orm' username password DB::Tutorial::DBIx::Class::PT::BR::Result::Amigo->table("amigo"); DB::Tutorial::DBIx::Class::PT::BR::Result::Amigo->add_columns( "id", { data_type => "integer", is_auto_increment => 1, is_nullable => 0, sequence => "amigo_id_seq", }, "nome", { data_type => "text", is_nullable => 1 }, "amigo_id", { data_type => "integer", is_foreign_key => 1, is_nullable => 1 }, ); DB::Tutorial::DBIx::Class::PT::BR::Result::Amigo->set_primary_key("id"); DB::Tutorial::DBIx::Class::PT::BR::Result::Filho->table("filho"); DB::Tutorial::DBIx::Class::PT::BR::Result::Filho->add_columns( "id", { data_type => "integer", is_auto_increment => 1, is_nullable => 0, sequence => "filho_id_seq", }, "nome", { data_type => "text", is_nullable => 1 }, "pai_id", { data_type => "integer", is_foreign_key => 1, is_nullable => 1 }, ); DB::Tutorial::DBIx::Class::PT::BR::Result::Filho->set_primary_key("id"); DB::Tutorial::DBIx::Class::PT::BR::Result::Namorada->table("namorada"); DB::Tutorial::DBIx::Class::PT::BR::Result::Namorada->add_columns( "id", { data_type => "integer", is_auto_increment => 1, is_nullable => 0, sequence => "namorada_id_seq", }, "nome", { data_type => "text", is_nullable => 1 }, "amigo_id", { data_type => "integer", is_foreign_key => 1, is_nullable => 1 }, ); DB::Tutorial::DBIx::Class::PT::BR::Result::Namorada->set_primary_key("id"); DB::Tutorial::DBIx::Class::PT::BR::Result::Pai->table("pai"); DB::Tutorial::DBIx::Class::PT::BR::Result::Pai->add_columns( "id", { data_type => "integer", is_auto_increment => 1, is_nullable => 0, sequence => "pai_id_seq", }, "nome", { data_type => "text", is_nullable => 1 },
... e nada de erros.
O DBIx::Class fala ingles por padrao.. então quando ele detectar e criar os relacionamentos para você, ele vai colocar os plurais em ingles. Então precisamos verificar e consertar essas inconsistências.
Então vamos editar os models do dbixclass que estão dentro do diretório:
app/lib/DB/Tutorial/DBIx/Class/PT/BR/Result/* app/lib/DB/Tutorial/DBIx/Class/PT/BR/Result/Amigo.pm app/lib/DB/Tutorial/DBIx/Class/PT/BR/Result/Filho.pm app/lib/DB/Tutorial/DBIx/Class/PT/BR/Result/Pai.pm
Primeiro edite o Filho.pm la no final, *após* a linha DO NOT MODIFY THIS OR ANYTHING ABOVE coloque: Não altere essa linha e nem o conteudo acima dela. Tudo que você escrever após essa linha vai sobrepor o que foi declarado em cima. A parte que está em cima pode ser sobre-escrita/atualizada se você rodar um comando para atualizar os campos do banco de dados por exemplo. Então se você mexer ali, o dbix class vai se perder, então evite mexer ali para não ter problemas. Sempre mexa após essa linha:
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:aaQcKvggMUd5YmFttR/eYw __PACKAGE__->has_many( "amigos", "DB::Tutorial::DBIx::Class::PT::BR::Result::Amigo", { "foreign.amigo_id" => "self.id" }, { cascade_copy => 0, cascade_delete => 0 }, );
agora o arquivo Pai.pm
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:B4I5N4/abdMhSMNWiYJFJQ __PACKAGE__->has_many( "filhos", "DB::Tutorial::DBIx::Class::PT::BR::Result::Filho", { "foreign.pai_id" => "self.id" }, { cascade_copy => 0, cascade_delete => 0 }, );
agora sim, alteramos de filhoes para filhos. E amigoes para amigos. bem melhor pois fica mais natural.
Veja a dica #C deste tutorial e crie um diretório para sua aplicação e edite um arquivo.pl para inserir o seguinte código de exemplo: Este código conecta no banco de dados utilizando postgres, e insere 1 pai, 1 filho, 1 amigo e uma namorada pro amigo. Abaixo está a saída dos comandos executados. Para executar o script digite no terminal:
$ export DBIC_TRACE=1 && perl app.pl
Segue o codigo fonte:
use lib ( "./lib" ); use DB::Tutorial::DBIx::Class::PT::BR; my $schema = DB::Tutorial::DBIx::Class::PT::BR->connect( 'dbi:Pg:dbname=tut_dbixclass_perl_orm', 'webdev', 'webdev123' ); my $pai = $schema->resultset('Pai')->new({ nome => 'joao' }); $pai->insert; warn $pai->nome; my $filho = $pai->add_to_filhos( { nome => 'filho 1' } ); warn $filho->nome; my $amigo = $filho->add_to_amigos( { nome => 'Nome amigo1', } ); my $namorada = $amigo->add_to_namoradas( { nome => 'Maria' } ); warn $namorada->nome; warn $namorada->id;
Saida do codigo acima:
INSERT INTO pai ( nome) VALUES ( ? ) RETURNING id: 'joao' joao at app.pl line 10. INSERT INTO filho ( nome, pai_id) VALUES ( ?, ? ) RETURNING id: 'filho 1', '23' filho 1 at app.pl line 12. INSERT INTO amigo ( amigo_id, nome) VALUES ( ?, ? ) RETURNING id: '6', 'Nome amigo1' INSERT INTO namorada ( amigo_id, nome) VALUES ( ?, ? ) RETURNING id: '3', 'Maria' Maria at app.pl line 21. 2 at app.pl line 22.
Hernan Lopes < hernanlopes gmail > cpan: http://search.cpan.org/~hernan/ github: http://github.com/hernan604/ Escrevam para eu saber se está ficando legal, ou como posso melhorar! Obg, -Hernan Lopes
1; # The preceding line will help the module return a true value
To install DB::Tutorial::DBIx::Class::PT::BR, copy and paste the appropriate command in to your terminal.
cpanm
cpanm DB::Tutorial::DBIx::Class::PT::BR
CPAN shell
perl -MCPAN -e shell install DB::Tutorial::DBIx::Class::PT::BR
For more information on module installation, please visit the detailed CPAN module installation guide.