Autor Zpráva
Chuchycek
Profil
Zdravím,

píšu si vlastní databázovou třídu a narazil jsem na problém, nevím jak vhodně napsat funkci na WHERE.

Výběry sloupců jsem vyřešil takto:

    private function rowsArray(array $array) {
        $num = count($array);
        if ($num == 0) {
            $data = "*";
        } else {
            for ($i = 0; $i < $num; $i++) {
                $data .= $this->realEscape($array[$i]);
                if ($i < ($num - 1)) {
                    $data .= ", ";
                }
            }
        }
        return $data;
    }

Třída pro select:
    public function select($table, array $row = NULL, $where = "", $order = "", $type = "assoc") {
        if ($this->existTable($table)) {
            $rows = $this->rowsArray($row);
            $where = $this->whereArray($where);
            $query = "SELECT " . $rows . " FROM " . $table . " " . $where . " " . $order;
            return $this->returnArray($query, $type);
        } else {
            return false;
        }
    }

A jelikož chci mít i v funkci whereArray() ošetřený každý vstup pomocí mysql_real_escape_string(), tak si nevím rady, jak to udělat, conejefektivněji. Děkuji za každou radu, která mě posune dál.

Prosím přesunout do kategorie PHP.
Tori
Profil
Metoda realEscape zajišťuje i zpětné apostrofy okolo názvů sloupců?
K metodě rowsArray: možná by bylo přehlednější místo cyklu použít array_map + implode.
Ad whereArray: jaký vstup chcete zpracovávat? pole [sloupec => hodnota]? Pokud bude víc hodnot tak jak rozlišíte mezi AND a OR (myslím jak to napíšu, když budu volat metodu select)?
Trochu mi tam nesedí 5.parametr - $type, protože se netýká dotazu ale získávání dat. Buď bych ho dala do jiné metody, která získává data, anebo select přejmenovala. Spíš to první, kvůli single responsibility.


Ještě k té metodě realEscape: tím "real" naznačuje, že se bude chovat jako mysql_real_escape_string, tzn. že je určena jen pro řetězce. Přitom ji ale používáte pro názvy sloupců, kde bych čekala spíš metodu escapeIdentifier nebo tak něco.
Chuchycek
Profil
Ano realEscape zajištuje SQL injection.
U whereArray jsem původně taky myslel pole, jenže když tam bude více hodnot, tak jak to udělat co nejefektivněji, kvůli AND a OR, taky jsem zamýšlel, že to nechám jako string a potom vše, co bude v uvozovkách ošetřím, jenže to se mi zdá, že by to mohlo být lehce napadnutelné.
Ten 5. paramatr si myslím, že je v pořádku, když není povinný...
Tori
Profil
Chuchycek:
A co třeba takováhle syntax? První položka pole, která má číselný klíč, určuje typ spojení. (Tzn. nemůžu odkazovat na vybrané sloupce pořadím, ale jen aliasem).
# pokud typ není zadaný, použije se implicitně OR
$where = array('col1' => 'value1', 'col2' => 'value2');
    -> "`col1` = 'value1' OR `col2` = 'value2'

# stejný výsledek jiným zápisem
$where = array('OR', 'col1' => 'value1', 'col2' => 'value2');  # nebo 
$where = array('col1' => 'value1', 'col2' => 'value2', 'OR');

# více skupin podmínek se zapíše jako několik vnořených polí
$where = array( 
    'OR',
    array('AND', 'col1' => 'value1', 'col2' => 'value2'), 
    array('col3' => 'value3')
);
    -> "(`col1` = 'value1' AND `col2` = 'value2') OR (`col3` => 'value3')"
taky jsem zamýšlel, že to nechám jako string a potom vše, co bude v uvozovkách ošetřím
Kromě AND a OR by mohl být další typ návěští (anebo specielní klíč místo názvu sloupce?), pokud byste chtěl zadat nějakou SQL funkci apod. (jako modifikátor %sql v dibi). Ale je to jen nápad, co by mi připadalo snadno zapamatovatelné na používání, sama jsem DB vrstvu nikdy nezkoušela psát.
Chuchycek
Profil
Tak jsem napsal jednu z možností a narazil jsem ještě na problém s výrazy v dotazu. Napadlo mě ještě todle, ale nejsem si jistý, jestli to je nejkvalitnější řešení..

    //$where = array('col1' => 'value1', 'col2' => 'value2.!=', 'OR');
    private function whereArray($array) {
        $num = count($array);
        if ($num == 0) {
            $data = "";
        } else {
            foreach ($array as $key => $value) {
                if ($key != 0) {
                    $result = explode(".", $value);
                    if (cout($result) == 1) {
                        $equals = "=";
                    } else {
                        $equals = $result[1];
                    }
                    $data .= $key . $equals . "'" . $this->realEscape($result[0]) . "'";
                } else {
                    $data .= " " . $value . " ";
                }
            }
        }
        return $data;
    }
peta
Profil
    foreach ($values as $key=>$value)
        {
        $key = mysql_real_escape_string($key);
        $where[] = "`$key`=" . $this->escape($value);
        }
    $where = implode(' AND ',$where);

U selectu where vubec neresim, protoze tam obvykle muze byt jakekoliv. U insertu, update a delete obvykle pouzivam jen AND. Updatuji a deletuji vetsinou jen vuci id.
panther
Profil
Chuchycek:
jestli dobre chapu, teckou u hodnoty oddelujes rovnost a nerovnost (= vs. !=)? Pokud ano, rozhodne to dobre navrhnute/vymyslene nemas. Nenapada te, kdy se to zacne hroutit?
Chuchycek
Profil
panther:
Ano, to mě taky napadlo a proto jsem taky píšu ohledně nejlepšího řešení. A hlavně je to jen takový první nápad, co se mi dostal do hlavy..
panther
Profil
Chuchycek:
staci, aby v hodnote ukladane do sloupce byla tecka. Treba ve vete, desetinnem cisle, ...

Asi bych osobne vychazel z toho, co napsala Tori, tzn. vkladat jednotlive rozdilne useky do poli, porovnani (ne)rovnosti by pak musel byt dalsi parametr v kazdem tom poli, s defaultnim „rovna se“.
Chuchycek
Profil
panther
ano to vím, že je to chybné, ale říkal jsem, že to byl první nápad. Taky bych tam místo tečky mohl dát nějakou kombinaci znaků..., ale o tom zase jiná.

Vycházím z toho co napsala Tori a nejspíš to bude asi nejlepší řešení, přdat do pole ještě jeden parametr navíc a nebo jak psal Peta, vůbec where neřešit..
panther
Profil
Chuchycek:
Taky bych tam místo tečky mohl dát nějakou kombinaci znaků...
ani kombinaci... zhrouti se ti to vzdy, kdyz bude nekdo tuto sekvenci znaku, byt sebeodpudivejsi, chtit vlozit. Na toto slouzi parametry, ne retezeni stringu pomoci nejakych pochybnych separatoru.
peta
Profil
Ja bych neresil cely select. Mam v ruznych projektech tak komplikovane where podminky, ze by to tvym systemem asi tezko proslo. A v jinych zas pouzivam kombinace s JOIN nebo propojeni 4 selectu pomoci UNION. Nastesti na to mame db specialistu, sam bych asi tak slozity dotaz neposkladal (asi 300 radku).
Chuchycek
Profil
peta
Ano, už nad tím taky přemýšlím, že vyřešit celý select, je komplikovanější než jsem čekal, ještě se s tím zkusím trošku poprat a uvidím jestli nezůstanu u úpravy jen části dotazu.

Teď jsem where vymyslel takto:
    //$where = array('col1' => array("value1"), 'OR', 'col2' => array('value2', "!="));
    private function whereArray($array) {   
        if (count($array) == 0) {
            $data = "";
        } else {
            $data = "WHERE ";
            foreach ($array as $key => $val) {
                if (is_int($key)) {
                    $data .= " " . $val . " ";
                } else {
                    if (!isset($val[1])) {
                        $comparison = "=";
                    } else {
                        $comparison = $val[1];
                    }
                    $data .= $key . $comparison . "'" . $this->realEscape($val[0]) . "'";
                }
            }
        }
        return $data;
    }

Ještě bych prosil o komentář k výběru tabulky, popř. spojováním tabulek s INNER JOIN, je to opět takový první nápad, co jsem dostal.
    //vstup retezec nebo pole $tables = array('table1' => 't1', 'table2' => 't2', 't1.id=t2.kdo');
    private function tables($tables) {
        $data = "FROM ";
        if (!is_array($tables)) {
            if ($this->existTable($tables)) {
                $data .= $this->dbindex . $table;
            } else {
                $data = false;
            }
        } else {
            $noTable = false;
            $firstTable = true;
            $firstON = true;
            foreach ($tables as $key => $val) {
                if (is_int($key)) {
                    if ($firstON) {
                        $data .= " ON " . $val . "";
                        $firstON = false;
                    } else {
                        $data .= ", " . $val . "";
                    }
                } else {
                    if ($this->existTable($key)) {
                        if ($firstTable) {
                            $data .= $this->dbindex . $key . " " . $val;
                            $firstTable = false;
                        } else {
                            $data .= " INNER JOIN " . $this->dbindex . $key . " " . $val;
                        }
                    } else {
                        $noTable = true;
                    }
                }
            }
            if ($noTable) {
                $data = false;
            }
        }

        return $data;
    }
Tori
Profil
Chuchycek:
Pěkně se to klube. :) Jestli můžu ještě pár poznámek:
Podle [#13] musím psát operátor (OR, AND) vícekrát, mezi každé dvě hodnoty. Šlo by to i tak, abych ho napsala jen jednou, v libovolném pořadí (napovím: $where = 'WHERE '.implode($operator, $podminky);)
V [#5] jste měl dobrý nápad rozlišovat operátory podle toho, že mají klíč 0, k tomu bych se ještě vrátila. Akorát potřebujete rozlišit stavy:
- $pole[0] obsahuje operátor porovnání array("sloupec" => "hodnota", '=')
- $pole[0] obsahuje logický operátor array("sloupec" => "hodnota", "sloupec2" => "hodnota2", 'OR')
- $pole[0] obsahuje nadbytečný log.operátor, který se má ignorovat array("sloupec" => "hodnota", 'AND')
- $pole[0] neobsahuje operátor, ale řetězec = chyba array("sloupec" => "hodnota", 'hodnota2')
A možná by nebylo špatné místo přímého zápisu operátorů jako řetězců používat konstanty třídy: DB::LT, DB::LTE, DB::EQ, DB::NONEQ, DB::AND, ...

panther:
Na toto slouzi parametry, ne retezeni stringu pomoci nejakych pochybnych separatoru.
Na druhou stranu jedna známá DB vrstva zrovna toto používá, akorát přilepuje řetězec k názvu sloupce (kde to imho nemůže ničemu vadit, pokud oddělovačem nebude tečka). ;-) Ale stejně jako tam bych to nepoužívala pro operátory, ale spíš jako návěští, že zadaná hodnota je něco jiného než řetězec (např. přímo zadaný SQL kód, název jiného sloupce, logická/nulová hodnota...), protože to ta escapovací metoda sama nemá šanci poznat.
Chuchycek
Profil
Možná jsem to dobře nepochopil s těmi stavy, ale není to trošku zbytečné, když budu kontrolovat, jestli mám dobře vyplněné pole?

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: