tiny ‘n’ smart
database layer

Odkazy: dibi | API reference

Oznámení

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

Lean Mapper – přidání nové „hasMany“ property do DB

před 6 lety

svezij
Člen | 69

Ahojte, pravděpodobně se jedná o úplně triviální otázku, ale nějak jsem na ni nenašel odpověď. Mám následovně definovanou entitu:

/**
 * @property int $id
 * @property string $username
 * @property string $password
 * @property string|NULL $name
 * @property string|NULL $company
 * @property string $default_resource
 * @property Role[] $roles m:hasMany(:user_roles::roles)
 * @property Resource[] $rights m:hasMany(:user_rights::resources)
 * @property UserAction[] $history m:belongsToMany(user_id:user_actions)
 */
class User extends \LeanMapper\Entity
{
}

Dále mám formulář, kde vyplním údaje a vyberu role uživatele. Nyní bych rád zpracoval data z formuláře, což dělám následovně:

$user = new \MyNS\Entity\User();

foreach ($form->values as $key => $value) {
  if ($value) {
    if (Strings::startsWith($key, 'role_')) {
      list($prefix, $role_id) = explode('_', $key);
      // tady bych rád přidal do tabulky user_roles pár $user_id, $role_id
    } else {
      $user->$key = $value;
    }
  }
}

// add it to the database
$this->userRepository->persist($user);

Jak jsem uvedl v kódu, ve větvi podmínky, zda se jedná o roli, tak bych rád přidal do tabulky user_roles pár id uživatele / id role, ale nevím, jak na to.

Za odpověď mockrát děkuji.

Oprava:
Resp. vím, jak na to, prostě bych si ty role uložil do pole, které bych po uložení uživatele do DB prošel a postupně vkládal přímo přes Dibi. Ptám se ale, jestli je to možné přímo přes Lean Mapper – např.:

$user->link_role($role_id);  // nebo tak nějak - prostě přímo přes entitu uživatele

Editoval svezij (12. 8. 2013 14:54)

před 6 lety

Tharos
Člen | 1042

Ahoj,

myslím, že hledáš přesně tohle. Bohužel je to zatím „zdokumentované“ jenom na fóru…

$user = new \MyNS\Entity\User();

foreach ($form->values as $key => $value) {
  if ($value) {
    if (Strings::startsWith($key, 'role_')) {
      list($prefix, $id) = explode('_', $key);
      $user->addToRoles($id);
    } else {
      $user->$key = $value;
    }
  }
}

$this->userRepository->persist($user);

před 6 lety

svezij
Člen | 69

Super, kouknu na to. Větev fóra o Lean Mapperu také čtu, ale je toho hodně, a tohoto jsem si nevšiml. Díky moc.

před 6 lety

svezij
Člen | 69

Opět vás šecky zdravím. Přečetl jsem si, co dalšího LeanMapper umí a musím říct: „dobrá práce“ (nejednou mnoho „maličkostí“, které mi velmi usnadní práci, využiji).
Možnost specifikace vlastního mapperu je super, má to téměř neomezené možnosti, hlavně pro nasazení na starší databáze. Samozřejmě se s tím pojilo několik problémů – laděnka na mě hned křičela, že:

  1. třídě \LeanMapper\Repository dávám DibiConnection místo LeanMapper\Connection
  2. mi chybí druhý argument (instance rozhraní IMapper – možná by to mohl být volitelný parametr a když ho nedám, mohla by se načíst instance třídy LeanMapper\DefaultMapper)
  3. neexistuje entita Users – tady mě trochu zaskočilo, že už to nevychází z názvu repozitáře, ale z názvu tabulky, tudíž mě to donutilo implementovat metodu getEntityClass($table, LeanMapper\Row $row = NULL) – nebylo by dobré, abych měl v Mapperu možnost získat název třídy repozitáře? Není náhodou už teď nějaká možnost specifikovat název entity přes anotaci repozitáře?
  4. atd.

Všech těchto „malých“ problémků jsem se zbavil, ale zůstal jeden, se kterým si nevím rady. Týká se kódu, který jsme společně s Tharosem řešili výše. Zavolám-li tohle (bez „dočasného“ persist($user)):

$user = new \MyNS\Entity\User();

foreach ($form->values as $key => $value) {
  if ($value) {
    if (Strings::startsWith($key, 'role_')) {
      list($prefix, $id) = explode('_', $key);
      $user->addToRoles($id);
    } else {
      $user->$key = $value;
    }
  }
}

$this->userRepository->persist($user);

laděnka vyhodí výjimku

Call to a member function getTable() on a non-object

File: ...\libs\LeanMapper\tharos\leanmapper\LeanMapper\Entity.php   Line: 656

656:            $table = $this->mapper->getTable($this->getCurrentReflection()->getName());

případně tohle (s „dočasným“ persist($user)):

$user = new \MyNS\Entity\User(array('sloupec' => 'docasna hodnota'));
$this->userRepository->persist($user);  // "dočasné" `persist($user)`

foreach ($form->values as $key => $value) {
  if ($value) {
    if (Strings::startsWith($key, 'role_')) {
      list($prefix, $id) = explode('_', $key);
      $user->addToRoles($id);
    } else {
      $user->$key = $value;
    }
  }
}

$this->userRepository->persist($user);

laděnka vyhodí výjimku

Argument 3 passed to LeanMapper\Row::createReferencingDataDifference() must be an instance of LeanMapper\Filtering, string given, called in D:\nette\libs\LeanMapper\tharos\leanmapper\LeanMapper\Entity.php on line 391 and defined

File: ...\libs\LeanMapper\tharos\leanmapper\LeanMapper\Row.php   Line: 231

231:        public function createReferencingDataDifference($table, $viaColumn = null, Filtering $filtering = null, $strategy = null)

Za pomoc znovu mockrát děkuji.

před 6 lety

Tharos
Člen | 1042

@svezij:

  1. mi chybí druhý argument (instance rozhraní IMapper – možná by to mohl být volitelný parametr a když ho nedám, mohla by se načíst instance třídy LeanMapper\DefaultMapper)

Implicitně vytvářet DefaultMapper se samozřejmě nabízí, ale v duchu DI by to nebylo optimální. Repository je prostě závislé na instanci IMapper a musí ji zvenčí dostat.

  1. neexistuje entita Users – tady mě trochu zaskočilo, že už to nevychází z názvu repozitáře, ale z názvu tabulky, tudíž mě to donutilo implementovat metodu getEntityClass($table, LeanMapper\Row $row = NULL) – nebylo by dobré, abych měl v Mapperu možnost získat název třídy repozitáře? Není náhodou už teď nějaká možnost specifikovat název entity přes anotaci repozitáře?

Nad repositářem může být anotace @table, která určí, se kterou (hlavní) tabulkou pracuje. Je pak úkolem mapperu přeložit to na správnou entitu (getEntityClass). Funkce pro získání názvu třídy repositáře by IMHO měla jen velmi malé využití.

Možnost specifikovat název entity v anotaci byla, ale odstranil jsem ji, protože to nebylo dotažené do všech důsledků a hlavně vlastní mapper tohle celé řeší mnohem lépe (a hlavně napříč celou aplikací).

Všech těchto „malých“ problémků jsem se zbavil, ale zůstal jeden, se kterým si nevím rady. Týká se kódu, který jsme společně s Tharosem řešili výše. Zavolám-li tohle (bez „dočasného“ persist($user)):

Zkus prosím update na aktuální develop, mělo by to být fixnuté. Stále ale platí omezení (o kterém jsem psal), že takhle pracovat je možné pouze s entitami, které „jsou attached“.

před 6 lety

svezij
Člen | 69

Výborně, výborně – funguje přesně tak, jak jsem si přál :-). Děkuji, jen tak dál ;-)