Importação de dados em massa, PHP, Doctrine e problemas

Se gostou, compartilhe...ou não :DShare on FacebookTweet about this on TwitterShare on Google+Share on RedditShare on LinkedInEmail this to someone

Salve,

Estava numa necessidade de importar uma quantidade grande de dados, para algumas pessoas não tão grande, que estava em um CSV. Os dados eram as cidades de todo o planeta e a quantidade de registros é imensa, quase 3milhões de linhas.

O sistema está usando Symfony2 com Doctrine2 e fiz uns comandos para importar países, estados e faltava cidades. Os outros foram beleza, teve certa demora mas não passou de 4mil linhas, com no mínimo 2 consultas ao banco antes de gravar.

Para importar cidades, segui o mesmo padrão: carregar a linha, consulta o estado a partir do código ISO do país + o código da região, se existir, checo se a cidade existe, se não existir, gravava. Mas não deu certo. Fiz ajustes no código para dá insert a cada 200 itens na fila de persistência, depois aumentei e o máximo que consegui foi 200mil registros. Muito, se comparar aos que já tinha feito, mas pouco se comparar ao total que eu tinha que conseguir. Fiz otimizações até chamando função do coletor de lixo do PHP, ajudou, mas não suficiente. Sem elas não passava de 150mil registros.

Até que eu lembrei do Redis, suas diversas estruturas de dados, sua capacidade de salvar cópia em disco e mais outras coisas, sou apaixonado pelo Redis :D.

Mudei a estratégia para fazer funcionar. Pelo fato de que talvez nunca houvesse uma atualização de cidades após essa importação, e tratando para excluir as cidades brasileiras, já que elas já existiam no banco de dados e essa importação tem o objetivo de fazer a aplicação suportar cidades do mundo todo nos seus cadastros. Passei a não checar se o estado e a cidade existiam, descartei o insert na hora, pois pior que trabalhar com milhões de registros num arquivo texto é trabalhar com milhões de consultas ou operações de gravação no banco de dados, é um caos.

Para a solução comecei a por os estados (~4mil) em uma estrutura de Hash, no Redis, com a chave sendo o código iso (ISO do país + código da região) e o valor sendo o ID do estado no banco de dados, usando uma consulta somente, a única envolvida nessa mudança.

Com esses dados no Redis, varri as cidades e gerando um código SQL de insert e guardando numa outra estrutura do Redis, a List, esse código. Com isso em menos de 25 minutos já tinha todas as cidades no Redis, com o código de insert correto, dando um total de 2.884.445 de cidades. Mas ainda tinha um problema, ainda não estava no banco de dados da aplicação, que é um MySQL.

Após ter todos os dados gerados corretamente, bastam 2 comandos: um para exportar os inserts para um arquivo .sql e outro pra importar o .sql no MySQL.

Creio que seja uma ótima solução e que não faz sair totalmente do ambiente PHP e usar outras linguagens, apesar de ainda ter que exportar e importar o arquivo sql, para processamento total. Além de ser bem rápido e simples.

Após perder várias horas tentando fazer um milagre sem sair do ambiente que estava, estou bastante satisfeito com a solução como um todo. E mais apaixonado pelo Redis :D

 

Se gostou, compartilhe...ou não :DShare on FacebookTweet about this on TwitterShare on Google+Share on RedditShare on LinkedInEmail this to someone

Symfony2: campo do tipo Entity, no Form type, com o texto do item usando diversas propriedades da entidade

Se gostou, compartilhe...ou não :DShare on FacebookTweet about this on TwitterShare on Google+Share on RedditShare on LinkedInEmail this to someone

Salve,

Estava com uma necessidade de aparecer num <select> a informação de duas propriedades da entidade utilizada, e achei, não de maneira fácil mas que parece ser uma saída elegante. Supondo que nossa entidade tem dois campos, codigo e nome, e no <option> preciso mostrar essas duas propriedades seguindo o padrão de formatação: $codigo$nome

<Form\MeuFormType.php>

<Entity\MeuTipo.php>

 

Explicando:

Basta criar um método na entidade que retorne uma string, não precisa ter a propriedade do método, pois apesar da configuração se chamar property, ele vai tentar acessar via métodos de acesso get(), has(), is*() e __get().

Fica ai a dica. Espero ter ajudado.

Se gostou, compartilhe...ou não :DShare on FacebookTweet about this on TwitterShare on Google+Share on RedditShare on LinkedInEmail this to someone

Filtro em coleção no Doctrine

Se gostou, compartilhe...ou não :DShare on FacebookTweet about this on TwitterShare on Google+Share on RedditShare on LinkedInEmail this to someone

Salve,

As vezes temos uma situação onde precisamos filtrar dados retornado de um relacionamento entre entidades, onde o fruto desse relacionamento é uma coleção de uma das 2 entidades, mais especificamente numa relação One to Many. O Doctrine retorna um ArrayCollection, que é uma estrutura do Doctrine que implementa vários tipos de estruturas do PHP como: Countable, IteratorAggregate e ArrayAccess.

Com isso você pode fazer diversas operações sobre a coleção, mas uma das mais bacanas que faz poupar consultas extras ao banco somente para retornar os dados filtrados como deseja é a matching(Criteria $criteria), que receber uma instância de Doctrine\Common\Collections\Criteria com as suas devidas regras de filtragem.

Um uso que faço é quando tenho nessa coleção registros com um determinado campo que desejo usar como filtro, um exemplo é uma entidade de mídia, onde se tem um campo com o tipo da mídia (vídeo, imagem, slide e etc), e você deseja filtrar essa coleção, que é acessível facilmente pelas mágicas do Doctrine, sem ter que fazer uma consulta manualmente específica para isso, trazendo as mídias que tem um determinado valor naquele ou naqueles campos:

Quando você fizer um $album->getFiles(), irá retornar todas as mídias de todos os tipos. Ai usamos o Criteria no ArrayCollection sobre o matching() para filtrar, adicionando um método simples na entidade Album, por exemplo:

Somente imagens

 Somente vídeos:

Ficando assim a entidade com as mudanças:

Você pode usar o Criteria para criar diversos filtros.

Então fica ai uma ajuda para tentar evitar ficar fazendo diversas consultas e códigos extras e confusos para filtros uma coleção.

Valeu!

Se gostou, compartilhe...ou não :DShare on FacebookTweet about this on TwitterShare on Google+Share on RedditShare on LinkedInEmail this to someone

Doctrine 2: consulta com DQL envolvendo tabela many to many nos joins

Se gostou, compartilhe...ou não :DShare on FacebookTweet about this on TwitterShare on Google+Share on RedditShare on LinkedInEmail this to someone

Salve,

Passando por problemas em como fazer uma consulta onde em um determinado JOIN teria uma tabela de relacionamento many to many, tive muitos problemas, principalmente na documentação, pois não explica algumas coisas.

Na documentação descobrir o MEMBER OF, que buscar o valor em uma coleção. Lá dá um único exemplo e uso o mesmo na cláusula WHERE:

http://docs.doctrine-project.org/en/2.1/reference/dql-doctrine-query-language.html

Desse jeito, funciona, somente no WHERE. Mas meu caso era para pegar registros de um usuário, mas o único relacionamento de entidades entre esses registros que eu necessitava e o usuário (só tinha o ID dele), era uma entidade com many to many, entre os relacionamentos many to one nas extremidades, seguindo essa idéia:

Após apanhar com erros consegui achar a solução, que é até aceitável quando você para pra pensar: O MEMBER OF, quando está no JOIN ele não recebe um valor simples (como um ID) mas sim um objeto, para checar se existe dentro da coleção:

O entendimento dela é como o contains do ArrayCollection do Doctrine:

Após apanhar bastante, conseguir resolver esse problema que estava me matando por não está na documentação desse modo.

Valeu!

Se gostou, compartilhe...ou não :DShare on FacebookTweet about this on TwitterShare on Google+Share on RedditShare on LinkedInEmail this to someone

Symfony2: injetando o service container em serviços

Se gostou, compartilhe...ou não :DShare on FacebookTweet about this on TwitterShare on Google+Share on RedditShare on LinkedInEmail this to someone

Salve,

As vezes precisamos acessar outros serviços dentro de um determinado serviço e o Symfony possui o Service Container para retornar instâncias dos serviços com as suas dependências sendo injetadas. Quando registramos esses serviços, passamos eles como parâmetros, um a um. Mas existem casos em que podemos deixar de lado isso e trabalhar internamente com o Service Container para poder acessar qualquer serviço registrado na aplicação.

O método de registro é igual em ambos os casos:

Fica:

Muda agora na classe, assim:

Agora você pode utilizar na classe o Service Container para recuperar seus serviços, assim:

Valeu!

Se gostou, compartilhe...ou não :DShare on FacebookTweet about this on TwitterShare on Google+Share on RedditShare on LinkedInEmail this to someone

Symfony2: testes funcionais com autenticação via WSSE

Se gostou, compartilhe...ou não :DShare on FacebookTweet about this on TwitterShare on Google+Share on RedditShare on LinkedInEmail this to someone

Salve,

Após apanhar bastante pesquisando em como fazer funcionar os testes funcionais de recursos que estão usando uma autenticação WSSE, no próprio Symfony2, um conhecido me deu a solução de como passar os cabeçalhos:

Cenário

Você já tem seu teste ok, passando a url e os parâmetros e está funcionando se você suspender o uso do WSSE no recurso, mas com ele ativo, ele não passa da autenticação do WSSE, logo os testes não rodam.

Para fazer os testes, está utilizando o client http do provido pelo próprio framework/WebTestCase. Exemplo de criação do client http:

$this->client = static::createClient(…);

E no request está assim:

$crawler = $this->client->request(
‘GET’,
‘/meu/recurso’
);

A solução

O quinto parâmetro do request é o de variáveis $_SERVER, que é onde você terá que colocar os cabeçalhos. O problema é que por padrão o cabeçalho http precisa possuir o prefixo HTTP_ para ele ser considerado/visto no client http.

Então, se você possui dos cabeçalhos Authorization, User-Agent e X-WSSE, você simplesmente transforma eles para:  HTTP_AUTHORIZATION, HTTP_USER_AGENT e HTTP_X-WSSE.

E passa isso num array no quinto parâmetro do request:

$crawler = $this->client->request(
‘GET’,
‘/meu/recurso’,
array(),
array(),
array(‘HTTP_AUTHORIZATION’ => ‘Authorization profile=”XXX”‘,
‘HTTP_USER_AGENT’ => ‘XXX’,
‘HTTP_X-WSSE’ => ‘XXX’)

);

Onde tem XXX você irá trocar pelo valor necessário, principalmente no HTTP_X-WSSE que possui regras para geração dos dados.

Valeu!

Se gostou, compartilhe...ou não :DShare on FacebookTweet about this on TwitterShare on Google+Share on RedditShare on LinkedInEmail this to someone

AngularJS – Adicionando loading nos requests via ngResource

Se gostou, compartilhe...ou não :DShare on FacebookTweet about this on TwitterShare on Google+Share on RedditShare on LinkedInEmail this to someone

Salve,

Uma  das coisas que achei complicado de achar facilmente ,e que sinceramente não achei na documentação, é em relação a criar um aviso de loading como resposta para o usuário aguardar os carregamentos de ajax e etc. No jQuery você poderia tratar individualmente, em cada chamada aos métodos $.ajax(), $.get(), $.post(), bem como de maneira geral utilizando o conjunto de funções $.ajax*().

Hoje tive que pesquisar e testar e consegui uma solução e daí não procurei outras, que podem ou não existir e que podem ou não serem fáceis. O que eu achei e estou utilizando é o angular-busy, que depende do promise-tracker e do ngAnimate, para fazer a mágica. No exemplo da documentação do promise você ver a utilização com o $http, mas existe um unit test para usá-lo com $resource, que é o que eu realmente necessitava saber. O resto foi ver os exemplos e a documentação do angular-busy e colocar pra rodar.

Basicamente foi:

- Injetar o ngAnimate, promise-tracker e angular-busy (que o módulo chama-se cgBusy) ao módulo que você quer tratar isso;

- Injetar o promise-tracker no(s) controller(s) onde você faz os requests;

- Inicializar e adicionar o promise para trackear o request;

- No bloco onde você vai carregar os dados, ou onde você quiser que apareça o loading, coloca a diretiva cg-busy:

loadingTracker é o ID do tracker. O mesmo que você deu na inicialização no controller.

 * Leia a documentação para entender o por que de passar o loadingTracker dentro de aspas simples, já que está dentro de aspas duplas.

Você ainda pode customizar o html do loading do angular-busy criando um template e configurando ele assim:

E lá você fará sua estrutura e poderá mexer com seu css normalmente, sem problema algum.

Ainda existem outras configurações que você saberá se ler a documentação do angular-busy e do promise-tracker.

Se alguém souber de algo mais fácil e bacana que nem esse, por favor me avise :D

Se gostou, compartilhe...ou não :DShare on FacebookTweet about this on TwitterShare on Google+Share on RedditShare on LinkedInEmail this to someone

Symfony2 e JMS Serializer – Adicionando tratamento na serialização e deserialização

Se gostou, compartilhe...ou não :DShare on FacebookTweet about this on TwitterShare on Google+Share on RedditShare on LinkedInEmail this to someone

Salve,

Utilizar o JMS Serializer no Symfony2 deixa muito fácil e rápido serializar dados , tendo um dos usos a construção de API RESTful usando o FOSRestBundle.

Mas uma dúvida que eu tive, e que é corriqueiro, é transformar dados antes ou depois, em alguns casos:

- Traduzir um status para um objeto ou string, ao invés de mandar um int, antes de serializar;

- Traduzir um status de objeto ou string para a sua representação real, na fonte de dados, que geralmente é um int na deserialização;

- Adicionar um campo  extra após a serialização, com uma informação extra, como um valor total de um pedido de compra, dentre outros.

Pesquisando achei alguns artigos no Stackoverflow, um com um exemplo pífio em relação a utilização e funcionamento e um mais completo, com toda explicação, mas a documentação do JMS Serializer, bem como a maioria das libs de PHP, é uma merda para explicar de forma prática, as vezes até na teoria são ruins.

Basicamente é isso:

Criar um subscriber

 

Cadastrar ele como um serviço, passando como argumento o serviço do Pedido

 

E pronto! É só trabalhar nos métodos e outros que quiser (tem que achar na documentação) e fazer suas mágicas.

Obrigado!

Se gostou, compartilhe...ou não :DShare on FacebookTweet about this on TwitterShare on Google+Share on RedditShare on LinkedInEmail this to someone

Symfony2: adicionando sufixo em um input em um Form Type utilizando o MopaBootstrap

Se gostou, compartilhe...ou não :DShare on FacebookTweet about this on TwitterShare on Google+Share on RedditShare on LinkedInEmail this to someone

Salve,

Estou em um projeto onde utilizo o Symfony2 e entre vários componentes/bundles, tenho o MopaBootstrap e necessitei de adicionar um sufixo num campo criado via Form Type e não encontrei na documentação/README do repositório e os links que achei no repositório não tinham também ou não estavam disponíveis, com isso fuçei o repositório e achei esse arquivo:

https://github.com/phiamo/MopaBootstrapBundle/blob/master/Form/Extension/WidgetFormTypeExtension.php

Olhando o código vi os parâmetros widget_suffix e widget_addon_append, testei e consegui colocar pra aparecer a caixinha no final, com o widget_addon_append. Mas o value não aparecia, então buscando nos códigos do bundle no meu projeto, achei um código twig que monta o template do campo, e ele utilizava o parâmetro text para pegar o texto, e assim funcionou. Então exemplo prático:

Infelizmente esse componente, bem como vários outros e também muitos frameworks, sofrem do problema de des-documentação, onde tem alguma coisa documentada, mínima, mas não tem tudo, e geralmente tem-se exemplos super básico e se você quiser mudar uma posição, você não encontrará SE pode e COMO faz.

Obrigado.

 

Se gostou, compartilhe...ou não :DShare on FacebookTweet about this on TwitterShare on Google+Share on RedditShare on LinkedInEmail this to someone