nette/database v3.2.9 – phpdoc `Selection::insert()` nepokrývá dokumentovaný bulk insert

MichalHaltuf
Bronze Partner | 16
+
+3
-

Ahoj,

v nette/database v3.2.9 (vyšla 22. 4. 2026) byl commitem 420ebb5 („improved phpDoc types“) zúžen phpdoc u Nette\Database\Table\Selection::insert():

/**
 * @param  iterable<string, mixed>|Selection<ActiveRow>  $data
 * @return ($data is array<string, mixed> ? T|array<string, mixed> : int)
 */
public function insert(iterable $data): ActiveRow|array|int

Typ iterable<string, mixed> přijímá pouze pole se stringovými klíči (tj. jeden řádek). Odmítá list<array<string, mixed>> (pole řádků), protože list má integer klíče.

Docs přitom bulk variantu stále uvádějí jako oficiální API – Vkládání více záznamů najednou:

Metoda insert() umožňuje vložit více záznamů pomocí jednoho SQL dotazu. V tomto případě vrací počet vložených řádků.

$insertedRows = $explorer->table('users')->insert([
    ['name' => 'John', 'year' => 1994],
    ['name' => 'Jack', 'year' => 1995],
]);

Runtime v v3.2.9 funguje stejně jako dřív (Nette to posílá přes ?values placeholder), jen PHPStan teď na takových voláních hlásí:

Parameter #1 $data of method Nette\Database\Table\Selection<...>::insert()
expects iterable<string, mixed>, list<array<string, int|string>> given.

Na v3.2.8 tohle projde, na v3.2.9 ne – aniž by se něco změnilo v kódu aplikace. Conditional return ($data is array<string, mixed> ? T|array : int) přitom s bulk variantou počítá (větev : int vrací počet řádků, jak docs slibují), takže zúžení vstupního typu vypadá spíš jako nedopatření než úmysl.


Dotaz: je zúžení typu záměr (tj. bulk přes Selection::insert() už oficiálně nemá být, a správnou cestou pro bulk je něco jiného – např. Explorer::query('INSERT INTO foo ?values', $rows))? Nebo jde o přehlédnutí a phpdoc by měl být rozšířen na něco jako:

@param iterable<string, mixed>|list<array<string, mixed>>|Selection<ActiveRow> $data

Díky za vyjasnění. Ať už je to tak nebo tak, docs a phpdoc by spolu asi měly souhlasit.

kminekmatej
Generous Backer | 42
+
0
-

Tak proto mi to dneska zahlásilo 130 chyb. Nějak sem neměl čas to studovat

David Grudl
founder | 8310
+
0
-

Je to bug, pošli prosím PR

MichalHaltuf
Bronze Partner | 16
+
0
-

Děkuji, poslal jsem. Po vyřešení jsem objevil i regresi v návratovém typu, runtime chování je složitější než to dokumentované a návratový typ min. v jedné else větvi neseděl s novým. Popravdě nevím, jak jinak to spravit, než návratem k původní anotaci + doplněné testy.

kminekmatej
Generous Backer | 42
+
0
-

Asi tam je hlubší bug než je úprava phpDoc – přišel jsem na to že bulk insert více řádků nevrátí (int) Number of affected rows, nýbrž ActiveRow prvního vloženého řádku. Příklad:

$inserts = [];
$inserts[] = [
    "contact_id" => $contact->getId(),
    "space_id" => $space,
];
$inserts[] = [
    "contact_id" => $contact->getId() - 1,
    "space_id" => $space,
];
$addedRows = $this->database->table(Contact::TABLE_SPACE)->insert($inserts);
echo gettype($addedRows); // ActiveRow instead of (int) 2
David Grudl
founder | 8310
+
+6
-

Díky za spolupráci, větěv 3.2 by už měla fungovat v pořádků (ještě jsem ale nevydal stable).

Přivedlo mě to k myšlence insert() rozdělit na dvě metody, tj. přidat insertMany() pro bulk vkládání a do současného insert() dát nějaky deprecated notice pro bulk. Aby se zjednodušily ty signatury.

kminekmatej
Generous Backer | 42
+
0
-

To by za mě dávalo smysl

m.brecher
Generous Backer | 920
+
0
-

Přivedlo mě to k myšlence insert() rozdělit na dvě metody, tj. přidat insertMany()

Výborný nápad, kód bude přehlednější!