169 lines
5.6 KiB
PHP
169 lines
5.6 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\OrderAttachment;
|
|
|
|
class AttachmentService
|
|
{
|
|
/**
|
|
* Tipos de arquivo permitidos para anexo.
|
|
* Mapeamento mime_type => extensões aceitas.
|
|
*/
|
|
private const ALLOWED_TYPES = [
|
|
'application/pdf' => 'pdf',
|
|
'image/png' => 'png',
|
|
'image/jpeg' => 'jpg',
|
|
'image/gif' => 'gif',
|
|
'application/msword' => 'doc',
|
|
'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx',
|
|
'application/vnd.ms-excel' => 'xls',
|
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx',
|
|
'text/plain' => 'txt',
|
|
];
|
|
|
|
/**
|
|
* Retorna o tamanho máximo de upload, lendo do PHP ini.
|
|
*/
|
|
private function getMaxSize(): int
|
|
{
|
|
$val = ini_get('upload_max_filesize');
|
|
if (empty($val)) return 20 * 1024 * 1024;
|
|
$val = trim($val);
|
|
$last = strtolower($val[strlen($val)-1]);
|
|
$val = (int)$val;
|
|
switch($last) {
|
|
case 'g': $val *= 1024;
|
|
case 'm': $val *= 1024;
|
|
case 'k': $val *= 1024;
|
|
}
|
|
return $val > 0 ? $val : 20 * 1024 * 1024;
|
|
}
|
|
|
|
/**
|
|
* Raiz do diretório de uploads (fora do public/).
|
|
*/
|
|
private function getBaseUploadDir(): string
|
|
{
|
|
return dirname(__DIR__, 2) . '/storage/uploads/orders';
|
|
}
|
|
|
|
/**
|
|
* Pega o caminho do diretório de upload de uma ordem específica.
|
|
*/
|
|
private function getOrderUploadDir(int $orderId): string
|
|
{
|
|
return $this->getBaseUploadDir() . '/' . $orderId;
|
|
}
|
|
|
|
/**
|
|
* Garante que o diretório de upload da ordem exista.
|
|
*/
|
|
private function ensureDirectory(string $path): void
|
|
{
|
|
if (!is_dir($path)) {
|
|
mkdir($path, 0775, true);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Processa e armazena os arquivos do campo de upload.
|
|
* Retorna a quantidade de arquivos salvos com sucesso.
|
|
*
|
|
* @param int $orderId
|
|
* @param array $filesInput Entry do $_FILES (já normalizado como array de arquivos)
|
|
* @return int
|
|
* @throws \Exception em caso de erro crítico
|
|
*/
|
|
public function storeFiles(int $orderId, array $filesInput): int
|
|
{
|
|
$uploadDir = $this->getOrderUploadDir($orderId);
|
|
$this->ensureDirectory($uploadDir);
|
|
|
|
$model = new OrderAttachment();
|
|
$saved = 0;
|
|
|
|
// Normaliza o $_FILES para iterar arquivo a arquivo
|
|
$files = $this->normalizeFilesArray($filesInput);
|
|
|
|
foreach ($files as $file) {
|
|
// Ignora entradas vazias (campo não preenchido)
|
|
if ($file['error'] === UPLOAD_ERR_NO_FILE) {
|
|
continue;
|
|
}
|
|
|
|
if ($file['error'] !== UPLOAD_ERR_OK) {
|
|
throw new \Exception("Erro no upload do arquivo '{$file['name']}': código {$file['error']}");
|
|
}
|
|
|
|
$maxSize = $this->getMaxSize();
|
|
if ($file['size'] > $maxSize) {
|
|
$maxSizeMB = floor($maxSize / (1024 * 1024));
|
|
throw new \Exception("O arquivo '{$file['name']}' excede o tamanho máximo permitido de {$maxSizeMB} MB.");
|
|
}
|
|
|
|
// Detecta o tipo MIME real do arquivo (não confiar só no header HTTP)
|
|
$finfo = new \finfo(FILEINFO_MIME_TYPE);
|
|
$mimeType = $finfo->file($file['tmp_name']);
|
|
|
|
if (!array_key_exists($mimeType, self::ALLOWED_TYPES)) {
|
|
throw new \Exception("Tipo de arquivo não permitido: '{$file['name']}' ({$mimeType}).");
|
|
}
|
|
|
|
$extension = self::ALLOWED_TYPES[$mimeType];
|
|
$storedName = uniqid('attach_', true) . '.' . $extension;
|
|
$destination = $uploadDir . '/' . $storedName;
|
|
|
|
if (!move_uploaded_file($file['tmp_name'], $destination)) {
|
|
throw new \Exception("Falha ao mover o arquivo '{$file['name']}' para o servidor.");
|
|
}
|
|
|
|
$model->create([
|
|
'order_id' => $orderId,
|
|
'original_name' => $file['name'],
|
|
'stored_name' => $storedName,
|
|
'mime_type' => $mimeType,
|
|
'size' => $file['size'],
|
|
]);
|
|
|
|
$saved++;
|
|
}
|
|
|
|
return $saved;
|
|
}
|
|
|
|
/**
|
|
* Retorna o caminho absoluto do arquivo no disco.
|
|
*/
|
|
public function getFilePath(int $orderId, string $storedName): string
|
|
{
|
|
return $this->getOrderUploadDir($orderId) . '/' . $storedName;
|
|
}
|
|
|
|
/**
|
|
* Normaliza o array $_FILES para uma lista linear de arquivos,
|
|
* independente de ser upload único ou múltiplo (name[]).
|
|
*/
|
|
private function normalizeFilesArray(array $files): array
|
|
{
|
|
// Se é um único arquivo, encapsula em array
|
|
if (!is_array($files['name'])) {
|
|
return [$files];
|
|
}
|
|
|
|
$normalized = [];
|
|
$count = count($files['name']);
|
|
for ($i = 0; $i < $count; $i++) {
|
|
$normalized[] = [
|
|
'name' => $files['name'][$i],
|
|
'type' => $files['type'][$i],
|
|
'tmp_name' => $files['tmp_name'][$i],
|
|
'error' => $files['error'][$i],
|
|
'size' => $files['size'][$i],
|
|
];
|
|
}
|
|
|
|
return $normalized;
|
|
}
|
|
}
|