Upload de arquivos e imagens no CakePHP sem plugin! Artigo

Conheça os cursos gratuitos do WebDevBr! - Inscreva-se!


Este artigo foi publicado a 3 anos, 10 meses, 4 semanas, 1 dia atrás.

Pra mim sempre pareceu trivial saber como enviar arquivos e imagens com o CakePHP, todo mundo devia saber como isso deve ser feito, mas em vez disso começamos procurando plugins que supram essa necessidade, pois bem, vou mostrar como funciona o processo de upload no CakePHP usando os Utilities Folder & File do CakePHP, aproveite para conhecer as outras funções do CakePHP.

Você pode querer criar um behavior ou um plugin, ou um plugin com um behavior, eu vou mostrar como fazer dentro do Model, mas nada impede de você migrar isso.

Como criar a função de Upload no CakePHP

Pra começar precisamos entender como funciona o upload de arquivos, toda vez que você cria um form e um campo do tipo 'file', este campo vai retornar um array desta forma:

Gostou deste artigo?

Receba atualizações semanais com novos artigos do WebDevBr e outras dicas!

this->request->data['Document']['submittedfile'] = array(
    'name' => 'conferenceschedule.pdf',
    'type' => 'application/pdf',
    'tmp_name' => '...',
    'error' => 0,
    'size' => 41737,
);

O grande problema no CakePHP é que este array gera um erro ao salvar no banco já que ao transformar os dados em SQL o CakePHP espera que aquele Array seja uma string, então precisamos cuidar para que ele remova este campo quando estiver vazio ou atualizar para o nome do arquivo (uma string como é o esperado), como fazer?

No seu Model use o callback baforeSave:

public function beforeSave($options = array())
{
    if(!empty($this->data['Model']['campoDoArquivo']['name'])) {
        $this->data['Model']['campoDoArquivo'] = $this->upload($this->data['Model']['campoDoArquivo']);
    } else {
        unset($this->data['Model']['campoDoArquivo']);
    }
}

Tá fácil né, se o campo não estiver em branco fazemos o upload e atualizamos a variável, caso contrário usamos o unset pra remover e evitar um erro no sql, mas o que a função upload faz alí na linha 4? Ela que vai organizar o upload e retornar o nome da imagem (substituindo o array que estava no campo anteriormente), vamos criar algumas funções para as diversas tarefas (mover o arquivo, criar diretórios e verificar o nome), mas primeiro vou passar a função upload.

/**
 * Organiza o upload.
 * @access public
 * @param Array $imagem
 * @param String $data
*/ 
public function upload($imagem = array(), $dir = 'img')
{
    $dir = WWW_ROOT.$dir.DS;

    if(($imagem['error']!=0) and ($imagem['size']==0)) {
        throw new NotImplementedException('Alguma coisa deu errado, o upload retornou erro '.$imagem['error'].' e tamanho '.$imagem['size']);
    }

    $this->checa_dir($dir);

    $imagem = $this->checa_nome($imagem, $dir);

    $this->move_arquivos($imagem, $dir);

    return $imagem['name'];
}

Note que ela tem dois parâmetros, o primeiro chama $imagem (é apenas um nome, você pode upar qualquer coisa), o segundo parâmetro seta o diretório que será salvo, não esqueça de usar a constante DS como separador, ou deixe em branco, ele vai enviar para img como você pode ver.

Na primeira linha ele acerta o diretório base como webroot (sim o app/webroot) juntando o diretório que você passou no parâmetro $dir, se você não passou nada ele vai salvar em app/webroot/img.

O bloco if na sequencia verifica se houve erros, se não houver ele passa adiante, caso contrário gera um erro interno.

Agora temos 3 funções, a checadir verifica se o diretório existe e cria se não existir, o checanome remove acentos, caracteres maiúsculos, troca espaços por hifens e já verifica se o arquivo existe, se existir adiciona um hífen e um número ao seu nome (tipo imagem-2.jpg, imagem-3.jpg...) e a ultima função cuida de mover os arquivos para o destino final ($dir), como estas funções devem parecer?

/**
 * Verifica se o diretório existe, se não ele cria.
 * @access public
 * @param Array $imagem
 * @param String $data
*/ 
public function checa_dir($dir)
{
    App::uses('Folder', 'Utility');
    $folder = new Folder();
    if (!is_dir($dir)){
        $folder->create($dir);
    }
}

/**
 * Verifica se o nome do arquivo já existe, se existir adiciona um numero ao nome e verifica novamente
 * @access public
 * @param Array $imagem
 * @param String $data
 * @return nome da imagem
*/ 
public function checa_nome($imagem, $dir)
{
    $imagem_info = pathinfo($dir.$imagem['name']);
    $imagem_nome = $this->trata_nome($imagem_info['filename']).'.'.$imagem_info['extension'];
    debug($imagem_nome);
    $conta = 2;
    while (file_exists($dir.$imagem_nome)) {
        $imagem_nome  = $this->trata_nome($imagem_info['filename']).'-'.$conta;
        $imagem_nome .= '.'.$imagem_info['extension'];
        $conta++;
        debug($imagem_nome);
    }
    $imagem['name'] = $imagem_nome;
    return $imagem;
}

/**
 * Trata o nome removendo espaços, acentos e caracteres em maiúsculo.
 * @access public
 * @param Array $imagem
 * @param String $data
*/ 
public function trata_nome($imagem_nome)
{
    $imagem_nome = strtolower(Inflector::slug($imagem_nome,'-'));
    return $imagem_nome;
}

/**
 * Move o arquivo para a pasta de destino.
 * @access public
 * @param Array $imagem
 * @param String $data
*/ 
public function move_arquivos($imagem, $dir)
{
    App::uses('File', 'Utility');
    $arquivo = new File($imagem['tmp_name']);
    $arquivo->copy($dir.$imagem['name']);
    $arquivo->close();
}

Eu comentei bem para vocês entenderem como funciona, ainda podemos validar o arquivo pela extensão ou mimetype e muito mais, tudo é muito simples e prático, de uma lida na documentação e incremente seu upload, quem sabe em breve eu não lance uma aula falando sobre isso mas já completei um pouco mais no artigo Usando o WideImage no CakePHP.

Uma coisa que quero que notem é que quem faz o upload realmente é a função move_arquivos usando o FileUtility.

O formulário de upload no CakePHP

Existem duas coisas que é preciso para se criar um formulário de upload no CakePHP, primeiro precisamos informar na tag form que vamos ter este recurso, e isso é feito usando enctype="multipart/form-data", no CakePHP basta usar array('type' => 'file') no FormHelper, assim:

echo $this->Form->create('Produto', array('type' => 'file'));
//ou
echo $this->Form->create('Produto', array('enctype' => 'multipart/form-data'));

E a segunda é criar o campo de upload, claro, para isso:

echo $this->Form->input('Model.campoDoArquivo', array(
    'type' => 'file'
));
//ou
echo $this->Form->file('Model.campoDoArquivo');

Como usar?

Apenas configure tudo e faça o upload, o model vai identificar sozinho o upload e enviar para a pasta que você quer.

É simples na verdade.

Se você incrementar este artigo, envie para gente ver :D.

Diferente da versão anterior funciona perfeitamente com qualquer PDF.


Cursos relacionados


* Parcelamento apenas cartão de crédito! Pode haver uma pequena variação no parcelamento em relação a simulações apresentadas!