tiny ‘n’ smart
database layer

Odkazy: dibi | API reference

Oznámení

Omlouváme se, provoz fóra byl ukončen

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