# 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 partepublic 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 enviar419 (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/*'
];