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