Vstupní data pro Nette/Database multi insert

m.brecher
Generous Backer | 717
+
0
-

Dobrý den, měl jsem tyto problémy s multi insertem do Nette\Database

$accounts = [
	1 => ['bank_id' => 44, 'number' => '444-888'],		// index nezačínají nulou - toto NEFUNGUJE
	2 => ['bank_id' => 32, 'number' => '556643'],
];

$user = $this->database->table('user')->get($userId);
$user->related('bank_account')->insert($accounts);

Obdržel jsem chybovou hlášku: Nette\Database\SqlPreprocessor::delimite(): Argument #1 ($name) must be of type string, int given

Po delším zkoušení jsem objevil, že problém není v tom, že by nějaký parametr měl být string a byl místo toho integer, ale v číselných indexech vstupního pole. Když jsem číselné indexy pole opravil tak, aby začínaly od 0 tak multi insert fungoval:

$accounts = [
	0 => ['bank_id' => 44, 'number' => '444-888'],		// indexy začínají nulou - toto funguje
	1 => ['bank_id' => 32, 'number' => '556643'],
];

$user = $this->database->table('user')->get($userId);
$user->related('bank_account')->insert($accounts);

Navrhuji provést tyto úpravy v metodě insert() pro multiinsert:

  1. Povolit vstupní pole začínající i jiným indexem než 0, pokud by to nešlo, vyhodit výjimku s odpovídající hláškou, že jsou špatně indexy pole, protože jinak se ta chyba špatně zjišťuje
  2. Jak už jsem psal v jiném příspěvku, metoda insert() by měla akceptovat na vstupu i „dvourozměrný“ ArrayHash (objekt ArrayHash obsahující další ArrayHashe), protože v tomto formátu vrací formuláře Nette $values.
  3. Další variantou dat na vstupu je pole objektů ArrayHash, i tuto variantu formátu dat pro mulitiinsert by měla metoda insert() akceptovat.
Polki
Člen | 553
+
0
-

a. Tak udělej PR
b. Proč? To, že má Nette pro práci s poli příjemnější třídu ArrayHash, a ne obyč pole, nebo object nemá co dělat s tím, co by měla přijmat metoda insert z NDBT. V rámci Nette formulářů můžeš klidně napsat $form->getValues(true), nebo $form->getValues(Nette\Forms\Container::ARRAY) a to ti vrátí rekurzivně zanořené pole, které už do insert metody narveš v klidu. Nebosi dokonce můžeš nechat vrátit objekt $form->getValues(User::class) a ty taky nenarveš do metody insert, protože prostě nejsou iterable… A stejně jako si tam můžeš narvat jakoukoliv třídu do návratové hodnoty formulářů, tak i ArrayHash je třída, takže jak by se to mělo chovat? Některé třídy by to mělo akceptovat a jiné ne? Nevidím důvod akceptovat ArrayHash. Když budeš používat mapování na sou třídu, tak stejně budeš dělat přetransformování na pole jako u ArrayHashe. Pokud ti to moc vadí prostě si nech z Values vrátit pole rovnou.
c. Stejný případ jako v b.

m.brecher
Generous Backer | 717
+
+1
-

Polki napsal(a):

a. Tak udělej PR
b. Proč? To, že má Nette pro práci s poli příjemnější třídu ArrayHash, a ne obyč pole, nebo object nemá co dělat s tím, co by měla přijmat metoda insert z NDBT. V rámci Nette formulářů můžeš klidně napsat $form->getValues(true), nebo $form->getValues(Nette\Forms\Container::ARRAY) a to ti vrátí rekurzivně zanořené pole, které už do insert metody narveš v klidu. Nebosi dokonce můžeš nechat vrátit objekt $form->getValues(User::class) a ty taky nenarveš do metody insert, protože prostě nejsou iterable… A stejně jako si tam můžeš narvat jakoukoliv třídu do návratové hodnoty formulářů, tak i ArrayHash je třída, takže jak by se to mělo chovat? Některé třídy by to mělo akceptovat a jiné ne? Nevidím důvod akceptovat ArrayHash. Když budeš používat mapování na sou třídu, tak stejně budeš dělat přetransformování na pole jako u ArrayHashe. Pokud ti to moc vadí prostě si nech z Values vrátit pole rovnou.
c. Stejný případ jako v b.

@Polki Když metoda insert() akceptuje jednorozměrný ArrayHash, nevidím jako logické, aby neuměla akceptovat dvourozměrný ArrayHash a právě fakt, že to nejde trochu kazí tu pohodlnost třídy ArrayHash. Dvourozměrný ArrayHash je iterable úplně stejně jako array. O možnostech $form->getValues(Nette\Forms\Container::ARRAY) nebo $form->getValues(true) jsem nevěděl, ArrayHash jsem si převedl na pole svojí funkcí, ale než jsem přišel na to čím to je, tak jsem tam strávil daleko víc času, než mě ušetřil jeden formulář Nette oproti PHP – chybová hláška vůbec nepomůže pochopit, kde je problém.

Polki
Člen | 553
+
0
-

@mbrecher Tam jde o to, že Selection třída přijmá na vstup iterable. Tedy jakýkoliv objekt implementující Traversable nebo čistý array. Zatímco vnitřně nad těmi hodnotami z toho prvního ArrayHashe se volají další metody jiných tříd, které pro jednoduchost vyžadují čistý array.
Dokážeš si představit to množství práce, kdyby si tam mohl uživatel narvat cokoliv a muselo by se ověřovat jestli to je to či ono aby to proběhlo jak kdo chce a nepadalo to? :D Už tak se vsadím, že nad tím bylo stráveno víc času, než kdokoliv z nás vůbec programuje. :D
Ato není všechno. Dotazy do databáze jsou už tak pomalá záležitost. Vsadím se, že nikdo nechce ještě čekat na ověřování, co se do těch metod posílá a potom to převádět na nějakou hodnotu se kterou se dá pracovat. Nikdo nechce aby příprava na db dotaz trvala několikrát déle, než samotný dotaz. Proto je lepší vyžádat si jednoduše Array a nemuset ověřovat a transformovat pokaždé.

Jestli si ale myslíš, že to je dobrá cesta, tak pořád je tu možnost PR nad upraveným Nette\Database\SqlPreprocessor konkrétně metodou formatValue()