# 5.- Creando nuestro primer (Article) POST

Antes de comenzar tenemos que realizar algunas configuraciones que más adelante se explicara.

  • Nos dirigimos al archivo app/Http/Middleware/VerifyCsrfToken.php para agregar /api/*dentro de la $exceptmatriz. Debería verse algo como esto:
    protected $except = [
        '/api/*'
    ];

Desajustar opción CSRF (opens new window)

# Backend

  • Para esto tenemos que ir al backend app/Http/Controllers/ArticleController en la parte public function store() tenemos que agregar algunas lineas
        //Al crear un articulo tenemos que enviar tambien el token para capturar al usuario y poder realizar validaciones
        $user = auth()->user()->id;

        //verificar los datos que tienen que llegar desde el frontend
        $article = Article::create([
            'title' => $request->title,
            'slug' => $request->slug,
            'body' => $request->body,
            'author_id' => $user,
        ]);

        return response()->json([
            'Message' => 'ok',
            'articles' => $article
        ]);

# Frontend NextJs install useForm() and reactstrap

La versión es "react-hook-form": "^7.34.2",

  • Primero tenemos que realizar algunas instalaciones, en este caso utilizaremos librerías que se utiliza en casi todos los proyectos para ahora tiempo de muchas configuraciones. Más adelante se realizará un POST sin librerias.
    npm install react-hook-form
    npm i bootstrap
    npm i reactstrap react react-dom
    //importamos los paquetes instalados
    import { Form, Row, Col, FormGroup, Label, Input, Button } from 'reactstrap';
    import 'bootstrap/dist/css/bootstrap.css';
    import { useForm, Controller } from 'react-hook-form'

Ahora utilizamos las funciones

    //capturamos las cookie del usuario logeado para enviar al backend
    const { getCookie } = useAuth()
	if (typeof window !== 'undefined') {
		var token = getCookie('token')
	}

    //Register new article
	const { handleSubmit, control } = useForm();

    async function onSubmit(data) {
		console.log(data)
		await axios.post(`${process.env.NEXT_PUBLIC_BACKEND_URL}/api/article`, data,
				{
					//Enviando la cookie del usuario logeado para poder realizar validaciones antes de hacer un post nuevo
					headers: { "Authorization": `Bearer ${token}` }
				})
			.then(function (response) {
				console.log(response)
				// router.push("/")
			})
			.catch(function (error) {
				console.log(error)
			})
	};

En el formulario para realizar el post

        <form>
			<Controller
				name={"title"}
				control={control}
				render={({ field: { onChange, value } }) => (
				<Input
					type="title"
					onChange={onChange}
					value={value}
					label={"title"} />
					)}
			/>
			<Controller
				name={"slug"}
				control={control}
				render={({ field: { onChange, value } }) => (
				<Input
					type="slug"
					onChange={onChange}
					value={value}
					label={"slug"} />
					)}
			/>
			<Controller
				name={"body"}
				control={control}
				render={({ field: { onChange, value } }) => (
				<Input
					type="textarea"
					onChange={onChange}
					value={value}
					label={"body"} />
				)}
			/>
			<Button onClick={handleSubmit(onSubmit)}>Submit<Button>
		</form>

Y así realizamos un nuevo article, ahora agreguemos una imagen

# Agrendo una imagen (Backend)

Para eso primero vamos a la parte del backend migración del article para agregar un nuevo campo para la imagen, en este caso guardaremos la imagen en formato Base64 que se almacenaría como un string.

    public function up()
    {
        Schema::create('articles', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->string('slug');
            $table->text('body');
            //campo image type text
            $table->longText('image');
            $table->foreignId('author_id')->constrained('users');
            $table->timestamps();
        });
    }

Ahora nos dirigimos a app/Models/Article.php para agregar el nuevo campo

    protected $fillable = [
        'title',
        'slug',
        'body',
        //new campo image
        'image',
        'author_id',
    ];

Ahora nos dirigimos a app/Http/Controllers/Article.php para agregar el nuevo campo

        $article = Article::create([
            'title' => $request->title,
            'slug' => $request->slug,
            'body' => $request->body,
            //new campo image
            'image' => $request->image,
            'author_id' => $user,
        ]);

Para finalizar realizamos una migration

    php artisan migrate:refresh

# Agrendo una imagen (Frontend)

Creamos un nuevo controller para el input image

    <Controller
		name={"image"}
		control={control}
		render={({ field: { onChange, value } }) => (
			<Input
				type="file"
				onChange={onChange = () => {
                    //creamos una función anonima y le pasamos el event
					submitImage(event)
				}}
				value={value}
				label={"image"} />
		    )}
	    />	
    // Mostraremos la imagen seleccionada
	<img id="output"/>
  • Ya teniendo el input para agregar una imagen ahora realicemos la función para capturar en base64 ya que así está configurado en nuestro backend
    //agregamos un const para almacenar la imagen seleccionada
    const [imageselect, setImage] = useState();

    //Esta es la funcion anonima que agregamos en el onchange
	    const submitImage = (event) => {
		const files = event.target.files
        const file = files[0]
        getBase64(file)

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

        //Guardando la imagen seleccionada en setImage
        const onLoad = fileString => {
            setImage(fileString)
        }

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

Ahora en la función onSubmit

    //En data estan todos lo datos que se enviaran
    async function onSubmit(data) {
        //guardando en data.image la imagen seleccionada por el input
		data.image = imageselect
        // Ahora creamos una nueva variable para separar los datos y saber que estamos enviando nos ayudara si más adelante crece el formulario
		var { title, slug, body, image  } = data
		console.log('datos', data)

		await axios.post(`${process.env.NEXT_PUBLIC_BACKEND_URL}/api/       article`, data,
				{
					//Enviando la cookie del usuario logeado para poder realizar validaciones antes de hacer un post nuevo
					headers: { "Authorization": `Bearer ${token}` }
				})
			.then(function (response) {
				console.log(response)
				// router.push("/")
			})
			.catch(function (error) {
				console.log(error)
			})
	};

# Agregando validaciones

    // Ahora agregamos formState: { errors } 
    const { handleSubmit, control, formState: { errors } } = useForm();

    // En nuestro input 
    <Controller
		name={"slug"}
		control={control}
        // Agregado
		rules={{
			required: true,
		 }}
		render={({ 
			field: { onChange, value },
		 }) => (
			<Input
				type="slug"
				onChange={onChange}
				value={value}
				label={"slug"} />
		)}
	/>
    //Agregado
	{errors.slug && <span>This is required.</span>}

Agregando valores definidos a los inputs

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

# Realizando Post, Update, Delete Middleware CSRF

Para realizar algunas de estas opciones tenemos de tener en cuenta

  • 1.- Sí estamos utilizando el middlware = "auth-sanctum" y las rutas están dentro o fuera, al momento de realizar una petición desde el frontend tenemos que enviar la opción 'axios.defaults.withCredentials = true' en cada petición que realicemos porque nos puede enviar 419 (unknown status)

Para solucionar esto tenemos que agregar la siguiente línea axios.defaults.withCredentials = true

	async function onSubmit(values) {
		//agregado
		axios.defaults.withCredentials = true
		const { file: image, title, slug, body } = values;
		await axios.post(`${process.env.NEXT_PUBLIC_BACKEND_URL}/api/article`, values,
				{headers: {"Authorization": `Bearer ${token}`}})
			.then(function (response) {
				console.log(response)
			})
			.catch(function (error) {
				console.log(error)
			})
	};

Ahora sí podemos desactivar nuevamente la opción

  • Nos dirigimos al archivo app/Http/Middleware/VerifyCsrfToken.php para comentar nuevamente /api/*dentro de la $exceptmatriz. Debería verse algo como esto:
    protected $except = [
        //'/api/*'
    ];