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

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: Reakce na: Pavel

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.
avatar © 2005-2020 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.