Skip to content

How-to : Gestion de notes avec tags ​

Vue d'ensemble ​

Ce guide couvre la construction d'une API de gestion de notes taguées avec NENE2. Les fonctionnalités incluent l'isolation par utilisateur, le filtrage par tag, la recherche par mot-clé en texte intégral, et le CRUD avec application de propriété.

Implémentation de référence : ../NENE2-FT/notelog/


Conception du schéma ​

sql
CREATE TABLE IF NOT EXISTS notes (
    id         INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id    INTEGER NOT NULL,
    title      TEXT    NOT NULL,
    body       TEXT    NOT NULL DEFAULT '',
    created_at TEXT    NOT NULL,
    updated_at TEXT    NOT NULL
);

CREATE TABLE IF NOT EXISTS note_tags (
    id      INTEGER PRIMARY KEY AUTOINCREMENT,
    note_id INTEGER NOT NULL,
    tag     TEXT    NOT NULL,
    FOREIGN KEY (note_id) REFERENCES notes(id) ON DELETE CASCADE,
    UNIQUE (note_id, tag)
);

ON DELETE CASCADE supprime automatiquement les tags quand une note est supprimée.


Table des routes ​

MéthodeCheminAuthDescription
POST/notesUtilisateurCréer une note
GET/notesUtilisateurLister ses propres notes (?tag= ou ?q= optionnel)
GET/notes/{id}UtilisateurObtenir une note
PUT/notes/{id}UtilisateurMettre à jour les champs d'une note
DELETE/notes/{id}UtilisateurSupprimer une note

Filtrage par tag ​

Filtrer par tag avec JOIN :

sql
SELECT n.* FROM notes n
JOIN note_tags t ON t.note_id = n.id
WHERE n.user_id = :uid AND t.tag = :tag
ORDER BY n.id DESC

Recherche par mot-clé ​

Recherche en texte intégral dans le titre et le corps avec LIKE :

sql
SELECT * FROM notes
WHERE user_id = :uid AND (title LIKE :kw OR body LIKE :kw)
ORDER BY id DESC

Le placeholder :kw est '%' . $keyword . '%'. Les requêtes paramétrées préviennent l'injection SQL.


Analyse des tags ​

Les tags doivent être des tableaux de chaînes ; normaliser en minuscules :

php
private function parseTags(mixed $raw): ?array
{
    if (!is_array($raw)) return [];
    $tags = [];
    foreach ($raw as $tag) {
        if (!is_string($tag)) return null;   // rejeter non-string → 422
        $t = trim($tag);
        if ($t !== '') $tags[] = strtolower($t);
    }
    return $tags;
}

Pattern IDOR / Propriété ​

Toutes les opérations de lecture et d'écriture sont scopées à user_id. Retourner 404 (pas 403) sur les lectures pour éviter de révéler l'existence ; retourner 403 sur les écritures pour que l'utilisateur sache que la ressource existe mais qu'il manque la permission :

php
// Lecture : 404 pour prévenir la divulgation d'information
if ((int) $note['user_id'] !== $uid) {
    return $this->problem(404, 'not-found', 'Note not found.');
}

// Écriture : 403 quand la ressource existe mais n'est pas possédée
if ((int) $note['user_id'] !== $userId) {
    return 'forbidden';
}

Mise à jour partielle (PUT) ​

Accepter null pour tout champ signifiant "pas de changement" :

php
$title    = isset($body['title']) ? trim((string) $body['title']) : null;
$noteBody = isset($body['body']) ? (string) $body['body'] : null;
$tags     = (isset($body['tags'])) ? $this->parseTags($body['tags']) : null;

Dans le repository, mettre à jour uniquement les champs non-null.


Codes de statut HTTP ​

SituationStatut
Note créée201
Note récupérée / liste200
Note mise à jour / supprimée200
Pas de X-User-Id400
Titre vide422
Valeurs de tag non-string422
Note non trouvée (ou IDOR)404
Mise à jour/suppression de la note d'un autre403

Publié sous licence MIT.