Oznámení
Jak vracet složený primární klíč u spojovací tabulky?
před 5 lety
- medhi
- Bronze Partner | 189
Reaguji k již položené otázce kdysi v tom megavlákně:
@Casper:
Je možné LM nějak „naučit pracovat“ s identifikační závislostí? Například tabulky user(user_id, …) kde user_id je PK a AI a user_settings(user_id, …), kde user_id je jen PK. Pokud perzistuji objekt bez AUTO_INCREMENT sloupce, skončím na výjimce Cannot retrieve last generated ID.
@Tharos
Vestavěná persist() metoda v repositáři by měla mít jistou inteligenci: pokud je v persistované entitě nastavena hodnota primárního klíče, použije se a žádné getInsertId() se nevolá. Lean Mapper se (předpokládám) bez auto incrementů úplně obejde. Jen je v takovém případě nutné u nově vytvořených entit hodnotu primárního klíče ručně nastavit.
Doufám, že odpovídám relevantně. Kdyby ne (nebo kdyby má rada nezabrala), usměrni mě. :)
Ale co když jde o spojovací tabulku, kde je primární klíč složený z např. user_id, message_id?
Dostal jsem se až k tomu, že jsem si ve svém mapperu udělal funkci:
public function getPrimaryKey($table)
{
if ($table === 'messagedelivery') {
return 'user_id message_id'; // zde nevím co vrátit
}
return parent::getPrimaryKey($table);
}
před 5 lety
- Tharos
- Člen | 1042
Ahoj, takové spojovací tabulky, které mají jenom dva sloupce a primární klíč je kompozicí právě těch dvou sloupců, jsou v Lean Mapperu standardně podporovány. V mapperu pro to nemusíš vůbec nic speciálně definovat. Jsou podporovány i takové spojovací tabulky, které vůbec nemají primární klíč. :)
Vyzkoušej to prosím a kdybys narazil na nějaký problém, pošli mi prosím nějakou ukázku kódu a schéma. Díky!
před 5 lety
- medhi
- Bronze Partner | 189
Právě, že jsem na to narazil. Vypadá to asi takhle:
Entity
// Entita Message
/**
* @property int $id
* @property string $subject
* @property string $message
* @property string $sent_datetime
* @property MessageDelivery[] $deliveries m:belongsToMany
* @property User|null $sender m:hasOne
*/
class Message extends BaseEntity
{
}
// Entita MessageDelivery (jde o spojovací tabulku, která obsahuje navíc parametr read - přečteno)
/**
* @property bool $read
* @property User $recipient m:hasOne
* @property Message $message m:hasOne
*/
class MessageDelivery extends BaseEntity
{
}
// Entita User
/**
* @property int $id
* @property string $name
* @property MessageDelivery[] $messages m:belongsToMany
* @property Message[] $sent_messages m:belongsToMany
*/
class User extends \LeanMapper\Entity
{
}
Presenter
Zde chci vytvořit zprávu pro X příjemců
// zpracování formuláře:
$message = new Message;
$message->sender = $this->userEntity;
$message->assign($values, ["subject", "message"]);
$this->messages->persist($message);
foreach ($values->recipient as $recipientId) {
$delivery = new MessageDelivery;
$delivery->message = $message;
$recipient = $this->school->findIn("users", $recipientId);
if ($recipient) {
$delivery->recipient = $recipient;
$this->messageDeliveries->persist($delivery); // zde vyskočí výjimka Cannot retrieve last generated ID
}
}
Schéma tabulky messagedelivery
CREATE TABLE `messagedelivery` (
`message_id` bigint(20) unsigned NOT NULL,
`user_id` int(10) unsigned NOT NULL,
`read` tinyint(1) unsigned NOT NULL,
PRIMARY KEY (`user_id`,`message_id`),
KEY `message_id` (`message_id`),
CONSTRAINT `messagedelivery_ibfk_1` FOREIGN KEY (`message_id`) REFERENCES `message` (`id`) ON DELETE CASCADE,
CONSTRAINT `messagedelivery_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
před 5 lety
- Tharos
- Člen | 1042
Oukej, pak je to jasné. :)
Mnozí programátoři „vazební tabulku, která má jen nějaký jeden extra sloupec s informací navíc“ již nenazývá vazební tabulkou a mají v podstatě věci pravdu. Pak bychom mohli totiž nazývat vazební tabulkou každou tabulku, která nese mimo jiné dva cizí klíče.
Lean Mapper umí pracovat s vazebními tabulkami typu PK-FK-FK nebo jen FK-FK, a to proto, protože on ve skutečnosti při práci s takovou tabulku s primárním klíčem nijak speciálně nepracuje. Jenomže přidáme-li do takové tabulky další informace, neumí takový řádek adresovat.
Řešením je mít v takové tabulce umělý primární klíč, protože jinak by to byla „obyčejná tabulka, nikoliv vazební, se složeným primárním klíčem“ a to je něco, co Lean Mapper aktuálně nepodporuje.
Ještě pro úplnost odkáži na částečně související čtení: https://forum.dibiphp.com/…ebni-tabulce#…
před 5 lety
- medhi
- Bronze Partner | 189
Takže jestli to dobře chápu, musím si přidat nový sloupec typu varchar, který budu plnit umělým klíčem ve tvaru {message_id}-{user_id} ?
Poradíš mi, jak má vypadat metoda, která bude takový klíč automaticky generovat a kam ji umístit?
Editoval medhi (16. 8. 2014 8:53)
před 5 lety
- Tharos
- Člen | 1042
Ne, proč tak složitě. :) Myslel jsem to takhle:
CREATE TABLE `messagedelivery` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`message_id` bigint(20) unsigned NOT NULL,
`user_id` int(10) unsigned NOT NULL,
`read` tinyint(1) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `message_id_user_id` (`message_id`,`user_id`),
KEY `user_id` (`user_id`),
CONSTRAINT `messagedelivery_ibfk_1` FOREIGN KEY (`message_id`) REFERENCES `message` (`id`) ON DELETE CASCADE,
CONSTRAINT `messagedelivery_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/**
* @property int $id
* @property User $recipient m:hasOne
* @property Message $message m:hasOne
* @property bool $read
*/
class MessageDelivery extends BaseEntity
{
}
Edit: Opravil jsem ten SQL export.
Editoval Tharos (16. 8. 2014 9:42)
před 5 lety
- castamir
- Člen | 631
Takřka totožné řešení, ale Tharos byl rychlejší =o)
Editoval castamir (16. 8. 2014 9:57)
před 5 lety
- medhi
- Bronze Partner | 189
Toto řešení s klasickým autoincrementačním id už jsem měl, ale zdálo se mi to jako zbytečné omezení tam, kde nemusí být (tím omezením myslím, že tabulka přestane být „nekonečnou“)
Díky