Diskuze: Uložení hodnoty do databáze
V předchozím kvízu, Online test znalostí PHP, jsme si ověřili nabyté zkušenosti z kurzu.


Martin Konečný (pavelco1998):18.5.2018 23:09
Ahoj, předáváš tam $zobuser, což je pole, proto ti to píše hlášku
"array to string conversion". Jak máš proměnnou $vlozit, tak do toho dotazu
předávej $zobuser["id"].
Je tam víc podivných věcí, ale zatím se budu držet otázky.
Neaktivní uživatel:19.5.2018 6:42
Proč je u toho prvního dotazu do DB "=?" ?
michal Čepelák:19.5.2018 11:14
Děkuji za radu už to funguje. A jaké jsou tam podivné věci ? vím že
tam nějaké jsou ale nevím kde přesně. Rád se nechám poučit.
Děkuji
Martin Konečný (pavelco1998):19.5.2018 13:25
Například první dotaz vůbec nemusí být fetchAll(), jelikož hledáš jenom jednoho uživatele podle $cardId. Tedy ten cyklus je tam zbytečně. Nevím, jak přesně vypadá ta knihovna Db, ale stačilo by něco jako
$query = Db::query('SELECT id,CardId FROM uzivatele WHERE CardId=?', $CardId);
$user = $query->fetch();
Další věc je, proč si prvně žádáš data o uživateli podle $cardId (navíc CardId nepotřebuješ vyhledávat, když to už znáš) a teprve potom zjišťuješ dalším dotazem, zda vůbec nějaký takový záznam existuje? Ten dotaz s proměnnou $existuje je zbytečný, protože pokud v prvním dotazu nic nenajdeš, tak v $user bude FALSE nebo něco takového.
Funkce htmlSpecialChars() se používá na výstupu, ne na vstupu. Proti SQL injection se používají jiné funkce, ale hlavně jelikož používáš parametrizované dotazy, tak si ta knihovna tu proměnnou ošetří sama. Tedy tam vůbec být nemusí a správně by ani neměla.
V podmínce if ($vlozit) se znovu připojuješ k databázi, proč?
Dotaz pod tím je stejný případ jako na začátku - hledáš jednoho
uživatele, nepotřebuješ tam nějaký cyklus. Navíc ta data o uživateli si
můžeš získat už v tom prvním dotazu, nepotřebuješ další
Takovéhle řešení by bylo v pohodě (píšu z hlavy, nemusí být 100% funkční):
Db::connect();
if (isset($_POST["CardId"])) {
$cardId = $_POST["CardId"];
$query = Db::query("SELECT * FROM uzivatele WHERE CardId = ?", $cardId);
$user = $query->fetch(PDO::FETCH_ASSOC);
if ($user !== FALSE) {
Db::query("INSERT INTO dochazka (date,id_user,CardId) VALUES (NOW(),?,?)", $user["id"], $cardId);
echo "Účast hasiče " . $user['jmeno'] . " " . $user['prijmeni'] . " bylo uloženo";
} else {
echo "K tomuto číslu karty není přiřazen žádný uživatel, zaregistrujte kartu";
}
}
použivám místní wrapper PDO vic příloha
tento kód
$user = Db::queryAll('SELECT id,CardId FROM uzivatele WHERE CardId=?', $CardId);
foreach ($user as $zobuser){
$zobuser['id'];
}
tam mám protože potřebuji získat z tabulky uzivatele, id uživatele ke
kterému je přidělena karta, abych pak mohl uživatele zobrazit pod jménem,
nevím jestli to jde udělat i jiným způsobem (bohužel jsem začátečník a
účím se to).
V kódu co jsi napsal ty tak máš pouze ověření jestli existuje v tabulce
uzivatele dané číslo karty(možná se pletu a špatně jsem si to
vyložil)
pokud použiju tvůj kód tak mi to napíše
Fatal error: Call to a member function fetch() on integer in
/var/www/html/sdh/dochazka.php on line 33
ale to je podlě mě skrz ten wrapper PDO co použivám
u podmínky vlozit znovu připojení : to bohužem mi kdysi řekl jeden kolega co použiva mysql ovladač a on u každého příkazu kde bylo připojení do databze znovu připojoval, počitám stím teda že mi stačí dát příkaz pro připojení nahoře na stránce a to bude fungovat pro všechny příkazy připojení.
Doufám že jsem to dobře vysvětlit
Děkuji za rady
PDO wrapper z místního fora nejde odeslat v příloze tak ho přikládám zde
<?php
/**
* __ __ __
* ____/ /__ _ __/ /_ ____ ____ / /__ _________
* / __ / _ \ | / / __ \/ __ \/ __ \/ //_// ___/_ /
* / /_/ / __/ |/ / /_/ / /_/ / /_/ / ,< _/ /__ / /_
* \__,_/\___/|___/_.___/\____/\____/_/|_(_)___/ /___/
*
*
* TUTORIÁLY <> DISKUZE <> KOMUNITA <> SOFTWARE
*
* Tento zdrojový kód je součástí tutoriálů na programátorské
* sociální síti WWW.DEVBOOK.CZ
*
* Kód můžete upravovat jak chcete, jen zmiňte odkaz
* na www.devbook.cz :-)
*
* Jednoduchý databázový wrapper nad PDO
*/
class Db
{
/**
* @var PDO Databázové spojení
*/
private static $connection;
/**
* @var array Výchozí nastavení ovladače
*/
private static $options = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING,
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8",
PDO::ATTR_EMULATE_PREPARES => false,
);
/**
* Připojí se k databázi pomocí daných údajů
* @param string $host Název hostitele
* @param string $database Název databáze
* @param string $user Uživatelské jméno
* @param string $password Heslo
*/
public static function connect($host="localhost", $database="sdh", $user="root", $password="")
// public static function connect($host="localhost", $database="sdh", $user="sdh", $password="")
// public static function connect($host, $database, $user, $password)
{
if (!isset(self::$connection)) {
$dsn = "mysql:host=$host;dbname=$database";
self::$connection = new PDO($dsn, $user, $password, self::$options);
}
}
/**
* Spustí dotaz a vrátí PDO statement
* @param array $params Pole, kde je prvním prvkem dotaz a dalšími jsou parametry
* @return \PDOStatement PDO statement
*/
private static function executeStatement($params)
{
$query = array_shift($params);
$statement = self::$connection->prepare($query);
$statement->execute($params);
return $statement;
}
/**
* Spustí dotaz a vrátí počet ovlivněných řádků. Dále se předá libovolný počet dalších parametrů.
* @param string $query Dotaz
* @return int Počet ovlivněných řádků
*/
public static function query($query) {
$statement = self::executeStatement(func_get_args());
return $statement->rowCount();
}
/**
* Spustí dotaz a vrátí z něj první sloupec prvního řádku. Dále se předá libovolný počet dalších parametrů.
* @param string $query Dotaz
* @return mixed Hodnota prvního sloupce z prvního řádku
*/
public static function querySingle($query) {
$statement = self::executeStatement(func_get_args());
$data = $statement->fetch();
return $data[0];
}
/**
* Spustí dotaz a vrátí z něj první řádek. Dále se předá libovolný počet dalších parametrů.
* @param string $query Dotaz
* @return mixed Pole výsledků nebo false při neúspěchu
*/
public static function queryOne($query) {
$statement = self::executeStatement(func_get_args());
return $statement->fetch(PDO::FETCH_ASSOC);
}
/**
* Spustí dotaz a vrátí všechny jeho řádky jako pole asociativních polí. Dále se předá libovolný počet dalších parametrů.
* @param string $query Dotaz
* @return mixed Pole řádků enbo false při neúspěchu
*/
public static function queryAll($query) {
$statement = self::executeStatement(func_get_args());
return $statement->fetchAll(PDO::FETCH_ASSOC);
}
/**
* Umožňuje snadné vložení záznamu do databáze pomocí asociativního pole
* @param string $table Název tabulky
* @param array $data Asociativní pole, kde jsou klíče sloupce a hodnoty hodnoty
* @return int Počet ovlivněných řádků
*/
public static function insert($table, $data) {
$keys = array_keys($data);
self::checkIdentifiers(array($table) + $keys);
$query = "
INSERT INTO `$table` (`" . implode('`, `', $keys) . "`)
VALUES (" . str_repeat('?,', count($data) - 1) . "?)
";
$params = array_merge(array($query), array_values($data));
$statement = self::executeStatement($params);
return $statement->rowCount();
}
/**
* Umožňuje snadnou modifikaci záznamu v databázi pomocí asociativního pole
* @param string $table Název tabulky
* @param array $data Asociativní pole, kde jsou klíče sloupce a hodnoty hodnoty
* @param string $condition Řetězec s SQL podmínkou (WHERE)
* @return mixed
*/
public static function update($table, $data, $condition) {
$keys = array_keys($data);
self::checkIdentifiers(array($table) + $keys);
$query = "
UPDATE `$table` SET `".
implode('` = ?, `', array_keys($data)) . "` = ?
$condition
";
$params = array_merge(array($query), array_values($data), array_slice(func_get_args(), 3));
$statement = self::executeStatement($params);
return $statement->rowCount();
}
/**
* Vrátí poslední ID posledního záznamu vloženého pomocí INSERT
* @return mixed Id posledního záznamu
*/
public static function getLastId()
{
return self::$connection->lastInsertId();
}
/**
* Ošetří string proti SQL injekci
* @param string $string Řetězec
* @return mixed Ošetřený řetězec
*/
public static function quote($string)
{
return self::$connection->quote($string);
}
/**
* Zkontroluje, zda identifikátory odpovídají formátu identifikátorů
* @param array $identifiers Pole identifikátorů
* @throws \Exception
*/
private static function checkIdentifiers($identifiers)
{
foreach ($identifiers as $identifier)
{
if (!preg_match('/^[a-zA-Z0-9\_\-]+$/u', $identifier))
throw new Exception('Dangerous identifier in SQL query');
}
}
}
Martin Konečný (pavelco1998):20.5.2018 13:29
V tom případě místo konstrukce
$query = Db::query("dotaz");
$user = $query->fetch();
napiš
$user = Db::querySingle("dotaz");
Ty tam děláš zbytečně dva dotazy - jeden na kontrolu, zda existuje
záznam, a druhý na výběr dat o uživateli. Jenže můžeš v klidu udělat
jeden dotaz na data o uživateli a pokud ti nepřijdou data o uživateli
(protože se nic nenašlo), tak to znamená, že takový záznam neexistuje
michal Čepelák:20.5.2018 13:58
Ale jak zobrazím dole v echo účast hasiče ... tak je tam jméno a
příjmení a to vytahuji z tabulky uzivatele, kterou použivám i pro user.
tak nahoře ten foreach musí zůstat nebo se pletu?
úprava kódu
if (isset($_POST['CardId'])){
$CardId = htmlspecialchars($_POST['CardId'],ENT_QUOTES);
$user = Db::querySingle('SELECT * FROM uzivatele WHERE CardId=?', $CardId);
foreach ($user as $zobuser){
$zobuser['id'];
}
$existuje = Db::querySingle('SELECT COUNT(*) FROM uzivatele WHERE CardId=?', $CardId);
if ($existuje){
$vlozit = Db::query('INSERT INTO dochazka (date,id_user,CardId) VALUES (NOW(),?,?)',$zobuser["id"], $CardId );
if ($vlozit){
echo 'Účast hasiče '.$zobuser['jmeno'].' '.$zobuser['prijmeni'].' bylo uloženo';
}
}else{
echo 'Chyba komunikace s databazí, kontaktujte administrátora';
}
}
takle to funguje jak má
vic foto v příloze
když se na to te´dka dívasm tak je tam zybtečný ta proměná existuje
když jsem to te´dka zkoušel tak bez této kontroli mi to nevypíše chybu K tomuto číslu karty není přiřazen žádný uživatel, zaregistrujte kartu
Martin Konečný (pavelco1998):20.5.2018 15:49
Tady to vypadá na nepochopení toho, jak ta data získáš z tabulky v
databázi.
Máš v podstatě tři možnosti
- vybírat více řádků naráz - k tomu slouží metoda queryAll(). Výsledkem je pole záznamů, kdy jeden záznam v poli = 1 řádek tabulky. To používáš v případě, že bys těch uživatelů hledal více naráz, a tedy potom použiješ cyklus (př. foreach), abys je všechny prošel.
- vybírat jeden řádek - k tomu slouží metoda queryOne(). Výsledkem je
jedno pole (případně objekt třídy \stdClass), které obsahuje data ve tvaru
název sloupce
=>hodnota sloupce
, tedy třebaid
=> 1,jmeno
=> "Michal,",prijmeni
=> "Čepelák". To použiješ ty, protože hledáš jednoho uživatele, ale více informací o něm - ID, jméno, příjmení atp. - vybírat jednu konkrétní hodnotu jednoho sloupce - k tomu slouží metoda querySingle(). Výsledkem je jeden řetězec, kterému odpovídá hodnota konkrétního sloupce. Tedy např.
$jmeno = Db::querySingle("SELECT jmeno FROM uzivatele WHERE id = 1");
v proměnné $jmeno bude hodnota sloupce jmeno
uživatele,
který má id
= 1.
Takže když se podíváš na to, co potřebuješ - potřebuješ získat
více informací o jednom uživateli podle ID jeho karty. Tedy z výše
uvedených možností zvolíš variantu b), tedy metodu queryOne(). Když se
podíváš do dokumentačních komentářů (nebo do manuálu PDO), tak
zjistíš, že ta metoda vrací buď pole, pokud se záznam našel, nebo FALSE,
pokud se nenašel.
Jinými slovy - pokud ti ta metoda vrátí pole, máš jistotu, že takový
uživatel existuje, a tedy můžeš zapsat jeho čas příchodu a vypsat jeho
jméno a příjmení (a jakákoliv další data, které si vySELECTuješ).
Pokud ti ta metoda vrátí FALSE, pak víš, že takový uživatel neexistuje, a
tedy vypíšeš tu hlášku.
Nepotřebuješ žádný dotaz typu COUNT(*) na zjištění, zda uživatel existuje. Nepotřebuješ žádný foreach, když výsledkem dotazu je jeden jediný záznam.
Martin Konečný (pavelco1998):20.5.2018 15:56
Jen tak mimochodem - do kódu třetí strany bys neměl sahat, zvlášť,
když to vůbec není potřeba. Nastuduj si, co znamenají výchozí hodnoty u
parametrů.
Metoda Db::connect() přijímá několik parametrů, které mají nějakou
výchozí hodnotu. To znamená, že když ty parametry nenastavíš ručně,
budou mít tu výchozí hodnotu. Ty, jelikož máš jiné login údaje k
databázi, než jsou ty výchozí, bys měl předat hodnoty ručně, a neměnit
kód té knihovny. Tedy
Db::connect("localhost", "sdh", "root", ....);
a ne měnit konstruktor té třídy.
Zobrazeno 15 zpráv z 15.