Nette\Database: podmíněné části dotazu

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
David Růžička
Člen | 43
+
0
-

Zkouším aplikaci Todolist z tutoriálu zbavit dibi a vyzkoušet místo toho Nette\Database. Pro ilustraci vybírám metodu todoCount, která s dibi vypadala takhle:

public function todoCount($where = NULL)
{
    return dibi::fetchSingle('SELECT COUNT([id]) FROM [tasks] %if',
                 isset($where), 'WHERE', isset($where) ? $where : array()
    );
}

Lze i s Nette\Database zkonstruovat nějak jednoduše dotaz s podmínkou „%if“? Zatím mám tohle:

public function todoCount($where = NULL)
{
		return self::$connection2->table('tasks')->count('id');
}

V $connection2 je instance Nette\Database\Connection.
Lze tam nějak dostat to podmíněné where nebo se to musí rozepsat do více příkazů?

Vojtěch Dobeš
Gold Partner | 1316
+
0
-
$count = self::$connection2->table('tasks')->count('id');

if (isset($where)) {
	$count->where($where);
}
return $count;
David Růžička
Člen | 43
+
0
-

Díky. Takže rozepsat. Ale co když těch podmínek bude víc? To pak ta konstrukce bude dost šílená, ne? Neexistuje jednodušší zápis?

Příklad opět z tutoriálu:

public function findAllTodos($order = NULL, $where = NULL, $offset = NULL, $limit = NULL)
{
    return dibi::query(
        'SELECT * FROM [tasks]',
        '%if', isset($where), 'WHERE %and', isset($where) ? $where : array(), '%end',
        '%if', isset($order), 'ORDER BY %by', $order, '%end',
        '%if', isset($limit), 'LIMIT %i %end', $limit,
        '%if', isset($offset), 'OFFSET %i %end', $offset
    )->setRowClass('Todo');
}
spidy
Člen | 55
+
0
-

No když už používáš Nette\Databse, tak je nejspíš blbost to řešit takle… Spíš jenom:

public function findAllTodos() {
	return return self::$connection2->table('tasks');
}

A v presenteru, popř. šabloně si můžeš ještě přidat třeba order:

public function renderDefault($order = 'added DESC') {
	$this->template->todos = $model->findAllTodos()->order($order)
}

viz MVC paradox

josef.sabl
Člen | 153
+
0
-

spidy napsal(a):

public function renderDefault($order = 'added DESC') {
	$this->template->todos = $model->findAllTodos()->order($order)
}

Řeším problém s escapováním sql identifikátoru. Kód viz. výše obsahuje SQL injection. Výraz v $order se totiž už neescapuje a vzhledem k tomu, že přichází přímo requestem, může si tam uživatel doplnit vlastní sql.

Zavolání URL http://test.loc/?order=title; DELETE FROM user smaže obsah tabulky user.

Jak správně a systémově v duchu Nette/Database opravit kód?

petr.pavel
Člen | 535
+
0
-
public function renderDefault($order = 'added, $direction = 'DESC') {
  $allowedOrder = in_array($order, array('added', 'modified', ...));
  $allowedDirection = in_array($direction, array('DESC', 'ASC'));
  if ((!$allowedOrder) || (!$allowedDirection)) {
    die('bugger off!');
  }
  $this->template->todos = $model->findAllTodos()->order($order.' '.$direction);
}
josef.sabl
Člen | 153
+
0
-

petr.pavel napsal(a):

public function renderDefault($order = 'added, $direction = 'DESC') {
  $allowedOrder = in_array($order, array('added', 'modified', ...));
  $allowedDirection = in_array($direction, array('DESC', 'ASC'));
  if ((!$allowedOrder) || (!$allowedDirection)) {
    die('bugger off!');
  }
  $this->template->todos = $model->findAllTodos()->order($order.' '.$direction);
}

Heh whitelist, takhle jsem to přesně udělal :-) Díky za ujištění ;-)

saimons
Člen | 293
+
0
-

Muzu mit k tomu doplnujici dotaz? Take zacinam s Nette DB. Kydz teda udelam toto:

Presenter (Control)

public function render() {
    $editLang = $this->getPresenter()->getParam('editLang');
    $template->menu = $this->menuModel->getArticlesMenu()->where('nazevSystem', $editLang);
}

Model komponenty

public function getArticlesMenu() {
    return $this->database->table('lokalizace');

}

tak je to nachylne na SQL inj.? Pokud bych predal $editLang do modelu a tam udelal where tak je to v poradku?

return $this->database->table('lokalizace')->where('nazevSystem', $editLang);

Editoval saimons (22. 12. 2011 15:37)

josef.sabl
Člen | 153
+
0
-

where() sám ošetří druhý parametr, tzn. tady by injekce narozdíl od order() být neměla.

Je úplně jedno, jestli where provedeš v presenteru nebo modelu. Kód v modelu se spouští stejně jako kód v presenteru. Oddělení je jen kvůli správné organizaci kódu.

Editoval josef.sabl (22. 12. 2011 15:52)

saimons
Člen | 293
+
0
-

Ok diky, ted uz to chapu. A ktere jsou a nejsou osetrene, doctu se to nekde v nake dokumentaci?