El componente de archivos utiliza Livewire, blade y alpine js, y hace uso de 3 archivos principales:
subir-archivo.blade.php es un componente de blade y se usa como vista general de los archivos.input-file.blade.php es la vista del componente de livewire, especificamente la parte donde se realiza la carga del archivo.input-file.php es donde se encuentra el código del componente.keyTracker: Es el id del archivo requerido que se debe de subir.
owner: Es el tipo del modelo al que pertenece el archivo a subir (empresa, alumno, etc).
tipo: Es para indicar de qué tipo es lo que se va a subir (archivo).
requerido: Es una instacia de App\Models\ArchivoRequerido que hace referencia al archivo que se debe de subir.
archivo: Es la instancia del archivo cargado, por defecto este está en null hasta que se suba el archivo o se le pase como parámetro una instancia de App\Models\Archivo.
class: Son las clases que se pasan al componente de blade para su visualización.
validar: Indica si el archivo puede o no ser validado, por defecto se pasa como true.
permisoValidarArchivo: Es el permiso que debe de tener el rol para validar el archivo.
En el componente de blade hay una variable de alpine que se llama existeArchivo que comprueba si hay o no un archivo cargado (true o false), si lo hay en la vista aparece el icono del tipo de archivo, información sobre este y las acciones que se pueden realizar.
Si no hay archivos cargados entonces se carga la vista con un icono en gris y aparece un botón con el texto "Subir Archivo".
Al dar clic en el botón se abre el explorador de archivos para que se elija el archivo a subir.
Mientras el archivo es cargado, se mostrará una barra de progreso, determinada por el siguiente código:
<div class="mt-4" x-data="{ progress: 0, isUploading: false }"
x-on:livewire-upload-start="isUploading = true"
x-on:livewire-upload-finish="isUploading = false"
x-on:livewire-upload-error="isUploading = false"
x-on:livewire-upload-progress="progress = $event. detail.progress">
<!-- Progress Bar -->
<div class="progress h-4 mt-3 mb-3" x-show="isUploading">
<div class="progress-bar w-3/4" role="progressbar"
aria-valuenow="75" aria-valuemin="0"
aria-valuemax="100" :style="{ width: progress + '%' }">
</div>
</div>
En el bloque anterior se hace uso de los eventos javascript que emite livewire al subir un archivo, este evento se emite sobre el <input> donde se realiza la acción de subir un archivo y en el <div> del progress bar se utilizan esos valores del evento para realizar la animación.
Una vez que se carga el archivo se guarda en una variable llamada contenidoArchivo y se actuliza la variable de alpine existeArchivo a true.
A su vez, en el componente se tiene un listener que realiza las validaciones para comprobar que el archivo es válido, para esto se usa un array con las siguientes validaciones:
public $rules = ['file', 'mimes:png,jpg,pdf', 'max:1024'];
public function updatedContenidoArchivo()
{
$this->validate(
[
'contenidoArchivo' => $this->rules, //1mb max
],
[
'contenidoArchivo.max' =>
'El :attribute excede el tamaño permitido. Máximo permitido:
:max kilobytes',
'contenidoArchivo.mimes' => 'El
:attribute debe de ser PDF, JPG o PNG.',
],
[
'contenidoArchivo' => 'archivo'
]
);
if ($this->saveOnUpload)
{
$this->guardararchivo();
}
}
Si el archivo es válido entonces entra a la función de guardararchivo(), donde se crea la ruta y la instancia del modelo Archivo para después guardarlo en la base de datos, por último se emite un evento al navegador y con alpine se escucha ese evento y las variables se modifican de acuerdo a lo que se mandó al momento de emitir el evento.
El código para guardar el archivo es el siguiente:
public function guardararchivo ()
{
$hash = $this->contenidoArchivo->
store(class_basename($this->owner) . '/' . $this->owner->id);
$this->archivo = new archivo([
'owner_type' => 'App\Models\' . class_basename($this->owner),
'owner_id' => $this->owner->id,
'archivo_requerido_id' => $this->keyTracker,
'nombre' => $this->contenidoArchivo->getClientOriginalName(),
'extension' =>
$this->contenidoArchivo->getClientOriginalExtension(),
'hash' => $hash,
'mime' => $this->contenidoArchivo->getMimeType(),
'estatus' => 'Cargado',
'comentario' => null,
]);
$this->archivo->save();
$this->emit('archivoGuardado', $this->keyTracker,
$this->archivo->id, $this->archivo);
$this->dispatchBrowserEvent('archivo-guardado', [
'keyTracker' => $this->keyTracker,
'archivo_id' => $this->archivo->id,
'nombre_archivo' => $this->archivo->nombre,
'estatus' => $this->archivo->estatus,
'mime' => $this->archivo->mime,
'hash' => route('archivo-show',
[base64_encode($this->archivo->hash)]),
]);
}
Si hay un archivo cargado entonces se habilitan los botones de "descargar", "visualizar" y "eliminar".
Con el botón de visualizar se redirecciona a una ruta en la que se pasa el hash del archivo cargado y este es recibido en un controlador, este redireccionamiento se realiza con una variable de alpine llamada hash.
Y en el controlador se retorna un response para visualizar el archivo en otra ventana dentro del navegador.
El código encargado de la visualización es el siguiente:
public function show($hash)
{
/* dd($hash); */
$archivo = Archivo::where('hash', base64_decode($hash))->first();
if(!$archivo){
abort(404);
}
$response = Response::make(Storage::get($archivo->hash), 200,
['Content-Type' => $archivo->mime]);
return $response;
}
Para la descarga del archivo cargado se realiza un evento al componente de livewire input-file donde se le manda como atributo el keyTracker (id del archivo requerido), dentro del componente se tiene un listener que redirecciona a una función donde está el código para descargar el archivo.
El código sería el siguiente:
public function downloadArchivo()
{
if (isset($this->archivo)) {
return response()->download(Storage::path
($this->archivo->hash), $this->archivo->nombre,
['Content-Type' => $this->archivo->mime]);
}
}
Para eliminar un archivo se hace un evento al componente de input-file donde se le pasa el keyTracker y el listener correspondiente redirecciona a deleteArchivo() el cual emite un evento a otro componente de livewire llamado openModal que se usa para abrir un modal de confirmación para eliminar, dentro de este componente se tiene el listener que redirecciona a deleteArchivo() y este emite un evento de regreso al componente input-file y es aquí donde el listener lo redirecciona a destroyArchivo() que tiene el código para eliminar.
Estos son los 3 bloques de código que se utilizan para eliminar:
/* Listener para emitir el actividad de abrir el modal */
public function deleteArchivo()
{
$this->emit('openModal', 'modal.delete', ['deleteArchivo', Archivo::class, $this->archivo, ['convenios.destroy'], 'eliminarDoc', $this->keyTracker]);
}
/* Listener del componente del modal */
public function deleteArchivo()
{
$this->hasPermission();
$this->emitTo('input-file','eliminarDoc' . ':' . $this->keyTracker);
$this->closeModal();
$this->resetVariables();
}
/* Listener de cuando se cierra el modal y código para eliminar */
public function destroyArchivo()
{
$this->archivo->delete();
Storage::delete($this->archivo->hash);
$this->resetInput();
$this->dispatchBrowserEvent('archivo-eliminado', [
'keyTracker' => $this->keyTracker,
'owner_type' => class_basename($this->owner),
'owner_id' => $this->owner->id,
]);
}
Para cambiar el estatus de un archivo se emite un evento al componente de livewire input-file en el que se tiene un listener que redirecciona a estadoAceptadoArchivo() o estadoRechazadoArchivo() dependiendo de si se rechaza o acepta en ambas funciones está el código correspondiente para actualizar el estado del archivo, luego de actualizar el estado se emite un evento de javascript al navegador el cual es recibido en el componente de blade y este actualiza la variables de alpine.
Los botones para cambiar el estatus sólo aparecen si el usuario autenticado tiene el permiso que se pasa como argumento al llamar al componente de blade.
El código es sería el siguiente:
public function estadoAceptadoArchivo()
{
$this->archivo->estatus = 'Aceptado';
$this->archivo->save();
$this->dispatchBrowserEvent('archivo-guardado', [
'keyTracker' => $this->keyTracker,
'archivo_id' => $this->archivo->id,
'nombre_archivo' => $this->archivo->nombre,
'estatus' => $this->archivo->estatus,
'mime' => $this->archivo->mime,
'hash' => route('archivo-show', [base64_encode($this->archivo->hash)]),
]);
}
Para usar el componente se manda a llamar al componente de blade pasandole los parámetros correspondientes, un ejemplo sería el siguiente:
En este ejemplo se está mandando a llamar desde una vista a la cual desde un controlador se le pasó un array con los archivos requeridos y por cada archivo se va a crear una card con las acciones e información del archivo a subir o si ya hay uno muestra la información de este.
<div class="grid grid-cols-12 gap-y-7">
@foreach ($archivoRequerido as $doc)
<x-subir-archivo keyTracker="{{ $doc->id }}"
:owner="Auth::user()"
tipo="archivo"
:requerido="$doc"
:validar='true'
:archivo="Auth::user()
->archivos()
->where('archivo_requerido_id', $doc->id)
->first()"
class='col-span-12 sm:col-span-6 2xl:col-span-2 p-2'
permisoValidarArchivo="archivos-empresa.validar" />
@endforeach
</div>
archivos requeridos: El componente se manda a llamar desde una vista en la que se le pasó un array con los archivos requeridos y por cada archivo requerido se crea la card correspondiente para las acciones del archivo.
keyTracker: Se pasa el id del archivo requerido.
owner: Se pasa el usuario autenticado y en este caso sería empresa.
tipo: Se indica que será un archivo.
requerido: Se pasa la instancia del archivo requerido.
archivo: Se pasa la instancia del archivo que tiene el usuario autenticado, si no tiene por defecto es null.
class: Es la clase para que sea responsivo.
validar: Se pasa como true, de que el archivo puede ser
validado.
permisoValidarArchivo: Es el permiso que debe tener el rol para poder validar los archivos.