Nextras\ORM – ORM nad Nextras\Dbal
- svezij
- Člen | 69
@hrach
Ahoj, chtěl bych si rozšířit QueryBuilder o možnost CREATE TABLE a
další. Když se podívám na kód QueryBuilderu, tak vypadá, že je určen
k dalšímu rozšiřování, ale bohužel to nejde, protože tam máš vše
private. Nemohu tedy využít metodu getQuerySql(), protože property
$generatedSql je také private atd. Můžu si napsat své vlastní, ale to pak
budu vytvářet jeden typ QueryBuilderu pro dotazy typu SELECT, druhý typ
QueryBuilderu pro dotazy typu CREATE TABLE atd. Bylo by možné, abys po
zvážení dal některé části protected místo private?
Také mě napadlo, že by obecně nebylo špatné mít přes config možnost vložit jinou třídu pro Connection, než tvoji Nextras\Dbal\Connection. Domnívám se, že by stačilo jednoduše upravit řádek 32 v souboru DbalExtension.php, kde se nachází definice připojení a konkrétně metoda setClass (ale tím si nejsem jistý, protože nevím, zda jsem v Nette natolik zběhlý, abych to mohl tvrdit). Nicméně, kdybych chtěl podědit třídu Nextras\Dbal\Connection, tak bych dopadl obdobně, jako u QueryBuilderu, také tam máš mnohé private.
Samozřejmě budu rád za tvůj názor. Díky, ahoj.
- hrach
- Člen | 1838
@svezij
- query builder: ohledne create table – to asi neni moc pro aktualni query builder, jeho api je spis pro selectovani, a do budoucna urcite insertovani a updatovani a mazani. vytvareni tabulek imo bude vyzadovat o dost jiny interface a uplne nevim, co se ti z aktualniho query builderu tolik hodi. pripadne navrhi ktere property a pak muzes udelat pull-request :)
- dedeni connection – co presne chces delat? ktere property potrebujes?
- extension pro jinou connection class – neni problem, otazka je, jestli ti to bez predchoziho bodu k necemu bude :-)
- svezij
- Člen | 69
@hrach
No, oba mé návrhy spolu souvisí. Je možné, jak píšeš, že není aktuální query builder ideální pro create table, ale každopádně je dobré používat jeden query builder pro „buildování queries“. Nehledě na to, že Connection::createQueryBuilder() vytváří právě tento konkrétní QueryBuilder. Z tohoto QueryBuilderu se mi hodí minimálně property $generatedSql, kterou využívá metoda getQuerySql(). Vše ostatní se týká pouze SELECTu, který máš vyřešený.
Nijak dopodrobna jsem Connection zatím nezkoumal, ale souvisí to právě s metodou createQueryBuilder(), ve které bych potřeboval vrátit jiný, poděděný QueryBuilder. S tím tedy souvisí i extension pro jinou Connection, která by byla potomkem té tvojí.
Situace není těžká – potřeboval jsem CREATE TABLE, díval jsem se, jak řešíš dotazování a zjistil jsem, že existuje QueryBuilder. Vytvořil jsem si tedy potomka, kterého jsem rozšířil o funkčnost CREATE TABLE a zjistil jsem, že není viditelná property $generatedSql, takže nemůžu využít rodičovskou getQuerySql(). Zjistil jsem také, že jej vytváříš pomocí Connection::createQueryBuilder(), tak jsem si říkal, že přetížím createQueryBuilder() ve vlastním Connection a zjistil jsem, že v Extension není možnost, jak nastavit svou Connection namísto tvé. To je vše. Každopádně zkusím víc projít tuto a související třídy.
- igor.pocta
- Člen | 100
Ahoj,
začal jsem přepisovat aplikaci na nextras/orm ale setkal jsem se s jedním problémem. Potřebuji použít nějaký datagrid ale nelze mu dodat data z orm :( Tak jsem si v presenteru přímo předal
/** @var Context $context */
public $context;
public function __construct(Context $context)
{
parent::__construct();
$this->context = $context;
}
a když chci použít klasický $this->context->table(„article“) tak mi to vypisuje chybu, Cannot read an undeclared column ‚Level‘. To je ale divný, protože to je v úplně jiném modulu a ten funguje dobře.
Nevíte co s tím?
Editoval igor.pocta (18. 6. 2016 14:39)
- Jan Tvrdík
- Nette guru | 2595
To nechápu, proč by datagridu nešlo dodat data z ORM? Každopádně z toho screenshotu nejde poznat imho vůbec nic poznat. Zkus smazat cache, víc poradit neumím.
- igor.pocta
- Člen | 100
@JanTvrdík Ahoj, a můžeš mi prosím poradit, jak z Nextras\Orm dodám data do Nextras\Datagrid, tak, abych tam mohl uplatňovat filtry apod.? Zkoušel jsem to a také to házelo chybu :)
&EDIT: Tak se podařilo, předtím tam byla chybička :) Díky
Editoval igor.pocta (18. 6. 2016 17:00)
- igor.pocta
- Člen | 100
Ahoj,
vytvořil jsem si vlastní metodu v mapperu u ORM a mám problém s tím, že mi vyskakuje chyba – tabulka s názvem databáze neexistuje
$month = new DateTime("-1 month");
$builder = $this->builder();
$builder->from($this->getTableName());
$builder->where('[created_by] = %s', $employee->id);
$builder->andWhere('[termin] > %s', $month->format("Y-m-d"));
return $builder;
mi vyhodí chybu
Nextras\Dbal\QueryException
Unknown table ‚wag.‘
SQL
SELECT ``.* FROM task_item WHERE (`created_by` = '805963') AND (`termin` > '2016-05-20')
Chyba je právě v ``.* a když dám cokoliv do $builder->select() nebo $builder->addSelect(), tak mi to ignoruje a vždy mi to postaví stejný SQL příkaz.
Lze to nějak obejít?
Editoval igor.pocta (20. 6. 2016 16:37)
- Jan Tvrdík
- Nette guru | 2595
@igor.pocta Zkus smazat ten řádek
$builder->from($this->getTableName());
, from
nastavuje už volání builder()
, akorát to na rozdíl od tebe
dělá správně.
- Felix
- Nette Core | 1245
@JanTvrdík Mozna to pusobi jenom na me, ale veta ‚..akorat to narozdil od tebe dela spravne‘ je podle me zbytecne drsna.
Jde to prece napsat i taktne. Rozhodne tenhle pristup propagaci Nextras/Orm neprospeje. A my vsichni chceme, aby se to pouzivalo co nejvice a tim i odladilo a zapojilo se vice lidi.
Jestli to myslim spatne, tak me prosim opravte. :)
- igor.pocta
- Člen | 100
Jan Tvrdík napsal(a):
@igor.pocta Zkus smazat ten řádek
$builder->from($this->getTableName());
,from
nastavuje už voláníbuilder()
, akorát to na rozdíl od tebe dělá správně.
@JanTvrdík Tak to pomohlo. Děkuji.
Mimochodem, já tam nejdřív zkoušel samotný název tabulky (což
v podstatě vrací i ta metoda) ale to vyhazovalo stejnou chybu, i když
v dokumentaci je napsáno:
The second argument is optional table alias, other arguments are arguments for used modifiers.
A až když jsem tam ten alias přidal, fungovalo to.
- Jan Tvrdík
- Nette guru | 2595
@igor.pocta Ono je to optional pouze pro obecné použití QueryBuilderu mimo ORM, ORM ten alias potřebuje.
- pfilipek
- Člen | 25
@JanTvrdík @hrach Zdravím, chtěl jsem se zeptat, jestli jste už někdy řešili a nebo zda to ORM umožňuje, abych přidal do modelu dynamicky repozitáře místo do anotace. Děláme modulární systém a potřebujeme dynamicky přidat repozitář pro dané extension. Zatím nás napadlo přidat to nějakým způsobem do anotací, protože nám s kolegou @svezij přijde, že jiným způsobem to ORM neumožňuje. Děkuji mnohokrát za odpověď.
- Vastlik
- Člen | 58
Ahoj, je nějaká možnost jak v Repository vytvořit query která obsahuje OR? Například
Select * from `table` where `status` = 1 and `number` = 1 and `created_at` > 1469526914 OR `number` = 2 and `status` = 1
Nějaký takovýhle php kód
return $this->findBy([
'this->account->id' => $accountId,
'createdAt>=' => $createdAt,
'status' => 1,
'or status' => 2,
'this->account->id' => $accountId
])->limitBy($perPage)->orderBy('id');
Díky!
Editoval Vastlik (26. 7. 2016 13:37)
- Spectator
- Člen | 48
Ahoj, mám takový problém. Používám vazební tabulku a v určitých případech funguje správně v určitých vytváří nesmyslná SQL – -konkrétně countStored()
Entity mám definovány takto:
/**
* Class User
* @property int $id {primary}
* @property string $status {enum self::STATUS_*}
* @property \DateTime $created {default now}
* @property string $email
* @property string $password
* @property string $role {enum self::ROLE_*}
* @property string $name
* @property ManyHasMany|Code[] $codes {m:n Code::$users, isMain=true, orderBy=[order, DESC]}
* @property OneHasMany|UserHasCode[] $userHasCode {1:m UserHasCode::$user}
*/
class User extends Entity
{
const STATUS_ACTIVE = 'active';
const STATUS_INACTIVE = 'inactive';
const ROLE_USER = 'user';
const ROLE_ADMIN = 'admin';
}
/**
* Class Code
*
* @property int $id {primary}
* @property bool $active
* @property \DateTime $created {default now}
* @property string $name
* @property int $order
* @property ManyHasMany|User[] $users {m:n User::$codes}
* @property OneHasMany|UserHasCode[] $userHasCode {1:m UserHasCode::$code}
*/
class Code extends Entity
{
}
/**
* Class UserHasCode
* @property mixed $id {primary-proxy}
* @property int $userId {primary}
* @property int $codeId {primary}
* @property \DateTime $created {default now}
* @property Code $code {m:1 Code::$userHasCode}
* @property User $user {m:1 User::$userHasCode}
*/
class UserHasCode extends Entity
{
}
Mapování mám změněno z %s_x_%s na %s_has_%s.
A zde problém:
$codes = $this->loggedUser->codes;
foreach ($codes as $code) {
dump($code->id); // 1, 2, (OK)
}
dump($codes->count()); // 2 (OK)
dump($codes->countStored()); // Column 'code_id' in field list is ambiguous
/*
SELECT `user_has_code`.`user_id`, COUNT(`code_id`) as count FROM `code` AS `code` LEFT JOIN `user_has_code` AS `user_has_code` ON (`user_has_code`.`code_id` = `code`.`code_id`) WHERE `user_id` IN (1) GROUP BY `user_id`
*/
Jde mi tedy o toto:
Mám právně nastavenou vazební tabulku a příslušně i navázané entity?
Popř. jak na to?
A dále – jestli je to nastaveno správně, tak proč nedoplňuje názvy
tabulek v SQL?
EDIT:
Tak ještě jedna věc mne překvapuje. V té vazební tabulce je při jejím dumpu vidět, že user i code jsou něco (čísla ID :-/), ale codeId a userId jsou null, včetně id.
Catchme\Orm\UserHasCode #0ca3
metadata protected => Nextras\Orm\Entity\Reflection\EntityMetadata #dd99
className private => "Catchme\Orm\UserHasCode" (23)
primaryKey private => array (2)
0 => "userId" (6)
1 => "codeId" (6)
properties private => array (6)
id => Nextras\Orm\Entity\Reflection\PropertyMetadata #d13b { ... }
userId => Nextras\Orm\Entity\Reflection\PropertyMetadata #16f5 { ... }
codeId => Nextras\Orm\Entity\Reflection\PropertyMetadata #b6a5 { ... }
created => Nextras\Orm\Entity\Reflection\PropertyMetadata #ee94 { ... }
code => Nextras\Orm\Entity\Reflection\PropertyMetadata #5bba { ... }
user => Nextras\Orm\Entity\Reflection\PropertyMetadata #cb44 { ... }
repository private => Catchme\Orm\UserHasCodeRepository #ca59
data private => array (6)
created => Nextras\Dbal\Utils\DateTime #1915
date => "2016-08-05 14:09:16.000000" (26)
timezone_type => 3
timezone => "Europe/Prague" (13)
code => 1
user => 1
id => NULL
userId => NULL
codeId => NULL
validated private => array (3)
id => TRUE
userId => TRUE
codeId => TRUE
modified private => array ()
persistedId private => array (2)
0 => NULL
1 => NULL
preloadContainer private => Nextras\Orm\Collection\EntityIterator #e34c
position private => 0
data private => array (3)
0 => Catchme\Orm\UserHasCode #0ca3 { RECURSION }
1 => Catchme\Orm\UserHasCode #0ca3 { RECURSION }
2 => Catchme\Orm\UserHasCode #0ca3 { RECURSION }
iteratable private => array (3)
0 => Catchme\Orm\UserHasCode #0ca3 { RECURSION }
1 => Catchme\Orm\UserHasCode #0ca3 { RECURSION }
2 => Catchme\Orm\UserHasCode #0ca3 { RECURSION }
hasSubarray private => FALSE
preloadCache private => NULL
Editoval Spectator (7. 8. 2016 20:20)
- jukie
- Člen | 5
Zdravím,
řeším logování updatů ve spojení s kdyby events, chtěl jsem nějak elegantně zajistit ve vyšší třídě načtení starých hodnot entity a udělat diff, ale narážím na problémy:
- nejsem schopen načíst hlouběji než ve vlastním presenteru staré hodnoty entity, zřejmě proto, že se dále posílají už jen entity s modifikovanými parametry, bohužel je v projektu nemožné upravovat každý presenter a v něm zajistit logování
- pořád jsem si myslel že toto někde musí být implementováno, že se přeci neprovádí update celé entity, ale jenom modifikovaných sloupců, ale on se celý update provádí
Dělám něco špatně? Předem děkuji za rady
- Jan Tvrdík
- Nette guru | 2595
@jukie ORM ví, které sloupce se změnily, viz https://github.com/…075a606c0396
- hitzoR
- Člen | 51
pfilipek napsal(a):
@JanTvrdík @hrach Zdravím, chtěl jsem se zeptat, jestli jste už někdy řešili a nebo zda to ORM umožňuje, abych přidal do modelu dynamicky repozitáře místo do anotace. Děláme modulární systém a potřebujeme dynamicky přidat repozitář pro dané extension. Zatím nás napadlo přidat to nějakým způsobem do anotací, protože nám s kolegou @svezij přijde, že jiným způsobem to ORM neumožňuje. Děkuji mnohokrát za odpověď.
Řešil jsem teď stejný problém a musel jsem přepsat metodu getRepositoryList v Extension. Místo projíždění anotace jsem si tam v bootstrapu přidat RobotLoader a natáhl všechny třídy implementující IRepository
bootstrap.php
$configurator->onCompile[] = function ($_, \Nette\DI\Compiler $compiler) use ($loader) {
...
$compiler->addExtension('orm', new \PgWay\Model\Orm\CustomOrmExtension($loader));
};
Tady by mě od zkušenějších zajímalo, jestli jde předání RobotLoaderu řešit nějak jinak, čístěji, třeba klasicky přes config?
CustomOrmExtension.php
<?php
namespace App\Model\Orm;
use Nette;
use Nette\PhpGenerator;
use Nette\Reflection\ClassType;
use Nextras\Orm\Bridges\NetteDI\OrmExtension;
use Nextras\Orm\InvalidStateException;
use Nextras\Orm\Repository\IRepository;
class CustomOrmExtension extends OrmExtension
{
private $robotLoader;
public function __construct(Nette\Loaders\RobotLoader $robotLoader)
{
$this->robotLoader = $robotLoader;
}
protected function getRepositoryList($modelClass)
{
$modelReflection = new ClassType($modelClass);
$builder = $this->getContainerBuilder();
$builder->addDependency($modelReflection->getFileName());
$repositories = [];
foreach ($this->robotLoader->getIndexedClasses() as $class => $file) {
$reflection = Nette\Reflection\ClassType::from($class);
if ($reflection->implementsInterface(IRepository::class)) {
/* úprava jména z např. 'ArticleCommentRepository' na 'articleComments',
což bude vpodstatě název property modelu pro konkrétní repozitář */
preg_match('/([A-Z][a-zA-Z]*)Repository$/', $reflection->name, $nameMatch);
$name = lcfirst($nameMatch[1]) . 's';
if (!array_key_exists($name, $repositories))
$repositories[$name] = $class;
else
throw new InvalidStateException("There are multiple repositories with name $nameMatch[1]Repository."); // TODO custom exception
}
}
return $repositories;
}
}
Je to takové trochu dirty řešení, ale svůj účel to plní. Ještě jsem upravoval MetadataParser a přidal si do argumentů u relationships volitelný modifikátor optional, abych mohl do entit psát propojení s jinýma entitama, které jsou v modulech, které nejsou nainstalované. Pří používání takových property se to musí ale dobře ošetřit. Já mám v databázi seznam nainstalovaných modulů a podle toho kontroluju, zda to můžu použít nebo ne, ale tohle už je implementační detail, který si každý může vyřešit po svém.
Anotace entity
/*
* @property NotExistingEntity|NULL $null {1:1 NotExistingEntity::$.., optional} {default NULL}
* @property NotExistingEntity $notNull {1:1 NotExistingEntity::$.., optional}
*/
Při zavolání $entity->null
se vrací NULL
,
při $entity->notNull
se
vyhodí InvalidStateException("Property ..\XxEntity::$notNull is not set.")
Takhle vypadají všechny čtyři metody na parsování relationships:
protected function parseXHasX(PropertyMetadata $property, array &$args)
{
$pos = strpos($args[0], '::');
if ($pos !== FALSE)
$class = substr($args[0], 0, $pos);
else
$class = $args[0];
$entity = $this->makeFQN($class);
$optional = in_array('optional', $args, TRUE);
if ($optional === TRUE)
unset($args[array_search('optional', $args, TRUE)]);
if (!$optional || isset($this->entityClassesMap[$entity])) {
/* původní kód metody, spustí se když není nastaven argument optional nebo když entita existuje */
} else
$args = []; /* $args se musí vracet z této metody prázdné, normálně se o to starají metody,
které se volají v posledním if, ale pokud jej přeskočíme, tak musíme takhle
ručně pole vymazat, jinak nám processPropertyModifier(..) vyhodí
InvalidModifierDefinitionException */
}
Funkční to všechno je (zatím, pořádně jsem to ještě neotestoval), ale pořád mi to přijde takové nečisté a neelegantní. Zajímal by mě názor zkušenějších a hlavně @JanTvrdík a @hrach na to, jak stabilní to bude a případně co a kde by se dalo udělat jinak, líp.
Editoval hitzoR (18. 8. 2016 9:45)
- hrach
- Člen | 1838
@hitzoR vypada to docela dobre a zajimave!
- ad repository z ruznych modulu, ktere nejsou v anotaci: pro aktualni kod je to urcite dobre reseni na neni tam nic zasadne spatne. Tim, ze se to deje v compile time tak je to v podstate v pohode. Slo by to asi optimalizovat, ze treba reflection classy nevytvaris, pokud ve jmenu tridy neni repository.
- ad optional property – tak tady to vypada zajimave a nevim jestli to uplne chapu, reseni je to pekne, ale ale zrejme je treba, aby databaze akceptovala na strane toho, kdo drzi klic, i null, protoze kdyz ten modul neni nainstalovanej… :-) tak orm vygeneruje insert bez toho no :-) Ale jo, to muze fungovat pekne, navic vetsinou jsou ty moduly z druhy strany asi – hodne zajimavej napad!
- Vastlik
- Člen | 58
Ahoj,
jak je to s implementací many to many? Mám 3 tabulky. Users, Tags,
users_tags. Jak udělám vazbu? Napadá mě řešení vytvořit tabulku
users_tags a pak one to many k users a tags.
Ale určitě to jde nějak za pomocí vazby ManyHasMany, ale nevím jak. Není
někde example i s návrhem nebo popisem DB?
Další otázkou je určování která strana je isMain? Jak vybrat nebo, co se
tím určuje?
Moc díky za odpověď.
Editoval Vastlik (7. 9. 2016 21:51)
- hrach
- Člen | 1838
https://nextras.org/…elationships v doc by to mělo být dost popsaný. Is main prostě řídí trn vztah, je k tomu něco i v jiných kapitolách.
- hitzoR
- Člen | 51
Vastlik napsal(a):
Udělal jsem dle docu, ale dostávám Undefined property: Nextras\Orm\Mapper\Dbal\DbalCollection::$users když se snažím získat u tagů users.
Hádám, že to bude tím, že se snažíš číst property entity z kolekce. Pokud sis třeba vytáhl jeden tag pomocí findById, tak to je špatně a měl bys to dělat pomocí getById. Pokud si dával nad repositářem findAll nebo findBy, aby ti to vrátilo víc záznamů, tak si to musíš buď fetchnout nebo projít foreachem a až pak číst $users.
Editoval hitzoR (8. 9. 2016 3:34)
- Vastlik
- Člen | 58
hitzoR napsal(a):
Vastlik napsal(a):
Udělal jsem dle docu, ale dostávám Undefined property: Nextras\Orm\Mapper\Dbal\DbalCollection::$users když se snažím získat u tagů users.
Hádám, že to bude tím, že se snažíš číst property entity z kolekce. Pokud sis třeba vytáhl jeden tag pomocí findById, tak to je špatně a měl bys to dělat pomocí getById. Pokud si dával nad repositářem findAll nebo findBy, aby ti to vrátilo víc záznamů, tak si to musíš buď fetchnout nebo projít foreachem a až pak číst $users.
Moc díky, pomohlo. Je nějaká možnost jak definovat spojovací tabulku? Tzn. aby ta tabulka nebyl users_x_tags ale users_tags? Díky
- hrach
- Člen | 1838
@Vastlik https://nextras.org/…/conventions dole
Asi by ti pomohlo procist celou dokumentaci ;)
- hrach
- Člen | 1838
@Spectator zkus toto, a sorry za přehlédnutí.
/**
* Class UserHasCode
* @property mixed $id {primary-proxy}
* @property \DateTime $created {default now}
* @property Code $code {m:1 Code::$userHasCode} {primary}
* @property User $user {m:1 User::$userHasCode} {primary}
*/ class UserHasCode extends Entity { }
- stpnkcrk
- Generous Backer | 190
Když mám následující entitu (výřez)
/**
* @property int $id {primary}
* @property OneHasMany|Child[] $children {1:m Child::$litter, orderBy=gender}
*/
final class Litter extends Nextras\Orm\Entity\Entity
{}
a používám možnost výchozího řazení orderBy=gender
, dá
se v tomhle místě nějak řadit podle dvou sloupců?
Zkoušel jsem ... orderBy=gender, orderBy=name
, ale nefunguje
(řadí podle posledního orderBy
).
- Jan Tvrdík
- Nette guru | 2595
@skocourek Myslím, že to nejde momentálně zapsat anotací, ale
pokud ručně upravíš odpovídající instanci
PropertyRelationshipMetadata
, tak by to mělo fungovat.
Nejjednodušeji třeba v konstruktoru entity.
/**
* @property int $id {primary}
* @property OneHasMany|Child[] $children {1:m Child::$litter}
*/
final class Litter extends Nextras\Orm\Entity\Entity
{
public function __construct()
{
parent::__construct();
$this->metadata->getProperty('children')->relationship->order = [
['gender' => 'ASC', 'name' => 'DESC'],
NULL
];
}
}
- hrach
- Člen | 1838
@skocourek na tvuj pozadavek urcite mrknu, asi to fakt nejde, kdyztak dodelame.
@JanTvrdík to nebude fungovat dobre, protoze entity z db vytazene nevolaji constructor. Asi by to chtelo pouzit event, ikdyz ted z docky to nevypada, ze by nejaky byl pro oba usecasy volany.
Ja to driv bez property resil tak, ze si clovek vytvori vlastni tridu pro
drzeni relationship a pretizi https://github.com/…/HasMany.php#…
metodu applyDefaultOrder, a pak nastavi pomoci {container}
anotace
tuto tridu danymu relationship. Protoze to bylo tezkopadne, vznikla podpora pres
anotaci, ktera ocividne neni dokonala.
- hrach
- Člen | 1838
@skocourek podpora pro defaultni razeni vice sloupci implementovana v masteru, viz. https://nextras.org/…elationships
- stpnkcrk
- Generous Backer | 190
@hrach Díky, perfektní, vyzkouším! Ještě jsem dneska objevil jednu věc, se kterou si nevím rady…
Mějme vztah m:n, ale kvůli potřebě extra dat ve vazební tabulce to řeším jako 2×1:m (výřez entit)
/**
* @property int $id {primary}
* @property OneHasMany|AnimalCertificate[] $certificates {1:m AnimalCertificate::$animal, orderBy=order}
*/
final class Animal extends Nextras\Orm\Entity\Entity
{}
/**
* @property int $id {primary}
* @property string $name
*/
final class Certificate extends Nextras\Orm\Entity\Entity
{}
/**
* @property int $id {primary-proxy}
* @property Animal $animal {m:1 Animal::$certificates} {primary}
* @property Certificate $certificate {m:1 Certificate, oneSided=true} {primary}
* @property int $order {default 0}
*/
final class AnimalCertificate extends Nextras\Orm\Entity\Entity
{
}
Dá se nějak udělat tohle? Níže zkoušený zápis nefunguje, ale třeba to jen zkouším blbě…
$animal->certificates->get()->fetchPairs(null, 'certificate->name')
- hrach
- Člen | 1838
@skocourek neda :( ale uz je na to dlouho issue :D https://github.com/…m/issues/147
- Vastlik
- Člen | 58
Ahoj, mám DB a v ní tabulku m:n. Je nějaká možnost přistupovat
dalším sloupcum, které jsou tam? Je tam např. created nebo state. Napadlo
mě to udělat si tu tabulku jako entitiu s vazbami 1:n a 1:m, ale mám
problém v tom, že v té tabulce není primární klíč.
Je nějaké mozne řešeni krom přesunutí techto sloupců do jiné tabulky
nebo přidání primárního klíče? Díky za radu.
- hitzoR
- Člen | 51
Vastlik napsal(a):
Ahoj, mám DB a v ní tabulku m:n. Je nějaká možnost přistupovat dalším sloupcum, které jsou tam? Je tam např. created nebo state. Napadlo mě to udělat si tu tabulku jako entitiu s vazbami 1:n a 1:m, ale mám problém v tom, že v té tabulce není primární klíč.
Je nějaké mozne řešeni krom přesunutí techto sloupců do jiné tabulky nebo přidání primárního klíče? Díky za radu.
Já ten vztah m:n s parametrem používám jako normální entitu se dvěma 1:m vztahy, kde je kompozitní primární klíč z IDček obou stran vztahu.
/**
* @property int $id {primary-proxy}
* @property ManyHasOne|Competition $competition {primary} {m:1 Competition::$admins}
* @property ManyHasOne|User $user {primary} {m:1 User::$admining}
* @property string $position
*/
class CompetitionAdmin extends Entity
{
}
- Petr Kubeš
- Člen | 2
Ahoj,
mám entity:
/**
* User
*
* @property int $id {primary}
* @property string $email
* @property OneHasMany|Friendship[] $myFriendships {1:m Friendship::$user}
* @property OneHasMany|Friendship[] $otherFriendships {1:m Friendship::$friend}
*/
class User extends Entity
{
}
/**
* Friendship
*
* @property int $id {primary}
* @property User $user {m:1 User::$myFriendships}
* @property User $friend {m:1 User::$otherFriendships}
* @property DateTime $createdAt {default now}
*/
class Friendship extends Entity
{
}
CREATE TABLE IF NOT EXISTS `users` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
`email` varchar(255) NOT NULL UNIQUE ,
) ENGINE='InnoDB';
CREATE TABLE IF NOT EXISTS `friendships` (
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`user_id` int(11) unsigned NOT NULL,
`friend_id` int(11) unsigned NOT NULL,
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT `friendships_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`),
CONSTRAINT `friendships_ibfk_2` FOREIGN KEY (`friend_id`) REFERENCES `users` (`id`)
) ENGINE='InnoDB' DEFAULT CHARSET=utf8;
Když spustím následující kód:
$friendship = new Friendship();
$friendship->user = $this->orm->users->getById(1);
$friendship->friend = $this->orm->users->getById(2);
$this->orm->friendships->persistAndFlush($friendship);
tak to vygeneruje dotaz:
INSERT INTO `friendships` (`user`, `friend`, `created_at`) VALUES (1, 2, '2016-10-14 16:59:52')
čekal bych, že to bude user_id a friend_id. Má to tak být, nebo mám někde chybu? Děkuji za odpověď.
- hrach
- Člen | 1838
Verze 2.2.0 je venku. Vsichni, co pouzivate verzi 2.1.0 prosim zkuste co nejdriv upgradovat, ve 2.1.0 jsou vazne problemy s nacitanim relationship & perzistenci. https://github.com/…s/tag/v2.2.0
- Hanz25
- Člen | 38
Ahoj,
upgradoval jsem na 2.2.0 a zatím je všechno super. Funguje jak má – dobrá práce. Jen bych se chtěl více doptat na mazání entit, protože to mi pořád už od předchozích verzí trochu blbne.
Dejme tomu, že mám entitu projekt, poté projekt má určité tasky tzn. jeden Pojekt má eN Tasků a na ty tasky jsou navázány všechny možné další entity (soubory, komentáře, tagy apod.) takže další vztahy 1:m i m:n.
/**
* Project
* @property int $id {primary}
* @property string $name
* @property Tasks[]|NULL $tasks {1:m Task::$project}
*/
/**
* Task
* @property int $id {primary}
* @property Project $project {n:1 Project::$tasks}
* @property string $name
* @property Topic|NULL $topic {1:1 Topic::$task, isMain=true, cascade=[remove]}
* @property Attachment[]|NULL $attachments {1:m Attachment::$task, cascade=[remove]}
...
*/
A když se pokouším smazat Task kódem
$task = $this->orm->tasks->getById($taskId);
$this->orm->tasks->removeAndFlush($task);
tak mi vyskočí
Nextras\Dbal\NotNullConstraintViolationException
Column ‚project_id‘ cannot be null
protože z nějakého důvodu se to snaží před smazáním ještě uložit. Takže tady nevím jestli jsem na něco nezapomněl, nebo jestli se jedná o chybu.
A ještě by mě zajímalo, jak by se měly v Nextras/ORM obecně mazat entity s nějakými dětmi. Protože v dokumentaci je, že stačí dát jako druhý parametr metody remove() true, což se ovšem už nemusí dělat, protože true je tam defaultně. A jaký je případný rozdíl mezi tímto řešením (true v remove metodě) a ještě přidáním cascade=[remove] do entity. Nebo to cascade je nějaká vnitřní podpora pro databázové cascade navázané na cizí klíče? Ta logika může být i v databázi, ale raději bych ji teda měl v ORMku…
Díky :)
- hrach
- Člen | 1838
@Hanz25
obecne:
- ORM pri odstraneni entity se snazi vynulovat vsechy ukazatele, ktere na ni ukazuji. (Mazu autora, snazi se vynulovat book.author_id)
- Pokud je dany vztah oznacen jako cascade=[remove], tak se misto nulovani rovnou odstrani i entita, ktera dany ukazel ma. Tj. pri smazani autora se odstrani i kniha, pokud v phpdoc u autora je nastavena kaskada.
- Pokud vztah neni kaskadovy a zaroven dany ukazatel nema povoleno byt null → je vyhozena vyjimka.
- parametr u metody remove zapina, jestli se ma resit kaskadove mazani. Pokud
je
false
, Orm nebude mazat a ukladat zadne jine entity, pouze vynuluje property u ukazatelu a je tvou odpovednosti, abys je persistnul – tyto zmeny. Toto je dost specialni chovani, ktere muzes chtit pouzit v extremnich pripadech, proto neni vychozi. A dokumentace neni prilis dobra a aktualni – upravim.
K tvemu kodu:
- mel bys upravit relationship u projektu, aby tasky smazal kaskadou,
tj.
* @property Tasks[]|NULL $tasks {1:m Task::$project, cascade=[persist, remove]}
- mas definovanou kaskadu jen na remove, ale dost pravdepodobne ji chces zachovat i pro persist, coz je vychozi chovani! Viz https://nextras.org/…elationships#…
Editoval hrach (27. 12. 2016 9:17)