Invalid argument supplied for foreach() při procházení tabulkou přes related

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
TomasHalasz
Bronze Partner | 79
+
0
-

Ahoj, mám následující problém.

Mám tři tabulky:
cl_types
cl_types_parameters
cl_parameters

cl_types_parameters je s cl_parameters svázáné přes foreign keys

ALTER TABLE `cl_types_parameters`
  ADD CONSTRAINT `cl_types_parameters_ibfk_2` FOREIGN KEY (`cl_parameters_id`) REFERENCES `cl_parameters` (`id`);

Následující kód mi funguje:

{foreach $ClTypes_radek->related('cl_types_parameters')->order('par_num') as $radek2}
	{$radek2->cl_parameters->label_parameter}
{/foreach}

Bez chyby pro každý řádek z cl_types vypisuje pro odpovídající cl_types_parameters hodnotu label_parametercl_parameters .

No a pak mám tyto tabulky:
offers_items
offers_items_parameters

a stejnou cl_parameters jako výše
Mám tam vazbu:

ALTER TABLE `offers_items_parameters`
  ADD CONSTRAINT `offers_items_parameters_ibfk_2` FOREIGN KEY (`cl_parameters_id`) REFERENCES `cl_parameters` (`id`);

A chci vypisovat pro každý řádek offers_items jeho záznamy offers_items_parameters a k nim z cl_parameters->label_parameter. Dělám to takto:

{foreach $OffersItems as $Items_radek}
	{foreach $Items_radek->related('offers_items_parameters') as $Parameters_radek2}
		<td>{$Parameters_radek2->cl_parameters->label_parameter}</td>
	{/foreach}
{/foreach}

a dostávám chybu: Invalid argument supplied for foreach()
na řádku: <td>{$Parameters_radek2->cl_parameters->label_parameter}</td>

Když to zkusím takto tak to jde:

{foreach $Items_radek->related('offers_items_parameters') as $Parameters_radek2}
	{foreach $Parameters_radek2->related('cl_parameters','id') as $Parameters_radek3}
		<td>{$Parameters_radek3->label_parameter}</td>
		<td>{$Parameters_radek2->value_parameter}</td>
		<td>{$Parameters_radek3->units}</td>
	{/foreach}
{/foreach}

Takže mi z nějakého důvodu nefunguje automatická vazba z tabulky offers_items_parameters přes foreign key na tabulku cl_parameters, která z tabulky cl_types_parameters funguje.
Sedím nad tím už pár hodin a pořád netuším kde je chyba :-(

Zkoušeno na Nette Framework 2.0.3 (revision eb558ae released on 2012–04–04)
i na Nette Framework 2.1-dev se stejným výsledkem.

díky za každou radu

Editoval TomasHalasz (23. 6. 2012 12:33)

jtousek
Člen | 951
+
0
-

$Parameters_radek2->related('cl_parameters','id') vrací množinu řádků, který z nich by asi mělo vracet $Parameters_radek2->cl_parameters? Myslím, že sis popletl vazbu 1:1 s vazbou 1:N.

TomasHalasz
Bronze Partner | 79
+
0
-

Ne, $Parameters_radek2->related(‚cl_parameters‘,‚id‘) vrací jen jeden řádek. V tabulce cl_parameters je id primární klíč a tedy pro každé id je tam jen jeden záznam.

Chápu, že $Parameters_radek2->related(‚cl_parameters‘,‚id‘) může vrátit více záznamů, ale tím jsem jen obešel to že mi nefungovalo toto: $Parameters_radek2->cl_parameters->label_parameter}

jtousek
Člen | 951
+
0
-

Jestli to dobře chápu tak se chceš prostě zbavit toho foreach?

$Parameters_radek2->related('cl_parameters','id')->fetch()->label_parameter

Editoval jtousek (23. 6. 2012 12:45)

vvoody
Člen | 910
+
0
-

TomasHalasz napsal(a):

Ne, $Parameters_radek2->related(‚cl_parameters‘,‚id‘) vrací jen jeden řádek. V tabulce cl_parameters je id primární klíč a tedy pro každé id je tam jen jeden záznam.

Na to je funkcia ref, nie related, nepliest si ich:

ref

  • vracia activerow (riadok)
  • druhy parameter je FK ktory je sucastou riadku nad ktorym ref volas

related

  • vracia selection (mnozinu riadkov)
  • druhy parameter je FK ktory je vo vezobnej tabulke

Co mozeme robit so selection?

  • Preiterovat vo foreach a ziskat jednotlive activerow.
  • atd

Co mozeme robit s activerow?

  • volat nad nim related a tym ziskat selection z inej tabulky (referencing rows)
  • volat nad nim ref a tym ziskat activerow z inej tabulky (referenced row)
  • referenced row mozno ziskat pri dodrzani urcitych konvencii aj priamo z property activerow jej nazov je nazov tabulky kde sa odkazovany riadok nachadza (samozrejme ze to property nieje ale vdaka magickym metodam to tak vyzera) ak nemas rad magiu tak proste pouzivaj funkciu ref
  • atd

Edit: teraz som si vsimol ze ako druhy parameter related davas PK, logicky je ale pre tu funkciu taka informacia uplne k nicomu, ona potrebuje vediet co je FK. PK jednotlivych tabuliek si vie zistit sama kedze PK je jeden, FK moze byt viacero.

Editoval vvoody (23. 6. 2012 13:02)

jtousek
Člen | 951
+
0
-

Pochopil jsem to tak, že metodu ref právě použít z nějakého důvodu nemůže, protože jinak by muselo fungovat už to $Parameters_radek2->cl_parameters.

vvoody
Člen | 910
+
0
-

Metodu ref(‚tabulka2‘,‚FK_v_tabulke1‘) mozes pouzit kedykolvek. Zapis $riadokVTabulke1->tabulka2 je len alternativa k ref ktora funguje len ak su dodrzane iste konvencie pomenovania FK.

Editoval vvoody (23. 6. 2012 13:07)

jtousek
Člen | 951
+
0
-

Anebo když si NDB ten FK zjistí pomocí DiscoveredReflection, ne?

vvoody
Člen | 910
+
0
-

Ja ze si pisem s TomasHalasz :D hej presne tak. Netusim ktoru funkciu musi pouzit lebo z prveho prispevku som nevylustil typ vazby medzi inkriminovanymi tabulkami. Ale aj keby som to zistil radsej sa pokusam vysvetlit ktoru funkciu kedy zvolit aby chybu neopakoval. Tiez som mal s tym spociatku strasny problem :).

TomasHalasz
Bronze Partner | 79
+
0
-

Už jsem tu :-) Díky za rady, hned se na to podívám.
Jen to shrnu:
Jde mi o to, že $riadokVTabulke1->tabulka2 mi v prvním případě funguje a v druhém $riadokVTabulke3->tabulka2 nefunguje. Pojmenování id primary key a foreign key podle NOTORM dodrženo mám. Když to zjednoduším tak toto funguje:
radek_z_tabulky_cl_types_parameters->cl_parameters->label_parameter

Toto nefunguje:
radek_z_tabulky_offers_items_parameters->cl_parameters->label_parameter

Tady jsou dumpy:

--
-- Databáze: `kalkulator`
--

-- --------------------------------------------------------

--
-- Struktura tabulky `cl_parameters`
--

CREATE TABLE IF NOT EXISTS `cl_parameters` (
  `company_id` int(11) NOT NULL COMMENT 'číslo firmy',
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'číslo parametru',
  `label_parameter` text NOT NULL COMMENT 'označení parametru',
  `units` varchar(10) NOT NULL COMMENT 'jednotky',
  `par_num` int(11) NOT NULL COMMENT 'pořadí parametru v cl_types_parameters',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COMMENT='číselník parametrů' AUTO_INCREMENT=6 ;

--
-- Vypisuji data pro tabulku `cl_parameters`
--

INSERT INTO `cl_parameters` (`company_id`, `id`, `label_parameter`, `units`, `par_num`) VALUES
(1, 1, 'šířka', 'mm', 1),
(1, 2, 'výška', 'mm', 2),
(1, 3, 'barva', '', 3),
(1, 4, 'barva 2', '', 4),
(1, 5, 'barva 3', '', 5);


-- --------------------------------------------------------

--
-- Struktura tabulky `cl_types`
--

CREATE TABLE IF NOT EXISTS `cl_types` (
  `id_firm` int(11) NOT NULL COMMENT 'číslo firmy',
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'číslo typu',
  `label_type` text NOT NULL COMMENT 'označení typu',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COMMENT='číselník typů' AUTO_INCREMENT=16 ;

--
-- Vypisuji data pro tabulku `cl_types`
--

INSERT INTO `cl_types` (`id_firm`, `id`, `label_type`) VALUES
(1, 1, 'Typ 1'),
(1, 2, 'Typ 2'),
(1, 3, 'Typ 3'),
(1, 4, 'Typ 4'),
(1, 5, 'Typ 5'),
(1, 6, 'Typ 6'),
(1, 7, 'Typ 7'),
(1, 8, 'Typ 8'),
(1, 9, 'Typ 9'),
(1, 10, 'Typ 10'),
(1, 11, 'Typ 11'),
(1, 12, 'Typ 12'),
(1, 13, 'Typ 13'),
(1, 14, 'Typ 14'),
(1, 15, 'Typ 15');

-- --------------------------------------------------------

--
-- Struktura tabulky `cl_types_parameters`
--

CREATE TABLE IF NOT EXISTS `cl_types_parameters` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `id_firm` int(11) NOT NULL COMMENT 'číslo firmy',
  `cl_types_id` int(11) NOT NULL COMMENT 'číslo typu',
  `cl_parameters_id` int(11) NOT NULL COMMENT 'číslo parametru',
  `min_value` float NOT NULL COMMENT 'minimální hodnota',
  `max_value` float NOT NULL COMMENT 'maximální hodnota',
  `par_num` int(11) NOT NULL COMMENT 'pořadí v par_num v ceníku',
  PRIMARY KEY (`id`),
  KEY `cl_parameters_id` (`cl_parameters_id`),
  KEY `cl_types_id` (`cl_types_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COMMENT='přiřazení parametrů k typům' AUTO_INCREMENT=30 ;

--
-- Vypisuji data pro tabulku `cl_types_parameters`
--

INSERT INTO `cl_types_parameters` (`id`, `id_firm`, `cl_types_id`, `cl_parameters_id`, `min_value`, `max_value`, `par_num`) VALUES
(3, 1, 1, 2, 105, 205, 1),
(4, 1, 1, 1, 305, 405, 2),
(7, 1, 2, 2, 555, 555, 1),
(13, 1, 1, 3, 0, 0, 3),
(25, 1, 2, 1, 300, 300, 2),
(27, 1, 3, 2, 500, 1500, 1),
(28, 1, 3, 1, 500, 1500, 2),
(29, 1, 1, 4, 0, 0, 4);

-- --------------------------------------------------------

--
-- Struktura tabulky `offers_items`
--

CREATE TABLE IF NOT EXISTS `offers_items` (
  `id_firm` int(11) NOT NULL COMMENT 'číslo firmy',
  `offers_main_id` int(11) NOT NULL COMMENT 'číslo nabídky',
  `offers_groups_id` int(11) NOT NULL COMMENT 'číslo skupiny',
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'číslo položky',
  `label_item` text NOT NULL COMMENT 'popis položky',
  `cl_types_id` int(11) NOT NULL COMMENT 'číslo typu výrobku',
  `id_material` int(11) NOT NULL COMMENT 'číslo materiálu z ceníku',
  `item_count` int(11) NOT NULL DEFAULT '1' COMMENT 'počet jednotek',
  `price_cost` decimal(14,2) NOT NULL COMMENT 'nákladová cena',
  `price_sale` decimal(14,2) NOT NULL COMMENT 'prodejní cena',
  PRIMARY KEY (`id`),
  KEY `cl_types_id` (`cl_types_id`),
  KEY `offers_main_id` (`offers_main_id`),
  KEY `id_firm` (`id_firm`),
  KEY `offers_group_id` (`offers_groups_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=8 ;

--
-- Vypisuji data pro tabulku `offers_items`
--

INSERT INTO `offers_items` (`id_firm`, `offers_main_id`, `offers_groups_id`, `id`, `label_item`, `cl_types_id`, `id_material`, `item_count`, `price_cost`, `price_sale`) VALUES
(1, 1, 1, 1, 'první položka ', 10, 0, 12, '0.00', '0.00'),
(1, 1, 1, 2, 'druhá položka', 1, 0, 2, '0.00', '0.00'),
(1, 1, 26, 3, 'třetí položka', 1, 0, 1, '0.00', '0.00'),
(1, 1, 26, 4, 'čtvrtá položka', 1, 0, 1, '0.00', '0.00'),
(1, 1, 1, 6, 'další třetí', 1, 0, 1, '0.00', '0.00'),
(1, 1, 1, 7, 'aaa', 1, 0, 1, '0.00', '0.00');

-- --------------------------------------------------------

--
-- Struktura tabulky `offers_items_parameters`
--

CREATE TABLE IF NOT EXISTS `offers_items_parameters` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `id_firm` int(11) NOT NULL COMMENT 'číslo firmy',
  `offers_items_id` int(11) NOT NULL COMMENT 'číslo položky',
  `cl_parameters_id` int(11) NOT NULL COMMENT 'číslo parametru',
  `value_parameter` varchar(20) NOT NULL COMMENT 'hodnota parametru',
  PRIMARY KEY (`id`),
  KEY `id_firm` (`id_firm`),
  KEY `offers_item_id` (`offers_items_id`),
  KEY `cl_parameters_id` (`cl_parameters_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COMMENT='parametry položky' AUTO_INCREMENT=3 ;

--
-- Vypisuji data pro tabulku `offers_items_parameters`
--

INSERT INTO `offers_items_parameters` (`id`, `id_firm`, `offers_items_id`, `cl_parameters_id`, `value_parameter`) VALUES
(1, 1, 2, 2, ''),
(2, 1, 2, 2, '');

--
-- Omezení pro exportované tabulky
--

--
-- Omezení pro tabulku `cl_types_parameters`
--
ALTER TABLE `cl_types_parameters`
  ADD CONSTRAINT `cl_types_parameters_ibfk_2` FOREIGN KEY (`cl_parameters_id`) REFERENCES `cl_parameters` (`id`),
  ADD CONSTRAINT `cl_types_parameters_ibfk_3` FOREIGN KEY (`cl_types_id`) REFERENCES `cl_types` (`id`) ON DELETE CASCADE;

--
-- Omezení pro tabulku `offers_items`
--
ALTER TABLE `offers_items`
  ADD CONSTRAINT `offers_items_ibfk_3` FOREIGN KEY (`cl_types_id`) REFERENCES `cl_types` (`id`),
  ADD CONSTRAINT `offers_items_ibfk_4` FOREIGN KEY (`offers_main_id`) REFERENCES `offers_main` (`id`) ON DELETE CASCADE,
  ADD CONSTRAINT `offers_items_ibfk_5` FOREIGN KEY (`offers_groups_id`) REFERENCES `offers_groups` (`id`) ON DELETE CASCADE;

--
-- Omezení pro tabulku `offers_items_parameters`
--
ALTER TABLE `offers_items_parameters`
  ADD CONSTRAINT `offers_items_parameters_ibfk_2` FOREIGN KEY (`cl_parameters_id`) REFERENCES `cl_parameters` (`id`),
  ADD CONSTRAINT `offers_items_parameters_ibfk_1` FOREIGN KEY (`offers_items_id`) REFERENCES `offers_items` (`id`) ON DELETE CASCADE;
vvoody
Člen | 910
+
0
-

s5 k tej chybe Invalid argument supplied for foreach(), pisal si ze vyskakuje na riadku <td>{$Parameters_radek2->cl_parameters->label_parameter}</td> kde ziadny foreach neni, pozri na to znovu kde to vyhadzuje

TomasHalasz
Bronze Partner | 79
+
0
-

No a to je právě divné. Opravdu mám v template default.latte jen tenhle kód:

{foreach $Items_radek->related('offers_items_parameters') as $Parameters_radek2}
<tr>
<td>{$Parameters_radek2->cl_parameters->label_parameter}</td>
{/foreach}

A Ladenka říká chybu Invalid argument supplied for foreach() na řádku 65:

63: <?php $iterations = 0; foreach ($Items_radek->related('offers_items_parameters') as $Parameters_radek2): ?>
64: <tr>
65: <td><?php echo Nette\Templating\Helpers::escapeHtml($Parameters_radek2->cl_parameters->label_parameter, ENT_NOQUOTES) ?></td>
66: <?php $iterations++; endforeach ?>

Stejný způsob používám v té vazbě radek_z_tabulky_cl_types_parameters->cl_parameters->label_parameter viz. výše a tam to funguje.

TomasHalasz
Bronze Partner | 79
+
0
-

Připadá mi to nějaké divné. Když zkusím zrušit foreign key z tabulky offers_items_parameters na cl_parameters tak dostanu logicky na řádku 65 tuto chybu:
No reference found for $offers_items_parameters->cl_parameters
Jakmile foreign key vytvořím takto:

ALTER TABLE `offers_items_parameters` ADD FOREIGN KEY ( `cl_parameters_id` ) REFERENCES `kalkulator`.`cl_parameters` (
`id`
);

tak dostanu na řádku 65 chybu:
Invalid argument supplied for foreach()

TomasHalasz
Bronze Partner | 79
+
0
-

vvoody napsal(a):

Metodu ref(‚tabulka2‘,‚FK_v_tabulke1‘) mozes pouzit kedykolvek. Zapis $riadokVTabulke1->tabulka2 je len alternativa k ref ktora funguje len ak su dodrzane iste konvencie pomenovania FK.

Zkusil jsem použít ref() a hlásí to stejnou chybu jako když mám přímý zápis. Udělal jsem to takto:

{$Parameters_radek2->ref('cl_parameters','cl_parameters_id')->label_parameter}
vvoody
Člen | 910
+
0
-

Nie o to nejde, ten DiscoveredReflection funguje samozrejme v pohode. Mohol by si niekde uploadnut ladenku s tou chybou Invalid argument supplied for foreach() ?

TomasHalasz
Bronze Partner | 79
+
0
-

Tady je ladenka

hrach
Člen | 1834
+
0
-

Zitra se na to podivam, bohuzel nemam cas resit to tak instantne. Diky za informace.

TomasHalasz
Bronze Partner | 79
+
0
-

Ok, to stačí. Díky.

vvoody
Člen | 910
+
0
-

Jediny foreach ku ktoremu to smeruje je tu, skus pred nim dupnut $this->rows, mozno nam to napovie.

TomasHalasz
Bronze Partner | 79
+
0
-

Asi jsem lama, ale kde mám udělat ten dump když to je v šabloně? $this->rows mi v šabloně nejde.

TomasHalasz
Bronze Partner | 79
+
0
-

Tak už jsem přišel na to jak chybu 100% vyvolat a jak ji obejít :-)

K chybě Invalid argument supplied for foreach() dojde tehdy pokud použiju v template vnořený foreach s related takto:

{foreach $OffersItems as $Items_radek}
..
..
	{foreach $Items_radek->related('offers_items_parameters') as $Parameters_radek2}
		{* zde už nemůžu použít následující konstrukci
		na jejím řádku dojde k chybě Invalid argument supplied for foreach() *}
		{$Parameters_radek2->cl_parameters->id}
	{/foreach}
{/foreach}

Jakmile si nachystám data v presenteru:

$this->template->ParamItems_radek=$this->context->createOffersItemsParameters()->where("id_firm", $this->section->id_firm);

Tak v šabloně toto funguje

{foreach $OffersItems as $Items_radek}
	..
	..
	{foreach $ParamItems_radek->where('offers_items_id', $Items_radek->id) as $Parameters_radek2}
		{$Parameters_radek2->cl_parameters->id};</td>
	{/foreach}
{/foreach}

Je to chyba moje nebo nette?

jtousek
Člen | 951
+
0
-

Je to určitě chyba Nette. I v případě, že toto použití je chybné, by Nette mělo přinejmenším vyhodit srozumitelnější chybovou hlášku.

hrach
Člen | 1834
+
0
-

Tento kod jsem plne vyzkousel na tvym sql dumpu. Jedna se o klasickou vazbu M:N a mne plne funguje i se zapnutou cache.

{foreach $OffersItems as $Items_radek}
..
..
        {foreach $Items_radek->related('offers_items_parameters') as $Parameters_radek2}
                {* zde už nemůžu použít následující konstrukci
                na jejím řádku dojde k chybě Invalid argument supplied for foreach() *}
                {$Parameters_radek2->cl_parameters->id}
        {/foreach}
{/foreach}

Asi si chybu spatne analyzoval. Jakou mas verzi PHP?

TomasHalasz
Bronze Partner | 79
+
0
-

Mám PHP Version 5.3.6-pl1-gentoo

Jinak mi to dělá pořád… tak nevím… co je špatně.

hrach
Člen | 1834
+
0
-

Musis dany problem pokud mozno co nejvic izolovat. Zkus si vytvorit skript mimo tvuj projekt pracujici s danou db, v nem vytvorit spojeni (s cachi) a zkusit provest dane foreache…

require './../../../Nette/loader.php';

$storage = new Nette\Caching\Storages\FileStorage(__DIR__.'/../../tmp');
$connection = new Nette\Database\Connection('mysql:host=localhost;dbname=nette_test', 'root');
$connection->setCacheStorage($storage);
$connection->setDatabaseReflection(new Nette\Database\Reflection\DiscoveredReflection($storage));

Nette\Diagnostics\Debugger::enable(Nette\Diagnostics\Debugger::DEVELOPMENT);
$dbPanel = new Nette\Database\Diagnostics\ConnectionPanel;
$connection->onQuery[] = callback($dbPanel, 'logQuery');
Nette\Diagnostics\Debugger::$bar->addPanel($dbPanel);

...
David Matějka
Moderator | 6445
+
0
-

nejakej pokrok? taky jsem se setkal s timhle problemem.. vse je v poradku, pokud prvni zaznam ma tu podpolozku..

<?php
{foreach $OffersItems as $Items_radek}
..
..
        {foreach $Items_radek->related('offers_items_parameters') as $Parameters_radek2}
                {* kdyz prvni $Items_radek nema nic v offers_items_parameters a dalsi uz jo, tak to skonci na tej chybe....*}

        {/foreach}
{/foreach}
?>

jelikoz nekde tady https://api.nette.org/…ion.php.html#62 (a mozna i jinde) dojde k vymazani $this->rows…

TomasHalasz
Bronze Partner | 79
+
0
-

Mě to dělá pořád stejně. Bohužel jsem na to neměl více času a musel jsem pokročit dál tak jsem to obešel jinudy. Jakmile budu mít chvilku tak to zkusím vyvolat v samostatném scriptu ať je to čítelnější…

David Matějka
Moderator | 6445
+
0
-

tak tahle chyba je opravena ve vetvy od hracha ( https://github.com/…-refactoring ), pokud jste na standardni verzi nette\database, tak by to mel resit tenhle commit https://github.com/…ae3086c8cc6c

patriksima
Člen | 58
+
0
-

A není problém v pojmenování těch tabulek a klíčů http://www.notorm.com/#…

tomask
Člen | 9
+
0
-

Potvrzuji, že jsem v Nette 2.0.4 zaznamenal zde popisovaný problém. Jednalo se o totožný příklad, jako je v dokumentaci, tedy:

{foreach $database->table('book')->order('title')->limit(5) as $book}
    <h2>{$book->title} ({$book->author->name})</h2>

    {foreach $book->related('book_tag') as $book_tag}
        {$book_tag->tag->name}{sep}, {/sep}
    {/foreach}
{/foreach}

Uvedený příklad funguje v pořádku, když pro první knihu/iteraci jsou v databázi related book_tagy. V případě, že první kniha má nula related book_tagů, tak se opravdu objeví notice s foreachem.

Odkazovaný patch https://github.com/…ae3086c8cc6c problém řeší.