# 4. PHP & databases

El treball de **PHP amb bases de dades (BBDD)** és un pilar fonamental en el desenvolupament web dinàmic. PHP va néixer estretament lligat a MySQL, però avui dia interactua de manera eficient amb pràcticament qualsevol sistema de gestió de bases de dades (DBMS), com PostgreSQL, SQLite, o SQL Server.  
  
**Extensions i Connexió**

PHP proporciona diverses extensions per connectar-se a BDD, sent les més importants i utilitzades:

1. **MySQLi (MySQL Improved)**: Una extensió orientada a objectes i funcional que és l'evolució de l'antic driver MySQL.
2. **PDO (PHP Data Objects)**: Aquesta és l'opció més recomanada i versàtil. PDO no és un driver per a una base de dades específica, sinó una capa d'abstracció de dades que permet utilitzar el mateix codi per connectar-se a múltiples tipus de bases de dades.

**PHP** treballa amb bases de dades principalment mitjançant **PDO** (capa d'abstracció de dades) o **MySQLi**. La seguretat en la interacció amb la BDD es garanteix mitjançant l'ús de **sentències preparades** (Prepared Statements), que separen les dades de l'usuari de la lògica de la consulta SQL per prevenir atacs d'**injecció SQL**.

# Introducció

## Extensions i Connexió

PHP proporciona diverses extensions per connectar-se a BDD, sent les més importants i utilitzades:

<div class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-mysqli-%28mysql-improv">1. **MySQLi (MySQL Improved):** Una extensió orientada a objectes i funcional que és l'evolució de l'antic *driver* MySQL. És la preferida per treballar amb MySQL/MariaDB per la seva velocitat i suport a característiques modernes.
2. **PDO (PHP Data Objects):** Aquesta és l'opció **més recomanada** i versàtil. PDO no és un *driver* per a una base de dades específica, sinó una **capa d'abstracció de dades** que permet utilitzar el mateix codi per connectar-se a múltiples tipus de bases de dades (MySQL, PostgreSQL, Oracle, etc.), simplement canviant la cadena de connexió.

</div>## Seguretat: Prepared Statements

El xifratge de contrasenyes no serveix de res si la interacció amb la BBDD és insegura. L'aspecte més crític de la seguretat en BBDD és prevenir els atacs d'**injecció SQL**. Tant MySQLi com PDO permeten l'ús de **sentències preparades** (*Prepared Statements*). Aquest mètode garanteix que les dades de l'usuari (variables) s'enviïn al servidor de BDD separadament de la consulta SQL. El servidor de BDD mai interpreta les dades de l'usuari com a part de la lògica de la consulta, tractant-les exclusivament com a valors de camp.

<div class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-"></div><div class="code-block ng-tns-c2531377157-160 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php"><div class="code-block-decoration header-formatted gds-title-s ng-tns-c2531377157-160 ng-star-inserted"><span class="ng-tns-c2531377157-160">PHP</span><div class="buttons ng-tns-c2531377157-160 ng-star-inserted"></div></div><div class="formatted-code-block-internal-container ng-tns-c2531377157-160"><div class="animated-opacity ng-tns-c2531377157-160"></div></div></div>```php
// Exemple de sentència preparada amb PDO
$stmt = $pdo->prepare("INSERT INTO usuaris (nom, email) VALUES (?, ?)");
$stmt->execute(['David', 'david@exemple.cat']); 
// Les dades s'envien separadament de la consulta, evitant la injecció SQL.
```

<div class="code-block ng-tns-c2531377157-160 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--1"><div class="formatted-code-block-internal-container ng-tns-c2531377157-160"><div class="animated-opacity ng-tns-c2531377157-160"></div></div></div><div class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--2"></div>## Flux de treball

El flux per interactuar amb una BBDD des de PHP és coherent:

<div class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-connexi%C3%B3%3A-establir-u">1. **Connexió:** Establir una connexió al servidor de BBDD (passant el nom de l'usuari, la contrasenya, i la BBDD).
2. **Preparació:** Preparar la sentència SQL.
3. **Execució:** Executar la sentència, passant les dades (vinculades).
4. **Processament:** Processar el resultat (si és una consulta `SELECT`), generalment extreient les files en formats d'array o objectes.
5. **Tancament:** Tancar la connexió (encara que PHP sovint ho fa automàticament en finalitzar l'script).

</div><p class="callout info">PHP actua com un pont eficient i segur que transforma les peticions web en operacions de BBDD i retorna les dades per generar la pàgina web final.</p>

<div class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--3"></div>

# Connexió BBDD

# Connectar base de dades

La connexió a bases de dades MySQL a PHP s'estableix principalment mitjançant l'extensió **MySQLi** (MySQL Improved), que ofereix mètodes orientats a objectes i procedurals. **PDO** (PHP Data Objects) és l'altra alternativa crucial.

## Connexió a MySQL amb MySQLi

L'extensió **MySQLi** és la manera recomanada de treballar amb bases de dades MySQL a PHP, ja que és una versió millorada i més ràpida que l'antiga (i obsoleta) extensió `mysql`.

### 1. Orientat a Objectes (OO)

Aquest és l'estil més modern i preferit.

<div _ngcontent-ng-c1448405485="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-funci%C3%B3%2Fm%C3%A8tode-par%C3%A0me" inline-copy-host=""><table style="width: 100%;"><thead><tr><td style="width: 25.8735%;">Funció/Mètode</td><td style="width: 48.024%;">Paràmetres</td><td style="width: 26.1025%;">Propòsit</td></tr></thead><tbody><tr><td style="width: 25.8735%;">**`new mysqli(...)`**</td><td style="width: 48.024%;">`$servername`, `$username`, `$password`, (opcional: `$dbname`, `$port`, `$socket`)</td><td style="width: 26.1025%;">Crea una nova instància de l'objecte `mysqli`, intentant connectar immediatament.</td></tr><tr><td style="width: 25.8735%;">**`$conn->connect_error`**</td><td style="width: 48.024%;">(Propietat)</td><td style="width: 26.1025%;">Retorna un missatge d'error de la connexió si falla.</td></tr><tr><td style="width: 25.8735%;">**`die(...)`**</td><td style="width: 48.024%;">(Funció)</td><td style="width: 26.1025%;">Atura l'execució de l'script i mostra un missatge. S'utilitza en cas d'error fatal.</td></tr></tbody></table>

</div>**Codi Base (OO):**

<div _ngcontent-ng-c1448405485="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-" inline-copy-host=""></div><div _ngcontent-ng-c2531377157="" class="code-block ng-tns-c2531377157-201 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_df043da227fcedaf","c_f93da456ed382884",null,"rc_9c1073eb338fc481",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c2531377157="" class="code-block-decoration header-formatted gds-title-s ng-tns-c2531377157-201 ng-star-inserted"><span class="ng-tns-c2531377157-201">PHP</span><div _ngcontent-ng-c2531377157="" class="buttons ng-tns-c2531377157-201 ng-star-inserted"><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c2531377157-201 mat-unthemed ng-star-inserted"></button></div></div><div _ngcontent-ng-c2531377157="" class="formatted-code-block-internal-container ng-tns-c2531377157-201"><div _ngcontent-ng-c2531377157="" class="animated-opacity ng-tns-c2531377157-201">  
</div></div></div>```php
$conn = new mysqli($servername, $username, $password);
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}
```

<div _ngcontent-ng-c2531377157="" class="code-block ng-tns-c2531377157-201 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--1" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_df043da227fcedaf","c_f93da456ed382884",null,"rc_9c1073eb338fc481",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c2531377157="" class="formatted-code-block-internal-container ng-tns-c2531377157-201"><div _ngcontent-ng-c2531377157="" class="animated-opacity ng-tns-c2531377157-201"></div></div></div><div _ngcontent-ng-c1448405485="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--2" inline-copy-host=""></div>### 2. Procedural

Aquest estil utilitza funcions que comencen amb el prefix `mysqli_`.

<div _ngcontent-ng-c1448405485="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-funci%C3%B3-par%C3%A0metres-pr" inline-copy-host=""><table style="width: 100%;"><thead><tr><td style="width: 25.6297%;">Funció</td><td style="width: 44.6921%;">Paràmetres</td><td style="width: 29.6782%;">Propòsit</td></tr></thead><tbody><tr><td style="width: 25.6297%;">**`mysqli_connect(...)`**</td><td style="width: 44.6921%;">`$servername`, `$username`, `$password`, (opcional: `$dbname`, `$port`, `$socket`)</td><td style="width: 29.6782%;">Intenta connectar i retorna l'enllaç de connexió ($conn) en cas d'èxit, o `FALSE` en cas d'error.</td></tr><tr><td style="width: 25.6297%;">**`mysqli_connect_error()`**</td><td style="width: 44.6921%;">(Sense paràmetres)</td><td style="width: 29.6782%;">Retorna un missatge d'error de la darrera funció `mysqli_connect()` que ha fallat.</td></tr></tbody></table>

</div>**Codi Base (Procedural):**

<div _ngcontent-ng-c1448405485="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--3" inline-copy-host=""></div><div _ngcontent-ng-c2531377157="" class="code-block ng-tns-c2531377157-202 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php-1" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_df043da227fcedaf","c_f93da456ed382884",null,"rc_9c1073eb338fc481",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c2531377157="" class="code-block-decoration header-formatted gds-title-s ng-tns-c2531377157-202 ng-star-inserted"><span class="ng-tns-c2531377157-202">PHP</span><div _ngcontent-ng-c2531377157="" class="buttons ng-tns-c2531377157-202 ng-star-inserted"><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c2531377157-202 mat-unthemed ng-star-inserted"></button></div></div><div _ngcontent-ng-c2531377157="" class="formatted-code-block-internal-container ng-tns-c2531377157-202"><div _ngcontent-ng-c2531377157="" class="animated-opacity ng-tns-c2531377157-202">  
</div></div></div>```php
$conn = mysqli_connect($servername, $username, $password);
if (!$conn) {
    die("Connection failed: " . mysqli_connect_error());
}
```

<div _ngcontent-ng-c2531377157="" class="code-block ng-tns-c2531377157-202 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--4" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_df043da227fcedaf","c_f93da456ed382884",null,"rc_9c1073eb338fc481",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c2531377157="" class="formatted-code-block-internal-container ng-tns-c2531377157-202"><div _ngcontent-ng-c2531377157="" class="animated-opacity ng-tns-c2531377157-202"></div></div></div>## Errors Més Comuns

Els errors en la connexió solen ser d'aquests tipus:

<div _ngcontent-ng-c1448405485="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-error-com%C3%BA-causa-sol" inline-copy-host=""><table style="width: 100%;"><thead><tr><td style="width: 27.6405%;">Error Comú</td><td style="width: 27.6635%;">Causa</td><td style="width: 44.8153%;">Solució Típica</td></tr></thead><tbody><tr><td style="width: 27.6405%;">**"Access denied for user..."**</td><td style="width: 27.6635%;">Credencials incorrectes (usuari o contrasenya).</td><td style="width: 44.8153%;">Verificar la configuració a l'allotjament o al servidor local.</td></tr><tr><td style="width: 27.6405%;">**"Unknown database..."**</td><td style="width: 27.6635%;">La base de dades especificada no existeix.</td><td style="width: 44.8153%;">Assegurar-se que `$dbname` (si s'ha afegit al constructor) sigui correcte.</td></tr><tr><td style="width: 27.6405%;">**"Connection refused"**</td><td style="width: 27.6635%;">El servidor MySQL no s'està executant o el *firewall* el bloqueja.</td><td style="width: 44.8153%;">Iniciar el servidor MySQL (p. ex., XAMPP/WAMP/MAMP) o comprovar ports.</td></tr><tr><td style="width: 27.6405%;">**"Undefined property..."**</td><td style="width: 27.6635%;">Si s'intenta accedir a l'objecte `$conn` després que hagi fallat.</td><td style="width: 44.8153%;">La verificació de l'error hauria d'aturar l'script (`die()`).</td></tr></tbody></table>

</div>## Paràmetres Essencials

Els paràmetres bàsics per a la connexió són:

<div _ngcontent-ng-c1448405485="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-%24servername%3A-normalm" inline-copy-host="">- `$servername`: Normalment **`"localhost"`** per a entorns de desenvolupament o el nom de l'amfitrió del servidor de BBDD a l'allotjament.
- `$username`: El nom d'usuari de MySQL (p. ex., `"root"` en desenvolupament).
- `$password`: La contrasenya de l'usuari de MySQL.
- **Recomanat:** Afegir el nom de la base de dades (`$dbname`) com a quart paràmetre, que permet seleccionar la BBDD alhora de connectar.

</div><div _ngcontent-ng-c1448405485="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--5" inline-copy-host="">  
</div>## I com funciona amb PDO?

És una excel·lent pràctica! **PDO** (**P**HP **D**ata **O**bjects) és la manera més recomanada d'establir connexions a bases de dades amb PHP per la seva seguretat i versatilitat. A diferència de MySQLi, PDO és purament **Orientat a Objectes** i s'utilitza una capa d'abstracció (el DSN) per especificar el tipus de base de dades, fent-lo molt més flexible.

Amb PDO, la connexió s'estableix creant una nova instància de l'objecte `PDO`. El procés es realitza dins d'un bloc **`try...catch`** per gestionar els errors mitjançant **excepcions**, una pràctica de gestió d'errors superior a la funció `die()`.

### Codi Base (Orientat a Objectes)

<div _ngcontent-ng-c1448405485="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--6" inline-copy-host=""></div><div _ngcontent-ng-c2531377157="" class="code-block ng-tns-c2531377157-214 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php-2" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_81cc8162bf8dbad9","c_f93da456ed382884",null,"rc_4f11b50b39265a8c",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c2531377157="" class="code-block-decoration header-formatted gds-title-s ng-tns-c2531377157-214 ng-star-inserted"><span class="ng-tns-c2531377157-214">PHP</span><div _ngcontent-ng-c2531377157="" class="buttons ng-tns-c2531377157-214 ng-star-inserted"><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c2531377157-214 mat-unthemed ng-star-inserted"></button></div></div><div _ngcontent-ng-c2531377157="" class="formatted-code-block-internal-container ng-tns-c2531377157-214"><div _ngcontent-ng-c2531377157="" class="animated-opacity ng-tns-c2531377157-214">  
</div></div></div>```php
<?php
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "database_name";

// 1. Construcció de la cadena DSN (Data Source Name)
// Especifica el tipus de BDD (mysql) i els detalls de la connexió.
$dsn = "mysql:host=$servername;dbname=$dbname;charset=utf8mb4";

// 2. Opcions de configuració (Recomanades per a seguretat i errors)
$opcions = [
    // Llança una excepció en cas d'error de SQL (millor gestió)
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    // Desactiva l'emulació de sentències preparades (més seguretat contra Injecció SQL)
    PDO::ATTR_EMULATE_PREPARES   => false,
    // Mode per defecte de retorn de resultats (objectes)
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
];

try {
    // Crear la connexió
    $pdo = new PDO($dsn, $username, $password, $opcions);
    echo "Connected successfully (via PDO)";
    
    // Aquí s'executarien les consultes utilitzant $pdo
    
} catch (PDOException $e) {
    // Captura els errors de connexió
    // Es mostra el missatge d'error i el codi (codi d'error més baix de la BDD).
    die("Connection failed: " . $e->getMessage() . " (Code: " . $e->getCode() . ")");
}
// La connexió es tanca automàticament quan l'script finalitza o si es fa $pdo = null;
?>

```

<div _ngcontent-ng-c2531377157="" class="code-block ng-tns-c2531377157-214 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--7" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_81cc8162bf8dbad9","c_f93da456ed382884",null,"rc_4f11b50b39265a8c",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c2531377157="" class="formatted-code-block-internal-container ng-tns-c2531377157-214"><div _ngcontent-ng-c2531377157="" class="animated-opacity ng-tns-c2531377157-214"></div></div></div>## Components de PDO

<div _ngcontent-ng-c1448405485="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-component%2Ffunci%C3%B3-par" inline-copy-host=""><table style="width: 100%;"><thead><tr><td style="width: 24.8959%;">Component/Funció</td><td style="width: 26.3556%;">Paràmetres/Valor</td><td style="width: 48.7485%;">Propòsit</td></tr></thead><tbody><tr><td style="width: 24.8959%;">**`$dsn` (Data Source Name)**</td><td style="width: 26.3556%;">`"mysql:host=...;dbname=...;charset=..."`</td><td style="width: 48.7485%;">**Defineix l'estructura** de la connexió (tipus de BDD, servidor, nom de la BDD i codificació). El `charset=utf8mb4` és crucial per al suport complet d'emojis.</td></tr><tr><td style="width: 24.8959%;">**`new PDO(...)`**</td><td style="width: 26.3556%;">`$dsn`, `$username`, `$password`, `$opcions`</td><td style="width: 48.7485%;">Crea l'objecte de connexió i inicia l'intent de connexió.</td></tr><tr><td style="width: 24.8959%;">**`try...catch`**</td><td style="width: 26.3556%;">`PDOException $e`</td><td style="width: 48.7485%;">Mecanisme de gestió d'errors estàndard i superior. Si la connexió falla, el `catch` intercepta l'error.</td></tr><tr><td style="width: 24.8959%;">**`$e->getMessage()`**</td><td style="width: 26.3556%;">(Mètode)</td><td style="width: 48.7485%;">Obté el missatge d'error de la base de dades (p. ex., "Access denied").</td></tr><tr><td style="width: 24.8959%;">**`PDO::ATTR_ERRMODE`**</td><td style="width: 26.3556%;">`PDO::ERRMODE_EXCEPTION`</td><td style="width: 48.7485%;">**Mecanisme Clau:** Força PDO a llançar objectes d'excepció en cas d'errors de SQL. Això és molt més net i segur que comprovar errors manuals.</td></tr><tr><td style="width: 24.8959%;">**`PDO::ATTR_EMULATE_PREPARES`**</td><td style="width: 26.3556%;">`false`</td><td style="width: 48.7485%;">**Mecanisme Clau de Seguretat:** Desactiva l'emulació de sentències preparades de PHP i força el *driver* a enviar les sentències preparades de manera nativa al servidor de BDD, oferint la màxima protecció contra la **Injecció SQL**.</td></tr></tbody></table>

</div>### Errors en PDO

<div _ngcontent-ng-c1448405485="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-dsn-incorrecte%3A-un-e" inline-copy-host="">1. **DSN Incorrecte:** Un error de sintaxi al `$dsn` (p. ex., oblidar el punt i coma o escriure malament `mysql:host`).
2. **Credencials:** Usuari/contrasenya incorrectes, com en MySQLi.
3. **Encoding:** No especificar `charset=utf8mb4` pot provocar problemes amb caràcters especials.

</div>### Avantatges PDO

<div _ngcontent-ng-c1448405485="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-seguretat-superior%3A-" inline-copy-host="">- **Seguretat Superior:** Fàcilment configurable per deshabilitar l'emulació de preparació i forçar les sentències preparades segures.
- **Uniformitat:** Utilitza el mateix codi per a qualsevol base de dades (MySQL, SQLite, etc.).
- **Gestió d'Errors Moderna:** Utilitza excepcions, que permeten al codi *continuar* o *gestionar* l'error de manera més elegant que amb un `die()`.

</div><div _ngcontent-ng-c1448405485="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--8" inline-copy-host=""></div>El format utilitzat en les cadenes de connexió **DSN** (**D**ata **S**ource **N**ame) serveix per proporcionar a l'extensió **PDO** de PHP tota la informació necessària per connectar-se a una base de dades (BDD) específica, independentment del seu tipus (MySQL, PostgreSQL, SQLite, etc.).

## Estructura del DSN

El format DSN és una cadena de text amb una sintaxi molt específica. La cadena es divideix en dues parts principals separades pel signe de dos punts (`:`):

### Driver (Tipus BBDD)

La primera part especifica el controlador de la base de dades que PDO ha d'utilitzar. Aquesta és la clau de la flexibilitat de PDO.

<div _ngcontent-ng-c1448405485="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-exemples-de-drivers%3A" inline-copy-host="">- **Exemples de Drivers:**
    
    
    - `mysql`
    - `pgsql` (PostgreSQL)
    - `sqlite`
    - `sqlsrv` (Microsoft SQL Server)

</div>### Paràmetres específics

La segona part (després dels dos punts) conté els detalls de la connexió. Aquests paràmetres s'especifiquen com a parelles **`nom=valor`** separades per un punt i coma (`;`). Els paràmetres més comuns per a una connexió **MySQL** són:

<div _ngcontent-ng-c1448405485="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-host%3A-el-nom-o-adre%C3%A7" inline-copy-host="">- **`host`:** El nom o adreça IP del servidor de la BBDD (p. ex., `localhost`).
- **`dbname`:** El nom de la base de dades a la qual es vol connectar.
- **`charset`:** La codificació de caràcters a utilitzar (p. ex., `utf8mb4` per al suport complet d'emojis i caràcters especials).

</div>### Exemple MySQL

La cadena DSN que has vist als exemples de PDO es construeix així:

<div _ngcontent-ng-c1448405485="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--9" inline-copy-host=""></div><div _ngcontent-ng-c2531377157="" class="code-block ng-tns-c2531377157-82 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php-3" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_a6de4734b95819f2","c_f93da456ed382884",null,"rc_ad3ccabef73cac90",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c2531377157="" class="code-block-decoration header-formatted gds-title-s ng-tns-c2531377157-82 ng-star-inserted"><span class="ng-tns-c2531377157-82">PHP</span><div _ngcontent-ng-c2531377157="" class="buttons ng-tns-c2531377157-82 ng-star-inserted"><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c2531377157-82 mat-unthemed _mat-animation-noopable ng-star-inserted"></button></div></div><div _ngcontent-ng-c2531377157="" class="formatted-code-block-internal-container ng-tns-c2531377157-82"><div _ngcontent-ng-c2531377157="" class="animated-opacity ng-tns-c2531377157-82">  
</div></div></div>```php
$servername = "localhost";
$dbname = "base_de_dades_prova";

$dsn = "mysql:host=$servername;dbname=$dbname;charset=utf8mb4";
//     ^^^^^ ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^
//     Driver    Host + Valor      BDD + Valor    Charset + Valor
```

<div _ngcontent-ng-c2531377157="" class="code-block ng-tns-c2531377157-82 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--10" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_a6de4734b95819f2","c_f93da456ed382884",null,"rc_ad3ccabef73cac90",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c2531377157="" class="formatted-code-block-internal-container ng-tns-c2531377157-82"><div _ngcontent-ng-c2531377157="" class="animated-opacity ng-tns-c2531377157-82"></div></div></div><div _ngcontent-ng-c1448405485="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--11" inline-copy-host=""></div>Aquesta cadena `$dsn` és un dels tres paràmetres essencials que es passen al constructor de l'objecte PDO, juntament amb el nom d'usuari i la contrasenya:

<div _ngcontent-ng-c1448405485="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--12" inline-copy-host=""></div><div _ngcontent-ng-c2531377157="" class="code-block ng-tns-c2531377157-83 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php-4" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_a6de4734b95819f2","c_f93da456ed382884",null,"rc_ad3ccabef73cac90",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c2531377157="" class="code-block-decoration header-formatted gds-title-s ng-tns-c2531377157-83 ng-star-inserted"><span class="ng-tns-c2531377157-83">PHP</span><div _ngcontent-ng-c2531377157="" class="buttons ng-tns-c2531377157-83 ng-star-inserted"><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c2531377157-83 mat-unthemed _mat-animation-noopable ng-star-inserted"></button></div></div><div _ngcontent-ng-c2531377157="" class="formatted-code-block-internal-container ng-tns-c2531377157-83"><div _ngcontent-ng-c2531377157="" class="animated-opacity ng-tns-c2531377157-83">  
</div></div></div>```php
$pdo = new PDO($dsn, $username, $password);
```

<div _ngcontent-ng-c2531377157="" class="code-block ng-tns-c2531377157-83 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--13" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_a6de4734b95819f2","c_f93da456ed382884",null,"rc_ad3ccabef73cac90",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c2531377157="" class="formatted-code-block-internal-container ng-tns-c2531377157-83"><div _ngcontent-ng-c2531377157="" class="animated-opacity ng-tns-c2531377157-83"></div></div></div><div _ngcontent-ng-c1448405485="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--14" inline-copy-host=""></div>

# Tancar connexió BBDD

Tancar la connexió amb la base de dades és una bona pràctica per alliberar recursos al servidor, tot i que PHP la tanca automàticament quan l'script finalitza. Aquí teniu com es tanca explícitament la connexió amb els tres mecanismes principals: **MySQLi OO**, **MySQLi Procedural** i **PDO**.

## MySQLi Orientat a Objectes (OO)

En l'estil orientat a objectes, s'utilitza el mètode **`close()`** de l'objecte de connexió.

<div _ngcontent-ng-c1448405485="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-" inline-copy-host=""></div><div _ngcontent-ng-c2531377157="" class="code-block ng-tns-c2531377157-227 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_134094fd3a729eba","c_f93da456ed382884",null,"rc_b5a592e8d948fa9c",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c2531377157="" class="code-block-decoration header-formatted gds-title-s ng-tns-c2531377157-227 ng-star-inserted"><span class="ng-tns-c2531377157-227">PHP</span><div _ngcontent-ng-c2531377157="" class="buttons ng-tns-c2531377157-227 ng-star-inserted"><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c2531377157-227 mat-unthemed ng-star-inserted"></button></div></div><div _ngcontent-ng-c2531377157="" class="formatted-code-block-internal-container ng-tns-c2531377157-227"><div _ngcontent-ng-c2531377157="" class="animated-opacity ng-tns-c2531377157-227"></div></div></div>```php
<?php
// Suposant que $conn és l'objecte de connexió mysqli
$conn = new mysqli($servername, $username, $password, $dbname); 

// ... Codi d'execució de consultes ...

// Tancar la connexió
$conn->close();
?>
```

<div _ngcontent-ng-c2531377157="" class="code-block ng-tns-c2531377157-227 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--1" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_134094fd3a729eba","c_f93da456ed382884",null,"rc_b5a592e8d948fa9c",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c2531377157="" class="formatted-code-block-internal-container ng-tns-c2531377157-227"><div _ngcontent-ng-c2531377157="" class="animated-opacity ng-tns-c2531377157-227"></div></div></div>## MySQLi Procedural (Funció)

En l'estil procedural, s'utilitza la funció **`mysqli_close()`**, passant l'enllaç de connexió com a paràmetre.

<div _ngcontent-ng-c1448405485="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--2" inline-copy-host=""></div><div _ngcontent-ng-c2531377157="" class="code-block ng-tns-c2531377157-228 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php-1" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_134094fd3a729eba","c_f93da456ed382884",null,"rc_b5a592e8d948fa9c",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c2531377157="" class="code-block-decoration header-formatted gds-title-s ng-tns-c2531377157-228 ng-star-inserted"><span class="ng-tns-c2531377157-228">PHP</span><div _ngcontent-ng-c2531377157="" class="buttons ng-tns-c2531377157-228 ng-star-inserted"><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c2531377157-228 mat-unthemed ng-star-inserted"></button></div></div><div _ngcontent-ng-c2531377157="" class="formatted-code-block-internal-container ng-tns-c2531377157-228"><div _ngcontent-ng-c2531377157="" class="animated-opacity ng-tns-c2531377157-228"></div></div></div>```php
<?php
// Suposant que $conn és l'enllaç de connexió retornat per mysqli_connect()
$conn = mysqli_connect($servername, $username, $password, $dbname);

// ... Codi d'execució de consultes ...

// Tancar la connexió
mysqli_close($conn);
?>
```

<div _ngcontent-ng-c2531377157="" class="code-block ng-tns-c2531377157-228 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--3" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_134094fd3a729eba","c_f93da456ed382884",null,"rc_b5a592e8d948fa9c",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c2531377157="" class="formatted-code-block-internal-container ng-tns-c2531377157-228"><div _ngcontent-ng-c2531377157="" class="animated-opacity ng-tns-c2531377157-228"></div></div></div>## PDO (PHP Data Objects)

Amb PDO, no hi ha cap mètode de tancament explícit com `close()`. La connexió es tanca eliminant la referència a l'objecte de connexió, és a dir, posant la variable a **`null`**.

<div _ngcontent-ng-c1448405485="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--4" inline-copy-host=""></div><div _ngcontent-ng-c2531377157="" class="code-block ng-tns-c2531377157-229 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php-2" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_134094fd3a729eba","c_f93da456ed382884",null,"rc_b5a592e8d948fa9c",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c2531377157="" class="code-block-decoration header-formatted gds-title-s ng-tns-c2531377157-229 ng-star-inserted"><span class="ng-tns-c2531377157-229">PHP</span><div _ngcontent-ng-c2531377157="" class="buttons ng-tns-c2531377157-229 ng-star-inserted"><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c2531377157-229 mat-unthemed ng-star-inserted"></button></div></div><div _ngcontent-ng-c2531377157="" class="formatted-code-block-internal-container ng-tns-c2531377157-229"><div _ngcontent-ng-c2531377157="" class="animated-opacity ng-tns-c2531377157-229"></div></div></div>```php
<?php
// Suposant que $pdo és l'objecte de connexió PDO
$pdo = new PDO($dsn, $username, $password);

// ... Codi d'execució de consultes ...

// Tancar la connexió (eliminant la referència a l'objecte)
$pdo = null; 
?>
```

<div _ngcontent-ng-c2531377157="" class="code-block ng-tns-c2531377157-229 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--5" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_134094fd3a729eba","c_f93da456ed382884",null,"rc_b5a592e8d948fa9c",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c2531377157="" class="formatted-code-block-internal-container ng-tns-c2531377157-229"><div _ngcontent-ng-c2531377157="" class="animated-opacity ng-tns-c2531377157-229"></div></div></div><div _ngcontent-ng-c1448405485="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--6" inline-copy-host=""></div>

# Seguretat

La seguretat en la interacció entre PHP i Bases de Dades (BDD) és crucial per protegir les aplicacions web de l'explotació de vulnerabilitats, sent l'atac més crític la Injecció SQL. La clau de la seguretat resideix en tractar sempre les dades d'entrada com a simples valors, mai com a codi executable.

1\. **Mecanisme Fonamental**: ***Sentències Preparades***

El pilar de la defensa són les Sentències Preparades (Prepared Statements). Aquest mètode, suportat per PDO (recomanat) i MySQLi, separa el codi SQL de les dades de l'usuari. De forma que si un atacant introdueix codi SQL (p. ex., 1 OR 1=1), el servidor de BBDD l'interpreta literalment com una cadena de text, no com una part de la consulta, neutralitzant l'atac.

**2. Capa d'Abstracció: PDO**

PDO (PHP Data Objects) és l'opció preferida perquè força l'ús de sentències preparades, ofereix una gestió d'errors més robusta mitjançant excepcions dins de blocs try...catch.

**3. Integritat i Control: Transaccions**

Per a operacions de múltiples passos (insercions/actualitzacions), l'ús de Transaccions (amb beginTransaction(), commit(), i rollBack()) garanteix l'integritat de dades. Si una de les consultes falla, cap no s'executa, evitant estats inconsistents a la BDD.

**4. Protecció de Credencials**

Finalment, la seguretat exigeix mai emmagatzemar credencials de BDD en repositoris públics (Git) i mantenir-les en fitxers de configuració fora de l'accés web (.env o similars). Així, la seguretat entre PHP i BDD es basa en una regla: mai concatenar dades d'usuari directament a la consulta SQL.

# SQL Injection

És important veure com un atac d'Injecció SQL pot ser més destructiu que només retornar dades. Aquí teniu un exemple que permetria a l'atacant executar una ordre per **eliminar una taula** (`DROP TABLE`).

## Atac drop table

En aquest cas, l'atacant utilitzarà el caràcter de **punt i coma (`;`)** per finalitzar la consulta original i encadenar una nova ordre SQL.

### PHP Vulnerable

El codi PHP, vulnerable a la concatenació:

<div _ngcontent-ng-c1448405485="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-" inline-copy-host=""></div><div _ngcontent-ng-c2531377157="" class="code-block ng-tns-c2531377157-186 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_868ba9aed7dfb36c","c_f93da456ed382884",null,"rc_e656dc7c6b431450",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c2531377157="" class="code-block-decoration header-formatted gds-title-s ng-tns-c2531377157-186 ng-star-inserted"><span class="ng-tns-c2531377157-186">PHP</span><div _ngcontent-ng-c2531377157="" class="buttons ng-tns-c2531377157-186 ng-star-inserted"><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c2531377157-186 mat-unthemed ng-star-inserted"></button></div></div><div _ngcontent-ng-c2531377157="" class="formatted-code-block-internal-container ng-tns-c2531377157-186"><div _ngcontent-ng-c2531377157="" class="animated-opacity ng-tns-c2531377157-186"></div></div></div>```php
// Codi PHP VULNERABLE
$product_id = $_GET['id']; // Valor obtingut de ?id=...

// Consulta original:
$sql = "SELECT nom, preu, descripcio FROM productes WHERE id = " . $product_id;

// Execució de la consulta...
// $db->query($sql);
```

<div _ngcontent-ng-c2531377157="" class="code-block ng-tns-c2531377157-186 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--1" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_868ba9aed7dfb36c","c_f93da456ed382884",null,"rc_e656dc7c6b431450",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c2531377157="" class="formatted-code-block-internal-container ng-tns-c2531377157-186"><div _ngcontent-ng-c2531377157="" class="animated-opacity ng-tns-c2531377157-186"></div></div></div><div _ngcontent-ng-c1448405485="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--2" inline-copy-host=""></div>### Vector d'atac

L'atacant aprofita que el motor SQL interpreta el punt i coma com el final d'una instrucció i l'inici d'una nova.

<div _ngcontent-ng-c1448405485="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-acci%C3%B3-url-sol%C2%B7licita" inline-copy-host=""><table style="width: 82.1429%;"><thead><tr><td style="width: 20.4644%;">Acció</td><td style="width: 41.5239%;">URL Sol·licitada</td><td style="width: 38.0116%;">Valor `$product_id`</td></tr></thead><tbody><tr><td style="width: 20.4644%;">**Atac**</td><td style="width: 41.5239%;">`?id=3; DROP TABLE productes; --`</td><td style="width: 38.0116%;">`3; DROP TABLE productes; --`</td></tr></tbody></table>

</div>### La SQL resultant

Quan el codi PHP concatena la cadena d'atac, el servidor de bases de dades rep **dues** instruccions per executar:

<div _ngcontent-ng-c1448405485="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-part-de-la-consulta-" inline-copy-host=""><table style="width: 100%;"><thead><tr><td style="width: 32.7669%;">Part de la Consulta</td><td style="width: 67.2331%;">Explicació</td></tr></thead><tbody><tr><td style="width: 32.7669%;">**`SELECT nom, preu, descripcio FROM productes WHERE id = 3;`**</td><td style="width: 67.2331%;">La primera consulta. El servidor l'executa i la processa (o la ignora, depenent de l'èxit o el context).</td></tr><tr><td style="width: 32.7669%;">**`DROP TABLE productes;`**</td><td style="width: 67.2331%;">**La segona consulta!** Aquesta ordre destrueix permanentment la taula de productes.</td></tr><tr><td style="width: 32.7669%;">**`--`**</td><td style="width: 67.2331%;">El doble guió (`--`) és un comentari a SQL. Aquest caràcter fa que el servidor ignori la resta de la consulta, incloent-hi qualsevol cometa o caràcter que PHP hagi afegit al final, assegurant que l'ordre `DROP TABLE` s'executi correctament.</td></tr></tbody></table>

</div>### Conseqüències

El servidor de BDD executa la segona ordre encadenada (`DROP TABLE productes;`), resultant en la **pèrdua irrecuperable de totes les dades** contingudes a la taula de productes.

## Solució

Com sempre, l'única defensa eficaç contra aquests atacs és l'ús de **Sentències Preparades** (amb **PDO** o **MySQLi**). Les sentències preparades no permeten que el caràcter punt i coma actuï com a separador d'ordres ni que l'apòstrof actuï com a tancament de camp, ja que tracten tota la cadena d'atac com una única dada literal.

<div _ngcontent-ng-c1448405485="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--3" inline-copy-host=""></div><div _ngcontent-ng-c2531377157="" class="code-block ng-tns-c2531377157-187 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php-1" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_868ba9aed7dfb36c","c_f93da456ed382884",null,"rc_e656dc7c6b431450",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c2531377157="" class="code-block-decoration header-formatted gds-title-s ng-tns-c2531377157-187 ng-star-inserted"><span class="ng-tns-c2531377157-187">PHP</span><div _ngcontent-ng-c2531377157="" class="buttons ng-tns-c2531377157-187 ng-star-inserted"><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c2531377157-187 mat-unthemed ng-star-inserted"></button></div></div><div _ngcontent-ng-c2531377157="" class="formatted-code-block-internal-container ng-tns-c2531377157-187"><div _ngcontent-ng-c2531377157="" class="animated-opacity ng-tns-c2531377157-187"></div></div></div>```php
// SQL SEGUR: L'ID es tracta com un valor, no com a codi SQL
$stmt = $pdo->prepare("SELECT nom, preu, descripcio FROM productes WHERE id = ?");
$stmt->execute(['3; DROP TABLE productes; --']); 
// El servidor cerca literalment un producte amb aquest ID inexistent i no executa l'ordre DROP TABLE.
```

<div _ngcontent-ng-c2531377157="" class="code-block ng-tns-c2531377157-187 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--4" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_868ba9aed7dfb36c","c_f93da456ed382884",null,"rc_e656dc7c6b431450",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c2531377157="" class="formatted-code-block-internal-container ng-tns-c2531377157-187"><div _ngcontent-ng-c2531377157="" class="animated-opacity ng-tns-c2531377157-187"></div></div></div><div _ngcontent-ng-c1448405485="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--5" inline-copy-host=""></div>

# Sentències preparades

Les **Sentències Preparades** (*Prepared Statements*) són un mecanisme de seguretat i eficiència en bases de dades que garanteix que les dades proporcionades per l'usuari es tractin exclusivament com a **valors de camp**, i mai com a part del **codi SQL**. Funcionen en dos passos principals:

### Pas 1. Preparació (Separació de codi i dades)

El codi SQL amb marcadors de posició (generalment **`?`** o `:nom`) s'envia al servidor de la base de dades **abans** de rebre cap dada de l'usuari.

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-" inline-copy-host=""></div><div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-265 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-sql" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_667d3ad98392c782","c_f93da456ed382884",null,"rc_f7bcda626649a130",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="code-block-decoration header-formatted gds-title-s ng-tns-c243700105-265 ng-star-inserted"><span class="ng-tns-c243700105-265">SQL</span><div _ngcontent-ng-c243700105="" class="buttons ng-tns-c243700105-265 ng-star-inserted"><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c243700105-265 mat-unthemed _mat-animation-noopable ng-star-inserted"></button></div></div><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-265"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-265"></div></div></div>```sql
SELECT nom, email FROM Usuaris WHERE id = ?  -- El servidor 'prepara' aquesta estructura.
```

<div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-265 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--1" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_667d3ad98392c782","c_f93da456ed382884",null,"rc_f7bcda626649a130",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-265"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-265"></div></div></div><div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--2" inline-copy-host=""></div>El servidor compila l'estructura de la consulta i sap exactament on esperar les dades.

### Pas 2. Execució (Vinculació segura)

Posteriorment, les variables que contenen les dades de l'usuari s'envien al servidor de manera **separada** (o "vinculada") per omplir els marcadors de posició. Si un usuari malintencionat envia una cadena amb codi SQL (p. ex., `3 OR 1=1`), el servidor de BBDD l'interpreta literalment com una cadena de text dins del camp (`'3 OR 1=1'`) i no com una instrucció SQL per executar.

#### Avantatges

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-seguretat%3A-prevenci%C3%B3" inline-copy-host="">- **Seguretat:** Prevenció total de l'**Injecció SQL**, ja que cap dada d'entrada es pot executar com a codi.
- **Eficiència:** Si una sentència s'executa diverses vegades (p. ex., dins d'un bucle o transacció), el servidor només l'ha de compilar una vegada, estalviant temps de processament.
- **Claredat:** Millora la llegibilitat del codi.

</div>## I en PHP?

Aquesta seria la base teòrica de les Sentències Preparades, ara veurem com s'implementen amb exemples pràctics en els tres mètodes: **MySQLi Procedural**, **MySQLi OO**, i **PDO**. Utilitzarem una consulta simple de selecció amb una clàusula `WHERE`.

### MySQLi Procedural

Aquest mètode utilitza funcions que comencen amb `mysqli_` per gestionar l'enllaç de connexió i l'objecte de la sentència.

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--3" inline-copy-host=""></div><div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-277 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_f558db386d01902b","c_f93da456ed382884",null,"rc_11862050a6554ab2",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="code-block-decoration header-formatted gds-title-s ng-tns-c243700105-277 ng-star-inserted"><span class="ng-tns-c243700105-277">PHP</span><div _ngcontent-ng-c243700105="" class="buttons ng-tns-c243700105-277 ng-star-inserted"><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c243700105-277 mat-unthemed _mat-animation-noopable ng-star-inserted"></button></div></div><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-277"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-277"></div></div></div>```php
<?php
// Suposant que $conn és l'enllaç de connexió
$id_a_cercar = 15;

// 1. Preparar la sentència: El ? marca el lloc de la dada.
$sql = "SELECT nom, email FROM Usuaris WHERE id = ?";
$stmt = mysqli_prepare($conn, $sql);

// 2. Vincular el paràmetre: 'i' = integer (la dada de l'usuari)
mysqli_stmt_bind_param($stmt, "i", $id_a_cercar);

// 3. Executar la sentència
mysqli_stmt_execute($stmt);

// 4. Obtenir el resultat
$result = mysqli_stmt_get_result($stmt);

// 5. Processar les dades
if ($row = mysqli_fetch_assoc($result)) {
    echo "Procedural: Trobada l'ID " . $id_a_cercar . " per a " . $row['nom'] . "\n";
} else {
    echo "Procedural: No s'ha trobat cap usuari amb aquesta ID.\n";
}

// 6. Tancar
mysqli_stmt_close($stmt);
?>
```

<div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-277 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--4" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_f558db386d01902b","c_f93da456ed382884",null,"rc_11862050a6554ab2",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-277"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-277"></div></div></div>### MySQLi Orientat a Objectes (OO)

Aquest mètode utilitza mètodes dels objectes `$conn` i `$stmt`. És més net que el procedural.

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--5" inline-copy-host=""></div><div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-278 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php-1" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_f558db386d01902b","c_f93da456ed382884",null,"rc_11862050a6554ab2",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="code-block-decoration header-formatted gds-title-s ng-tns-c243700105-278 ng-star-inserted"><span class="ng-tns-c243700105-278">PHP</span><div _ngcontent-ng-c243700105="" class="buttons ng-tns-c243700105-278 ng-star-inserted"><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c243700105-278 mat-unthemed _mat-animation-noopable ng-star-inserted"></button></div></div><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-278"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-278"></div></div></div>```php
<?php
// Suposant que $conn és l'objecte de connexió mysqli
$id_a_cercar = 20;

// 1. Preparar la sentència
$sql = "SELECT nom, email FROM Usuaris WHERE id = ?";
$stmt = $conn->prepare($sql);

// 2. Vincular el paràmetre: 'i' = integer
$stmt->bind_param("i", $id_a_cercar);

// 3. Executar la sentència
$stmt->execute();

// 4. Obtenir el resultat
$result = $stmt->get_result();

// 5. Processar les dades
if ($row = $result->fetch_assoc()) {
    echo "OO: Trobada l'ID " . $id_a_cercar . " per a " . $row['nom'] . "\n";
} else {
    echo "OO: No s'ha trobat cap usuari amb aquesta ID.\n";
}

// 6. Tancar
$stmt->close();
?>
```

<div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-278 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--6" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_f558db386d01902b","c_f93da456ed382884",null,"rc_11862050a6554ab2",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-278"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-278"></div></div></div>### PDO (PHP Data Objects)

PDO és el mètode més recomanat per la seva simplicitat en la vinculació de paràmetres i gestió d'errors.

#### Mètode amb marcador de posició (`?`)

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--7" inline-copy-host=""></div><div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-279 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php-2" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_f558db386d01902b","c_f93da456ed382884",null,"rc_11862050a6554ab2",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="code-block-decoration header-formatted gds-title-s ng-tns-c243700105-279 ng-star-inserted"><span class="ng-tns-c243700105-279">PHP</span><div _ngcontent-ng-c243700105="" class="buttons ng-tns-c243700105-279 ng-star-inserted"><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c243700105-279 mat-unthemed _mat-animation-noopable ng-star-inserted"></button></div></div><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-279"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-279"></div></div></div>```php
<?php
// Suposant que $pdo és l'objecte de connexió PDO
$id_a_cercar = 25;

try {
    // 1. Preparar la sentència
    $sql = "SELECT nom, email FROM Usuaris WHERE id = ?";
    $stmt = $pdo->prepare($sql);

    // 2. Executar la sentència: Es passen les dades directament com un array.
    $stmt->execute([$id_a_cercar]); 

    // 3. Obtenir el resultat (només la primera fila)
    $row = $stmt->fetch(PDO::FETCH_ASSOC);

    // 4. Processar les dades
    if ($row) {
        echo "PDO: Trobada l'ID " . $id_a_cercar . " per a " . $row['nom'] . "\n";
    } else {
        echo "PDO: No s'ha trobat cap usuari amb aquesta ID.\n";
    }

} catch (PDOException $e) {
    die("Error de PDO: " . $e->getMessage());
}
// $pdo = null;
?>
```

<div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-279 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--8" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_f558db386d01902b","c_f93da456ed382884",null,"rc_11862050a6554ab2",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-279"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-279"></div></div></div><div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--9" inline-copy-host=""></div>#### Mètode alternatiu amb marcador nominal (`:nom`)

PDO també permet utilitzar noms en lloc de signes d'interrogació, cosa que és útil quan hi ha molts paràmetres.

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--10" inline-copy-host=""></div><div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-280 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php-3" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_f558db386d01902b","c_f93da456ed382884",null,"rc_11862050a6554ab2",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="code-block-decoration header-formatted gds-title-s ng-tns-c243700105-280 ng-star-inserted"><span class="ng-tns-c243700105-280">PHP</span><div _ngcontent-ng-c243700105="" class="buttons ng-tns-c243700105-280 ng-star-inserted"><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c243700105-280 mat-unthemed _mat-animation-noopable ng-star-inserted"></button></div></div><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-280"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-280"></div></div></div>```php
// PDO amb marcador nominal
$id_a_cercar = 30;
$sql = "SELECT nom, email FROM Usuaris WHERE id = :id_usuari";
$stmt = $pdo->prepare($sql);

// Executar: Es passa un array associatiu amb el nom del marcador (sense els ':')
$stmt->execute(['id_usuari' => $id_a_cercar]);
// ... resta del processament ...
```

<div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-280 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--11" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_f558db386d01902b","c_f93da456ed382884",null,"rc_11862050a6554ab2",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-280"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-280"></div></div></div>Com veieu, en tots els mètodes s'evita la concatenació (p. ex., `WHERE id = '{$id_a_cercar}'`) i s'utilitza la **funció de preparació**. Aquest petit canvi és la diferència entre un codi **vulnerable a la Injecció SQL** i un codi **segur** i professional. **PDO** simplifica notablement el procés en l'etapa d'execució, ja que no requereix la definició explícita dels tipus de dades (`'i'`, `'s'`, etc., com fa MySQLi). En lloc d'això, **PDO infereix el tipus de dada** (com *string* o *integer*) **directament de la variable de PHP** subministrada en l'array d'execució, o bé es pot definir explícitament (p. ex., `PDO::PARAM_INT`) per a una major precisió.

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--12" inline-copy-host=""></div>##### El mètode `bindValue()` (Alternativa)

Tot i que l'execució amb l'array associatiu (`$stmt->execute([...])`) és la forma més ràpida i recomanada amb PDO, també podeu vincular els marcadors nominals un per un amb el mètode `bindValue()`:

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--13" inline-copy-host=""></div><div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-304 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php-4" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_6050ef3ec6ce6cf1","c_f93da456ed382884",null,"rc_8f34c5519abe0e76",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="code-block-decoration header-formatted gds-title-s ng-tns-c243700105-304 ng-star-inserted"><span class="ng-tns-c243700105-304">PHP</span><div _ngcontent-ng-c243700105="" class="buttons ng-tns-c243700105-304 ng-star-inserted"><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c243700105-304 mat-unthemed _mat-animation-noopable ng-star-inserted"></button></div></div><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-304"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-304"></div></div></div>```php
// 2. Mètode bindValue() (més verbós, però útil si cal definir tipus específics)
$stmt->bindValue(':id_usuari', $id_a_cercar, PDO::PARAM_INT);  //PDO::PARAM_STR per a tipus string

$stmt->execute();
// ... resta del processament ...
```

<div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-304 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--14" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_6050ef3ec6ce6cf1","c_f93da456ed382884",null,"rc_8f34c5519abe0e76",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-304"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-304"></div></div></div><div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--15" inline-copy-host=""></div>

# Transaccions

Les **Transaccions** amb PDO permeten agrupar múltiples consultes SQL (insercions, actualitzacions, eliminacions) en una única unitat de treball. Això assegura l'**Atomicitat** de les dades: o bé **totes** les consultes dins la transacció s'executen amb èxit i s'apliquen permanentment a la base de dades, o bé **cap** no s'aplica, i es desfà qualsevol canvi fet (`rollback`). Això és fonamental per a l'**integritat de dades**.

## Mecanismes PDO

PDO utilitza tres mètodes essencials en el seu objecte de connexió (`$pdo`) per gestionar transaccions. Aquest procés només funciona amb taules que suporten transaccions, com **InnoDB** a MySQL.

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-m%C3%A8tode-prop%C3%B2sit-acci" inline-copy-host=""><table style="width: 100%;"><thead><tr><td style="width: 25.614%;">**Mètode**</td><td style="width: 58.7722%;">**Propòsit**</td><td style="width: 15.4946%;">**Acció SQL Equivalent**</td></tr></thead><tbody><tr><td style="width: 25.614%;">**`beginTransaction()`**</td><td style="width: 58.7722%;">Inicia la transacció. Totes les consultes següents s'executen en memòria i no afecten la BDD fins a la confirmació.</td><td style="width: 15.4946%;">`START TRANSACTION;`</td></tr><tr><td style="width: 25.614%;">**`commit()`**</td><td style="width: 58.7722%;">Fa els canvis permanents a la base de dades. Només s'executa si totes les consultes anteriors han tingut èxit.</td><td style="width: 15.4946%;">`COMMIT;`</td></tr><tr><td style="width: 25.614%;">**`rollBack()`**</td><td style="width: 58.7722%;">Desfà tots els canvis fets des de l'inici de la transacció si es produeix un error.</td><td style="width: 15.4946%;">`ROLLBACK;`</td></tr></tbody></table>

</div>## Procediment habitual (Try...Catch)

El patró de disseny més segur i recomanat per a les transaccions amb PDO és utilitzar un bloc **`try...catch`** per gestionar automàticament qualsevol error.

### Exemple: Transferència bancària

En una transferència, s'han de fer dues operacions (`UPDATE`): restar d'un compte i sumar a l'altre. Si la resta falla, la suma no s'ha de fer.

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-" inline-copy-host=""></div><div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-119 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_d3ed057509b7c046","c_f93da456ed382884",null,"rc_3be95c26100f4abd",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="code-block-decoration header-formatted gds-title-s ng-tns-c243700105-119 ng-star-inserted"><span class="ng-tns-c243700105-119">PHP</span><div _ngcontent-ng-c243700105="" class="buttons ng-tns-c243700105-119 ng-star-inserted"><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c243700105-119 mat-unthemed _mat-animation-noopable ng-star-inserted"></button></div></div><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-119"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-119"></div></div></div>```php
<?php
// Suposant que $pdo és una connexió PDO vàlida
$compte_origen = 101;
$compte_desti = 202;
$quantitat = 50.00;

try {
    // 1. INICIAR la transacció
    $pdo->beginTransaction(); 
    
    // --- CONSULTA 1: Restar de l'origen ---
    $sql1 = "UPDATE Comptes SET saldo = saldo - ? WHERE id = ?";
    $stmt1 = $pdo->prepare($sql1);
    $stmt1->execute([$quantitat, $compte_origen]);
    
    // NOTA: Es podria afegir una comprovació (p. ex., si saldo < quantitat)
    // if ($stmt1->rowCount() == 0) { throw new Exception("Compte origen no trobat."); }

    // --- CONSULTA 2: Sumar al destí ---
    $sql2 = "UPDATE Comptes SET saldo = saldo + ? WHERE id = ?";
    $stmt2 = $pdo->prepare($sql2);
    $stmt2->execute([$quantitat, $compte_desti]);
    
    // 2. CONFIRMAR: Si tot ha anat bé, es fa permanent
    $pdo->commit();
    echo "Transferència completada amb èxit. Els canvis són permanents.";

} catch (PDOException $e) {
    // 3. DESFER: Si hi ha un error SQL, desfem tots els canvis
    if ($pdo->inTransaction()) {
        $pdo->rollBack();
    }
    echo "ERROR: La transferència ha fallat. Canvis desfets. Detalls: " . $e->getMessage();
} catch (Exception $e) {
    // Captura d'errors lògics (p. ex., la nostra excepció de saldo)
    if ($pdo->inTransaction()) {
        $pdo->rollBack();
    }
    echo "ERROR LÒGIC: " . $e->getMessage();
}

$pdo = null;
?>

```

<div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-119 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--1" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_d3ed057509b7c046","c_f93da456ed382884",null,"rc_3be95c26100f4abd",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-119"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-119"></div></div></div>## Avantatges

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-integritat%3A-garantei" inline-copy-host="">- **Integritat:** Garanteix que la base de dades es mantingui en un estat consistent. Sense transaccions, si la primera consulta té èxit i la segona falla (per un problema de connexió, per exemple), el compte d'origen perdria diners sense que el compte de destí els rebés.
- **Eficiència:** L'execució de múltiples consultes en una transacció sol ser **més ràpida** que l'execució individual, ja que el servidor de BDD optimitza les escriptures.
- **Aïllament:** Mentre es realitza una transacció, la resta d'usuaris no veuran els canvis parcials (escriptures), garantint l'aïllament del procés.

</div>

# Escapar caràcters maliciosos

Escapar caràcters maliciosos és una part essencial de la seguretat web, ja que neutralitza les dades d'entrada per assegurar-se que només es tractin com a text, i no com a codi executable. Aquesta protecció s'ha d'aplicar en dos contextos principals: **SQL** (per prevenir la Injecció SQL) i **HTML** (per prevenir l'Scripting entre Llocs o XSS).

## Escapar caràcters per a SQL (BBDD)

La funció d'escapament s'utilitza per afegir una barra invertida (`\`) davant de caràcters especials de SQL que podrien ser interpretats com a codi, com les cometes simples (`'`), les dobles cometes (`"`), i la barra invertida mateixa (`\`).

### Mètode antic i desaconsellat

Les funcions **`mysqli::real_escape_string()`** (OO) o **`mysqli_real_escape_string()`** (Procedural) són eines vàlides, però el seu ús directament a les consultes concatenades és **perillós** i **obsolet** com a mètode de seguretat primari.

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-estil-funci%C3%B3-%C3%9As-oo-%24" inline-copy-host=""><table style="width: 84.5238%;"><thead><tr><td style="width: 23.9671%;">**Estil**</td><td style="width: 52.4786%;">**Funció**</td><td style="width: 23.5543%;">**Ús**</td></tr></thead><tbody><tr><td style="width: 23.9671%;">**OO**</td><td style="width: 52.4786%;">`$conn->real_escape_string($string)`</td><td style="width: 23.5543%;">Escapa la cadena.</td></tr><tr><td style="width: 23.9671%;">**Procedural**</td><td style="width: 52.4786%;">`mysqli_real_escape_string($conn, $string)`</td><td style="width: 23.5543%;">Escapa la cadena.</td></tr></tbody></table>

</div>**Per què és obsolet?** Encara que aquesta funció escapa les dades, depèn que el desenvolupador utilitzi **sempre** la funció correctament en **totes** les consultes. Un oblit provoca una vulnerabilitat.

### Mètode recomanat: Sentències preparades

La manera correcta d'assegurar la integritat de SQL és utilitzar **Sentències Preparades (Prepared Statements)** amb **PDO** o **MySQLi**. Aquest mètode:

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-separa-el-codi-de-le" inline-copy-host="">1. **Separa el Codi de les Dades:** El motor SQL rep la consulta i les dades per separat.
2. **L'escapament es Fa Automàticament:** El *driver* de la base de dades (i no el PHP) s'encarrega d'escapar o gestionar internament les dades de manera segura abans d'executar-les, eliminant la necessitat de cridar manualment `real_escape_string()`.

</div>Si utilitzeu sentències preparades, **no necessiteu** cridar `real_escape_string()`.

## Escapar caràcters HTML (Output)

La funció **`htmlspecialchars()`** s'utilitza quan es mostra una dada d'usuari (que s'ha obtingut de la base de dades) a la pàgina HTML. Això és vital per prevenir atacs de **Scripting entre Llocs (XSS)**.

<p class="callout warning">**ATENCIÓ!** Convertir caràcters que tenen un significat especial en HTML (com `<` i `>`) en les seves entitats HTML (`&lt;` i `&gt;`).</p>

### Funció

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-funci%C3%B3-prop%C3%B2sit-html" inline-copy-host=""><table style="width: 93.3333%;"><thead><tr><td style="width: 34.0936%;">**Funció**</td><td style="width: 65.9064%;">**Propòsit**</td></tr></thead><tbody><tr><td style="width: 34.0936%;">**`htmlspecialchars($string)`**</td><td style="width: 65.9064%;">Converteix caràcters especials en entitats HTML, neutralitzant el codi incrustat.</td></tr></tbody></table>

</div>### Exemple

Si un atacant introdueix el nom `NomUsuari <script>alert('XSS!')</script>`, la funció ho converteix:

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-" inline-copy-host=""></div><div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-319 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_a69308ac002603c4","c_f93da456ed382884",null,"rc_6d0b42a17471fc3f",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="code-block-decoration header-formatted gds-title-s ng-tns-c243700105-319 ng-star-inserted"><span class="ng-tns-c243700105-319">PHP</span><div _ngcontent-ng-c243700105="" class="buttons ng-tns-c243700105-319 ng-star-inserted"><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c243700105-319 mat-unthemed _mat-animation-noopable ng-star-inserted"></button></div></div><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-319"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-319"></div></div></div>```php
$dada_bruta = "NomUsuari <script>alert('XSS!')</script>";

// 1. Aplicar htmlspecialchars
$dada_neta = htmlspecialchars($dada_bruta, ENT_QUOTES, 'UTF-8');

// 2. El resultat és segur per a ser mostrat en HTML
echo $dada_neta;
// Sortida: NomUsuari &lt;script&gt;alert('XSS!')&lt;/script&gt;
```

<div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-319 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--1" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_a69308ac002603c4","c_f93da456ed382884",null,"rc_6d0b42a17471fc3f",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-319"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-319"></div></div></div><div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--2" inline-copy-host=""></div>#### Paràmetres

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-%24flags-%28ent_quotes%29%3A" inline-copy-host="">1. **`$flags` (`ENT_QUOTES`):** Aquest és el paràmetre més important. Indica a PHP que també ha d'escapar les cometes simples (`'`) i les dobles cometes (`"`) com a entitats.
    
    
    - **Sense `ENT_QUOTES`:** Un atacant podria tancar un atribut HTML (p. ex., `<input value="...">`) amb cometes, fent fallar la protecció.
    - **Amb `ENT_QUOTES`:** Tanca aquesta vulnerabilitat.
2. **`$encoding` (`'UTF-8'`):** Defineix la codificació de caràcters, assegurant el tractament correcte de tots els símbols internacionals.

</div>## Resum

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-context-risc-m%C3%A8tode-" inline-copy-host=""><table style="width: 98.4524%;"><thead><tr><td style="width: 20.3315%;">**Context**</td><td style="width: 23.4941%;">**Risc**</td><td style="width: 56.1743%;">**Mètode Recomanat**</td></tr></thead><tbody><tr><td style="width: 20.3315%;">**Dades a SQL**</td><td style="width: 23.4941%;">Injecció SQL</td><td style="width: 56.1743%;">**Sentències Preparades (PDO/MySQLi).** Oblida't de `real_escape_string()`.</td></tr><tr><td style="width: 20.3315%;">**Dades a HTML**</td><td style="width: 23.4941%;">XSS (Cross-Site Scripting)</td><td style="width: 56.1743%;">**`htmlspecialchars($dada, ENT_QUOTES, 'UTF-8')`** just abans de mostrar-la.</td></tr></tbody></table>

</div>

# Crear una BBDD i una taula

Utilitzant els tres mètodes —**MySQLi Orientat a Objectes (OO)**, **MySQLi Procedural**, i **PDO**—, el procés de creació d'una base de dades (BBDD) i una taula a dins segueix el mateix patró: primer s'estableix la connexió al servidor MySQL (sense especificar la BBDD), es crea la BBDD amb `CREATE DATABASE`, i finalment s'estableix una nova connexió o es selecciona la BBDD per crear la taula amb `CREATE TABLE`.

## MySQLi Procedural

Aquest mètode utilitza funcions amb el prefix `mysqli_`.

### Pas 1: Creació de la Base de Dades

Primer, connectem al servidor sense seleccionar cap BDD, i després utilitzem `mysqli_query()` per crear-la.

<div class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-"></div><div class="code-block ng-tns-c2531377157-250 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php"><div class="code-block-decoration header-formatted gds-title-s ng-tns-c2531377157-250 ng-star-inserted"><span class="ng-tns-c2531377157-250">PHP</span><div class="buttons ng-tns-c2531377157-250 ng-star-inserted"></div></div><div class="formatted-code-block-internal-container ng-tns-c2531377157-250"><div class="animated-opacity ng-tns-c2531377157-250"></div></div></div>```php
<?php
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "NovaBDDProcedural";

// 1. Connexió al servidor
$conn = mysqli_connect($servername, $username, $password);
if (!$conn) {
    die("Connexió fallida: " . mysqli_connect_error());
}

// 2. Creació de la Base de Dades
$sql_db = "CREATE DATABASE IF NOT EXISTS $dbname";
if (mysqli_query($conn, $sql_db)) {
    echo "BDD '$dbname' creada amb èxit.\n";
} else {
    echo "Error creant BDD: " . mysqli_error($conn) . "\n";
}
// Tanca la connexió al servidor
mysqli_close($conn); 
?>
```

<div class="code-block ng-tns-c2531377157-250 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--1"><div class="formatted-code-block-internal-container ng-tns-c2531377157-250"><div class="animated-opacity ng-tns-c2531377157-250"></div></div></div><div class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--2"></div>### Pas 2: Creació de la Taula

Obrim una nova connexió, seleccionant directament la BBDD que acabem de crear, i llavors creem la taula.

<div class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--3"></div><div class="code-block ng-tns-c2531377157-251 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php-1"><div class="code-block-decoration header-formatted gds-title-s ng-tns-c2531377157-251 ng-star-inserted"><span class="ng-tns-c2531377157-251">PHP</span><div class="buttons ng-tns-c2531377157-251 ng-star-inserted"></div></div><div class="formatted-code-block-internal-container ng-tns-c2531377157-251"><div class="animated-opacity ng-tns-c2531377157-251"></div></div></div>```php
<?php
// Nova Connexió (amb la BDD seleccionada)
$conn = mysqli_connect($servername, $username, $password, $dbname);
if (!$conn) {
    die("Connexió fallida: " . mysqli_connect_error());
}

// Creació de la Taula
$sql_table = "CREATE TABLE IF NOT EXISTS Usuaris (
    id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    nom VARCHAR(30) NOT NULL,
    email VARCHAR(50),
    data_registre TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
)";

if (mysqli_query($conn, $sql_table)) {
    echo "Taula 'Usuaris' creada amb èxit (Procedural).\n";
} else {
    echo "Error creant taula: " . mysqli_error($conn) . "\n";
}
mysqli_close($conn); 
?>
```

<div class="code-block ng-tns-c2531377157-251 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--4"><div class="formatted-code-block-internal-container ng-tns-c2531377157-251"><div class="animated-opacity ng-tns-c2531377157-251"></div></div></div>## MySQLi Orientat a Objectes (OO)

Aquest mètode utilitza l'objecte `$conn` i el mètode **`query()`**.

### Pas 1: Creació de la Base de Dades

<div class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--5"></div><div class="code-block ng-tns-c2531377157-252 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php-2"><div class="code-block-decoration header-formatted gds-title-s ng-tns-c2531377157-252 ng-star-inserted"><span class="ng-tns-c2531377157-252">PHP</span><div class="buttons ng-tns-c2531377157-252 ng-star-inserted"></div></div><div class="formatted-code-block-internal-container ng-tns-c2531377157-252"><div class="animated-opacity ng-tns-c2531377157-252"></div></div></div>```php
<?php
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "NovaBDDOO";

// 1. Connexió al servidor (sense BDD)
$conn = new mysqli($servername, $username, $password);
if ($conn->connect_error) {
    die("Connexió fallida: " . $conn->connect_error);
}

// 2. Creació de la Base de Dades
$sql_db = "CREATE DATABASE IF NOT EXISTS $dbname";
if ($conn->query($sql_db) === TRUE) {
    echo "BDD '$dbname' creada amb èxit.\n";
} else {
    echo "Error creant BDD: " . $conn->error . "\n";
}
$conn->close();
?>
```

<div class="code-block ng-tns-c2531377157-252 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--6"><div class="formatted-code-block-internal-container ng-tns-c2531377157-252"><div class="animated-opacity ng-tns-c2531377157-252"></div></div></div><div class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--7"></div>### Pas 2: Creació de la Taula

Obrim una nova connexió, especificant la BBDD al constructor.

<div class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--8"></div><div class="code-block ng-tns-c2531377157-253 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php-3"><div class="code-block-decoration header-formatted gds-title-s ng-tns-c2531377157-253 ng-star-inserted"><span class="ng-tns-c2531377157-253">PHP</span><div class="buttons ng-tns-c2531377157-253 ng-star-inserted"></div></div><div class="formatted-code-block-internal-container ng-tns-c2531377157-253"><div class="animated-opacity ng-tns-c2531377157-253"></div></div></div>```php
<?php
// Nova Connexió (amb la BDD seleccionada)
$conn = new mysqli($servername, $username, $password, $dbname);
if ($conn->connect_error) {
    die("Connexió fallida: " . $conn->connect_error);
}

// Creació de la Taula
$sql_table = "CREATE TABLE IF NOT EXISTS Usuaris (
    id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    nom VARCHAR(30) NOT NULL,
    email VARCHAR(50),
    data_registre TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
)";

if ($conn->query($sql_table) === TRUE) {
    echo "Taula 'Usuaris' creada amb èxit (OO).\n";
} else {
    echo "Error creant taula: " . $conn->error . "\n";
}
$conn->close();
?>
```

<div class="code-block ng-tns-c2531377157-253 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--9"><div class="formatted-code-block-internal-container ng-tns-c2531377157-253"><div class="animated-opacity ng-tns-c2531377157-253"></div></div></div>## PDO (PHP Data Objects)

PDO utilitza el mètode **`exec()`** per executar consultes SQL que no retornen un conjunt de resultats (com `CREATE` o `DROP`).

### Pas 1: Creació de la Base de Dades

Connectem al servidor amb un **DSN** (Data Source Name) sense la clau `dbname`.

<div class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--10"></div><div class="code-block ng-tns-c2531377157-254 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php-4"><div class="code-block-decoration header-formatted gds-title-s ng-tns-c2531377157-254 ng-star-inserted"><span class="ng-tns-c2531377157-254">PHP</span><div class="buttons ng-tns-c2531377157-254 ng-star-inserted"></div></div><div class="formatted-code-block-internal-container ng-tns-c2531377157-254"><div class="animated-opacity ng-tns-c2531377157-254"></div></div></div>```php
<?php
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "NovaBDDPDO";

// DSN que només apunta al servidor (sense especificar BDD)
$dsn_server = "mysql:host=$servername;charset=utf8mb4";

try {
    // Connexió al servidor
    $pdo = new PDO($dsn_server, $username, $password);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    // Creació de la Base de Dades
    $sql_db = "CREATE DATABASE IF NOT EXISTS $dbname";
    $pdo->exec($sql_db);
    echo "BDD '$dbname' creada amb èxit.\n";
    
    // Tancar la connexió
    $pdo = null;

} catch (PDOException $e) {
    die("Connexió fallida o error de BDD: " . $e->getMessage());
}
?>
```

<div class="code-block ng-tns-c2531377157-254 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--11"><div class="formatted-code-block-internal-container ng-tns-c2531377157-254"><div class="animated-opacity ng-tns-c2531377157-254"></div></div></div><div class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--12"></div>### Pas 2: Creació de la Taula

Obrim una nova connexió al servidor, ara sí, especificant la BBDD.

<div class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--13"></div><div class="code-block ng-tns-c2531377157-255 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php-5"><div class="code-block-decoration header-formatted gds-title-s ng-tns-c2531377157-255 ng-star-inserted"><span class="ng-tns-c2531377157-255">PHP</span><div class="buttons ng-tns-c2531377157-255 ng-star-inserted"></div></div><div class="formatted-code-block-internal-container ng-tns-c2531377157-255"><div class="animated-opacity ng-tns-c2531377157-255"></div></div></div>```php
<?php
// DSN amb la BDD seleccionada
$dsn_db = "mysql:host=$servername;dbname=$dbname;charset=utf8mb4";

try {
    // Nova Connexió a la BDD creada
    $pdo = new PDO($dsn_db, $username, $password);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    // Creació de la Taula
    $sql_table = "CREATE TABLE IF NOT EXISTS Usuaris (
        id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
        nom VARCHAR(30) NOT NULL,
        email VARCHAR(50),
        data_registre TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
    )";

    $pdo->exec($sql_table);
    echo "Taula 'Usuaris' creada amb èxit (PDO).\n";
    
    // Tancar la connexió
    $pdo = null;

} catch (PDOException $e) {
    die("Connexió fallida o error de BDD: " . $e->getMessage());
}
?>
```

<div class="code-block ng-tns-c2531377157-255 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--14"><div class="formatted-code-block-internal-container ng-tns-c2531377157-255"><div class="animated-opacity ng-tns-c2531377157-255"></div></div></div><div class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--15"></div>

# Manipulació dades

# Select data

La realització de consultes **`SELECT`** segures amb **clàusules `WHERE`** i la implementació de **paginació** són operacions fonamentals. Utilitzarem la tècnica de **Sentències preparades** per a la clàusula `WHERE` per garantir la seguretat contra la injecció SQL, i les clàusules **`LIMIT`** i **`OFFSET`** per a la paginació.

## MySQLi Procedural

Aquest mètode utilitza funcions prefixades amb `mysqli_` i punts d'interrogació (`?`) com a marcadors de posició.

### Consulta, filtre (`WHERE`) i paginació

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-" inline-copy-host=""></div><div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-215 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_cadaeafcd36dc7a3","c_f93da456ed382884",null,"rc_9c614b6d6a41e66d",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="code-block-decoration header-formatted gds-title-s ng-tns-c243700105-215 ng-star-inserted"><span class="ng-tns-c243700105-215">PHP</span><div _ngcontent-ng-c243700105="" class="buttons ng-tns-c243700105-215 ng-star-inserted"><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c243700105-215 mat-unthemed _mat-animation-noopable ng-star-inserted"></button></div></div><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-215"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-215"></div></div></div>```php
<?php
// Suposant que $conn és la connexió establerta
// Dades de Paginació (pàgina 2, 10 ítems per pàgina)
$elements_per_pagina = 10;
$pagina_actual = 2;
$offset = ($pagina_actual - 1) * $elements_per_pagina;
$limit = $elements_per_pagina;
$filtre_nom = 'A%'; // Cerca noms que comencin per 'A'

// 1. Preparar la sentència
// Ús de ? per al filtre WHERE. LIMIT i OFFSET es concatenen per a mysqli_prepare (és una limitació)
$sql = "SELECT nom, email FROM Usuaris WHERE nom LIKE ? LIMIT ?, ?";
$stmt = mysqli_prepare($conn, $sql);

// 2. Vincular les variables
// Tipus: 's' per a string, 'i' per a integer. IMPORTANT: L'offset i el limit han de ser 'i'
mysqli_stmt_bind_param($stmt, "sii", $filtre_nom, $offset, $limit);

// 3. Executar la sentència
mysqli_stmt_execute($stmt);

// 4. Obtenir el conjunt de resultats
$result = mysqli_stmt_get_result($stmt);

// 5. Processar i mostrar les dades
if (mysqli_num_rows($result) > 0) {
    echo "<h2>Resultats (MySQLi Procedural)</h2>";
    while ($row = mysqli_fetch_assoc($result)) {
        echo "Nom: " . $row['nom'] . " - Email: " . $row['email'] . "<br>";
    }
} else {
    echo "No s'han trobat resultats.";
}

// 6. Tancar
mysqli_stmt_close($stmt);
mysqli_close($conn);
?>
```

<div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-215 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--1" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_cadaeafcd36dc7a3","c_f93da456ed382884",null,"rc_9c614b6d6a41e66d",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-215"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-215"></div></div></div>## MySQLi Orientat a Objectes (OO)

Aquest mètode utilitza mètodes de l'objecte `$conn` i l'objecte `$stmt`.

### Consulta, filtre (`WHERE`) i paginació

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--2" inline-copy-host=""></div><div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-216 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php-1" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_cadaeafcd36dc7a3","c_f93da456ed382884",null,"rc_9c614b6d6a41e66d",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="code-block-decoration header-formatted gds-title-s ng-tns-c243700105-216 ng-star-inserted"><span class="ng-tns-c243700105-216">PHP</span><div _ngcontent-ng-c243700105="" class="buttons ng-tns-c243700105-216 ng-star-inserted"><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c243700105-216 mat-unthemed _mat-animation-noopable ng-star-inserted"></button></div></div><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-216"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-216"></div></div></div>```php
<?php
// Suposant que $conn és l'objecte de connexió mysqli
// Dades de Paginació
$elements_per_pagina = 10;
$pagina_actual = 2;
$offset = ($pagina_actual - 1) * $elements_per_pagina;
$limit = $elements_per_pagina;
$filtre_nom = 'A%';

// 1. Preparar la sentència
$sql = "SELECT nom, email FROM Usuaris WHERE nom LIKE ? LIMIT ?, ?";
$stmt = $conn->prepare($sql);

// 2. Vincular les variables
$stmt->bind_param("sii", $filtre_nom, $offset, $limit);

// 3. Executar la sentència
$stmt->execute();

// 4. Obtenir el resultat
$result = $stmt->get_result();

// 5. Processar i mostrar les dades
if ($result->num_rows > 0) {
    echo "<h2>Resultats (MySQLi OO)</h2>";
    while ($row = $result->fetch_assoc()) {
        echo "Nom: " . $row['nom'] . " - Email: " . $row['email'] . "<br>";
    }
} else {
    echo "No s'han trobat resultats.";
}

// 6. Tancar
$stmt->close();
$conn->close();
?>
```

<div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-216 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--3" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_cadaeafcd36dc7a3","c_f93da456ed382884",null,"rc_9c614b6d6a41e66d",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-216"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-216"></div></div></div>## PDO (PHP Data Objects)

PDO és la forma més simple i segura per als selects, especialment en la vinculació de paràmetres.

### Consulta, filtre (`WHERE`) i paginació

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--4" inline-copy-host=""></div><div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-217 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php-2" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_cadaeafcd36dc7a3","c_f93da456ed382884",null,"rc_9c614b6d6a41e66d",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="code-block-decoration header-formatted gds-title-s ng-tns-c243700105-217 ng-star-inserted"><span class="ng-tns-c243700105-217">PHP</span><div _ngcontent-ng-c243700105="" class="buttons ng-tns-c243700105-217 ng-star-inserted"><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c243700105-217 mat-unthemed _mat-animation-noopable ng-star-inserted"></button></div></div><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-217"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-217"></div></div></div>```php
<?php
// Suposant que $pdo és l'objecte de connexió PDO
// Dades de Paginació
$elements_per_pagina = 10;
$pagina_actual = 2;
$offset = ($pagina_actual - 1) * $elements_per_pagina;
$limit = $elements_per_pagina;
$filtre_nom = 'A%';

try {
    // 1. Preparar la sentència (utilitzant marcadors de posició '?')
    $sql = "SELECT nom, email FROM Usuaris WHERE nom LIKE ? LIMIT ? OFFSET ?";
    $stmt = $pdo->prepare($sql);

    // 2. Vincular i executar (la manera més neta)
    // IMPORTANT: PDO NO necessita definir els tipus (s, i, d), però sí que s'han de passar al mateix ordre.
    // L'offset i el limit han de ser int, per la qual cosa cal assegurar-se que $offset i $limit són numèrics.
    $stmt->execute([$filtre_nom, $limit, $offset]);

    // 3. Obtenir totes les dades com un array associatiu
    $resultats = $stmt->fetchAll(PDO::FETCH_ASSOC);

    // 4. Processar i mostrar les dades
    if (count($resultats) > 0) {
        echo "<h2>Resultats (PDO)</h2>";
        foreach ($resultats as $row) {
            echo "Nom: " . $row['nom'] . " - Email: " . $row['email'] . "<br>";
        }
    } else {
        echo "No s'han trobat resultats.";
    }

} catch (PDOException $e) {
    die("Error en la consulta: " . $e->getMessage());
}

// 5. Tancar
$pdo = null;
?>
```

<div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-217 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--5" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_cadaeafcd36dc7a3","c_f93da456ed382884",null,"rc_9c614b6d6a41e66d",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-217"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-217"></div></div></div><div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--6" inline-copy-host=""></div>### Paginació

La **paginació** es realitza a nivell de SQL mitjançant les clàusules **`LIMIT`** i **`OFFSET`**:

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-limit-n%3A-indica-el-n" inline-copy-host="">- **`LIMIT N`**: Indica el nombre màxim de files que es volen retornar (els elements per pàgina).
- **`OFFSET M`**: Indica el nombre de files que s'han de saltar des del principi del conjunt de resultats.

</div>La fórmula per calcular l'offset sempre és:

[![image.png](https://siensis.com/books/uploads/images/gallery/2025-10/scaled-1680-/N4FrLzFsrbXlRC3m-image.png)](https://siensis.com/books/uploads/images/gallery/2025-10/N4FrLzFsrbXlRC3m-image.png)

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--8" inline-copy-host=""><div class="math-block align-center" data-math="\text{offset} = (\text{pàgina\_actual} - 1) \times \text{elements\_per\_pàgina}">  
</div></div>

# Paginació

La **paginació** és un mecanisme essencial en el desenvolupament web i de bases de dades per dividir grans conjunts de dades en pàgines més petites i manejables. Això millora dràsticament el rendiment i l'experiència de l'usuari. El mètode de paginació més comú, es coneix com a **paginació basada en desplaçament (Offset)**. Funciona utilitzant les clàusules **`LIMIT`** i **`OFFSET`** de SQL per dir al motor de la base de dades que retorni només un subconjunt de les dades.

El procés es divideix en tres parts:

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-c%C3%A0lcul-de-par%C3%A0metres" inline-copy-host="">1. **Càlcul de Paràmetres (PHP/Servidor):** El servidor (PHP) determina quina pàgina s'està sol·licitant i quants registres s'han de saltar (`OFFSET`).
2. **Consulta SQL:** El servidor envia la consulta amb els valors `LIMIT` i `OFFSET` inserits de manera segura.
3. **Presentació:** La base de dades retorna només les dades sol·licitades, que es mostren a l'usuari, juntament amb els enllaços de "Pàgina Anterior" i "Pàgina Següent".

</div>### Fórmules

Assumim que `$elements_per_pagina` és el valor de `LIMIT` i `$pagina_actual` és el número de pàgina (començant per 1):

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-par%C3%A0metre-prop%C3%B2sit-f" inline-copy-host=""><table><thead><tr><td>**Paràmetre**</td><td>**Propòsit**</td><td>**Fórmula de Càlcul**</td></tr></thead><tbody><tr><td>**`LIMIT`**</td><td>Nombre màxim de registres a retornar (mida de la pàgina).</td><td>`$elements_per_pagina`</td></tr><tr><td>**`OFFSET`**</td><td>Nombre de registres a saltar des del principi.</td><td>`($pagina_actual - 1) * $elements_per_pagina`</td></tr></tbody></table>

</div>### Exemple SQL

Per obtenir la **tercera pàgina** amb **15 registres** per pàgina:

[![image.png](https://siensis.com/books/uploads/images/gallery/2025-10/scaled-1680-/ViUFLWiUsKklGa1y-image.png)](https://siensis.com/books/uploads/images/gallery/2025-10/ViUFLWiUsKklGa1y-image.png)

<div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-330 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-sql" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_1f8f61dc9b5a3b88","c_f93da456ed382884",null,"rc_65f691da10b05eff",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="code-block-decoration header-formatted gds-title-s ng-tns-c243700105-330 ng-star-inserted"><span class="ng-tns-c243700105-330">SQL</span><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c243700105-330 mat-unthemed _mat-animation-noopable ng-star-inserted"></button></div><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-330"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-330">  
</div></div></div>```sql
SELECT * FROM Orders ORDER BY id LIMIT 15 OFFSET 30;
-- Retorna 15 registres, començant pel registre 31.
```

<div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-330 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--1" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_1f8f61dc9b5a3b88","c_f93da456ed382884",null,"rc_65f691da10b05eff",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-330"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-330"></div></div></div><div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--2" inline-copy-host=""></div><p class="callout danger">**ATENCIÓ** L'`ORDER BY` és crucial; sense ell, els registres retornats podrien canviar a cada consulta</p>

### Avantatges

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-simplicitat%3A-el-conc" inline-copy-host="">- **Simplicitat:** El concepte de `LIMIT` i `OFFSET` és molt fàcil d'entendre i d'implementar en la majoria de bases de dades (MySQL, PostgreSQL, etc.).
- **Accessibilitat:** Permet accedir directament a qualsevol número de pàgina, cosa que facilita la creació d'interfícies amb un menú de navegació numèric (p. ex., 1, 2, 3, 4...).
- **Control del Motor:** Tota la lògica de salt de dades es delega al motor SQL, que és el més eficient per gestionar-ho.

</div>### Inconvenients (rendiment)

El principal desavantatge d'aquest mètode es manifesta en conjunts de dades molt grans i a pàgines molt avançades:

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-escalat-lent-%28slow-s" inline-copy-host="">- **Escalat lent (Slow Scaling):** Quan l'usuari demana una pàgina amb un `OFFSET` molt gran (p. ex., `OFFSET 100000`), el motor de la base de dades *encara ha de recórrer i descartar* 100.000 registres.
- **Bloqueig implícit:** Si les dades estan canviant ràpidament (insercions/eliminacions), la pàgina 3 consultada un minut més tard podria mostrar registres duplicats o ometre'n alguns, ja que els índexs de desplaçament no són estables en el temps.

</div>## Paginació basada en cursors (Keyset/Seek pagination)

Per superar les limitacions d'escalabilitat de l'`OFFSET`, s'utilitza la **Paginació basada en cursors** (o Keysets). En lloc de dir "salta 1000 registres", es pregunta: **"Dona'm els 20 registres següents a la ID 1000"**.

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--3" inline-copy-host=""></div><div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-331 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-sql-1" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_1f8f61dc9b5a3b88","c_f93da456ed382884",null,"rc_65f691da10b05eff",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="code-block-decoration header-formatted gds-title-s ng-tns-c243700105-331 ng-star-inserted"><span class="ng-tns-c243700105-331">SQL</span><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c243700105-331 mat-unthemed _mat-animation-noopable ng-star-inserted"></button></div><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-331"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-331">  
</div></div></div>```php
SELECT * FROM Orders WHERE id > 1000 ORDER BY id ASC LIMIT 20;
```

<div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-331 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--4" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_1f8f61dc9b5a3b88","c_f93da456ed382884",null,"rc_65f691da10b05eff",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-331"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-331"></div></div></div><div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--5" inline-copy-host=""></div>Això evita que el motor hagi de comptar i descartar files. Simplement utilitza l'índex de la columna (`id` en aquest cas) per anar directament al registre desitjat. Aquesta tècnica és molt **més ràpida i eficient** en escales grans, encara que limita la navegació a "Següent" i "Anterior" (no permet anar directament a la pàgina 50).

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--6" inline-copy-host=""></div>### Que passa si no hi ha ID numèric?

Fer una paginació Keyset (o Seek Pagination) quan els IDs no són numèrics és totalment possible. La clau és utilitzar una columna que sigui **ordenada** i **indexada** com a punt de referència (el cursor), tot i que aquesta no sigui l'ID principal. Normalment, s'utilitza una columna de **data/hora** o una combinació de la columna no numèrica i l'ID (per a desempat).

#### Ordre consistent i únic

El principi de Keyset Pagination és trobar el "pròxim" registre basant-se en el valor de l'últim registre vist. Això requereix un camp o una combinació de camps que siguin:

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-ordenables-%28order-by" inline-copy-host="">1. **Ordenables** (`ORDER BY`).
2. **Únics** o gairebé únics (per evitar saltar-se o duplicar registres).

</div>##### Paginació camps de Data/Hora (Timestamps)

Si el teu cursor no pot ser un ID numèric, el millor substitut és una columna **`TIMESTAMP`** o **`DATETIME`** ben indexada, ja que les dates són inherentment ordenables.

**Dades necessàries a cada petició:**

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-%24limit%3A-mida-de-la-p" inline-copy-host="">- `$limit`: Mida de la pàgina (p. ex., 20).
- `$ultima_data`: La data/hora de l'últim registre de la pàgina anterior.

</div>**Consulta SQL:**

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--8" inline-copy-host=""></div><div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-343 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-sql-2" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_e85bf8695a573c08","c_f93da456ed382884",null,"rc_6f009f4171a48282",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="code-block-decoration header-formatted gds-title-s ng-tns-c243700105-343 ng-star-inserted"><span class="ng-tns-c243700105-343">SQL</span><div _ngcontent-ng-c243700105="" class="buttons ng-tns-c243700105-343 ng-star-inserted"><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c243700105-343 mat-unthemed _mat-animation-noopable ng-star-inserted"></button></div></div><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-343"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-343"></div></div></div>```sql
SELECT * FROM Productes 
WHERE data_creacio < :ultima_data 
ORDER BY data_creacio DESC 
LIMIT :limit;
```

<div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-343 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--9" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_e85bf8695a573c08","c_f93da456ed382884",null,"rc_6f009f4171a48282",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-343"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-343"></div></div></div><div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-com-funciona%3A-busca-" inline-copy-host="">- **Com funciona:** Busca productes que siguin **anteriors** a la data de l'últim producte que vam veure.
- **Per a la primera pàgina:** Si `$ultima_data` és null, es pot utilitzar una data molt recent o un alias.

</div>##### Paginació combinació columnes (Desempat)

Si hi ha la possibilitat que dos registres tinguin exactament la mateixa data/hora de creació, necessitem una segona columna per desempatar. S'utilitza la columna no numèrica i després el camp ID (o qualsevol camp únic).

Imaginem que tens un camp **`codi_sku`** (string) i el vols utilitzar per ordenar.

**Dades necessàries a cada petició:**

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-%24limit%3A-mida-de-la-p-1" inline-copy-host="">- `$limit`: Mida de la pàgina.
- `$ultim_sku`: El valor `codi_sku` de l'últim registre de la pàgina anterior.
- `$ultim_id_unic`: El valor ID (o data/hora) de l'últim registre de la pàgina anterior.

</div>**Consulta SQL (Utilitzant `codi_sku` i `id` per desempat):**

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--10" inline-copy-host=""></div><div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-344 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-sql-3" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_e85bf8695a573c08","c_f93da456ed382884",null,"rc_6f009f4171a48282",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="code-block-decoration header-formatted gds-title-s ng-tns-c243700105-344 ng-star-inserted"><span class="ng-tns-c243700105-344">SQL</span><div _ngcontent-ng-c243700105="" class="buttons ng-tns-c243700105-344 ng-star-inserted"><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c243700105-344 mat-unthemed _mat-animation-noopable ng-star-inserted"></button></div></div><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-344"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-344"></div></div></div>```sql
SELECT codi_sku, nom, id
FROM Productes 
WHERE codi_sku > :ultim_sku 
   OR (codi_sku = :ultim_sku AND id > :ultim_id_unic)
ORDER BY codi_sku ASC, id ASC
LIMIT :limit;
```

<div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-344 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--11" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_e85bf8695a573c08","c_f93da456ed382884",null,"rc_6f009f4171a48282",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-344"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-344"></div></div></div><div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-com-funciona%3A-busca--1" inline-copy-host="">- **Com funciona:** Busca tots els productes on el `codi_sku` és lexicogràficament **més gran** que l'últim vist. Si hi ha SKUs idèntics, desempatarà amb l'`id` per evitar omissions.

</div>Aquest mètode és robust i trasllada l'eficiència dels índexs de BBDD a la navegació, independentment de si el teu ID principal és numèric o no.

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--12" inline-copy-host=""></div>

# Inserir dades

## MySQLi Procedural

Aquest mètode utilitza funcions prefixades amb `mysqli_` i les marques de posició (`?`).

### Inserció de dades i obtenció de l'ID

<div _ngcontent-ng-c1448405485="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-" inline-copy-host=""></div><div _ngcontent-ng-c2531377157="" class="code-block ng-tns-c2531377157-97 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_5a4b9f7c9bb4fd95","c_f93da456ed382884",null,"rc_c24b0ffab3af524a",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c2531377157="" class="code-block-decoration header-formatted gds-title-s ng-tns-c2531377157-97 ng-star-inserted"><span class="ng-tns-c2531377157-97">PHP</span><div _ngcontent-ng-c2531377157="" class="buttons ng-tns-c2531377157-97 ng-star-inserted"><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c2531377157-97 mat-unthemed _mat-animation-noopable ng-star-inserted"></button></div></div><div _ngcontent-ng-c2531377157="" class="formatted-code-block-internal-container ng-tns-c2531377157-97"><div _ngcontent-ng-c2531377157="" class="animated-opacity ng-tns-c2531377157-97"></div></div></div>```php
<?php
// Suposant que $conn és la connexió establerta

$nom = "Jordi";
$email = "jordi@exemple.cat";

// 1. Preparar la sentència
$stmt = mysqli_prepare($conn, "INSERT INTO Usuaris (nom, email) VALUES (?, ?)");

// 2. Vincular les variables a la sentència
// El primer paràmetre indica els tipus de dades: 's' per a string, 'i' per a integer, 'd' per a double
mysqli_stmt_bind_param($stmt, "ss", $nom, $email);

// 3. Executar la sentència
if (mysqli_stmt_execute($stmt)) {
    // 4. Obtenir l'últim ID Inserit
    $last_id = mysqli_insert_id($conn);
    echo "Nou registre creat amb èxit. ID: " . $last_id . ".\n";
} else {
    echo "Error en la inserció: " . mysqli_error($conn) . "\n";
}

// 5. Tancar el statement (i opcionalment la connexió)
mysqli_stmt_close($stmt);
// mysqli_close($conn); 
?>
```

<div _ngcontent-ng-c2531377157="" class="code-block ng-tns-c2531377157-97 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--1" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_5a4b9f7c9bb4fd95","c_f93da456ed382884",null,"rc_c24b0ffab3af524a",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c2531377157="" class="formatted-code-block-internal-container ng-tns-c2531377157-97"><div _ngcontent-ng-c2531377157="" class="animated-opacity ng-tns-c2531377157-97"></div></div></div>## MySQLi Orientat a Objectes (OO)

Aquest mètode utilitza mètodes de l'objecte `$conn` i l'objecte `$stmt`.

### Inserció de dades i obtenció de l'ID

<div _ngcontent-ng-c1448405485="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--2" inline-copy-host=""></div><div _ngcontent-ng-c2531377157="" class="code-block ng-tns-c2531377157-98 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php-1" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_5a4b9f7c9bb4fd95","c_f93da456ed382884",null,"rc_c24b0ffab3af524a",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c2531377157="" class="code-block-decoration header-formatted gds-title-s ng-tns-c2531377157-98 ng-star-inserted"><span class="ng-tns-c2531377157-98">PHP</span><div _ngcontent-ng-c2531377157="" class="buttons ng-tns-c2531377157-98 ng-star-inserted"><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c2531377157-98 mat-unthemed _mat-animation-noopable ng-star-inserted"></button></div></div><div _ngcontent-ng-c2531377157="" class="formatted-code-block-internal-container ng-tns-c2531377157-98"><div _ngcontent-ng-c2531377157="" class="animated-opacity ng-tns-c2531377157-98"></div></div></div>```php
<?php
// Suposant que $conn és l'objecte de connexió mysqli

$nom = "Anna";
$email = "anna@exemple.cat";

try {
    // 1. Preparar la sentència
    $stmt = $conn->prepare("INSERT INTO Usuaris (nom, email) VALUES (?, ?)");

    // 2. Vincular les variables a la sentència
    $stmt->bind_param("ss", $nom, $email);

    // 3. Executar la sentència
    if ($stmt->execute()) {
        // 4. Obtenir l'últim ID Inserit
        $last_id = $conn->insert_id;
        echo "Nou registre creat amb èxit. ID: " . $last_id . ".\n";
    } else {
        throw new Exception("L'execució de la consulta va fallar.");
    }
} catch (Exception $e) {
    echo "Error: " . $e->getMessage() . " (" . $conn->error . ")\n";
}

// 5. Tancar el statement (i opcionalment la connexió)
$stmt->close();
// $conn->close();
?>
```

<div _ngcontent-ng-c2531377157="" class="code-block ng-tns-c2531377157-98 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--3" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_5a4b9f7c9bb4fd95","c_f93da456ed382884",null,"rc_c24b0ffab3af524a",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c2531377157="" class="formatted-code-block-internal-container ng-tns-c2531377157-98"><div _ngcontent-ng-c2531377157="" class="animated-opacity ng-tns-c2531377157-98"></div></div></div>## PDO (PHP Data Objects)

PDO és la forma més neta i segura. Utilitza la funció **`lastInsertId()`** directament sobre l'objecte PDO.

### Inserció de dades i obtenció de l'ID

<div _ngcontent-ng-c1448405485="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--4" inline-copy-host=""></div><div _ngcontent-ng-c2531377157="" class="code-block ng-tns-c2531377157-99 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php-2" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_5a4b9f7c9bb4fd95","c_f93da456ed382884",null,"rc_c24b0ffab3af524a",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c2531377157="" class="code-block-decoration header-formatted gds-title-s ng-tns-c2531377157-99 ng-star-inserted"><span class="ng-tns-c2531377157-99">PHP</span><div _ngcontent-ng-c2531377157="" class="buttons ng-tns-c2531377157-99 ng-star-inserted"><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c2531377157-99 mat-unthemed _mat-animation-noopable ng-star-inserted"></button></div></div><div _ngcontent-ng-c2531377157="" class="formatted-code-block-internal-container ng-tns-c2531377157-99"><div _ngcontent-ng-c2531377157="" class="animated-opacity ng-tns-c2531377157-99"></div></div></div>```php
<?php
// Suposant que $pdo és l'objecte de connexió PDO
// Recordeu tenir la gestió d'errors (ERRMODE_EXCEPTION) activada al $pdo

$nom = "Marc";
$email = "marc@exemple.cat";

try {
    // 1. Preparar la sentència
    $stmt = $pdo->prepare("INSERT INTO Usuaris (nom, email) VALUES (?, ?)");

    // 2. Executar la sentència, passant les dades com un array
    // PDO determina automàticament els tipus, simplificant la vinculació
    $stmt->execute([$nom, $email]);

    // 3. Obtenir l'últim ID Inserit
    $last_id = $pdo->lastInsertId();
    echo "Nou registre creat amb èxit. ID: " . $last_id . ".\n";

} catch (PDOException $e) {
    echo "Error en la inserció: " . $e->getMessage() . ".\n";
}

// El statement es tanca automàticament en finalitzar
// $pdo = null; // Tancar connexió
?>
```

<div _ngcontent-ng-c2531377157="" class="code-block ng-tns-c2531377157-99 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--5" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_5a4b9f7c9bb4fd95","c_f93da456ed382884",null,"rc_c24b0ffab3af524a",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c2531377157="" class="formatted-code-block-internal-container ng-tns-c2531377157-99"><div _ngcontent-ng-c2531377157="" class="animated-opacity ng-tns-c2531377157-99"></div></div></div><div _ngcontent-ng-c1448405485="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--6" inline-copy-host=""></div>

# Inserir dades multiples

Podem utilitzar la funció **`multi_query`** (o `mysqli_multi_query`) amb MySQLi per executar múltiples sentències SQL en una sola trucada. Aquesta és la tècnica habitual per a la inserció de *múltiples registres*. No obstant això, amb PDO i, en general, per raons de **seguretat** i **eficiència**, la manera més professional de fer múltiples insercions és utilitzar **Sentències Preparades** dins d'una **Transacció** (especialment amb PDO). A continuació, s'explica com fer-ho amb els tres mètodes.

## MySQLi Procedural: `mysqli_multi_query()`

Aquest mètode és el més directe per a consultes que **no** utilitzen dades de l'usuari (és a dir, sense risc d'injecció SQL, com en el vostre exemple).

Es concatenen totes les sentències `INSERT` separades per un punt i coma (`;`) i s'executa una sola funció.

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-" inline-copy-host=""></div><div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-106 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_55b2f78c995709ed","c_f93da456ed382884",null,"rc_e1f554985cbaa9cf",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="code-block-decoration header-formatted gds-title-s ng-tns-c243700105-106 ng-star-inserted"><span class="ng-tns-c243700105-106">PHP</span><div _ngcontent-ng-c243700105="" class="buttons ng-tns-c243700105-106 ng-star-inserted"><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c243700105-106 mat-unthemed _mat-animation-noopable ng-star-inserted"></button></div></div><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-106"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-106"></div></div></div>```php
<?php
// Suposant que $conn és la connexió establerta

$sql = "INSERT INTO MyGuests (firstname, lastname, email) VALUES ('John', 'Doe', 'john@example.com');";
$sql .= "INSERT INTO MyGuests (firstname, lastname, email) VALUES ('Mary', 'Moe', 'mary@example.com');";
$sql .= "INSERT INTO MyGuests (firstname, lastname, email) VALUES ('Julie', 'Dooley', 'julie@example.com')";

if (mysqli_multi_query($conn, $sql)) {
    echo "New records created successfully (Procedural multi_query).";
    // Nota: El codi addicional seria necessari si les consultes fossin de tipus SELECT
} else {
    echo "Error: " . mysqli_error($conn);
}

mysqli_close($conn);
?>
```

<div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-106 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--1" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_55b2f78c995709ed","c_f93da456ed382884",null,"rc_e1f554985cbaa9cf",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-106"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-106"></div></div></div><div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-mecanisme%3A-mysqli_mu" inline-copy-host="">- **Mecanisme:** `mysqli_multi_query()`
- **Avantatge:** Molt ràpid per a sentències fixes.
- **Inconvenient:** **Insegur** si les dades provenen d'un usuari. No gestiona bé l'obtenció d'IDs de múltiples insercions.

</div>## MySQLi Orientat a Objectes (OO): `$conn->multi_query()`

Igual que el mètode procedural, utilitza la funció nativa per executar múltiples sentències encadenades.

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--2" inline-copy-host=""></div><div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-107 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php-1" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_55b2f78c995709ed","c_f93da456ed382884",null,"rc_e1f554985cbaa9cf",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="code-block-decoration header-formatted gds-title-s ng-tns-c243700105-107 ng-star-inserted"><span class="ng-tns-c243700105-107">PHP</span><div _ngcontent-ng-c243700105="" class="buttons ng-tns-c243700105-107 ng-star-inserted"><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c243700105-107 mat-unthemed _mat-animation-noopable ng-star-inserted"></button></div></div><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-107"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-107"></div></div></div>```php
<?php
// Suposant que $conn és l'objecte de connexió mysqli

$sql = "INSERT INTO MyGuests (firstname, lastname, email) VALUES ('John', 'Doe', 'john@example.com');";
$sql .= "INSERT INTO MyGuests (firstname, lastname, email) VALUES ('Mary', 'Moe', 'mary@example.com');";
$sql .= "INSERT INTO MyGuests (firstname, lastname, email) VALUES ('Julie', 'Dooley', 'julie@example.com')";

if ($conn->multi_query($sql) === TRUE) {
    echo "New records created successfully (OO multi_query).";
} else {
    echo "Error: " . $conn->error;
}

$conn->close();
?>
```

<div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-107 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--3" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_55b2f78c995709ed","c_f93da456ed382884",null,"rc_e1f554985cbaa9cf",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-107"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-107"></div></div></div><div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-mecanisme%3A-%24conn-%3Emu" inline-copy-host="">- **Mecanisme:** `$conn->multi_query($sql)`
- **Avantatge/Inconvenient:** Igual que el procedural.

</div>## PDO: Sentències preparades i transaccions (Recomanat)

Aquesta és la manera **més segura i controlada**. Es prepara una sola sentència `INSERT` i s'executa repetidament dins d'una **transacció**. Una transacció garanteix que, o bé **totes** les insercions es completen (`commit`), o bé **cap** no es fa (`rollback`) si alguna falla.

### Inserció múltiple i segura

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--4" inline-copy-host=""></div><div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-108 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php-2" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_55b2f78c995709ed","c_f93da456ed382884",null,"rc_e1f554985cbaa9cf",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="code-block-decoration header-formatted gds-title-s ng-tns-c243700105-108 ng-star-inserted"><span class="ng-tns-c243700105-108">PHP</span><div _ngcontent-ng-c243700105="" class="buttons ng-tns-c243700105-108 ng-star-inserted"><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c243700105-108 mat-unthemed _mat-animation-noopable ng-star-inserted"></button></div></div><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-108"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-108"></div></div></div>```php
<?php
// Suposant que $pdo és l'objecte de connexió PDO

$dades = [
    ['John', 'Doe', 'john@example.com'],
    ['Mary', 'Moe', 'mary@example.com'],
    ['Julie', 'Dooley', 'julie@example.com']
];

$sql = "INSERT INTO MyGuests (firstname, lastname, email) VALUES (?, ?, ?)";

try {
    $pdo->beginTransaction(); // 1. Iniciar la Transacció
    $stmt = $pdo->prepare($sql); // 2. Preparar la Sentència (només una vegada)
    $comptador = 0;

    foreach ($dades as $registre) {
        $stmt->execute($registre); // 3. Executar la Sentència per cada registre
        $comptador++;
        // NOTA: Per obtenir IDs individuals, s'hauria de cridar $pdo->lastInsertId() dins del bucle
    }

    $pdo->commit(); // 4. Confirmar els canvis
    echo "New records created successfully ($comptador insercions via PDO Transaction).";

} catch (PDOException $e) {
    $pdo->rollBack(); // 5. Desfer els canvis si alguna cosa ha fallat
    echo "Error en la transacció: " . $e->getMessage();
}

$pdo = null; // Tancar connexió
?>

```

<div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-108 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--5" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_55b2f78c995709ed","c_f93da456ed382884",null,"rc_e1f554985cbaa9cf",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-108"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-108"></div></div></div><div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-mecanisme%3A-transacci" inline-copy-host="">- **Mecanisme:** Transaccions (`beginTransaction`, `commit`, `rollBack`) amb Sentències Preparades (`prepare`, `execute`).
- **Avantatge:** **Màxima seguretat** (contra SQL Injection) i **Integritat de dades** (Atomicidad: totes o cap).
- **Eficiència:** Preparar la sentència només una vegada i executar-la múltiples vegades és molt ràpid.

</div>

# Delete data

Per eliminar un registre d'una taula, farem servir la sentència **`DELETE FROM`** de SQL. Igual que amb les altres operacions que involucren dades de l'usuari (en aquest cas, l'ID del registre a eliminar), utilitzarem **Sentències preparades** per evitar l'atac de la Injecció SQL.

Aquí teniu com implementar l'eliminació amb **MySQLi Procedural**, **MySQLi OO** i **PDO**.

## MySQLi Procedural

Aquest mètode utilitza la funció `mysqli_prepare()` i l'enllaç de connexió.

### Eliminació del registre

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-" inline-copy-host=""></div><div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-232 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_f30ee86c60c3ee53","c_f93da456ed382884",null,"rc_9e90cfb6b92791ef",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="code-block-decoration header-formatted gds-title-s ng-tns-c243700105-232 ng-star-inserted"><span class="ng-tns-c243700105-232">PHP</span><div _ngcontent-ng-c243700105="" class="buttons ng-tns-c243700105-232 ng-star-inserted"><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c243700105-232 mat-unthemed _mat-animation-noopable ng-star-inserted"></button></div></div><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-232"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-232"></div></div></div>```php
<?php
// Suposant que $conn és la connexió establerta
$id_a_eliminar = 5; // ID del registre que volem eliminar

// 1. Preparar la sentència DELETE
$sql = "DELETE FROM Usuaris WHERE id = ?";
$stmt = mysqli_prepare($conn, $sql);

// 2. Vincular el paràmetre (la ID és un enter: 'i')
mysqli_stmt_bind_param($stmt, "i", $id_a_eliminar);

// 3. Executar la sentència
if (mysqli_stmt_execute($stmt)) {
    // 4. Comprovar si s'ha eliminat alguna fila
    if (mysqli_stmt_affected_rows($stmt) > 0) {
        echo "Registre amb ID " . $id_a_eliminar . " eliminat amb èxit (Procedural). \n";
    } else {
        echo "No s'ha trobat cap registre amb ID " . $id_a_eliminar . " per eliminar.\n";
    }
} else {
    echo "Error en l'execució: " . mysqli_error($conn) . "\n";
}

// 5. Tancar el statement
mysqli_stmt_close($stmt);
// mysqli_close($conn); 
?>
```

<div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-232 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--1" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_f30ee86c60c3ee53","c_f93da456ed382884",null,"rc_9e90cfb6b92791ef",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-232"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-232"></div></div></div>## MySQLi Orientat a Objectes (OO)

Aquest mètode utilitza el mètode `prepare()` de l'objecte `$conn`.

### Eliminació del registre

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--2" inline-copy-host=""></div><div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-233 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php-1" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_f30ee86c60c3ee53","c_f93da456ed382884",null,"rc_9e90cfb6b92791ef",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="code-block-decoration header-formatted gds-title-s ng-tns-c243700105-233 ng-star-inserted"><span class="ng-tns-c243700105-233">PHP</span><div _ngcontent-ng-c243700105="" class="buttons ng-tns-c243700105-233 ng-star-inserted"><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c243700105-233 mat-unthemed _mat-animation-noopable ng-star-inserted"></button></div></div><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-233"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-233"></div></div></div>```php
<?php
// Suposant que $conn és l'objecte de connexió mysqli
$id_a_eliminar = 6;

try {
    // 1. Preparar la sentència DELETE
    $stmt = $conn->prepare("DELETE FROM Usuaris WHERE id = ?");

    // 2. Vincular el paràmetre (la ID és un enter: 'i')
    $stmt->bind_param("i", $id_a_eliminar);

    // 3. Executar la sentència
    if ($stmt->execute()) {
        // 4. Comprovar si s'ha eliminat alguna fila
        if ($stmt->affected_rows > 0) {
            echo "Registre amb ID " . $id_a_eliminar . " eliminat amb èxit (OO). \n";
        } else {
            echo "No s'ha trobat cap registre amb ID " . $id_a_eliminar . " per eliminar.\n";
        }
    } else {
        throw new Exception("L'execució de la consulta va fallar.");
    }
} catch (Exception $e) {
    echo "Error: " . $e->getMessage() . " (" . $conn->error . ")\n";
}

// 5. Tancar el statement
$stmt->close();
// $conn->close();
?>
```

<div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-233 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--3" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_f30ee86c60c3ee53","c_f93da456ed382884",null,"rc_9e90cfb6b92791ef",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-233"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-233"></div></div></div>## PDO (PHP Data Objects)

PDO és el mètode més net, utilitzant **`execute()`** amb un *array* de dades.

### Eliminació del registre

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--4" inline-copy-host=""></div><div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-234 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php-2" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_f30ee86c60c3ee53","c_f93da456ed382884",null,"rc_9e90cfb6b92791ef",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="code-block-decoration header-formatted gds-title-s ng-tns-c243700105-234 ng-star-inserted"><span class="ng-tns-c243700105-234">PHP</span><div _ngcontent-ng-c243700105="" class="buttons ng-tns-c243700105-234 ng-star-inserted"><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c243700105-234 mat-unthemed _mat-animation-noopable ng-star-inserted"></button></div></div><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-234"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-234"></div></div></div>```php
<?php
// Suposant que $pdo és l'objecte de connexió PDO
$id_a_eliminar = 7;

try {
    // 1. Preparar la sentència DELETE
    $sql = "DELETE FROM Usuaris WHERE id = ?";
    $stmt = $pdo->prepare($sql);

    // 2. Executar la sentència, passant la ID com a array
    $stmt->execute([$id_a_eliminar]);

    // 3. Comprovar si s'ha eliminat alguna fila
    // s'utilitza rowCount() directament sobre el statement
    if ($stmt->rowCount() > 0) {
        echo "Registre amb ID " . $id_a_eliminar . " eliminat amb èxit (PDO). \n";
    } else {
        echo "No s'ha trobat cap registre amb ID " . $id_a_eliminar . " per eliminar.\n";
    }

} catch (PDOException $e) {
    die("Error en l'eliminació: " . $e->getMessage() . ".\n");
}

// 4. Tancar
// $pdo = null; // Tancar connexió
?>
```

<div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-234 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--5" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_f30ee86c60c3ee53","c_f93da456ed382884",null,"rc_9e90cfb6b92791ef",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-234"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-234"></div></div></div><div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--6" inline-copy-host=""></div>### Com comprovar-ho?

Per confirmar que s'ha eliminat un registre, en lloc de comprovar l'èxit de l'execució (`true`), el que es comprova és el nombre de files afectades:

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-mysqli-%28oo%29%3A-%24stmt-%3E" inline-copy-host="">- **MySQLi (OO):** `$stmt->affected_rows`
- **MySQLi (Procedural):** `mysqli_stmt_affected_rows($stmt)`
- **PDO:** `$stmt->rowCount()`

</div>Si el valor retornat és **major que 0**, vol dir que s'ha eliminat un o més registres. Si és 0, la consulta ha funcionat, però el registre amb aquesta `ID` no existia.

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--7" inline-copy-host=""></div>

# Update data

Per actualitzar un registre existent a una taula, s'utilitza la sentència **`UPDATE`** de SQL, especificant els camps i valors nous i utilitzant una clàusula **`WHERE`** per identificar el registre concret a modificar (normalment per la seva ID). Com en totes les operacions amb dades d'usuari, és crucial utilitzar **Sentències preparades** per prevenir la Injecció SQL.

## MySQLi Procedural

Aquest mètode utilitza la funció `mysqli_prepare()` i l'enllaç de connexió.

### Actualització registre

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-" inline-copy-host=""></div><div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-249 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_44ea539e2ecdf0ee","c_f93da456ed382884",null,"rc_6769ecda7580cc3e",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="code-block-decoration header-formatted gds-title-s ng-tns-c243700105-249 ng-star-inserted"><span class="ng-tns-c243700105-249">PHP</span><div _ngcontent-ng-c243700105="" class="buttons ng-tns-c243700105-249 ng-star-inserted"><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c243700105-249 mat-unthemed _mat-animation-noopable ng-star-inserted"></button></div></div><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-249"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-249"></div></div></div>```php
<?php
// Suposant que $conn és la connexió establerta

$nou_nom = "Carla López";
$nou_email = "carla.lopez@nova.cat";
$id_a_actualitzar = 8; 

// 1. Preparar la sentència UPDATE amb marcadors de posició (?)
$sql = "UPDATE Usuaris SET nom = ?, email = ? WHERE id = ?";
$stmt = mysqli_prepare($conn, $sql);

// 2. Vincular els paràmetres
// Tipus: "ssi" (dos strings i un integer)
mysqli_stmt_bind_param($stmt, "ssi", $nou_nom, $nou_email, $id_a_actualitzar);

// 3. Executar la sentència
if (mysqli_stmt_execute($stmt)) {
    // 4. Comprovar si s'ha actualitzat alguna fila
    if (mysqli_stmt_affected_rows($stmt) > 0) {
        echo "Registre amb ID " . $id_a_actualitzar . " actualitzat amb èxit (Procedural). \n";
    } else {
        echo "No s'ha fet cap canvi (el registre no existeix o les dades són les mateixes).\n";
    }
} else {
    echo "Error en l'execució: " . mysqli_error($conn) . "\n";
}

// 5. Tancar el statement
mysqli_stmt_close($stmt);
// mysqli_close($conn); 
?>
```

<div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-249 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--1" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_44ea539e2ecdf0ee","c_f93da456ed382884",null,"rc_6769ecda7580cc3e",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-249"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-249"></div></div></div>## MySQLi Orientat a Objectes (OO)

Aquest mètode utilitza el mètode `prepare()` de l'objecte `$conn`.

### Actualització registre

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--2" inline-copy-host=""></div><div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-250 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php-1" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_44ea539e2ecdf0ee","c_f93da456ed382884",null,"rc_6769ecda7580cc3e",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="code-block-decoration header-formatted gds-title-s ng-tns-c243700105-250 ng-star-inserted"><span class="ng-tns-c243700105-250">PHP</span><div _ngcontent-ng-c243700105="" class="buttons ng-tns-c243700105-250 ng-star-inserted"><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c243700105-250 mat-unthemed _mat-animation-noopable ng-star-inserted"></button></div></div><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-250"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-250"></div></div></div>```php
<?php
// Suposant que $conn és l'objecte de connexió mysqli
$nou_nom = "Toni G.";
$nou_email = "toni.g@nova.cat";
$id_a_actualitzar = 9;

try {
    // 1. Preparar la sentència UPDATE
    $stmt = $conn->prepare("UPDATE Usuaris SET nom = ?, email = ? WHERE id = ?");

    // 2. Vincular els paràmetres (ssi)
    $stmt->bind_param("ssi", $nou_nom, $nou_email, $id_a_actualitzar);

    // 3. Executar la sentència
    if ($stmt->execute()) {
        // 4. Comprovar si s'ha actualitzat alguna fila
        if ($stmt->affected_rows > 0) {
            echo "Registre amb ID " . $id_a_actualitzar . " actualitzat amb èxit (OO). \n";
        } else {
            echo "No s'ha fet cap canvi (el registre no existeix o les dades són les mateixes).\n";
        }
    } else {
        throw new Exception("L'execució de la consulta va fallar.");
    }
} catch (Exception $e) {
    echo "Error: " . $e->getMessage() . " (" . $conn->error . ")\n";
}

// 5. Tancar el statement
$stmt->close();
// $conn->close();
?>
```

<div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-250 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--3" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_44ea539e2ecdf0ee","c_f93da456ed382884",null,"rc_6769ecda7580cc3e",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-250"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-250"></div></div></div>## PDO (PHP Data Objects)

PDO és el mètode més concís i prefereix l'execució amb un *array* de dades.

### Actualització registre

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--4" inline-copy-host=""></div><div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-251 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-php-2" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_44ea539e2ecdf0ee","c_f93da456ed382884",null,"rc_6769ecda7580cc3e",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="code-block-decoration header-formatted gds-title-s ng-tns-c243700105-251 ng-star-inserted"><span class="ng-tns-c243700105-251">PHP</span><div _ngcontent-ng-c243700105="" class="buttons ng-tns-c243700105-251 ng-star-inserted"><button aria-label="Copiar código" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c243700105-251 mat-unthemed _mat-animation-noopable ng-star-inserted"></button></div></div><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-251"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-251"></div></div></div>```php
<?php
// Suposant que $pdo és l'objecte de connexió PDO
$nou_nom = "Marta Riera";
$nou_email = "marta.riera@nova.cat";
$id_a_actualitzar = 10;

// NOTA: Amb PDO, l'ordre de les dades a l'array d'execució ha de coincidir amb l'ordre dels '?'
try {
    // 1. Preparar la sentència UPDATE
    $sql = "UPDATE Usuaris SET nom = ?, email = ? WHERE id = ?";
    $stmt = $pdo->prepare($sql);

    // 2. Executar la sentència, passant totes les dades en un array
    $stmt->execute([$nou_nom, $nou_email, $id_a_actualitzar]);

    // 3. Comprovar si s'ha actualitzat alguna fila
    if ($stmt->rowCount() > 0) {
        echo "Registre amb ID " . $id_a_actualitzar . " actualitzat amb èxit (PDO). ✅\n";
    } else {
        echo "No s'ha fet cap canvi (el registre no existeix o les dades són les mateixes).\n";
    }

} catch (PDOException $e) {
    die("Error en l'actualització: " . $e->getMessage() . ".\n");
}

// 4. Tancar
// $pdo = null; // Tancar connexió
?>
```

<div _ngcontent-ng-c243700105="" class="code-block ng-tns-c243700105-251 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--5" jslog="223238;track:impression,attention;BardVeMetadataKey:[["r_44ea539e2ecdf0ee","c_f93da456ed382884",null,"rc_6769ecda7580cc3e",null,null,"ca",null,1,null,null,1,0]]"><div _ngcontent-ng-c243700105="" class="formatted-code-block-internal-container ng-tns-c243700105-251"><div _ngcontent-ng-c243700105="" class="animated-opacity ng-tns-c243700105-251"></div></div></div><div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--6" inline-copy-host=""></div>### Com ho comprovem?

En els tres mètodes, la clau per saber si l'actualització ha tingut lloc és utilitzar la funció que retorna el **nombre de files afectades**:

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk-mysqli-%28oo%29%3A-%24stmt-%3E" inline-copy-host="">- **MySQLi (OO):** `$stmt->affected_rows`
- **MySQLi (Procedural):** `mysqli_stmt_affected_rows($stmt)`
- **PDO:** `$stmt->rowCount()`

</div>Si el valor retornat és **major que 0**, s'ha fet l'actualització. Si és 0, potser la fila ja tenia els mateixos valors o la `WHERE` no va trobar cap registre.

<div _ngcontent-ng-c2268849367="" class="markdown markdown-main-panel enable-updated-hr-color" dir="ltr" id="bkmrk--7" inline-copy-host=""></div>