Introduzione
Quando si sviluppa una API RESTful in PHP, non tutti gli aggiornamenti devono riscrivere l’intera risorsa. In molti casi è più utile applicare una modifica parziale, ad esempio cambiare solo il titolo di un articolo, aggiornare lo stato di un ordine o modificare un singolo campo profilo. In questi scenari entra in gioco il metodo HTTP PATCH.
Questo tutorial spiega come realizzare un endpoint PHP per l’aggiornamento parziale di una risorsa, con un approccio pratico e adatto a progetti reali. Vedremo come leggere il payload JSON, validare i dati, costruire query sicure e restituire risposte coerenti. L’obiettivo è creare un endpoint semplice ma solido, utile come base per API più grandi.
Per l’esempio useremo una risorsa articoli, ma la logica è facilmente riutilizzabile per utenti, ordini, prodotti o qualsiasi altra entità.
Codice completo
<?php
declare(strict_types=1);
header(´Content-Type: application/json; charset=utf-8´);
// Connessione PDO di esempio
$dsn = ´mysql:host=localhost;dbname=blog_api;charset=utf8mb4´;
$user = ´root´;
$pass = ´´;
try {
$pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
} catch (PDOException $e) {
http_response_code(500);
echo json_encode([
´success´ => false,
´message´ => ´Errore di connessione al database´
]);
exit;
}
// Verifica metodo HTTP
if ($_SERVER[´REQUEST_METHOD´] !== ´PATCH´) {
http_response_code(405);
echo json_encode([
´success´ => false,
´message´ => ´Metodo non consentito. Usa PATCH.´
]);
exit;
}
// Recupero ID dalla query string: /article.php?id=10
$id = filter_input(INPUT_GET, ´id´, FILTER_VALIDATE_INT);
if (!$id) {
http_response_code(400);
echo json_encode([
´success´ => false,
´message´ => ´ID non valido o mancante´
]);
exit;
}
// Leggo il corpo JSON della richiesta
$rawInput = file_get_contents(´php://input´);
$data = json_decode($rawInput, true);
if (!is_array($data)) {
http_response_code(400);
echo json_encode([
´success´ => false,
´message´ => ´Payload JSON non valido´
]);
exit;
}
// Campi consentiti per l’aggiornamento parziale
$allowedFields = [´title´, ´content´, ´status´];
$updateFields = [];
$params = [´id´ => $id];
// Validazione e costruzione dinamica della query
if (array_key_exists(´title´, $data)) {
$title = trim((string)$data[´title´]);
if ($title === ´´ || mb_strlen($title) > 150) {
http_response_code(422);
echo json_encode([
´success´ => false,
´message´ => ´Il titolo deve contenere tra 1 e 150 caratteri´
]);
exit;
}
$updateFields[] = ´title = :title´;
$params[´title´] = $title;
}
if (array_key_exists(´content´, $data)) {
$content = trim((string)$data[´content´]);
if ($content === ´´) {
http_response_code(422);
echo json_encode([
´success´ => false,
´message´ => ´Il contenuto non può essere vuoto´
]);
exit;
}
$updateFields[] = ´content = :content´;
$params[´content´] = $content;
}
if (array_key_exists(´status´, $data)) {
$status = strtolower(trim((string)$data[´status´]));
$validStatuses = [´draft´, ´published´, ´archived´];
if (!in_array($status, $validStatuses, true)) {
http_response_code(422);
echo json_encode([
´success´ => false,
´message´ => ´Status non valido´
]);
exit;
}
$updateFields[] = ´status = :status´;
$params[´status´] = $status;
}
if (empty($updateFields)) {
http_response_code(400);
echo json_encode([
´success´ => false,
´message´ => ´Nessun campo valido da aggiornare´
]);
exit;
}
// Verifico che la risorsa esista
$checkStmt = $pdo->prepare(´SELECT id, title, content, status FROM articles WHERE id = :id´);
$checkStmt->execute([´id´ => $id]);
$article = $checkStmt->fetch();
if (!$article) {
http_response_code(404);
echo json_encode([
´success´ => false,
´message´ => ´Articolo non trovato´
]);
exit;
}
// Query UPDATE dinamica
$sql = ´UPDATE articles SET ´ . implode(´, ´, $updateFields) . ´, updated_at = NOW() WHERE id = :id´;
$stmt = $pdo->prepare($sql);
try {
$stmt->execute($params);
} catch (PDOException $e) {
http_response_code(500);
echo json_encode([
´success´ => false,
´message´ => ´Errore durante l´aggiornamento´
]);
exit;
}
// Ricarico il record aggiornato
$refreshStmt = $pdo->prepare(´SELECT id, title, content, status, updated_at FROM articles WHERE id = :id´);
$refreshStmt->execute([´id´ => $id]);
$updatedArticle = $refreshStmt->fetch();
http_response_code(200);
echo json_encode([
´success´ => true,
´message´ => ´Articolo aggiornato correttamente´,
´data´ => $updatedArticle
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); Spiegazione
Il codice sopra implementa un endpoint PATCH che aggiorna solo i campi presenti nel JSON inviato dal client. Questo è uno dei casi d’uso più comuni nelle API RESTful moderne, perché evita di dover inviare l’intera risorsa per una piccola modifica.
1. Impostazione della risposta JSON
La prima istruzione importante è l’header:
Content-Type: application/json. Serve a dichiarare che l’endpoint risponderà sempre in JSON. È una buona pratica fondamentale, perché rende il comportamento dell’API prevedibile per chi la consuma.
2. Controllo del metodo HTTP
Il codice accetta solo PATCH. Se arriva una richiesta con GET, POST o PUT, l’endpoint risponde con 405 Method Not Allowed. In una API ben progettata, ogni endpoint deve accettare solo i metodi previsti dal contratto.
3. Lettura dell’ID della risorsa
In questo esempio l’ID viene preso dalla query string. In un’applicazione più strutturata potresti usare URL come /articles/10 gestiti da router dedicati. Il controllo con filter_input evita valori non numerici o assenti.
4. Parsing del JSON
Il contenuto della richiesta viene letto con php://input e convertito con json_decode. Se il corpo non è un JSON valido, l’endpoint restituisce 400 Bad Request.
5. Validazione dei campi
Ogni campo viene controllato singolarmente:
- title: deve avere una lunghezza compresa tra 1 e 150 caratteri.
- content: non può essere vuoto.
- status: deve appartenere a un insieme limitato di valori ammessi.
Questa logica è importante perché un endpoint PATCH non deve aggiornare tutto “alla cieca”. Deve accettare solo i campi previsti e rifiutare dati incoerenti con un codice 422 Unprocessable Entity.
6. Query dinamica sicura
Una parte delicata è la costruzione della query SQL. Il codice crea un array di campi da aggiornare e poi li unisce con implode(). Questo permette di generare una query dinamica senza concatenare direttamente i valori utente all’interno della SQL.
I valori vengono passati tramite parametri nominati PDO, una scelta che protegge da SQL injection e rende il codice più leggibile.
7. Verifica dell’esistenza della risorsa
Prima di aggiornare, il codice controlla che l’articolo esista davvero. Se non viene trovato, la risposta è 404 Not Found. Questo evita di eseguire update inutili e migliora la chiarezza dell’API.
8. Aggiornamento e risposta finale
Dopo l’update, il record viene ricaricato dal database e restituito al client. È una scelta pratica perché il client riceve subito i dati aggiornati, compreso il timestamp updated_at.
Best practice
- Usa PATCH solo per aggiornamenti parziali: se devi sostituire completamente una risorsa, valuta PUT.
- Valida sempre i dati in ingresso: non fidarti mai del JSON ricevuto dal client.
- Restituisci codici HTTP corretti: 200 per successo, 400 per input errato, 404 se la risorsa non esiste, 405 per metodi non consentiti, 422 per dati semanticamente invalidi.
- Usa query parametrizzate: evita concatenazioni dirette e proteggi il database da injection.
- Aggiorna solo i campi ammessi: non lasciare che il client modifichi colonne sensibili come id, created_at o campi interni.
- Rendi coerente il formato delle risposte: usa sempre una struttura simile con success, message e, quando serve, data.
- Gestisci i casi vuoti: se il payload non contiene campi validi, rispondi con un errore chiaro invece di eseguire una query inutile.
Riepilogo
In questo tutorial abbiamo visto come costruire in PHP un endpoint RESTful per l’aggiornamento parziale di una risorsa tramite PATCH. Questo approccio è molto utile quando vuoi modificare solo alcuni campi, mantenendo l’API efficiente e facile da usare.
I punti chiave da ricordare sono:
- accettare solo il metodo HTTP corretto;
- leggere e validare il JSON in ingresso;
- costruire query dinamiche sicure con PDO;
- restituire risposte coerenti e codici HTTP appropriati;
- aggiornare solo i campi consentiti.
Con questa base puoi implementare endpoint PATCH per molte risorse diverse, mantenendo il codice pulito e professionale.
Approfondisci con risorse ufficiali
- PHP Manual - PDO: documentazione ufficiale su connessioni e query sicure con PDO.
- PHP Manual - json_decode: riferimento per la gestione del JSON in PHP.
- MDN Web Docs - HTTP request methods: panoramica chiara su GET, POST, PUT, PATCH e DELETE.
- MDN Web Docs - HTTP response status codes: guida ai codici di stato HTTP più usati nelle API.
- RFC 5789: specifica ufficiale del metodo PATCH.
