Načrtovanje in razvoj spletnih aplikacij

Večkratni vnos zapisov v tabelo podatkovne zbirke

Včasih želimo v podatkovno zbirko dodati več zapisov hkrati. Tak pristop imenujemo večkratni vnos oziroma multivnos. Uporaben je takrat, ko uporabnik v enem obrazcu vnaša več podobnih podatkov, na primer več knjig.

Pri večkratnem vnosu ni pomembno samo shranjevanje zapisov, ampak tudi pravilna obdelava posameznih vrstic. Nekatere vrstice so lahko prazne, nekatere delno izpolnjene, nekatere pa popolne in primerne za shranjevanje.

Pomni: Pri večkratnem vnosu vsako vrstico obrazca obravnavamo posebej. Popolnoma prazne vrstice lahko preskočimo, delno izpolnjene vrstice pa moramo obravnavati kot napako.

Pri večkratnem vnosu je pripravljen stavek posebej uporaben, ker ga lahko pripravimo enkrat in ga nato večkrat izvedemo z različnimi vrednostmi.

Vsebina strani

Osnovna pravila

Pri večkratnem vnosu uporabnik v enem obrazcu vnese več vrstic podatkov, aplikacija pa nato shrani samo tiste vrstice, ki so pravilno izpolnjene.

  • Obrazec vsebuje več skupin enakih polj.
  • Vsaka vrstica obrazca predstavlja en možen zapis.
  • Popolnoma prazne vrstice lahko preskočimo.
  • Delno izpolnjene vrstice moramo zaznati in uporabniku prikazati napako.
  • Vsako izpolnjeno vrstico preverimo posebej.
  • Za shranjevanje uporabimo pripravljen stavek.
  • Pri zahtevnejšem večkratnem vnosu uporabimo transakcijo.
  • Po obdelavi prikažemo število uspešno dodanih zapisov.

Pozor: Večkratni vnos ne pomeni, da lahko podatke iz obrazca neposredno sestavimo v dolg ukaz INSERT INTO. Podatke je treba preveriti in varno vezati na parametre.

Večkratni vnos zapisov

Pri večkratnem vnosu uporabnik v enem obrazcu izpolni več vrstic, aplikacija pa nato obdela vse veljavne vnose in jih shrani v tabelo.

Osnovna sintaksa za vnos več zapisov z enim ukazom je:

INSERT INTO imeTabele (stolpec1, stolpec2, stolpec3)
VALUES
(vrednost1a, vrednost2a, vrednost3a),
(vrednost1b, vrednost2b, vrednost3b),
(vrednost1c, vrednost2c, vrednost3c);

V spletni aplikaciji večkratni vnos pogosto izvedemo tako, da uporabnik izpolni več vrstic obrazca, PHP pa vsako vrstico posebej preveri in nato shrani v bazo.

Tak pristop je posebej uporaben pri vnosu več knjig, izdelkov ali drugih podobnih zapisov v enem koraku.

Pomni: En ukaz z več skupinami vrednosti je primeren pri znanih podatkih. Pri podatkih iz obrazca pa je pogosto preglednejše, da en pripravljen stavek izvedemo večkrat v zanki.

Osnovni primer z mysqli

Spodnji zgled prikaže večkratni vnos treh vrstic obrazca v tabelo knjige. Vsako neprazno vrstico posebej preverimo in nato izvedemo pripravljen ukaz INSERT INTO.

<?php
define('DB_SERVER', 'localhost');
define('DB_USER', 'uporabnik');
define('DB_PASS', 'skritoGeslo');
define('DB_NAME', 'knjiznica');

$connection = mysqli_connect(DB_SERVER, DB_USER, DB_PASS, DB_NAME);

if (!$connection) {
    die(
        'Povezava s podatkovno zbirko ni vzpostavljena: ' .
        mysqli_connect_error() .
        ' (' . mysqli_connect_errno() . ')'
    );
}

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $stmt = mysqli_prepare(
        $connection,
        "INSERT INTO knjige
        (Priimek_avtorja, Ime_avtorja, Naslov, Strani, Cena, Leto)
        VALUES (?, ?, ?, ?, ?, ?)"
    );

    $uspesniVnosi = 0;

    for ($i = 1; $i <= 3; $i++) {
        $priimek = trim($_POST['priimek' . $i] ?? '');
        $ime = trim($_POST['ime' . $i] ?? '');
        $naslov = trim($_POST['naslov' . $i] ?? '');
        $strani = trim($_POST['strani' . $i] ?? '');
        $cena = trim($_POST['cena' . $i] ?? '');
        $leto = trim($_POST['leto' . $i] ?? '');

        $praznaVrstica =
            $priimek === '' &&
            $ime === '' &&
            $naslov === '' &&
            $strani === '' &&
            $cena === '' &&
            $leto === '';

        if ($praznaVrstica) {
            continue;
        }

        if (
            $priimek !== '' &&
            $ime !== '' &&
            $naslov !== '' &&
            filter_var($strani, FILTER_VALIDATE_INT) !== false &&
            is_numeric($cena) &&
            filter_var($leto, FILTER_VALIDATE_INT) !== false
        ) {
            $straniInt = (int)$strani;
            $cenaFloat = (float)$cena;
            $letoInt = (int)$leto;

            mysqli_stmt_bind_param(
                $stmt,
                'sssidi',
                $priimek,
                $ime,
                $naslov,
                $straniInt,
                $cenaFloat,
                $letoInt
            );

            if (mysqli_stmt_execute($stmt)) {
                $uspesniVnosi++;
            }
        }
    }

    echo 'Uspešno dodani zapisi: ' . $uspesniVnosi;
    mysqli_stmt_close($stmt);
}

mysqli_close($connection);
?>

Pri mysqli pripravimo en stavek s funkcijo mysqli_prepare(), nato v zanki za vsako veljavno vrstico povežemo nove vrednosti in izvedemo mysqli_stmt_execute().

Osnovni primer s PDO

Tudi z vmesnikom PDO lahko večkratni vnos izvedemo tako, da en pripravljen stavek večkrat uporabimo v zanki.

<?php
$streznik = 'localhost';
$baza = 'knjiznica';
$uporabnik = 'uporabnik';
$geslo = 'skritoGeslo';

try {
    $pdo = new PDO("mysql:host=$streznik;dbname=$baza;charset=utf8mb4", $uporabnik, $geslo);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        $stmt = $pdo->prepare(
            "INSERT INTO knjige
            (Priimek_avtorja, Ime_avtorja, Naslov, Strani, Cena, Leto)
            VALUES
            (:priimek, :ime, :naslov, :strani, :cena, :leto)"
        );

        $uspesniVnosi = 0;

        for ($i = 1; $i <= 3; $i++) {
            $priimek = trim($_POST['priimek' . $i] ?? '');
            $ime = trim($_POST['ime' . $i] ?? '');
            $naslov = trim($_POST['naslov' . $i] ?? '');
            $strani = trim($_POST['strani' . $i] ?? '');
            $cena = trim($_POST['cena' . $i] ?? '');
            $leto = trim($_POST['leto' . $i] ?? '');

            $praznaVrstica =
                $priimek === '' &&
                $ime === '' &&
                $naslov === '' &&
                $strani === '' &&
                $cena === '' &&
                $leto === '';

            if ($praznaVrstica) {
                continue;
            }

            if (
                $priimek !== '' &&
                $ime !== '' &&
                $naslov !== '' &&
                filter_var($strani, FILTER_VALIDATE_INT) !== false &&
                is_numeric($cena) &&
                filter_var($leto, FILTER_VALIDATE_INT) !== false
            ) {
                $stmt->bindValue(':priimek', $priimek, PDO::PARAM_STR);
                $stmt->bindValue(':ime', $ime, PDO::PARAM_STR);
                $stmt->bindValue(':naslov', $naslov, PDO::PARAM_STR);
                $stmt->bindValue(':strani', (int)$strani, PDO::PARAM_INT);
                $stmt->bindValue(':cena', number_format((float)$cena, 2, '.', ''), PDO::PARAM_STR);
                $stmt->bindValue(':leto', (int)$leto, PDO::PARAM_INT);

                $stmt->execute();
                $uspesniVnosi++;
            }
        }

        echo 'Uspešno dodani zapisi: ' . $uspesniVnosi;
    }
}
catch (PDOException $e) {
    echo 'Napaka pri večkratnem vnosu: ' . $e->getMessage();
}
?>

Pri PDO pripravljen stavek pripravimo z metodo prepare(), vrednosti posamezne vrstice povežemo z metodo bindValue(), nato pa stavek izvedemo z metodo execute().

Preverjanje vrstic pri večkratnem vnosu

Pri večkratnem vnosu moramo vsako vrstico obrazca obravnavati posebej. Nekatere vrstice so lahko popolnoma prazne, druge pa delno ali v celoti izpolnjene.

Vrsta vrstice Obravnava
Popolnoma prazna vrstica Vrstico preskočimo, ker uporabnik v njej ni želel vnesti zapisa.
Delno izpolnjena vrstica Vrstica je neveljavna, zato uporabniku prikažemo opozorilo.
V celoti izpolnjena vrstica Preverimo podatkovne tipe in jo ob pravilnih podatkih shranimo.
  • priimek, ime in naslov naj ne bodo prazni,
  • število strani naj bo pozitivno celo število,
  • cena naj bo veljavno število,
  • leto naj bo veljavno celo število,
  • smiselno je voditi števec uspešno dodanih zapisov.

Pozor: Če delno izpolnjeno vrstico samo preskočimo, lahko uporabnik dobi napačen vtis, da je bil zapis shranjen. Bolje je prikazati jasno opozorilo.

Transakcija pri večkratnem vnosu

Če želimo zagotoviti, da se vsi zapisi shranijo skupaj ali pa nobeden, uporabimo transakcijo. To je posebej koristno pri večkratnem vnosu, saj lahko ob napaki vse spremembe razveljavimo.

Pri transakciji običajno:

  • začnemo transakcijo,
  • izvedemo vse ukaze INSERT INTO,
  • ob uspehu izvedemo potrditev,
  • ob napaki izvedemo razveljavitev.
$pdo->beginTransaction();

try {
    // večkratna izvedba pripravljenega stavka
    $pdo->commit();
}
catch (Exception $e) {
    $pdo->rollBack();
}

Pomni: Transakcija je smiselna takrat, ko več vnosov skupaj predstavlja eno celoto. Če eden od vnosov ne uspe, lahko razveljavimo tudi že izvedene vnose.

Primerjava pristopov mysqli in PDO

Oba pristopa omogočata večkratno izvajanje pripravljenega stavka v zanki. Pri zahtevnejših primerih je PDO pogosto preglednejši zaradi poimenovanih parametrov in enostavne uporabe transakcij.

Korak mysqli PDO
Priprava stavka mysqli_prepare() $pdo->prepare()
Vezava vrednosti mysqli_stmt_bind_param() $stmt->bindValue()
Izvedba v zanki mysqli_stmt_execute() $stmt->execute()
Transakcija Možna, vendar zapis pogosto zahteva dodatno obravnavo. beginTransaction(), commit(), rollBack()
Štetje uspešnih vnosov števec po uspešni izvedbi števec po uspešni izvedbi

Aplikacija Knjige

📘Aplikacija Knjige

V priloženi aplikaciji Knjige je večkratni vnos razdeljen na dve datoteki. Datoteka 13_multivnos-obrazec.php pripravi obrazec s tremi vrsticami, v katere lahko uporabnik vnese do tri knjige naenkrat.

Obrazec vsebuje polja za priimek, ime, naslov, število strani, ceno in leto izida, poleg tega pa uporablja tudi CSRF žeton za zaščito obrazca.

Datoteka 13_multivnos.php obdela vse tri vrstice, preskoči popolnoma prazne vrstice, preveri pravilnost podatkov in veljavne vrstice shrani v tabelo knjige.

Pri shranjevanju uporablja pripravljen stavek PDO v zanki in transakcijo, zato se ob napaki vsi vnosi razveljavijo. Po uspešnem večkratnem vnosu se prikažeta potrditveno sporočilo in posodobljen seznam vseh zapisov.

Primer: aplikacija Knjige – 13_multivnos-obrazec.php
Primer: aplikacija Knjige – 13_multivnos.php
  1. Najprej pripravimo obrazec z več vrsticami za vnos podatkov.
  2. V obrazec dodamo vsa potrebna polja za vsako vrstico posebej.
  3. Obrazec zaščitimo s CSRF žetonom.
  4. Ob oddaji obrazca vse vrstice preberemo v PHP.
  5. Popolnoma prazne vrstice preskočimo.
  6. Delno izpolnjene vrstice označimo kot neveljavne.
  7. Veljavne vrstice pripravimo za vnos v tabelo.
  8. Za vnos uporabimo pripravljen stavek INSERT INTO.
  9. Pri večkratnem shranjevanju uporabimo transakcijo.
  10. Na koncu uporabniku izpišemo število uspešnih vnosov in posodobljen seznam zapisov.

Pozor: Pri transakciji je treba ob napaki izvesti rollBack(). Če tega ne naredimo, lahko v podatkovni bazi ostane samo del predvidenih zapisov.

Priporočila

  • Obrazec za večkratni vnos naj ima jasno ločene vrstice.
  • Popolnoma prazne vrstice preskoči.
  • Delno izpolnjene vrstice obravnavaj kot napako.
  • Vsako vrstico preveri ločeno.
  • Za shranjevanje uporabi pripravljen stavek.
  • Pripravljen stavek pripravi enkrat in ga večkrat izvedi v zanki.
  • Pri skupinskem vnosu uporabi transakcijo.
  • Po obdelavi prikaži število uspešno dodanih zapisov.

Pogoste napake

  • Delno izpolnjene vrstice so tiho preskočene.
  • Vrstice niso preverjene posamezno.
  • Podatki iz obrazca se neposredno sestavijo v ukaz INSERT INTO.
  • Manjka CSRF zaščita obrazca.
  • Pri številskih poljih ni preverjen podatkovni tip.
  • Pripravljen stavek se po nepotrebnem pripravlja znotraj vsake ponovitve zanke.
  • Pri več povezanih vnosih ni uporabljena transakcija.
  • Uporabniku ni prikazano, koliko zapisov je bilo uspešno dodanih.

Večkratni vnos poveča možnost napak, ker se obdeluje več zapisov hkrati. Zato mora biti preverjanje podatkov natančno, sporočila uporabniku pa jasna.