💡 React et Laravel – Création CRUD

06:57:13

Réaliser un CRUD (« Create, Read, Update, Delete ») dans une application React en utilisant Axios.

📌 Installation de l’API

  • Installez ou réactivez l’API Laravel « Tuto-Laravel-API » via le lien GitHub indiqué plus haut.
  • Créez une nouvelle base de données.
  • Configurez le fichier .env pour connecter votre base de données à votre API.
  • Exécutez composer update pour installer les dépendances.
  • Exécutez php artisan migrate pour créer les tables.

📌 Installation de l’application React.js

npx create-react-app nom-de-votre-projet
cd nom-de-votre-projet

 

Installez les dépendances :

npm i -s react-router-dom sass react-bootstrap bootstrap axios

Importez Bootstrap dans index.js :

import 'bootstrap/dist/css/bootstrap.min.css';

Lancez votre projet :

npm start

📌 Création du système de routage

Créez le dossier pages dans src avec les fichiers suivants :

- src/pages/clubs/Clubs.jsx
- src/pages/clubs/AddClub.jsx
- src/pages/clubs/EditClub.jsx
- src/pages/players/Players.jsx
- src/pages/players/AddPlayer.jsx
- src/pages/players/EditPlayer.jsx
- src/pages/Home.jsx

 

Une fois ces fichiers crées, paramétrez vos routes dans le fichier app.js :

// Importation des bibliothèques React et React Router
import React from 'react';
import { BrowserRouter, Routes, Route } from "react-router-dom";

// Importation des composants/pages pour la navigation
import Clubs from './pages/clubs/Clubs';
import AddClub from './pages/clubs/AddClub';
import EditClub from './pages/clubs/EditClub';
import Players from './pages/players/Players';
import AddPlayer from './pages/players/AddPlayer';
import EditPlayer from './pages/players/EditPlayer';
import Home from "./pages/Home";

// Définition du composant principal App
function App() {
    return (
        // BrowserRouter est le conteneur principal qui permet la gestion du routage
        <BrowserRouter>
            {/* Routes englobe toutes les routes définies pour l'application */}
            <Routes>

                {/* Route principale : affichage de la page d'accueil */}
                <Route path="/" element={<Home />} />

                {/* Routes pour la gestion des clubs */}
                <Route path="/clubs" element={<Clubs />} />  {/* Liste des clubs */}
                <Route path="/clubs/add" element={<AddClub />} />  {/* Ajout d'un club */}
                <Route path="/clubs/edit/:club" element={<EditClub />} />  {/* Modification d'un club */}

                {/* Routes pour la gestion des joueurs */}
                <Route path="/players" element={<Players />} />  {/* Liste des joueurs */}
                <Route path="/players/add" element={<AddPlayer />} />  {/* Ajout d'un joueur */}
                <Route path="/players/edit/:player" element={<EditPlayer />} />  {/* Modification d'un joueur */}

                {/* Route par défaut : redirige vers Home si aucune route ne correspond */}
                <Route path="*" element={<Home />} />

            </Routes>
        </BrowserRouter>
    );
}

// Exportation du composant App pour qu'il puisse être utilisé ailleurs dans l'application
export default App;

 

📌 Création des composants

Le menu

Créez un fichier Menu.jsx dans src/components/ et intégrez-le dans Home.jsx.

// Importation des bibliothèques React et des composants Bootstrap pour la barre de navigation
import React from "react";
import Container from "react-bootstrap/Container";
import Nav from "react-bootstrap/Nav";
import Navbar from "react-bootstrap/Navbar";
import NavDropdown from "react-bootstrap/NavDropdown";

// Définition du composant Menu
const Menu = () => {
    return (
        <div>
            {/* Création de la barre de navigation Bootstrap */}
            <Navbar bg="light" expand="lg">
                <Container fluid>

                    {/* Bouton pour afficher/cacher le menu en mode responsive */}
                    <Navbar.Toggle aria-controls="navbarScroll" />

                    {/* Contenu du menu déroulant */}
                    <Navbar.Collapse id="navbarScroll">
                        <Nav
                            className="me-auto my-2 my-lg-0"
                            style={{ maxHeight: "100px" }}
                            navbarScroll
                        >

                            {/* Lien vers la page d'accueil */}
                            <Nav.Link href="/">Home</Nav.Link>

                            {/* Menu déroulant pour la gestion des clubs */}
                            <NavDropdown title="Clubs" id="navbarScrollingDropdown">
                                <NavDropdown.Item href="/clubs/add">
                                    Créer un nouveau club
                                </NavDropdown.Item>
                                <NavDropdown.Item href="/clubs">
                                    Liste des clubs
                                </NavDropdown.Item>
                            </NavDropdown>

                            {/* Menu déroulant pour la gestion des joueurs */}
                            <NavDropdown title="Joueurs" id="navbarScrollingDropdown">
                                <NavDropdown.Item href="/players/add">
                                    Créer un nouveau joueur
                                </NavDropdown.Item>
                                <NavDropdown.Item href="/players">
                                    Liste des joueurs
                                </NavDropdown.Item>
                            </NavDropdown>

                        </Nav>
                    </Navbar.Collapse>

                </Container>
            </Navbar>
        </div>
    );
};

// Exportation du composant Menu pour être utilisé dans d'autres fichiers
export default Menu;

Une fois le composant Menu.jsx codé, intégrez-le à la page Home pour le tester :

// Importation de React et du composant Component pour la création d'un composant de classe
import React, { Component } from 'react';

// Importation du composant Menu qui sera affiché sur la page d'accueil
import Menu from '../components/Menu';

// Définition du composant Home en tant que classe React
export class Home extends Component {
    // La méthode render() permet d'afficher le contenu du composant
    render() {
        return (
            <div>
                {/* Affichage du menu de navigation */}
                <Menu />
            </div>
        );
    }
}

// Exportation du composant Home pour être utilisé dans d'autres fichiers
export default Home;

 

📌 Mise en place des formulaires

Ajoutez les fichiers AddClub.jsx et AddPlayer.jsx avec les champs nécessaires pour la création.

// Importation des bibliothèques React et des composants nécessaires de Bootstrap
import React, { useState } from "react";
import Form from "react-bootstrap/Form";
import Button from "react-bootstrap/Button";
import Row from "react-bootstrap/Row";
import Col from "react-bootstrap/Col";
import axios from "axios"; // Bibliothèque pour effectuer les requêtes HTTP
import { useNavigate } from "react-router-dom"; // Hook pour la navigation
import Menu from "../../components/Menu"; // Importation du menu de navigation

// Définition du composant AddClub
const AddClub = () => {
    // Hook pour la navigation après la soumission du formulaire
    const navigate = useNavigate();

    // États pour stocker les valeurs des champs du formulaire
    const [nameClub, setNameClub] = useState(""); // Nom du club
    const [logoClub, setLogoClub] = useState(""); // Logo du club (fichier)
    const [validationError, setValidationError] = useState({}); // Stocke les erreurs de validation

    // Gestionnaire de changement pour le fichier du logo
    const changeHandler = (event) => {
        setLogoClub(event.target.files[0]); // Stocke le fichier sélectionné
    };

    // Fonction d'ajout d'un club via une requête HTTP POST
    const addClub = async (e) => {
        e.preventDefault(); // Empêche le rechargement de la page lors de la soumission du formulaire
        
        // Création d'un objet FormData pour envoyer des fichiers avec la requête
        const formData = new FormData();
        formData.append("nameClub", nameClub);
        formData.append("logoClub", logoClub);

        // Envoi de la requête à l'API pour ajouter un club
        await axios
            .post(`http://127.0.0.1:8000/api/clubs`, formData)
            .then(() => navigate('/clubs')) // Redirige vers la liste des clubs après succès
            .catch(({ response }) => {
                if (response.status === 422) { // Si l'API retourne une erreur de validation
                    setValidationError(response.data.errors); // Stocke les erreurs
                }
            });
    };

    return (
        <div>
            {/* Affichage du menu de navigation */}
            <Menu />

            <div className="container">
                <div className="row justify-content-center">
                    <div className="col-12 col-sm-12 col-md-6">
                        <div className="card">
                            <div className="card-body">
                                <h4 className="card-title">Création d'un nouveau club</h4>
                                <hr />

                                <div className="form-wrapper">
                                    {/* Affichage des erreurs de validation si elles existent */}
                                    {Object.keys(validationError).length > 0 && (
                                        <div className="row">
                                            <div className="col-12">
                                                <div className="alert alert-danger">
                                                    <ul className="mb-0">
                                                        {Object.entries(validationError).map(([key, value]) => (
                                                            <li key={key}>{value}</li>
                                                        ))}
                                                    </ul>
                                                </div>
                                            </div>
                                        </div>
                                    )}

                                    {/* Formulaire d'ajout de club */}
                                    <Form onSubmit={addClub}>
                                        <Row>
                                            <Col>
                                                <Form.Group controlId="Name">
                                                    <Form.Label>Nom du club</Form.Label>
                                                    <Form.Control
                                                        type="text"
                                                        value={nameClub}
                                                        onChange={(event) => setNameClub(event.target.value)}
                                                    />
                                                </Form.Group>
                                            </Col>
                                        </Row>

                                        <Row>
                                            <Col>
                                                <Form.Group controlId="Logo" className="mb-3">
                                                    <Form.Label>Logo</Form.Label>
                                                    <Form.Control type="file" onChange={changeHandler} />
                                                </Form.Group>
                                            </Col>
                                        </Row>

                                        {/* Bouton de soumission du formulaire */}
                                        <Button
                                            variant="primary"
                                            className="mt-2"
                                            size="lg"
                                            block="block"
                                            type="submit"
                                        >
                                            Créer
                                        </Button>
                                    </Form>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    );
};

// Exportation du composant AddClub pour être utilisé ailleurs dans l'application
export default AddClub;

Puis réalisons le fichier d’ajout de joueur qui sera un peu plus compliqué que celui des clubs car le formulaire d’ajout comporte une clé étrangère à renseigner (club_id). Ce code sera à insérer dans le fichier AddPlayer.js :

// Importation des bibliothèques React et des composants Bootstrap pour le formulaire
import React, { useState, useEffect } from "react";
import Form from "react-bootstrap/Form";
import Button from "react-bootstrap/Button";
import Row from "react-bootstrap/Row";
import Col from "react-bootstrap/Col";
import axios from "axios"; // Importation d'Axios pour les requêtes HTTP
import { useNavigate } from "react-router-dom"; // Hook pour la navigation entre pages
import Menu from "../../components/Menu"; // Importation du menu de navigation

// Définition du composant AddPlayer pour l'ajout d'un joueur
const AddPlayer = () => {
    const navigate = useNavigate(); // Hook pour rediriger après l'ajout

    // États pour stocker les valeurs du formulaire
    const [firstName, setFirstName] = useState(""); // Prénom du joueur
    const [lastName, setLastName] = useState(""); // Nom du joueur
    const [height, setHeight] = useState(""); // Taille du joueur
    const [position, setPosition] = useState(""); // Position sur le terrain
    const [club_id, setClubId] = useState(""); // ID du club sélectionné
    const [photoPlayer, setPhotoPlayer] = useState(""); // Fichier photo du joueur
    const [validationError, setValidationError] = useState({}); // Gestion des erreurs
    const [clubs, setClubs] = useState([]); // Liste des clubs disponibles

    // Gestionnaire de changement pour l'image (photo du joueur)
    const changeHandler = (event) => {
        setPhotoPlayer(event.target.files[0]); // Stocke le fichier sélectionné
    };

    // Gestionnaire de changement pour la sélection du club
    const handleChange = (event) => {
        setClubId(event.target.value); // Met à jour l'ID du club sélectionné
    };

    // useEffect pour charger la liste des clubs disponibles dès que le composant est monté
    useEffect(() => {
        getClubs();
    }, []);

    // Fonction pour récupérer la liste des clubs via une requête GET
    const getClubs = async () => {
        await axios.get('http://127.0.0.1:8000/api/clubs')
            .then(res => {
                setClubs(res.data); // Stocke la liste des clubs dans le state
            });
    };

    // Fonction pour ajouter un joueur via une requête HTTP POST
    const addPlayer = async (e) => { 
        e.preventDefault(); // Empêche le rechargement de la page lors de la soumission

        // Création d'un objet FormData pour envoyer les données avec le fichier
        const formData = new FormData();
        formData.append("firstName", firstName);
        formData.append("lastName", lastName);
        formData.append("height", height);
        formData.append("position", position);
        formData.append("club_id", club_id);
        formData.append("photoPlayer", photoPlayer);

        console.log(club_id); // Affichage de l'ID du club sélectionné pour debug

        // Envoi des données au serveur
        await axios.post(`http://127.0.0.1:8000/api/players`, formData)
            .then(() => navigate("/players")) // Redirection vers la liste des joueurs après succès
            .catch(({ response }) => {
                if (response.status !== 200) {
                    setValidationError(response.data); // Stocke les erreurs de validation
                }
            });
    };

    return (
        <div>
            {/* Affichage du menu de navigation */}
            <Menu />

            <div className="container container mt-5">
                <div className="row justify-content-center">
                    <div className="col-12 col-sm-12 col-md-6">
                        <div className="card">
                            <div className="card-body">
                                <h4 className="card-title">Création d'un nouveau joueur</h4>
                                <hr />

                                <div className="form-wrapper">
                                    {/* Affichage des erreurs de validation */}
                                    {Object.keys(validationError).length > 0 && (
                                        <div className="row">
                                            <div className="col-12">
                                                <div className="alert alert-danger">
                                                    <ul className="mb-0">
                                                        {Object.entries(validationError).map(
                                                            ([key, value]) => (
                                                                <li key={key}>{value}</li>
                                                            )
                                                        )}
                                                    </ul>
                                                </div>
                                            </div>
                                        </div>
                                    )}

                                    {/* Formulaire d'ajout de joueur */}
                                    <Form onSubmit={addPlayer}>
                                        <Row>
                                            <Col>
                                                <Form.Group controlId="firstName">
                                                    <Form.Label>Prénom</Form.Label>
                                                    <Form.Control
                                                        type="text"
                                                        value={firstName}
                                                        onChange={(event) => setFirstName(event.target.value)}
                                                    />
                                                </Form.Group>
                                            </Col>
                                        </Row>

                                        <Row>
                                            <Col>
                                                <Form.Group controlId="lastName">
                                                    <Form.Label>Nom</Form.Label>
                                                    <Form.Control
                                                        type="text"
                                                        value={lastName}
                                                        onChange={(event) => setLastName(event.target.value)}
                                                    />
                                                </Form.Group>
                                            </Col>
                                        </Row>

                                        <Row>
                                            <Col>
                                                <Form.Group controlId="height">
                                                    <Form.Label>Taille</Form.Label>
                                                    <Form.Control
                                                        type="text"
                                                        value={height}
                                                        onChange={(event) => setHeight(event.target.value)}
                                                    />
                                                </Form.Group>
                                            </Col>
                                        </Row>

                                        <Row>
                                            <Col>
                                                <Form.Group controlId="position">
                                                    <Form.Label>Position</Form.Label>
                                                    <Form.Control
                                                        type="text"
                                                        value={position}
                                                        onChange={(event) => setPosition(event.target.value)}
                                                    />
                                                </Form.Group>
                                            </Col>
                                        </Row>

                                        <Row>
                                            <Col>
                                                <Form.Group controlId="club">
                                                    <Form.Label>Club</Form.Label>
                                                    <Form.Select aria-label="Sélection du club" onChange={handleChange}>
                                                        <option>Choisissez un club</option>
                                                        {clubs.map(club => (
                                                            <option key={club.id} value={club.id}>
                                                                {club.nameClub}
                                                            </option>
                                                        ))}
                                                    </Form.Select>
                                                </Form.Group>
                                            </Col>
                                        </Row>

                                        <Row>
                                            <Col>
                                                <Form.Group controlId="PhotoPlayer" className="mb-3">
                                                    <Form.Label>Photo du joueur</Form.Label>
                                                    <Form.Control type="file" onChange={changeHandler} />
                                                </Form.Group>
                                            </Col>
                                        </Row>

                                        {/* Bouton de soumission */}
                                        <Button
                                            variant="primary"
                                            className="mt-2"
                                            size="lg"
                                            block="block"
                                            type="submit"
                                        >
                                            Créer un nouveau joueur
                                        </Button>
                                    </Form>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    );
};

// Exportation du composant AddPlayer pour être utilisé ailleurs
export default AddPlayer;

 

 

 

📌 Création des listes

Créez Clubs.jsx et Players.jsx pour afficher les données sous forme de tableau.

Dans le fichier Clubs.js :

// Importation des bibliothèques React et des composants Bootstrap pour l'affichage des données
import React, { useEffect, useState } from "react";
import Table from "react-bootstrap/Table";
import Button from "react-bootstrap/Button";
import Menu from "../../components/Menu"; // Importation du menu de navigation
import axios from "axios"; // Importation d'Axios pour les requêtes HTTP

// Définition du composant Clubs pour afficher la liste des clubs
const Clubs = () => {
    // Définition d'un état pour stocker les clubs récupérés depuis l'API
    const [clubs, setClubs] = useState([]);

    // useEffect est exécuté au montage du composant pour récupérer les clubs
    useEffect(() => {
        displayClubs();
    }, []); // Le tableau vide signifie que l'effet ne s'exécute qu'une seule fois

    // Fonction pour récupérer la liste des clubs via une requête GET
    const displayClubs = async () => {
        await axios.get("http://127.0.0.1:8000/api/clubs").then((res) => {
            setClubs(res.data); // Stocke les clubs récupérés dans l'état
        });
    };

    // Fonction pour supprimer un club via une requête DELETE
    const deleteClub = (id) => {
        axios.delete(`http://127.0.0.1:8000/api/clubs/${id}`).then(displayClubs);
    };

    return (
        <div>
            {/* Affichage du menu de navigation */}
            <Menu />

            <div className="container mt-5">
                {/* Affichage du tableau des clubs */}
                <Table striped bordered hover>
                    <thead>
                        <tr>
                            <th>Nom du club</th>
                            <th>Logo</th>
                            <th>Actions</th>
                        </tr>
                    </thead>
                    <tbody>
                        {/* Boucle sur le tableau des clubs et affichage des données */}
                        {clubs.map((club) => (
                            <tr key={club.id}>
                                <td>{club.nameClub}</td>
                                <td>
                                    <img
                                        src={`http://127.0.0.1:8000/storage/uploads/${club.logoClub}`}
                                        width="75px"
                                        alt="Logo du club"
                                    />
                                </td>
                                <td>
                                    {/* Bouton pour supprimer un club */}
                                    <Button
                                        variant="danger"
                                        onClick={() => {
                                            deleteClub(club.id);
                                        }}
                                    >
                                        Supprimer
                                    </Button>
                                </td>
                            </tr>
                        ))}
                    </tbody>
                </Table>
            </div>
        </div>
    );
};

// Exportation du composant Clubs pour être utilisé ailleurs
export default Clubs;

Si après avoir copier le code vos images ne s’affichent pas entrez cette ligne de commande dans votre API :

#php artisan storage:link

 

📌 Création de l’update

L’update se construit de la même manière que le create sauf que nous devons récupérer les informations avant de les modifier.

Tout d’abord, on commence par ajouter le lien de modification dans le fichier Clubs.js au niveau de la colonne

« Actions » de votre tableau :

<Link to={`/clubs/edit/${club.id}`} className='btn btn-success me-2'>
Edit
</Link>

Pensez à importer le composant Link en haut de votre fichier :

import { Link } from 'react-router-dom';

Ensuite compléter votre fichier EditClub.js de cette manière :

// Importation des bibliothèques React et des composants Bootstrap pour le formulaire
import React, { useState, useEffect } from "react";
import Form from "react-bootstrap/Form";
import Button from "react-bootstrap/Button";
import Row from "react-bootstrap/Row";
import Col from "react-bootstrap/Col";
import axios from "axios"; // Bibliothèque pour effectuer les requêtes HTTP
import { useNavigate, useParams } from "react-router-dom"; // Hooks pour la navigation et la récupération des paramètres d'URL
import Menu from "../../components/Menu"; // Importation du menu de navigation

// Définition du composant EditClub pour modifier un club existant
const EditClub = () => {
    // Récupération de l'ID du club depuis les paramètres de l'URL
    const { club } = useParams();

    // Hook pour rediriger après la modification
    const navigate = useNavigate();

    // États pour stocker les valeurs des champs du formulaire
    const [nameClub, setNameClub] = useState(""); // Nom du club
    const [logoClub, setLogoClub] = useState(null); // Logo du club (fichier)
    const [validationError, setValidationError] = useState({}); // Gestion des erreurs de validation

    // useEffect pour récupérer les informations du club dès le chargement du composant
    useEffect(() => {
        getClub();
    }, []);

    // Fonction pour récupérer les informations du club via une requête GET
    const getClub = async () => {
        await axios
            .get(`http://127.0.0.1:8000/api/clubs/${club}`)
            .then(res => {
                setNameClub(res.data.nameClub); // Stocke le nom du club dans l'état
            })
            .catch(error => {
                console.log(error); // Affichage des erreurs dans la console pour debug
            });
    };

    // Gestionnaire de changement pour le fichier du logo
    const changeHandler = (event) => {
        setLogoClub(event.target.files[0]); // Stocke le fichier sélectionné
    };

    // Fonction pour mettre à jour le club via une requête PATCH
    const updateClub = async (e) => {
        e.preventDefault(); // Empêche le rechargement de la page lors de la soumission

        // Création d'un objet FormData pour envoyer les données avec le fichier
        const formData = new FormData();
        formData.append('_method', 'PATCH'); // Méthode HTTP pour mettre à jour les données
        formData.append("nameClub", nameClub);

        // Vérifie si un logo a été sélectionné avant de l'envoyer
        if (logoClub !== null) {
            formData.append("logoClub", logoClub);
        }

        // Envoi de la requête PATCH pour mettre à jour les informations du club
        await axios
            .post(`http://127.0.0.1:8000/api/clubs/${club}`, formData)
            .then(() => navigate("/clubs")) // Redirection vers la liste des clubs après modification
            .catch(({ response }) => {
                if (response.status === 422) {
                    setValidationError(response.data.errors); // Stocke les erreurs de validation
                }
            });
    };

    return (
        <div>
            {/* Affichage du menu de navigation */}
            <Menu />

            <div className="container mt-5">
                <div className="row justify-content-center">
                    <div className="col-12 col-sm-12 col-md-6">
                        <div className="card">
                            <div className="card-body">
                                <h4 className="card-title">Modifier un club</h4>
                                <hr />

                                <div className="form-wrapper">
                                    {/* Affichage des erreurs de validation si elles existent */}
                                    {Object.keys(validationError).length > 0 && (
                                        <div className="row">
                                            <div className="col-12">
                                                <div className="alert alert-danger">
                                                    <ul className="mb-0">
                                                        {Object.entries(validationError).map(
                                                            ([key, value]) => (
                                                                <li key={key}>{value}</li>
                                                            )
                                                        )}
                                                    </ul>
                                                </div>
                                            </div>
                                        </div>
                                    )}

                                    {/* Formulaire de modification d'un club */}
                                    <Form onSubmit={updateClub}>
                                        <Row>
                                            <Col>
                                                <Form.Group controlId="Name">
                                                    <Form.Label>Nom du club</Form.Label>
                                                    <Form.Control
                                                        type="text"
                                                        value={nameClub}
                                                        onChange={(event) => setNameClub(event.target.value)}
                                                    />
                                                </Form.Group>
                                            </Col>
                                        </Row>

                                        <Row>
                                            <Col>
                                                <Form.Group controlId="Logo" className="mb-3">
                                                    <Form.Label>Logo</Form.Label>
                                                    <Form.Control type="file" onChange={changeHandler} />
                                                </Form.Group>
                                            </Col>
                                        </Row>

                                        {/* Bouton de soumission du formulaire */}
                                        <Button
                                            variant="primary"
                                            className="mt-2"
                                            size="lg"
                                            block="block"
                                            type="submit"
                                        >
                                            Modifier
                                        </Button>
                                    </Form>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    );
};

// Exportation du composant EditClub pour être utilisé ailleurs dans l'application
export default EditClub;

 

 

 

 

 

par