Curso Laravel Inertia - Upload o carga de archivos, cambios en el controlador
En este capítulo, vamos a conocer cómo funciona el upload o carga de archivos en Laravel Inertia; como puedes suponer, todo lo que tiene que ver con el backend en Laravel, se mantiene exactamente igual, y es en el cliente, que tenemos los cambios para poder usar la integración de la carga de archivos desde Inertia.
Desde la vista de Edit.vue
Inicialmente, vamos a manejar la carga de archivos, específicamente la carga de imágenes de las publicaciones, de manera independiente al formulario que ya tenemos para la edición; usaremos el de Save.vue colocando un condicional solamente para editar:
app/Http/Controllers/Dashboard/PostController.php
public function edit(Post $post)
{
$categories = Category::get();
return inertia("Dashboard/Post/Edit", compact('post', 'categories'));
}
Vamos a crear el disco donde se van a cargar las imágenes de las publicaciones, que serían en la carpeta public:
config/filesystems.php
'public_upload' => [
'driver' => 'local',
'root' => public_path(),
],
El controlador, para la carga de imágenes es lo normal, aplicar validaciones:
$request->validate(
[
'image' => 'required|mimes:jpg,jpeg,png,gif|max:10240'
]
);
Eliminar la imagen anterior (en caso de que exista):
Storage::disk("public_upload")->delete("image/post/".$post->image);
Generar un nombre aleatorio de la imagen:
time()."." .$request['image']->extension();
Mover a la carpeta public:
$request->image->move(public_path("image/post"),$filename);
Y actualizar el post:
$post->update($data);
Finalmente, tenemos:
app/Http/Controllers/Dashboard/PostController.php
***
use Illuminate\Support\Facades\Storage;
***
public function upload(Request $request, Post $post)
{
$request->validate(
[
'image' => 'required|mimes:jpg,jpeg,png,gif|max:10240'
]
);
Storage::disk("public_upload")->delete("image/post/" . $post->image);
$data['image'] = $filename = time() . "." . $request['image']->extension();
$request->image->move(public_path("image/post"), $filename);
$post->update($data);
return to_route('post.index')->with('message', "Upload image to post successfully");
}
Creamos la ruta:
routes/web.php
Route::group(['middleware' => [
'auth:sanctum',
'prefix' => 'dashboard',
config('jetstream.auth_session'),
'verified',
]], function () {
Route::resource('/category', App\Http\Controllers\Dashboard\CategoryController::class);
Route::resource('/post', App\Http\Controllers\Dashboard\PostController::class);
Route::post('/post/upload/{post}', [App\Http\Controllers\Dashboard\PostController::class,'upload'])->name('post.upload');
});
En el formulario de editar, definimos un prop para la imagen:
const form = useForm({
// ***
category_id: props.post.category_id,
image: "",
});
Y ahora, vamos a colocar un bloque adicional, para manejar la carga de archivos, como se comentó antes, la carga de la imagen, se realizará de manera independiente del formulario de actualización:
resources/js/Pages/Dashboard/Post/Save.vue
<div v-if="post.id != ''">
<div class="container">
<div class="card">
<div class="card-body">
<div class="grid grid-cols-2 gap-2">
<div class="col-span-6">
<InputLabel value="Image" />
<TextInput type="file" @input="form.image = $event.target.files[0]" />
<InputError :message="errors.image" class="mt-2" />
<PrimaryButton @click="upload">Upload</PrimaryButton>
</div>
</div>
</div>
</div>
</div>
</div>
</AppLayout>
***
setup(props) {
const form = useForm({
// ***
image: "",
});
function submit() {
router.put(route("post.update", form.id), form);
}
function upload() {
router.post(route("post.upload", form.id), form);
}
return { form, submit, upload };
},
El código anterior, es sencillo, un campo de tipo file con su botón, que llama a una función llamada upload() en la cual enviamos el formulario, tal cual hacemos cuando se actualiza un registro; es importante notar que, para usar la carga de archivos, debe ser una petición de tipo POST; si quieres usar la carga de archivos en otro tipo de peticiones como DELETE, PATCH o PUT, debes de usar la petición de tipo POST y especificar el método en cuestión (esto lo veremos más adelante):
router.post(route(***), {
_method: "put",
// tus datos
},
Con esto, tendremos la carga de archivos completamente funcional.
Transcripción del vídeo
El siguiente paso que vamos a ver es cómo realizar el proceso upload que viene siendo lo que vamos a tratar en esta sección, Entonces por aquí te muestro un poco qué es lo que vamos a hacer como toda en la vida, tienes muchas formas de implementarlo yo te voy a mostrar la que yo implemento ya que yo siempre coloco lo como un paso opcional por lo tanto tengo que hacer algunas implementaciones o vamos a hacer unas implementaciones en base a eso entonces entre esta primera parte de opcional sería que el registro tiene que existir para poder habilitar lo que es el upload aunque esto se va a ver más claramente cuando estemos implementando los cambios en la parte de edit Mientras tanto por aquí vamos a como quiero subir los recursos al path, tenemos que crear un Driver que indique que sea local es decir local al proyecto Recuerda que podemos emplear aquí lo que sería servicios externos como el de son web server Entonces por aquí se colocaría las configuraciones pero está el ser local colocamos local y la ubicación es el pipat es decir la carpeta public Y a partir de ahí, colocamos un l las carpetas que queramos pero esta viene siendo como quien dice la raíz una vez configurado esto creamos lo que sería la validación en este caso lo estoy haciendo mediante el objeto request directamente en el controlador porque es otra vez opcional y no lo voy a colocar como parte obligatoria del proceso como siempre tenemos que eliminar la imagen anterior para evitar guardar basura y llenar ahí nuestro servidor sin eh de recursos que no vamos a emplear entonces es por eso que antes de cargar buscamos si el post tiene una imagen y le eliminamos aquí le puedes colocar cualquier nombre también puedes conservar el de local de la imagen natural pero como puede repetirse para evitar problemas yo le coloco un nombre único y en este caso empleamos la función de Time para que nos genere un nombre único en base al tiempo. Recuerda que esto nos da el tiempo en milisegundos desde 1900 y algo entonces siempre es único luego también movemos en la lo que es la imagen que vamos a cargar una vez eliminada la anterior Recuerda que esta es la anterior la que tiene el post ahorita ve el código completo igual la movemos hacia su ubicación ya Que de momento se encuentra en memoria o en una ubicación temporal en nuestra aplicación entonces la movemos a su ubicación que otra vez viene siendo path y la carpeta image y post por dar un ejemplo:
$request->image->move(public_path("image/post"), $filename);
Aunque aquí puedes colocar cualquier otra finalmente actualizamos el post y eso sería todo y aquí te muestra el proceso que es el que vamos a implementar por aquí creamos la ruta y luego vemos la parte del cliente es decir el componente Vue pero ya con esto tenemos trabajito Entonces vamos a comenzar por acá voy a abrir Aquí voy a implementarlo aquí en el dep tal cual pudiste ver entonces vamos a crear un método llamado unat coloco aquí Pit Bueno ya colocar aquí F coloco loat bien ya lo tenemos aquí obviamente recibimos lo que es el request colocamos request request Recuerda que el request aunque no lo empleamos así directamente acá pero ya se encuentra definido por defecto deberías de tenerlo bueno como que no está creo que lo eliminé control espacio illuminate http request viene siendo ese bien ya a partir de aquí lo que vamos a hacer sería crear una validación que sea aquí local para request para eso aquí empleamos el método de validate y por aquí indicamos el nombre del parámetro al igual que siempre en este caso lo voy a llamar como image ya que vamos a cargar es una imagen y aquí tú colocas lo que tú quieras ya que otra vez aunque es opcional si vamos a entrar en este controlador obviamente queremos emplear aquí el recurso de la imagen y es por eso que la coloco aquí como obligatoria ya que otra vez opcional es llamar al método pero una vez estado en el método ya sería obligatoria tiene toda la lógica hay que colocamos los mimes coloca los que tú quieras voy a colocar los más comunes jpg jpeg png gif y esto sería todo por acá y un máximo de 1024 kb coloco aquí dos puntos y eso sería todo:
$request->validate(
[
'image' => 'required|mimes:jpg,jpeg,png,gif|max:10240'
]
);
Entonces por aquí coloco el punto y coma por aquí abajo y ahora el siguiente paso es Acceder al disco voy a buscar aquí el de System System y voy a crear aquí el de Path este me sirve clono aquí coloco aquí public, el nombre que tú prefieras creo que fue el nombre que le destiné Aunque otra vez puede ser personalizable el Driver va a ser local quito esto y aquí voy a colocar que el Root es decir la raíz va a ser el public:
'public_upload' => [
'driver' => 'local',
'root' => public_path(),
],
Esto es para que podamos emplear la función de disco para y todo eso Entonces es mejor hacerlo así ya esto también es del Laravel básico Bueno aquí también había uno similar aquí ya lo tenemos Entonces ya podemos cerrar acá y continuar Entonces el siguiente paso sería Acceder al disco colocamos aquí dis perdón Creo que aquí arriba también me faltó el post el post que vamos a editar coloco aquí coma post Sorry por esto porque otra vez el post tiene que existir así también lo puedes adapar más fácil si también lo quieres emplear en crear bien Vamos a Acceder al disco de public que creamos antes Y a partir de Aquí vamos a eliminar la imagen anterior de la ubicación de image que va a ser la que vamos a utilizar es decir aquí van a estar nuestras imágenes entonces aquí coloco punto dólar post image para que acceda a la imagen que tenemos ahí:
Storage::disk("public_upload")->delete("image/post/".$post->image);
en caso de que exista si no existe no pasa nada no elimina nada pero si existe la va Eliminar esta sería otra vez la imagen anterior ya que fíjate que simplemente la estamos referenciando entonces aquí importamos este que sería illuminate Support store ahí lo tenemos y aquí eliminaríamos imagen otra vez bien Cuál es el siguiente paso ya trabajar con la Data o la imagen que tenemos en el request Así que voy a empezar aquí a componerla en un objeto Data para que luego lo podamos actualizar fácilmente voy a agarrar aquí el name o mejor dicho Esto va a ser también igual a name para poder manipularlo que va a ser igual a Time:
time()."." .$request['image']->extension();
Recuerda que estos son dos igualdades es decir es como colocar Data igual a fill name y luego lo puedes evaluar a esto puedes hacer todo esto en un mismo paso colocamos importantísimo el punto para colocar la extensión colocamos aquí punto request imagen y extension tensión Ahí va y aquí tenemos el nombre de la imagen en la función de Time no la quiere encontrar Perdón es time sin esto ahí está el siguiente paso ya es actualizar aquí colocamos post update y colocamos aquí la Data:
$post->update($data);
Estoy viendo que esto no es necesario entonces voy a quitarlo no vamos a ampliar nada del F name y esto ya sería todo por acá ya lo siguiente sería hacer alguna redirección to Road y podemos ir al post index por ejemplo post punto Index le pasamos el mensaje with recordemos que se llama aquí como message indicamos eh carga exitosa y esto sería prácticamente todo aunque aquí estoy viendo que no faltó disculpa no faltó Aquí también mover la imagen así que aquí colocamos request image
$request->image->move(public_path("image/post"),$filename);
Aquí voy a colocar el de move y por aquí va a colocar el path otra vez colocamos el de image post es lo mismo que acceder así lo que pasa que esto es para manipular el disco y est para acceder ya el path que es la ubicación de nuestra de nuestro disco y a la carpeta image post en particular y esto nos devuelve la ruta y por aquí Bueno aquí puedes conservar el name si quieres o le colocas esto directamente yo creo que lo voy a volver a colocar F name para que quede más limpio Me faltó la igualdad y aquí lo coloco name aquí es donde faltaba bien esto sería todo entonces eliminamos la anterior generamos el nuevo nombre de la imagen y con esto la movemos a esta ubicación a la ubicación final donde vamos a almacenar nuestra imagen y finalmente aquí guardamos la imagen o el nombre de la imagen en la base de datos Falta ahora crear la ruta Así que abrimos aquí el de web vamos a agarrar uno de estos vo a colocarle que la petición sea de tipo post aquí va a ser post un y le pasamos el parámetro de pos y por aquí tenemos que colocar esto en un array para poder colocar el nombre del método colocamos esto y aquí el método se llama como lo y podemos darle aquí un nombre que sea el de y esto ya sería para Prácticamente todo ya con esto creamos el esquema en el servidor es decir el Laravel faltaría el cliente pero para no alargar esto demás vamos a dejarlo para la siguiente clase Así que vamos allá.
Transcripción del Video
Ahora vamos a completar el proceso upload, aquí en el cliente en la de edit bueno en este caso sería la de edit si es la que si no emple el esquema que presentamos Antes aunque Aquí voy a colocarle la de save porque es la que tenemos fusionada Entonces te muestra un poco el proceso vuelvo acá arriba hicimos todo esto faltaría por aquí en el cliente creamos un nuevo campo dentro del formulario y por aquí Aunque parezca mucho html esto es para organizarlo todo este bloque y este por aquí colocamos el Label aquí el input Bueno
<TextInput type="file" @input="form.image = $event.target.files[0]" />
<InputError :message="errors.image" class="mt-2" />
<PrimaryButton @click="upload">Upload</PrimaryButton>
Aquí recuerda que es otra sintaxis le Estoy actualizando pero aquí es un poco lo mismo aquí en el input lo que es el valor vamos a colocarle mediante este objeto accedemos al evento del usuario el evento en este caso click ya que es de tipo input y accedemos al target y finalmente aquí a un archivo ya que nos interesa es solamente un archivo es un poquito rad la sintaxis pero ya ahorita la vamos a ver cómo funciona qué es lo que vamos a recibir pero aquí Lo importante es que notes que le estamos enviando es un archivo mediante fields en la posición cero ya que podemos pasarle varios pero en este ejercicio sería solamente uno Mostrar los errores de formulario y Bueno finalmente aquí el botón de click para activar el proceso upload que es hacer una petición de tipo post según lo que hicimos anteriormente pasándole la Data correspondiente:
<PrimaryButton @click="upload">Upload</PrimaryButton>
Aquí también en caso de que sea necesario para ti puedes también hacer peticiones o enviar archivos en peticiones de tipo delete Patch y put y para eso aquí recuerda colocar el método al igual que ocurría con laravel cuando quería simular otro tipo de petición dentro de un formulario Bueno por lo demás vamos a tener esto que es el input de tipo profil que creamos por aquí Entonces vamos allá para ver qué pasa vamos a regresar aquí al de save y otra vez esto solamente lo quiero utilizar para cuando vamos a actualizar Entonces por aquí tenemos que colocar un condicional tal cual hacíamos antes por aquí voy a permitir copiar esto por aquí voy a colocar un condicional coloco:
<div v-if="post.id != ''">
Entonces mostramos el siguiente contenido:
<div v-if="post.id != ''">
<div class="container">
<div class="card">
<div class="card-body">
<div class="grid grid-cols-2 gap-2">
<div class="col-span-6">
<InputLabel value="Image" />
<TextInput type="file" @input="form.image = $event.target.files[0]" />
<InputError :message="errors.image" class="mt-2" />
<PrimaryButton @click="upload">Upload</PrimaryButton>
</div>
</div>
</div>
</div>
</div>
</div>
Pero en fin, yo lo voy a dejar así un poquito más simple Bueno entonces para que no se nos olvide vamos a crear aquí el de campo para el image coloco image y ahí lo tenemos volvemos acá y faltan hacer algunas cositas como te indicaba Esto va a ser un proceso aislado ahora que estoy viendo también esto lo tenemos que sacar del formulario porque si no podemos tener problemas con elemento clit así que mejor lo quito de acá voy a colocarlo por aquí abajo a ver por está como roto el estilo Bueno ahorita termino de chequear okay ya por aquí estamos aquí seleccioné una para crear No pasa nada en base condicional y aquí en editar creo que es esta cosita a ver Ahí está está por aquí abajo ya que quiero que escape del formulario es el formulario section que tenemos acá bien seguimos un poco con esto entonces aquí un poquito lo que es la magia o lo extraño que es colocar aquí el input que es un evento que podemos tener en los input y que nos permite establecer el valor Recuerda que esto se va a ejecutar cuando le peguemos y seleccionemos un archivo es decir cuando le demos un click a este botón y seleccionemos el archivo esto se va a ejecutar este evento que tenemos aquí al igual que ocurre con un evento clit recuerda Cuando tenemos una roba es significa que es un evento y en este caso el evento se llama input según lo que tenemos en inertia esto también lo puedes ver aquí si colocas por ejemplo @input voy a colocar aquí inertia aquí tienes también El ejercicio es el que estamos realizando y por aquí lo tienes es básicamente lo mismo el nombre de campo del formulario y por aquí le estable es esto aquí puedes también colocar un alert o algo para que veas cuánd se ejecuta Pero en fin no quiero alargar esto demás Entonces por aquí colocamos formulario pun image va a ser igual y aquí sería solamente el primero como todo evento es decir esto es otra vez un evento tenemos aquí valga la redundancia un evento en el cual tenemos información sobre qué es lo que ocurrió y en este caso sobre la carga del archivo y para Acceder al mismo tenemos que colocar aquí target ya este es como se encuentra estructurado sería Target punto fills y aquí tuviéramos lo que es finalmente nuestros archivos que hayamos seleccionado pero nos interesa es solamente uno y esto sería ya todo por acá Perdón Esto va Lo coloqué en el Field errors va obviamente arriba por acá y esto sería todo por acá volvemos perfecto Falta ahora aquí implementar el botón del de así que, colocamos nuestro button:
<TextInput type="file" @input="form.image = $event.target.files[0]" />
<InputError :message="errors.image" class="mt-2" />
<PrimaryButton @click="upload">Upload</PrimaryButton>
Esto es todo, entonces vo recargar para ver que no tenemos nada malo antes era una advertencia porque no existía el método d o la función y faltaría por aquí probar bueno poquito pegado puedes ver que al menos está llamando el método este campo requerido y esto es gracias a las validaciones que colocamos para el request voy a buscar aquí alguna por ejemplo este alguna imagen que tenga por ahí Okay aquí tenemos un problema esto es en el evento Debería ser en el evento.
Bueno tengo que chequear bueno por aquí estoy leyendo esto bien el problema es la asignación Solo que no es tan claro aquí dice que el elemento solamente accepta acepta son fi name y está inicializado como un Stream ahora que estoy viendo aquí lo inicializar como como un nulo yo lo inicial cé como un vacío puede que ese sea el problema Entonces vamos a chequear a colocarle aquí null voy a recargar para quitar el error y selecciono otra vez pues no no era eso lo otro es este que que no tiene que estar aquí entonces lo voy a quitar creo que es esto voy a colocar aquí otra vez vacío porque conflicto es ese es cuando estamos lo que pasa es que creo que ahora la ha el bmel que le establecer el nombre el texto pero es la conversión ahí entre un tipo archivo a un Stream ahora fíjate que no dio entonces ahí puedes colocar nulo o vacío yo voy a dejar vacío ya que es lo que estoy manejando ahí damos un load y no sé ni qué hizo puede que sí puede que no Bueno aquí redireccionó fíjate Entonces al menos hizo algo vamos a buscar aquí en la aquí la carpeta public señala bien está en verde que significa que tenemos aquí nueva Data y lo tenemos esa era mi imagen del curso de electron entonces Bueno aquí todo funcionó perfectamente recuerda otra vez la validación eliminamos la imagen anterior si existe si no existe Entonces no pasa nada mantenemos generamos el nuevo nombre aquí movemos la imagen que tenemos aquí en el request lo movemos a la ubicación que queramos en este caso es image post recuerda que aquí emple el la función de ayuda llamada publicad para simplemente obtener la ruta puedes imprimir un dd y te va a dar la ubicación completa o absoluta hasta el image post que es lo que queremos aquí para subir aquí el archivo y aquí sí accedemos al disco para la gestión del mismo entiéndase eliminar crear o lo que sea también pudiéramos utilizarlo para crear Pero bueno pero este es el enfoque que yo conozco mediante la función aquí de move Entonces esto es ya externo a la parte de la carga mientras que aquí lo que necesitamos para utilizar la función de move es la ruta hacia donde lo quieres mover Bueno finalmente actualizamos aquí el nombre del post específicamente la imagen Mejor dicho finalmente aquí actualizamos el nombre de la imagen que tenemos aquí en el post la referencia y hacemos una redirección y aquí no hay mucho más misterio que es otra vez al no emplear los B model porque Bueno si funciona inertia no hay mucho más que decir el enfoque que ellos lo dieron es mediante un evento que se ejecuta justamente y pudistes ver en base a los errores cuando seleccionamos un archivo entonces hacemos algo en base a un evento y ese algo viene viene siendo establecer alguna Data aquí en el formulario específicamente en el campo image y establecemos accedemos a lo que sería el valor o al archivo que nosotros cargamos y esto sería prácticamente todo aquí importante en caso de que tengas por ahí algún problema Te voy a mostrar qué es lo que tengo aquí en el request image para que lo tengas ahí de referencia vuelvo Aquí voy a seleccionar otra por acá buenoa seleccionado la misma Esto es lo que tenemos aquí tenemos es un illuminate http o lo fill es decir exactamente lo mismo que tenemos en larabel básico esto no cambia Este es el objeto que se recibe cuando le pasamos un archivo al arabel Y esto no cambia en inercia se mantiene Exactamente lo mismo y es por eso que vemos que por acá es exactamente la misma sintaxis que empleamos en el arab básico lo único que cambia aquí es en el cliente Que obviamente aquí estamos empleando View y no sería un formulario html de toda la vida entonces si recibes aquí un Stream o algo por el estilo algo estás haciendo mal entonces aclarado esto un poquito voy a intentar cargar otra imagen para ver si la que tenemos anteriormente se está eliminando en base a esta regla que tenemos acá ya que nos ha probado Así que Recuerda que solamente tengo esta voy a darle aquí otra vez aquí en principio se cargó Y fíjate que desaparece la anterior y se mantiene la nueva que sería la que puedes ver por acá Recuerda que la anterior ha sía una referencia electron y este sería ya de de flame con flurer Entonces ya aquí completamos el proceso de load y podemos pasar a la siguiente clase.
- Andrés Cruz
Desarrollo con Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter