Společný formulář pro editaci a vložení záznamu

Školení, která pořádám

Pokud ve webové aplikaci chceme upravovat záznamy uložené v databázi, je potřeba vytvořit formulář pro úpravu a vložení záznamu. Nejpozději po vytvoření prvních tří formulářů asi každému dojde, že jsou si natolik podobné, že by se daly sjednotit do jednoho. Jak to řeším já?

<?php
// výpis dat - soubor tabulka.php
if (isset($_GET["insert"])) {
    echo "<p><b>Záznam byl v pořádku uložen.</b></p>\n";
}
echo "<p><a href='tabulka_1.php'><i>nový záznam</i></a></p>\n";
$result = mysql_query("SELECT * FROM tabulka");
while ($row = mysql_fetch_assoc($result)) {
    echo "<p><a href='tabulka_1.php?select=$row[id]'>" . htmlspecialchars($row["nazev"]) . "</a></p>\n";
}
mysql_free_result($result);
?>
<?php
// úprava dat - soubor tabulka_1.php
if ($_POST) {
    $set = array();
    $set["nazev"] = "'" . mysql_real_escape_string($_POST["nazev"]) . "'";
    $set["cena"] = intval($_POST["cena"]);
    // další položky spolu s případným ošetřením dat
    if (isset($_GET["select"])) {
        array_walk($set, create_function('&$val, $key', '$val = "$key = $val";'));
        $result = mysql_query("UPDATE tabulka SET " . implode(", ", $set) . " WHERE id = " . intval($_GET["select"]));
    } else {
        $result = mysql_query("INSERT INTO tabulka (" . implode(", ", array_keys($set)) . ") VALUES (" . implode(", ", $set) . ")");
    }
    if ($result) {
        header("Location: tabulka.php?insert=1");
        exit;
    }
    echo "<p><b>Při zpracování došlo k chybě.</b><!-- " . mysql_error() . " --></p>\n";
} elseif (isset($_GET["select"])) {
    $row = mysql_fetch_assoc(mysql_query("SELECT * FROM tabulka WHERE id = " . intval($_GET["select"])));
} else {
    $row = array(); // nastavení výchozích hodnot
}
?>
<form action="" method="post">
<p>Název: <input name="nazev" value="<?php echo htmlspecialchars($row["nazev"]); ?>" maxlength="50" /></p>
<p>Cena: <input name="cena" value="<?php echo htmlspecialchars($row["cena"]); ?>" maxlength="9" /></p>
<p><input type="submit" value="Uložit" /></p>
</form>

Souboru tabulka_1.php (_1 znamená, že soubor slouží ke zobrazení jednoho záznamu) se předá v parametru select hodnota primárního klíče editovaného záznamu. Pokud se jedná o vložení záznamu, tak tento parametr předán není. Skript podle toho nastaví proměnnou $row, kterou používá ve formuláři. Po odeslání formuláře metodou POST se tato data uloží. Všimněte si, že atribut action je prázdný – to znamená, že se pro zpracování formuláře použije stejné URL včetně parametrů, takže hodnota parametru select se automaticky předá dál. Při zpracování formuláře se naplní proměnná $set, která se použije pro uložení dat. Poněkud kryptická funkce array_walk zajistí překlad pole do tvaru používaného příkazem UPDATE. Pokud nedojde k chybě, provede se přesměrování zpět na výpis hodnot (spolu s předáním parametru informujícím o výsledku), jinak se vypíše chybová hláška a naplní se pole $row tím, co zadal uživatel. Všimněte si, že SQL chyba se vypisuje do HTML komentáře, což neruší uživatele, ale informovaný člověk se o ní dozví – ve veřejně dostupných formulářích by se nicméně chyba neměla z bezpečnostních důvodů vypisovat vůbec.

Mazání záznamu by bylo o poznání jednodušší – odkaz lze umístit jak na stránku výpisu, tak na stránku editace, zpracování je přímočaré.

Jakub Vrána, Řešení problému, 15.4.2005, diskuse: 28 (nové: 0)

Diskuse

Petr:

Jakube díky za užitečné informace, jen pro nás začátečníky by bylo hodně užitečné vytvořit jednoduchou, funkční ukázku příkladu.

ikona Jakub Vrána OpenID:

Já myslel, že příklad je sám o sobě jednoduchou funkční ukázkou - stačí první výpis kódu (nadepsaný "soubor tabulka.php") uložit do souboru tabulka.php, další dva výpisy pod sebe do souboru tabulka_1.php a po doplnění připojení k databázi a výpisu HTML hlaviček by to mělo fungovat.

Skript používá tuto tabulku:
CREATE TABLE tabulka (
    id int NOT NULL AUTO_INCREMENT,
    nazev varchar(50) NOT NULL,
    cena int NOT NULL,
    PRIMARY KEY (id)
);

Biker:

Zdar.lidi, hele jsem začatečnik,mužete mi někdo prosim napsat odpověď - nejlepe kod jak na tohle - po doplnění připojení k databázi a výpisu HTML hlaviček by to mělo fungovat.(viz první příspěvek nahoře)
díky

<em>anonymní</em>:

Pěkně uděláno, ale při posílání hlaviček  header("Location: ") je problém, že předtím nesmí být odeslána žádná jiná hlavička. Tím se komplikuje použití totho scriptu jako vloženého (include("tabulka_1.php");).

shion:

reseni:
ob_start();

ikona Jakub Vrána OpenID:

Vkládat soubor nedoporučuji - viz http://php.vrana.cz/vzajemne-propojeni-souboru.php. Lepší je na vhodném místě zavolat funkci pro výpis začátku stránky.

Ni7R0:

Děkuji za pěkný článek, jenom bych chtěl podotknout, že v protokolu HTTP 1.1 by se měla používat absolutní adresa v hlavičce Location. Viz PHP manuál:
http://cz2.php.net/manual/en/function.header.php

zdenek:

doplnil bych jeste nejakou fci na zmenu nazvu submitu pro upload ci edit pro prehlednost

Techi:

Taková hustá vychytávka! Že mě to nenapadlo taky :D

Techi:

Dovolil sem si trochu pozměnit kód, aby byl ještě přítulnější, stačí takhle jedna univerzální funkce...

<?php
/**
* ModifyTable
*
* This function can easy modify table (insert or update line)
*
* @param $table       target SQL table
* @param $set         array with items to update/insert
* @param $condition   SQL condition if UPDATE table
* @param $delete      TRUE if delete line(s)
* @return id of last inserted line
*/

function ModifyTable($table, array $set, $condition = "", $delete = FALSE)
{
    if($delete)
    {
        new CQuery("DELETE FROM $table WHERE $condition");
    }

    // insert string into slashes
    array_walk($set, create_function('&$a', '$a = "\'".AddSlashes($a)."\'";'));

    if(!Empty($condition))
    {
        array_walk($set, create_function('&$val, $key', '$val = "$key = $val";'));
        new CQuery("UPDATE $table SET " . implode(", ", $set) . " WHERE $condition");
    }
    else
    {
        new CQuery("INSERT INTO $table (" . implode(", ", array_keys($set)) . ") VALUES (" . implode(", ", $set) . ")");
        return mysql_insert_id();
    }
}
?>

kuba:

Ten postup v článku jsem zprovoznil a je celkem pochopitelný, ale tenhle příklad - z toho jsem na větvi. Vůbec nevím jak bych ho zkloubil s ukázkou z článku. Chtěl bych ale mít i možnost mazání z tabulky. Poradil by někdo, jak to jednoduše začlenit, nebo jak využít předchozí příklad - trochu popsat. Dík

24k:

zajímavé a jak se to bude resit pokud updatujete tabulku a pouzivat napr funkce?

* lastupdate=NOW()

apod. ??

ikona Jakub Vrána OpenID:

Zcela stejně: <?php $set["lastupdate"] = "NOW()"; ?>

24k:

jak je mozne ze se to provede kdyz ta funkce bude v uvozovkach? pokud bych to napsal takto primo do SQL
<?php
$sel
=mysql_query("udpate bla bla set lastupdate='NOW()'");
?>
tak je zcela jasny parse error. Muzete mi to objasnit, stale tomu nejak nerozumim. A diky za rychlou odezvu :)

ikona Jakub Vrána OpenID:

Výsledný dotaz bude <?php $result = mysql_query("UPDATE tabulka SET lastupdate = NOW()"); ?>. Žádné apostrofy tam nejsou.

24k:

Aha promin, spatne jsem si to precetl. Ted jeste premyslim

zkusil jsem toto:
<?php
    $set
= array();
    $set["A"] = "'hodnota1'";
    $set["B"] = "NOW()";

    $result = ("UPDATE tabulka SET " . implode(", ", $set) . " WHERE id = '1'");
    echo $result;
?>
a to mi vrati -> UPDATE tabulka SET 'hodnota1', NOW() WHERE id = '1'

Nemelo by to vracet i klice pole?

ikona Jakub Vrána OpenID:

Člověče, dávej trochu pozor a čti pečlivěji. Ve větvi pro UPDATE je důležitý kód <?php array_walk($set, create_function('&$val, $key', '$val = "$key = $val";')); ?>, který hodnoty prvků pole převede z "val" na "key = val".

24k:

Du si lehnout, chripka a zbrklost mi nesvedci.Thx

Techi:

Mate pravdu, ze funkce NOW() se hodi do uvozovek, tj. bude se s ní pracovat
jako se stringem, ale nenapadlo mě, že při vkládání/editování používáte často sql funkce
Já jsem zvyklej na PHP ekvivalenty...

Používam tuhle funkci v hodně skriptech, docela dost mi to zpřehlední dotaz a nemusíte jak blbci se patlat s uvozovkama a addslashes()... :)

Jediný co jsem upravil je, aby to akceptovalo NULL
Jinak je funkce v pořádku, kdo mi nevěří, ať se podívá na výslednej dotaz :)
new CQuery() - je jenom moje pomocná MySQL třída, hází mi výjimky...

<?php

/**
* ModifyTable
*
* This function can easy modify table (insert or update line)
* all variables are added into slashes and dangerous characters are escaped
* NULL pointers are re-transformed to NULL in SQL language
*
* @param $table       target SQL table
* @param $set         array with items to update/insert
* @param $condition   SQL condition if UPDATE table
* @param $delete      TRUE if delete line(s)
*
* @return id of last inserted line in case of INSERT operation
*/

function ModifyTable($table, array $set, $condition = "", $delete = FALSE)
{
    if($delete)
    {
        new CQuery("DELETE FROM $table WHERE $condition");
    }

    // insert string into slashes and re-transform NULL
    array_walk($set, create_function('&$a', 'if(isset($a)): $a = "\'".AddSlashes($a)."\'"; else: $a = "NULL"; endif;'));

    if(!Empty($condition))
    {
        array_walk($set, create_function('&$val, $key', '$val = "$key = $val";'));
        new CQuery("UPDATE $table SET " . implode(", ", $set) . " WHERE $condition");
    }
    else
    {
        new CQuery("INSERT INTO $table (" . implode(", ", array_keys($set)) . ") VALUES (" . implode(", ", $set) . ")");
        return mysql_insert_id();
    }
}

// příklad

$set = array();

$set['hotel_name'] = "Corinthia Towers";
$set['stars_count'] = 5;
$set['fk_hgr'] = NULL;

ModifyTable("hotels", $set, "pk_hot = $_REQUEST[hotel]");

?>

Techi:

Teda proto jsem napsal tuhle funkci, abych nemusel všude psát apostrofy... Jakubova verze právě uvozovky automaticky nedělá...

ikona Jakub Vrána OpenID:

Ještě bys měl dát pozor na $_REQUEST["hotel"] v podmínce. Trestuhodně se spoléhá na to, že to bude vždy číslo.

Matěj:

jestli to dobře chápu, tak ten formulář slouží pro vkládání i editaci. V tom případě by to chtělo ale ošetřit ty hodnoty value u prvků, protože při vkládání nebudou definovány.

ikona Jakub Vrána OpenID:

Jak je uvedeno v patičce, skripty předpokládají nastavení error_reporting = E_ALL & ~E_NOTICE, takže nedefinovanost položek pole nevadí.

Pavel:

trochu pozdě k původnímu tématu ale ta patička je teda hodně daleko od těch skriptů musel jsem si dát hledání na stránce abych ji našel

Michal:

Chtel sem se zeptat jak je mozne ze mi nefunguje cast kodu s Update ale vzdy se mi vlozi uplne novy zaznam???nevite v cem by mohl byt problem??rad bych toto pouzil..Dekuji

Michal Krkavec:

Ahoj, abych ozivil starou diskuzi...
Trochu me mate, proc se v prikladu pouziva <?php $_REQUEST["select"] ?> a ne primo <?php $_GET["select"] ?>? Ma to nejaky vyznam? Mne to prijde (kdyz ne rovnou spatne) jen na zmateni nepritele. Diky. MK

ikona Jakub Vrána OpenID:

Díky za upozornění, opravil jsem to.

Pavel:

Díky přesně tohle jsem potřeboval jako inspiraci :-).
Já používám ještě další stav - Copy který vytvoří nový záznam se stejnými hodnotami - uživatel si změní třeba jen jedno pole z mnoha.

Vložit komentář

Používejte diakritiku. Vstup se chápe jako čistý text, ale URL budou převedeny na odkazy a PHP kód uzavřený do <?php ?> bude zvýrazněn. Pokud máte dotaz, který nesouvisí s článkem, zkuste raději diskusi o PHP, zde se odpovědi pravděpodobně nedočkáte.

Jméno: URL:

avatar © 2005-2018 Jakub Vrána. Publikované texty můžete přetiskovat pouze se svolením autora. Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons. Můžeme si tykat. Skripty předpokládají nastavení: magic_quotes_gpc=Off, magic_quotes_runtime=Off, error_reporting=E_ALL & ~E_NOTICE a očekávají předchozí zavolání mysql_set_charset. Skripty by měly být funkční v PHP >= 4.3 a PHP >= 5.0.