# 6.- Editando, Eliminando nuestro article

Para eso ya tenemos nuestra api Route::get('/articles', [ArticleController::class, 'index']) y la utilizamos en el frontend para traer todos los articles

  • En este caso solo los usuarios logeados pueden realizar un petición GET y para eso desde el front tenemos que enviar su token del usuario para capturar el user
    import { useAuth } from '@/hooks/auth'

    const [articles, setArticles] = useState()
	console.log('articles', articles) // miramos los datos

    // Para eso necesitamos el token que almacenamos en las cookies
    const { getCookie } = useAuth()
	if (typeof window !== 'undefined') {
		var token = getCookie('token')
	}

    // Creamos función
    async function getFilms() {
		const response = await axios.get(
			`${process.env.NEXT_PUBLIC_BACKEND_URL}/api/articles`,
			{
				headers: { 
                    // Enviando la cookie de usuario
					"Authorization": `Bearer ${token}`,
				 },
			})
		const data = response.data
        // Almacenamos los datos
		setArticles(response.data.articles.data)
	}
  • Ya tenemos los datos en setArticles.
  • Recordar que aparte de los datos también tenemos la paginación.
    // Mostrando los datos
        <div>
            {articles !== undefined ?
				(<div>
					{articles.map(article => (
						<div key={article.id}>
						<div>{article.title}</div>
					</div>
					))}
				</div>) : 
			(<p> cargando datos</p>)}
        </div>

# Editar un article y también funciona como detalle (Frontend)

*Para mostrar el detalle de un articulo tenemos que crear una carpeta nueva con el nombre 'article' y dentro crear un archivo '[id].js' en este caso editaremos un article y al mismo tiempo mostraremos solo los datos del article que queremos actualizar

En el archivo principal donde mostramos todos los articles debemos de agregar un Botón para que nos envíe a una nueva ruta donde solo mostraremos los datos de ese artículo seleccionado

Para eso importaremos useRouter

	import { useRouter } from 'next/router'

	const router = useRouter()

	<div key={article?.id}>
		<div>{article?.title}</div>
		<button
				onClick={e =>
				router.push(
				'/articles/[id]',
				`/articles/${article?.id}`,
			)}> Edit
		</button>
		</div>
  • En el otro archivo creado ahora colocamos los siguientes datos [id].js

Los datos ya traídos se encuentran en las props const ArticleDetail = ({ articles })

Tenemos una función donde traemos los datos de un solo article en getServerSideProps y almacenamos en un const [article, setArticle] = useState(articles.article)

	import { useRouter } from 'next/router'
	import { useEffect, useState, useSyncExternalStore } from 'react'
	import axios from 'axios'
	import { useAuth } from '@/hooks/auth'

	const ArticleDetail = ({ articles }) => {

		const [article, setArticle] = useState(articles.article)
    	console.log(article)
		return (
			<div>
				hello
			</div>
		)
	}

	export const getServerSideProps = async context => {
		const { data: articles } = await axios.get(
			`${process.env.NEXT_PUBLIC_BACKEND_URL}/api/article/` + context.query.id,
		)
		return {
			props: {
				articles,
			},
		}
	}
	export default ArticleDetail
  • Para editar un artículo ahora lo realizamos sin utilizar librerías.

En esta oportunidad Next ya nos creó componentes reutilizables como Input y Label

	import Label from '@/components/Label'
	import Input from '@/components/Input'

	<Label htmlFor="title">title</Label>
    <Input
      id="title"
      type="text"
      placeholder="title"
    //value={updateTitle}
    //onChange={event => setUpdateTitle(event.target.value)}
  />
  • Ahora creamos todos los parámetros ya sea input y const que necesitamos para todos los datos de un article
	const [id, setId] = useState(articles.article.id) 
  const [title, setTitle] = useState(articles.article.title) 
	const [slug, setSlug] = useState(articles.article.slug)
  const [body, setBody] = useState(articles.article.body) 
  const [image, setImage] = useState(articles.article.image) 
  • Ahora en los Input colocamos el value value={updateTitle}
	<Label htmlFor="title">title</Label>
	<Input
		id="title"
		type="text"
		placeholder="title"
		value={title}
		onChange={event => setTitle(event.target.value)}
	/>
  • Así se tendría que tener todos los inputs
	<form>
		<Label htmlFor="title">title</Label>
		<Input
			id="title"
			type="text"
			placeholder="title"
			value={title}
			onChange={event => setTitle(event.target.value)}
		/>
		<Label htmlFor="slug">slug</Label>
		<Input
			id="slug"
			type="text"
			placeholder="slug"
			value={slug}
			onChange={event => setSlug(event.target.value)}
		/>
		<Label htmlFor="body">body</Label>
		<Input
			id="body"
			type="text"
			placeholder="body"
			value={body}
			onChange={event => setBody(event.target.value)}
		/>
		<Label htmlFor="image">image</Label>
		<Input
			id="image"
			type="file"
			value={image}
			onChange={event => setImage(event.target.value)}
		/>
		<img src={image}/>
		<button >Update</button>
		</form>
  • Para la parte de la imagen tenemos que crear una función nuevamente para poder actualizar y mostrar en pantalla si ingresamos una imagen nueva al input file.

Así queda nuestro input para la imagen

	<Label htmlFor="image">image</Label>
	<Input
		id="image"
		type="file"
		//Creamos una función nueva ya dentro de esta función guardaremos la imagen
		onChange={ event => submitImage(event)}
	/>
	<img id="output"
		src={image}
	/>

Ya capturamos la imagen ahora tenemos que enviar los datos nuevos que queremos enviar para actualizar el dato

  • Para enviar los datos utilizaremos FormData() así que al botón le agregamos la opción onClick
	function submitImage(event) {
		const files = event.target.files
		const file = files[0]
		getBase64(file)

		//show image
		var reader = new FileReader();
		reader.onload = function () {
			var output = document.getElementById('output');
			output.src = reader.result;
		};
		reader.readAsDataURL(event.target.files[0]);
	}

	const onLoad = fileString => {
		setImage(fileString)
	}

	const getBase64 = file => {
		let reader = new FileReader()
		reader.readAsDataURL(file)
		reader.onload = () => {
			onLoad(reader.result)
		}
	}

Ya capturamos la imagen, ahora tenemos que enviar los datos nuevos que queremos enviar para actualizar el dato

  • Para enviar los datos utilizaremos FormData() así que al botón le agregamos la opción onClick
	<button  onClick={updateArticle}>Update</button>

	const updateFilm = async e => {
		// Colocamos los datos a enviar
	}

En nuestra función agregamos lo siguiente

	//submitDataUpdate article
	const updateArticle = async e => {
		e.preventDefault()
		let formData = new FormData()
    	formData.append('title', title)
    	formData.append('slug', slug)
    	formData.append('body', body)
    	formData.append('image', image)
		await axios
		.post(
			`${process.env.NEXT_PUBLIC_BACKEND_URL}/api/article/${id}/edit`,
			formData,
		)
		.then(function (response) {
			console.log(response, 'llega')
			// router.push("/")
		})
		.catch(function (error) {
			console.log(error)
		})
	}

# Editar un article (Backend)

En nuestro backend tenemos que crear un endpoint para actualizar nuestro article

Vamos a ArticleController.php y buscamos la función public function update(){}

	public function update(Request $request, $id)
    {
        $article = Article::find($id);
        $article->title = $request->title;
        $article->body = $request->body;
        $article->slug = $request->slug;
        $article->image = $request->image;
        $article->save();
        return response()->json(['message' => 'Article update succesfully']);
  }

Ahora vamos al archivo api.php y creamos la ruta

	Route::post('/article/{id}/edit', [ArticleController::class, 'update']);

Y listo ya podemos actualizar nuestro article, recordar que faltan las validaciones como: Que el usuario con el mismo Id pueda actualizar y otras validaciones más para que sea más seguro.

# Eliminando un article (Frontend)

  • Ahora vamos a eliminar un article
		//Enviamos el id del article
		const handleDelete = async (id) =>{
			await axios
			.post(
				`${process.env.NEXT_PUBLIC_BACKEND_URL}/api/article/${id}/delete`,
			)
			.then(function (response) {
				console.log(response, 'llega')
			})
			.catch(function (error) {
				console.log(error)
			})
		}

		<button	
			//Enviamos el Id del article
			onClick={() => handleDelete(article?.id)}
			className="btn btn-danger">
			Delete
		</button>

# Eliminando un article (Backend)

  • Ahora vamos a la parte del backend a crear nuestro endpoint para poder eliminar

Vamos al archivo api.php y agregamos la nueva ruta de nuestro endpoint.

	Route::post('/article/{id}/delete', [ArticleController::class, 'destroy']);

Ahora vamos al archivo ArticleController.php y buscamos la función destroy

	public function destroy($id)
    {
        $article = Article::find($id);
        $article->delete();
        return response()->json(['message' => 'Article delete succesfully']);
    }

Listo así de fácil es eliminar un article

# Actualizando los datos de la lista sin refrescar página

Ahora actualizaremos los datos sin recargar la página y vaciamos los input, acordarse que estamos utilizando una librería externa para poder crear un article así que tendremos que realizar algunas configuraciones diferentes para que funcione correctamente.

Para reset los input tenemos que utilizar una función de la librería react-hooks-form llamara reset y colocarla acá

	const { handleSubmit, control, formState: { errors }, reset  } = useForm(
		{
			defaultValues: {
				title: '',
				slug: '',
				body: '',
				image: '',
			}
		}
	);

Además coloquemos los datos restantes que faltan como body y image para que funcione correctamente

  • Al momento de crear un nuevo article en la función onSubmit tenemos que agregar unas líneas para actualizar la lista de articles sin refrezcar la página
	async function onSubmit(data) {
		data.image = imageselect
		var { title, slug, body, image  } = data 
		await axios.post(`${process.env.NEXT_PUBLIC_BACKEND_URL}/api/article`, data,
				{headers: { "Authorization": `Bearer ${token}` }})
			.then(function (response) {
				//Actualizando articles pero esto va con un nuevo useEffects
				setArticles()
				//Ocultando la imagen después de crear un article
				var output = document.getElementById('output');
				output.src = '';
				//Limpiando valor del input imageselect para dejar vacía
				setImage('')
				//Limpiando los datos restantes
				reset();
			})
			.catch(function (error) {
				console.log(error)
			})
	};

Ya habiendo limpiado los inputs ahora nos toca actualizar la lista de articles

  • Se agregó esta parte porque al momento de crear un article en la parte const [articles, setArticles] = useState() donde almacenamos la lista. Por consola podemos darnos cuenta de que nos llega undefined así que añadiendo un nuevo useEffect hacemos que la lista se actualice nuevamente.
	useEffect(() => {
	if(articles === undefined){
		getArticles()
	}}, [articles])

# Eliminar article sin refrescar página

  • En este caso como arriba creamos un useEffect indicando que actualicé la lista si es que en algún momento le llegan undefined ya sea al crear o eliminar lo que realizara es refrezcar cada vez que se realice un evento de crear o eliminar
	const handleDelete = async (id) =>{
		await axios
		.post(`${process.env.NEXT_PUBLIC_BACKEND_URL}/api/article/${id}/delete`,)
		.then(function (response) {
			//Actualizando lista
			setArticles()
		})
		.catch(function (error) {
			console.log(error)
		})
	}