Vícenásobný where v akci presenteru

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
wicked
Člen | 290
+
0
-

Zdravím přátelé,

na webu využívám vyhledávání (jeden formulář s 5 poličky) který odesílám takto

$values = $form->getValues();
        $q = $values;
        $this->redirect("hledat", array(
            'firma' => $q->firma,
            'jmeno' => $q->jmeno,
            'ic' => $q->ic,
            'prijmeni' => $q->prijmeni,
            'dic'=> $q->dic,
            'mesto' => $q->mesto
            ));

Následně v akci je zpracovávám

public function actionHledat($firma, $jmeno) {
        $firma = $this->zakaznici->findAll()->where("firma LIKE ?", $firma OR "jmeno LIKE ?", $jmeno);
        if (!$firma->count()) {
            $this->flashMessage ("Zákazník dle parametrů nenalezen!", "alert alert-danger");
            $this->redirect("default");
        }
        $firma = $firma->fetch();
        $this->template->q = $firma;
    }

Pokud uvedu v 1. radku akce jenom firmu, vše se zobrazi, ale jakmile to necham takto, dostavám
Argument count does not match placeholder count

Co dělám špatně?

Mám ještě pred oslanim kontrolovat, zda je pole vyplněno?

Děkuji

EDIT:
ted mám toto


        $firma = $this->zakaznici->findAll()->where("firma = ? OR jmeno = ?", $firma, $jmeno);

Ale neprojde kdyz nemam vyplnene jmeno, respektive
Column operator does not accept NULL argument

Editoval wicked (17. 8. 2014 14:14)

radas
Člen | 226
+
0
-

Nejspíš bys měl mít operátor OR v řetezci SQL dotazu.

wicked
Člen | 290
+
0
-

radas napsal(a):

Nejspíš bys měl mít operátor OR v řetezci SQL dotazu.

ANo měl jsi pravdu, to jsem přehledl, ale teď to píše chybu viz. můj edit …

potřebuji, aby jdyž pole neni vyplněno, to proslo, tahle to neakcptuje null

Děkuji

Mysteria
Člen | 797
+
0
-

Co ti to vygeneruje za SQL dotaz (pokud se k tomu vůbec dostane)?

David Matějka
Moderator | 6445
+
+1
-

za foo = ? nemuzes do parametru dosadit NULL, musi to byt foo IS NULL. Ale stejne nepredpokladam, ze bys chtel filtrovat ... OR firma IS NULL, takze bych spis kontroloval, ktere policka jsou nastaveny a dle toho sestavil podminku do where

wicked
Člen | 290
+
0
-

Mysteria napsal(a):

Co ti to vygeneruje za SQL dotaz (pokud se k tomu vůbec dostane)?

Nedostane se k tomu, hodí mě to Tracy s

Column operator does not accept NULL argument.

A dole v baru mám jen nastavení names na utf8

wicked
Člen | 290
+
0
-

matej21 napsal(a):

za foo = ? nemuzes do parametru dosadit NULL, musi to byt foo IS NULL. Ale stejne nepredpokladam, ze bys chtel filtrovat ... OR firma IS NULL, takze bych spis kontroloval, ktere policka jsou nastaveny a dle toho sestavil podminku do where

Takže to bych musel už upravit funkci na odeslaní

public function hledatFormSucceeded($form) {

        // Nacteme data z formulare
        $values = $form->getValues();
        $q = $values;
        $this->redirect("hledat", array(
            'firma' => $q->firma,
            'jmeno' => $q->jmeno,
            'ic' => $q->ic,
            'prijmeni' => $q->prijmeni,
            'dic'=> $q->dic,
            'mesto' => $q->mesto
            ));
    }

O to, že jestli je pole vyplneno, predej jej, ale jak potom v actionHledat zjistim, ktere pole jsou predana?

David Matějka
Moderator | 6445
+
0
-

ne, v tom action kontroluj, co je nastaveno a co ne…

Editoval matej21 (17. 8. 2014 14:38)

wicked
Člen | 290
+
0
-

matej21 napsal(a):

ne, v tom action kontroluj, co je nastaveno a co ne…

Prosím, nemohl by jsi mě uvést příklad?

Kontrolovat jestli neobsahuje NULL ok, ale jak pro to udělat podmínku?

David Matějka
Moderator | 6445
+
0
-

bohuzel NDB na to asi nema zadnej helper, budes to muset udelat rucne, treba:

$cond = array();
if($firma !== NULL) {
	$cond["firma = ?"] = $firma;
}
...
$...->where(implode(" OR ", array_keys($cond)), $cond);
wicked
Člen | 290
+
0
-

matej21 napsal(a):

bohuzel NDB na to asi nema zadnej helper, budes to muset udelat rucne, treba:
`php
$cond = array();
if($firma !== NULL) {
$cond[„firma = ?“] = $firma;
}

$…->where(implode(" OR ", array_keys($cond)), $cond);

Tak jsem to zkusil udělat jak jsi psal, ale …

public function actionHledat($firma, $jmeno) {
        $cond = array();
        if ($firma !== NULL) {
            $cond["firma = ?"] = $firma;
        } elseif($jmeno !== NULL) {
            $cond["jmeno = ?"] = $jmeno;
        }
        $firma = $this->zakaznici->findAll()->where(implode(" OR ", array_keys($cond)), $cond);
        if (!$firma->count()) {
        $this->flashMessage ("Zákazník dle parametrů nenalezen!", "alert alert-danger");
        $this->redirect("default");
    }
    $firma = $firma->fetch();
    $this->template->q = $firma;
}
> ```

A skončím na
**Column operator does not accept array argument**

Což chápu, že práce s poly nejde ...
David Matějka
Moderator | 6445
+
0
-

ach, tam je v sql builderu debilni (vlastne tam byt musi… no i kdyz…) podminka :\
takze budes muset udelat:

//$cond bude stejny
$firma = $this->zakaznici->findAll();
array_unshift($cond, implode(" OR ", array_keys($cond)));
call_user_func_array(array($firma, 'where'), $cond);

a btw, dej si to z presenteru do toho repozitare se zakaznikama.. metodu treba findByFilter a posli tam pole s filtrem…

Editoval matej21 (17. 8. 2014 16:01)

wicked
Člen | 290
+
0
-

matej21 napsal(a):

ach, tam je v sql builderu debilni (vlastne tam byt musi…) podminka :\
takze budes muset udelat:

//$cond bude stejny
$firma = $this->zakaznici->findAll()
array_unshift(implode(" OR ", array_keys($cond)), $cond);
call_user_func_array(array($firma, 'where'), $cond);

a btw, dej si to z presenteru do toho repozitare se zakaznikama.. metodu treba findByFilter a posli tam pole s filtrem…

Tak tohle jde mimo mě … vůbec to nechápu …

btw, ani to nejde, respektivě:
syntax error, unexpected ‚array_unshift‘ (T_STRING)

A kdyz udělam misto středníků → tak
Call to undefined method Nette\Database\Table\Selection::array_unshift().

David Matějka
Moderator | 6445
+
0
-

chybel mi tam za findAll() strednik… doplnil jsem to

Editoval matej21 (17. 8. 2014 15:34)

wicked
Člen | 290
+
0
-

matej21 napsal(a):

chybel mi tam za findAll() strednik… doplnil jsem to

Promin, ze jsem otravny, ale jelikoz tomu nerozumim, tak si nemuzu pomoc…

Doplnil jsem stredniky ale …

Only variables should be passed by reference

Ted to je takto

public function actionHledat($firma, $jmeno) {
        $cond = array();
        if ($firma !== NULL) {
            $cond["firma = ?"] = $firma;
        } elseif ($jmeno !== NULL) {
            $cond["jmeno = ?"] = $jmeno;
        }
        $firma = $this->zakaznici->findAll();
        array_unshift(implode(" OR ", array_keys($cond)), $cond);
        call_user_func_array(array($firma, 'where'), $cond);
        if (!$firma->count()) {
            $this->flashMessage("Zákazník dle parametrů nenalezen!", "alert alert-danger");
            $this->redirect("default");
        }
        $firma = $firma->fetch();
        $this->template->q = $firma;
    }

Nezlob se a dekuji

David Matějka
Moderator | 6445
+
0
-

sorry, prohodil jsem poradi argumentu do array_unshift :) uz jsem to tam opravil…

wicked
Člen | 290
+
0
-

matej21 napsal(a):

sorry, prohodil jsem poradi argumentu do array_unshift :) uz jsem to tam opravil…

Už to funguje! :-)

Prosím, ale tohle je pro mě vyšší dívčí … Mohl by jsi mě to krapet vysvětlit? Tak nějak, pro lamu?

Děkuji

David Matějka
Moderator | 6445
+
0
-

1. vytvoris to pole $cond, to bude pote obsahovat treba:

$cond = array(
	"firma = ?" => 'foo',
	"jmeno = ?" => 'bar',
);

2. pomoci implode se spojeji klice do podminky firma = ? OR jmeno = ?
3. tohle se pomoci array_unshift da jako prvni prvek pole
4. a call_user_func_array zavola onu fci where na onom objektu s parametry v $cond, takze je to jako volani

->where('firma = ? OR jmeno = ?', 'foo', 'bar');

Editoval matej21 (17. 8. 2014 16:18)

wicked
Člen | 290
+
0
-

matej21 napsal(a):

1. vytvoris to pole $cond, to bude pote obsahovat treba:

$cond = array(
	"firma = ?" => 'foo',
	"jmeno = ?" => 'bar',
);

2. pomoci implode se spojeji klice do podminky firma = ? OR jmeno = ?
3. tohle se pomoci array_unshift da jako prvni prvek pole
4. a call_user_func_array zavola onu fci where na onom objektu s parametry v $cond, takze je to jako volani

->where('firma = ? OR jmeno = ?', 'foo', 'bar');

Asi … to začínám chápat :-)

Zjistim jeste vic infa ohledne tech funkcí.

Každopádně děkuji ;)

wicked
Člen | 290
+
0
-

Mám další dotaz …

DOufám, že mi s ním pomůžete.

Jak jsem tu pomohl @matej21 s tím výtahem z DB …

Musel jsem DB předělat tak, že mám

zakaznici
id
firma
id
dic

a

zakaznici_kontakt
id
zakaznici_id
atd …

Samozdrejmne spojene FK zakaznici_id → zakaznici.id

Jen mě není jasné, jak bych měl ten script upravit aby to fungovalo stále jako vyhledávání.

Ve výpisu mě je to jasné, spojil jsem to ve foreach related…

ale jak to udělat tu?

Děkuji

EDIT:

Ještě druhá věc, když chci data z DB spojit abych pak mohl naplnit formulář pro editaci, dle dokumentace jsem to udělal takto

        $zakaznici = $this->zakaznici->findAll()->where("id", $id);
        $zakaznici->where('zakaznici_kontakty.zakaznici_id ?', $id);

Ale to mě píše že nebylo nic nalezeno
No reference found for $zakaznici->zakaznici_kontakty

Měl jsem za to, že to je jako join

EDIT2
postup
mam toto

$zakaznici = $this->zakaznici->findAll()->where("id", $id);
        $zakaznici->where(':zakaznici_kontakty.zakaznici_id ?', $id)
                ->group('zakaznici.id')
                ->having('COUNT(:zakaznici.id)', $id);

Vytvori to dotaz

SELECT `zakaznici`.*
FROM `zakaznici`
LEFT JOIN `zakaznici_kontakty` AS `zakaznici` ON `zakaznici`.`id` = `zakaznici`.`zakaznici_id`
WHERE (`id` = '172') AND (`zakaznici_kontakty`.`zakaznici_id` = '172')
GROUP BY `zakaznici`.`id`
HAVING COUNT(`zakaznici`.`id`) 172

Ale dostanu chybuSQLSTATE[42000]: Syntax error or access violation: 1066 Not unique table/alias: ‚zakaznici‘

Editoval wicked (24. 8. 2014 16:35)

ludek
Člen | 83
+
0
-

Tohle je skutečně funkční způsob jak operativně upravovat WHERE dotaz s podmínkami OR a třeba i LIKE. Když chci podmínky AND, můžu si dotaz libovolně skládat. Prostě připisuju WHERE a ono se mi to spojí.

$hledat = 'slovo';

$hledej = $this->database->table('spisy');
$hledej->where('nazev LIKE ?', '%'.$hledat.'%';
$hledej->where('popis LIKE ?', '%'.$hledat.'%';
$hledej->where('misto LIKE ?', '%'.$hledat.'%';

$this->template->vysledek = $hledej;

mi udělá:

SELECT * FROM `spisy` WHERE `nazev` LIKE '%slovo%' AND `popis` LIKE '%slovo%' AND `misto` LIKE '%slovo%';

Jenomže když chci hledat s OR jsem v háji. Musel bych napsat snad jedině:

$hledej = $this->database->table('spisy')
->where('nazev LIKE ? OR popis LIKE ? OR misto LIKE', '%'.$hledat.'%', '%'.$hledat.'%', '%'.$hledat.'%');

A co když těch polí k prohledání je mnohem víc? A co když předem nevím, která chci prohledat?

Jde to teda udělat takto:

$hledat = 'slovo';

// Pole s podmínkami si sestavím podle potřeby, třeba ho v nějakém cyklu podle situace vygeneruju.
// na ukázku:
$podminky = array(
   "nazev LIKE ?" => '%'.$hledat.'%',
   "popis LIKE ?" => '%'.$hledat.'%',
   "misto LIKE ?" => '%'.$hledat.'%',
   "neco_dalsiho LIKE ?" => '%nejake_jine_slovo%'
   );

Teď je třeba pole upravit tak, aby bylo vhodné pro PHP příkaz call_user_func_array().
Ten zavolá na připraveném objektu $hledej funkci where a předá jí za sebou parametry v poli $podminky.
Jako první musí být popis podmínky se zástupnými otazníčky, za tím dosazované parametry.
První popisný parametr sestavím pomocí implode, kdy pospojuju názvy klíčů (array_keys).
A dopředu ho přešoupnu příkazem array_unshift.

// sestavím první parametr:
$prvni = implode(" OR ", array_keys($podminky));

// zapíšu jako první prvek toho stejného pole
array_unshift($podminky, $prvni);

// jestli nevěříte, dumpněte si to
// dump($podminky);

// a předhodím hotové pole ke zpracování funkcí `where`:
$hledej = $this->database->table('spisy');
call_user_func_array(array($hledej, 'where'), $podminky);

$this->template->vysledek = $hledej;

A ono to fakt udělá:

SELECT *
FROM `spisy` WHERE (`nazev` LIKE '%slovo%' OR `popis` LIKE '%slovo%' OR `misto` LIKE '%slovo%' OR `neco_dalsiho` LIKE '%nejake_jine_slovo%');

Davidu Matějkovi velký dík.