Dica: salvando vários registros de um mesmo model de uma só vez (dentro de um loop)

Olá pessoal,

esta dica é bem valiosa e foi mais um problema enfrentado durante a programação do meu projeto para a faculdade. No meu projeto o usuário pode enviar N imagens para o sistema de uma só vez. N imagens porque o campo file de envio é criado com JS de acordo com a necessidade e vontade do usuário.

A primeira coisa que pensamos é fazer o seguinte:

PHP:
  1. foreach($imagens as $imagem){
  2. $img["Imagem"]["nome"] = $imagem;
  3. $this->Imagem->save($img);
  4.  
  5. }

Legal, logicamente parece estar correto. Mas o que o Cake faz? Isso mesmo, ele cria um registro e os outros ele altera esse primeiro. Por quê? Porque a chave primária está preenchida.

A solução é bem simples, basta fazer isso:

PHP:
  1. foreach($imagens as $imagem){
  2. $img["Imagem"]["nome"] = $imagem;
  3.  
  4. $this->Imagem->id = null;
  5. $this->Imagem->save($img);
  6.  
  7. }

Setar o id da imagem (ou do model) como null. Este id após alguma inserção fica preenchido com o código autoincremento do último save. Sendo que o Cake dá um insert quando o mesmo é null e um update caso contrário.

Bom é isso...

Qualquer dúvida, comentem!

Abraços e até a próxima...

16 Comentários »

  1. Gustavo Carreno said,

    Outubro 19, 2007 @ 18:59

    Boas Tulio,

    Tem sempre o $var = $this->model-create(); né ?

    Um abraço,
    Gustavo Carreno

  2. max said,

    Novembro 3, 2007 @ 14:15

    muito bom seu site. estou estudando cakephp graças a vc. esssa dica entao.
    "Sendo que o Cake dá um insert quando o mesmo é null e um update caso contrário." uma informação muito importante.
    gostaria de saber uma coisa:
    o cake tem alguma biblioteca nativa de tratar imagem?
    como eu posso fazer pra diminuir o tamanho de uma imagem , por exemplo, antes de inserir no banco?

    abrigado

  3. Sadjow Medeiros Leão said,

    Novembro 14, 2007 @ 17:41

    Túlio você poderia disponibilizar o html do formulário ?

    Obrigado!

  4. Sadjow Medeiros Leão said,

    Novembro 14, 2007 @ 17:42

    e se no banco tiver um lengenda para cada imagem na hora de cadastrar ?

    Como séria?

  5. Tulio Faria said,

    Novembro 17, 2007 @ 15:48

    Gustave,

    é uma saída também :)

    Max,

    o cake infelizmente não possui... Eu estou começando com o projeto: CakePHP ext: http://code.google.com/p/cakephp-ext/

    Lá estou colocando os meus components, helpers, e etc... Por enquanto só está o JAX (helper para ajax), em breve publicarei o de tratamento de imagens e upload.

    Sadjow,

    seria algo como:

    PHP:
    1. $img["Imagem"]["legenda"] = "legenda legal";

    dentro do loop...

    Abraços a todos...

  6. Tykoth said,

    Dezembro 3, 2007 @ 08:29

    Opa, tudo certo Tulio?
    Fiz uma função bacana baseada na sua solução, com direito a associação :)
    ( ps: espero que aqui aceite highlight de comentários de fora hehe )

    Salvei no app_model.php

    PHP:
    1. function multisave($multidata = null, $key = null, $keyval = null)
    2.     {
    3.         // Verifica se o mesmo é um array, e se não está vazio
    4.         if(!is_array($multidata) || empty($multidata)) return false;
    5.        
    6.         // Verifica se existe a chave com nome do Model
    7.         if(key_exists($this->name,$multidata)) $multidata = $multidata[$this->name];
    8.  
    9.         foreach($multidata as $data)
    10.         {
    11.             if($key !== null && $keyval !== null) $data[$key] = $keyval;
    12.             $this->create();
    13.             if(!$this->save($data)) return false;
    14.         }
    15.         return true;
    16.     }

    Está um pouco genérica ainda essa função, estou utilizando-a assim:

    PHP:
    1. if ($this->Client->save($this->data))
    2. {
    3.     $this->Client->Email->multisave($this->data, 'client_id', $this->Client->id);
    4. }

    Onde $this->data contem "Client" => dados e "Email" => indice => dados

    Bom, caso tenha alguma sugestão sobre essa solução, me deixa um recado lá no meu blog hehehe.

    E aproveitando o espaço, gostaria de saber se há possibilidade de um desenvolvimento em equipe no cakephp-ext, pois tenho muitas idéias para botar para fora!

    Abraços

  7. Tykoth said,

    Dezembro 3, 2007 @ 08:32

    ps2: poxa, fiquei triste! coloquei o código html certinho do syntax hilite e não ficou :(

  8. Tulio Faria said,

    Dezembro 3, 2007 @ 17:29

    Tykoth,

    legal seu código, irei testá-lo depois...

    Sobre o Cakephp-Ext, entre lá e inscreva-se como desenvolvedor que te libero para adicionar conteúdo...

    Bom saber que tem pessoas querendo ampliar as possibilidades deste incrível framework...

    Abraços,

  9. Tykoth said,

    Dezembro 4, 2007 @ 13:51

    Opa, valeu por embeleza o código aqui :)
    Então, fui lá e cai nessa pagina:
    http://code.google.com/support/bin/answer.py?answer=56534&topic=10382

    Acho que você teria de colocar meu e-mail na lista de developers. Estou empolgado para colocar algumas idéias lá!

    Abraços

  10. Tulio Faria said,

    Dezembro 4, 2007 @ 21:55

    Opa,
    já te adicionei lá... abraços...

  11. Vitor said,

    Fevereiro 5, 2008 @ 12:12

    Olá Tulio,

    Preciso de classificar um post com várias categorias de uma só vez... dessa forma preciso inserir no meu banco de dados 'categorias_posts' mais de um dado em apenas 1 click no submit buttom.
    Esse código do post resolve meu caso? Estou aprendendo agora a programar e comecei com Cake PHP. =´)

    Mandei um e-mail para seu e-mail que aparece nos videos-tutoriais pedindo um help.
    Obrigado pela atencao e pelos tutoriais que MUITO me ajudam a aprender Cake.

    Abracos,

  12. Tulio Faria said,

    Fevereiro 5, 2008 @ 16:17

    Olá Vitor,

    há um outro vídeo tutorial aqui no blog que pode lhe ajudar, salvando relacionamentos muitos para muitos, o famoso hasAndBelongsToMany.

    Abraços,

  13. Fernando said,

    Fevereiro 6, 2008 @ 10:01

    Olá Túlio,
    Estou com uma dúvida relacionada a alguns passos anteriores ao início desse post.
    Tenho um formulário de cadastro de produtos onde o usuário via javascript pode adicionar quantos produtos ele quiser. É uma tabela, onde cada linha corresponde a um produto com vários "inputs" para cada produto. AO final da tabela, tem um botão "Adicionar produto" cujo evento OnClick duplica uma linha padrão da tabela.

    Estou usando o Helper do cake para gerar os "inputs":
    $html->input('Produto/descricao', array('size' => '50'))
    Aí, no meu controle, gostaria que viesse uma matriz com cada linha correspondente a cada produto que o usuário cadastrou, para realizar o foreach e percorrer a matriz salvando cada registro.(aqui chegamos no seu post)
    Normalmente (codificando na mão), para cada linha de produto, o nome dos inputs eu adicionaria colchetes [] para quando passar os dados via post, enviar uma matriz.
    Mas no meu caso, o helper do cake está gerando um input assim:

    Mas assim, cada nova linha da tabela sobrescreve a última e não encontrei maneira de adicionar [] aos "names" dos "inputs".
    Então como posso fazer isso usando o helper $html->input e outros helpers do Cake?

    Valeu!

  14. Fernando said,

    Fevereiro 14, 2008 @ 08:48

    Pois é....acabei não usando os helpers do Cake e escrevendo o HTML na mão.
    Para poder recuperar no controller os produtos de uma nota com foreach, defini o nome de cada campo do produto assim:
    data[Produto][1][descricao]

    Aí, no controle consigo fazer um loop assim:

    foreach($this->data['Produto'] as $produto) {
    echo $produto['descricao'];

    De qualquer forma, o esquema de salvar vários registros de um mesmo model de uma só vez me ajudou muito nessa.

    Mas agora to com uma baita dúvida. No meu sistema, preciso cadastrar Notas Fiscais. Cada Nota pode ter 1 ou muitos produtos.

    Tenho uma tabela notas, outra produtos e outra notas_produtos para o relacionamento N-N.

    Então, no modelo nota.php:

    var $hasAndBelongsToMany = array('Produto' =>
    array('className' => 'Produto',
    'joinTable' => 'notas_produtos',
    'foreignKey' => 'nota_id',
    'associationForeignKey'=> 'produto_id',
    'conditions' => '',
    'order' => '',
    'limit' => '',
    'uniq' => true,
    'finderQuery' => '',
    'deleteQuery' => '',
    )
    );

    Só que tem um detalhe. Um produto tem as informações dele como descrição, unidade, peso, preço, etc. Mas em uma Nota, eu posso comprar X unidades de um produto. Em qual tabela irei gravar os dados específicos de cada nota fiscal em relação à quantidade de produtos de uma nota?

    Naturalmente, eu gravaria isso na tabela notas_produtos, pois é a tabela que relaciona os produtos de uma nota. Mas como faço para gravar isso pelo Cake, no meu notas_controller.php?

  15. Jânio said,

    Fevereiro 21, 2008 @ 01:59

    Tentei fazer como o exemplo que vc fez , porem não tive sucesso, ele só grava a ultima linha!!! aff

    tenho um formulario para varios inserts na mesma tabela ex;

    Setor1 / setor 2 / nivel relacionamento
    financeiro / recursos / 5
    financeiro / administrativo / 4
    financeito / crediario / 2

    Mas só grava a ultima linha, tentei da um ptrint_r($data) mas só imprime a palavra array. me Ajude por favor!!!

  16. Tulio Faria said,

    Fevereiro 22, 2008 @ 10:48

    Fernando,

    seu problema se resolve mais facilmente criando uma classe (Model) intermediário, chamada de classe associativa. No seu caso um model NotaProduto. Dizem que no cake 1.2 tem uma outra maneira de fazer isso usando uma associação chamada with (senão me engano), mas ainda não a testei.

    Janio,

    Verifique se os inputs do formulário não estejam com os mesmos nomes, se sim, só irá o último mesmo.

    Abraços a todos,

RSS feed for comments on this post · URI do TrackBack

Deixe seu comentário