Autor Zpráva
Martin Kroutil
Profil
Dobrý den,

prosím někoho o pomoc. Je to již nějaký ten pátek, kdy jsem se věnoval PHP a MySQL. HTML ještě zvládám, ale databáze jako takové mi dělají problém a tak bych potřeboval pomoci s následujícím konceptem.

Mělo by se jednat o jednoduchý webový formulář, kde zadám buď název produktu, nebo si v rolovací liště vyberu název daného produktu, stiskne se tlačítko ZJISTIT CENU a na další stránce se objeví informace o výkupní ceně.

Příklad: Klávesnice Logitech -> ZJISTIT VÝKUPNÍ CENU -> VÝKUPNÍ CENA : 200 Kč.

Ještě mě napadlo, zda není možné při výběru dané položky přímo z rolování po kliknutí na danou položku rovnou rozbalit cenu aniž bych musel klikat na tlačítko ZJISTIT CENU, ale to bohužel asi bez vyžádání dotazu nelze, že?

Prosím někoho o nějaký jednoduchý script, berte v úvahu, že jsem PHP kód a MySQL neviděl už minimálně 5 let, takže budu rád i za radu, jak správně vytvořit funkční tabulku v phpmyadmin.

Děkuji moc, Martin.
Kajman
Profil
Nejjednodušší bude vypsat ceny všech položek hned do tabulky v html stránce. Nemusíte programovat žádné formuláře a pokud nepotřebujete ceny i na něco jiného, tak ani tu databázi vlastně nepotřebujete.
Martin Kroutil
Profil
Budou tam ceny na protiúčet a cena v hotovosti. Rád bych to měl v PHP a MySQL, protože do budoucna se s tím bude dát lépe pracovat, než to vypisovat do tabulky v HTML. Navíc si tu tabulku nedokážu představit, když počet těch položek je asi 3000.
Keeehi
Profil
Martin Kroutil:
Rád bych to měl v PHP a MySQL, protože do budoucna se s tím bude dát lépe pracovat, než to vypisovat do tabulky v HTML
To mít můžete. Kajman předvším poukazoval na to, že by bylo dobré ty ceny do stránky vypsat rovnou a ne se na ně později doptávat. To jestli je tam vypíšete natvrdo, nebo je tam budete generovat je až druhořadé.

Navíc si tu tabulku nedokážu představit, když počet těch položek je asi 3000.
Budete mít snad 3000 cen pro jednu položku? Ne budete mít 2. Tudíž ta tabulka bude o dvou řádcích. Máte 3000 položek na stránce? Pak budete mít 3000 dvouřádkových tabulek. To ale není moc rozdíl oproti 3000 selectům.

Toto byste mohl se svými zkušenostmi zvládnout. Pokud si seženete někoho s většími zkušenostmi, dá se samozřejmě udělat i ta varianta, kde by se cena zjišťovala až dodatečně. A klidně i bez klikání na tlačítko zjistit cenu.
Martin Kroutil
Profil
A je tu někdo, kdo by to dokázal, nějaký kontakt? Přijde mi to jako jednoduché, viděl jsem zde něco na podobný styl, jen by to chtělo asi malinko upravit? Něco na tento styl -> Filtrování dat z mysql tabulky .. pokud by byl někdo ochoten, jsem na oplátku já ochoten zaplatit.
Keeehi
Profil
Je tu spousta lidí, kteří to zvládnou. Založ si téma v kategorii Práce a zakázky a někdo se určitě ozve.
Martin Kroutil
Profil
Takže už máme dalo by se říct naplněnou databázi, hotový design a potřebujeme akorát "rozchodit" poslední, nejdůležitější funkci.
Pro ukázku jsem udělal jednoduchý formulář (+jednoduchou databázi, pro demonstraci), kde ze zadá název a podle toho by se měli vypsat všechny hry, které tento název mají.
Na webu jsem udělal skript, který mi vypíše všechny hry, které jsou v testovací databázi, vyhledávání dle názvu, nebo alespoň počátečního písmena se mi nedaří rozchodit.
Výpis všech her + formulář:


Přidávám také kód fomuláře + celého skriptu na vypsání všech hodnot v databázi.

<form method="POST" action="">
<label>Název:</label>
<input type="text" name="nazev">
<br />
<input type="submit" value="Vyhledat" name="search">
</form>

<?php
                require_once('Db.php');
                Db::connect('xxxxxxxx', 'xxxxxxxxxx', 'xxxxxxxxxx', 'xxxxxxxxx');
                if ($_POST)
                {
                        Db::query('
                                VALUES (?, ?, ?)
                        ', $_POST['nazev'], $_POST['protiucet'], $_POST['hotovost']);

                }
        
        $hry = Db::queryAll('
            SELECT *
            FROM hry
        ');
        echo('<h2>Hry v databázi</h2>
<table border="1">');
        foreach ($hry as $u)
        {
            echo('<tr><td>' . htmlspecialchars($u['nazev']));
            echo('</td><td>' . htmlspecialchars($u['protiucet']));
            echo('</td><td>' . htmlspecialchars($u['hotovost']));
            echo('</td></tr>');
        }
        echo('</table>');
        

 ?>

Předem děkuji za radu!
Keeehi
Profil
SELECT *
FROM hry
WHERE nazev LIKE '%$hodnota%'
Nezapomeň proměnnou $hodnota správně ošetřit, neboť vstupu $_POST["nazev"] se nedá věřit.
Martin Kroutil
Profil
Nějaký nápad, proč to nefunguje? Když přidám do funkci WHERE, tak se mi nezobrazí již žádné hodnoty a po zadání názvu a stisknutí tlačítka Vyhledat se taktéž nic neukáže ..
Kajman
Profil
Nejpíše to bude špatným přidáním.
Martin Kroutil
Profil
Možné to je, ale kde ta chyba je? Nejlepší by bylo, vyjmout část kódu a poslat jí sem v přesném znění. vstup POST se dá nahradit čím?
Kajman
Profil
Naopak. Lepší bude, když sem pošlete problematickou část kódu Vy. Třeba bude stačit drobná oprava.
Martin Kroutil
Profil
Dobře, kód je o pár příspěvků výše, ale koukněme na něj znovu. Formulář v HTML asi netřeba znovu vypisovat, takže rovnou k PHP.
<?php
                require_once('Db.php');
                Db::connect('xxxxxxxx', 'xxxxxxxxxx', 'xxxxxxxxxx', 'xxxxxxxxx');
                if ($_POST)
                {
                        Db::query('
                                VALUES (?, ?, ?)
                        ', $_POST['nazev'], $_POST['protiucet'], $_POST['hotovost']);
                }
        $hry = Db::queryAll('
            SELECT *
            FROM hry
        ');
        echo('<h2>Hry v databázi</h2>
<table border="1">');
        foreach ($hry as $u)
        {
            echo('<tr><td>' . htmlspecialchars($u['nazev']));
            echo('</td><td>' . htmlspecialchars($u['protiucet']));
            echo('</td><td>' . htmlspecialchars($u['hotovost']));
            echo('</td></tr>');
        }
        echo('</table>');
 ?>
Po přidání funkce WHERE a LIKE se bohužel nezobrazí již žádné hry (což je pochopitelné, doposud se vypisovali všechny hry bez ohledu na vyhledávání, po přidání funkcí WHERE a LIKE by se již měli vypsat všechny hodnoty začínající např. písmenem G (pokud jsem do formuláře zadal vyhledávání písmena G), jenže se bohužel nic nevypisuje.
        $hry = Db::queryAll('
            SELECT *
            FROM hry
            WHERE nazev LIKE '%$_POST["nazev"]%'
Je vůbec takto kód správně?

Jinak jsem ochoten zaplatit někomu menší částku za vyřešení a zprovoznění, docela na to spěchám. (v sekci práce a zakázky jsem oslovil několik lidí, spousta se jich už neozvala, nebo napsala částky, které jsme dávali za zhotovení celého e-shopu ...).

Díky za radu!
Kajman
Profil
Kód jste nevložil vůči contextu ohraničení řetězce správně. Navic je potřeba ho escapovat. Neznám Vaši databázou vrstvu, zkuste

$hry = Db::queryAll('
            SELECT *
            FROM hry
            WHERE nazev LIKE ?
        ', '%'.$_POST["nazev"].'%');
Martin Kroutil
Profil
Také nefunguje. Ve formuláři odkazuji na action="vypisher.php", kde je vložen ten php kód, který jsem posílal. Nemám něco vložit také na index, kde je ve své podstatě pouze kód formuláře? Pokud mi popíšete, co mám vyfotit, poslat, atd., není problém to sem okamžitě nahodit.
Martin Kroutil
Profil
Zde přikládám ještě screeen struktury databáze, třeba je chyba někde tam.


Děkuji za každou radu!
Keeehi
Profil
Chyba je akorát v tom, že v PHP neumíš správně pospojovat řetězec aby ti vyšlo WHERE nazev LIKE '%hledaný výraz%' a to včetně jak těch apostrofů tak i těch procent. Což je velmi jednoduché. Ovšem zároveň si musíš dát pozor, aby byl hledaný výraz správně ošetřený, aby tím nevznikla bezpečnostní díra. Jak? Ti my nevíme, protože jsi nám nenapsal, jakou používáš databázovou vrstvu.
Martin Kroutil
Profil
A ta databázová vrstva znamená co? Hledal jsem na netu, ale nic konkrétního jsem bohužel nenašel. Když si dám do skriptu následující kód, tak funguje, ale jakmile hledám v názvu, tak to nechce nic nalézt ..

$hry = Db::queryAll('
            SELECT *
            FROM hry
            WHERE protiucet LIKE 500
            ');

Divné také je, že když zadám WHERE nazev bez funkce LIKE, tak místo 3800 hodnot to nalezne pouhých cca. 9 a všechny začínají číslovkou.
tiso
Profil
$hry = Db::queryAll("
            SELECT *
            FROM hry
            WHERE protiucet LIKE '%500%'
           ");
alebo:
$hry = Db::queryAll('
            SELECT *
            FROM hry
            WHERE protiucet LIKE \'%500%\'
           ');
Martin Kroutil
Profil
tiso:
Díky za odpověď!! Druhá možnost funguje perfektně na 100%!

Jen se mi teď nedaří rozchodit, aby při zadání do fomuláře třeba Fifa po kliknutí na odeslat zobrazily všechny hodnoty, které mají v názvu slovo Fifa ..
Keeehi
Profil
Martin Kroutil:
A ta databázová vrstva znamená co?
To je to tvé Db::queryAll což je nějaká knihovna usnadňující práci s databází. Když nám ale neřekneš co za knihovnu to je, těžko ti můžeme poradit, jak s ní správně pracovat. Na základě zkušeností s prácí s jinými knihovnami si můžeme tipnout, jak to asi má být správně ale to pak vůbec nemůžeme zaručit, že to bude fungovat a už vůbec ne, že to bude bezpečné!

Jen se mi teď nedaří rozchodit, aby při zadání do fomuláře třeba Fifa po kliknutí na odeslat zobrazily všechny hodnoty, které mají v názvu slovo Fifa
Místo v sloupci protiucet budeš hledat v název a 500 nahradíš řetězcem Fifa. Jak? To budeme schopni říct až odpovíš na první část mé odpovědi.
Martin Kroutil
Profil
Špatně jsme se pochopili, pokud pozměnit dotaz na:
WHERE nazev LIKE LIKE \'%Fifa%\'
tak se mi zobrazí všechny hodnoty, které začínají na Fifa, nebo ho alespoň obsahují někde v názvu, což je samozřejmě správně a tak by to mělo být.

Teď je ale otázka, jak rozchodit formulář
<form method="POST" action="">
<label>Název:</label>
<input type="text" name="nazev">
<br />
<input type="submit" value="Vyhledat" name="search">
</form>
Aby se mi po zadání do tohoto formuláře (třeba opět příklad Fifa) a stisknutí tlačítka Vyhledat zobrazily všechny hodnoty obsahují text Fifa. Výše zmíněné S_POST nefungují. Nejspíše to už je jen banalita, ale je to poslední krok, který potřebuji k dokončení.

Děkuji za odpověď!
Kajman
Profil
Martin Kroutil:
Data z formuláře je nutné ošetřit proti sql injection, jinak budou celé stránky lehce zranitelné. A bez znalosti, jak se ve Vámi používané databázové vrstvě řetězce ošetřují, není možné správně poradit. Odpovězte tedy na [#21]
Martin Kroutil
Profil
Zde zasílám celý kód Db.php
<?php

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, $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');
        }
    }
}

Díky za odpověď.
Kajman
Profil
Zkuste

$hry = Db::queryAll('
            SELECT *
            FROM hry
            WHERE nazev LIKE ' . Db::quote("%".$_POST["nazev"]."%")
            );
Martin Kroutil
Profil
Taktéž nefunguje, pokud to nikomu slušnému nebude vadit, jsem ochoten poslat všechny soubory včetně přístupu do databáze, aby to zkusil zprovoznit. Asi by to bylo nejrychlejší řešení, než to takto střílet. Já s tím nemám problém.
Kajman
Profil
Jestli quote escapuje i procenta, tak bych zkusil ještě
$hry = Db::queryAll("
            SELECT *
            FROM hry
            WHERE nazev LIKE concat('%'," . Db::quote($_POST["nazev"]) . ",'%')"
            );
Martin Kroutil
Profil
Pane "Kajman" Vy jste bůh! Děkuji moc, už to funguje, super! Kdybych se Vám mohl nějak odvděčit, tak sem s tím :). Jinak se zeptám, takto je formlulář ošetřený proti SQL Injection?

Vaše odpověď

Mohlo by se hodit


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm: