Nextras\ORM – ORM nad Nextras\Dbal
- batko
- Člen | 219
Ahoj,
řeším následující:
<?php
/**
*
* @property OneHasMany|Book[] $book {1:m Book::$author}
*/
class Author extends Entity {
}
/**
*
* @property Author $author {m:1 Author ::$book}
*
*/
class Book extends Entity
?>
Potřebuji, aby když si vytáhnu entitu Author (pomocí dotazu v mapperu), tak abych mohl nějak dofiltrovat knihy (například knihy jen starší 5ti let) a netvořilo mi to zbytečně mnoho dotazů do DB. Je to možno udělat v mapperu jedním dotazem?
Díky
- goood
- Člen | 26
Zdravím, potřeboval bych poradit. Dal jsem na Zoner hosting aplikaci
v nette používající nextras orm. Bohužel mi to padá na
„Nextras\Dbal\QueryException Unknown or incorrect time zone:
‚Europe/Prague‘“.
Poslal jsem jim odkaz jak to zprovoznit: https://nextras.org/…ysql-support
Podle vyjádření administrátora hostingu není možné toto zařídit. Lze to
tedy v orm nějak obejít? Nebo mám zvolit jiný hosting? Díky moc
Editoval goood (12. 1. 2017 19:27)
- prebijak
- Člen | 21
CZechBoY napsal(a):
@goood Co to je za kravinu? Českej hosting nemá timezonu Prahy?
Spíš nemá žádné informace o časových pásmech, MySQL je ve výchozím stavu neobsahuje: https://nextras.org/…ysql-support
goood napsal(a):
Zatím jsem to obešel zakomentováním řádku přímo v dbal v MysqliDriver.php, ale to se mi teda nelíbí
Nechá se to obejít také nastavením pevného časového pásma: https://nextras.org/dbal/docs/2.1/
Já mám ve své aplikaci
connectionTz: auto-offset
Edit: Ale pokud můžeš, je lepší se tomu vyhnout: https://forum.nette.org/…nextras-dbal?p=3
Editoval prebijak (12. 1. 2017 21:30)
- hrach
- Člen | 1838
Je to jak pisou kluci. auto-offset to vyresi, ale je treba si uvedomit, ze neni pak dobre pouzivat MySQL funkce, ktere pocitaji rozdil mezi dvema datumy. Tento rozdil pak muze byt o hodinu spatne, respektive nerespektovat korektne casove zony. That's all, vetsina aplikaci to stejne nepotrebuje, takze auto-offset muze stacit.
- hrach
- Člen | 1838
Nova bugfix verze 2.2.1 – opravuje dve neprijemne chyby: https://github.com/…s/tag/v2.2.1
- pokud jste pouzili derivovanou kolekci relationshipu
(
$author->books->get()->findBy(...)
), tak pri persistu dane entity se dany relationship nacetl cely ($author->books), coz muze vest k zasadnimu zpomaleni a narazeni na memory limit. - razeni podle datetime property nefungovalo spravne, pokud nejaka z hodnot byla null.
- David Kregl
- Člen | 52
Ahoj,
existuje nějaký způsob, jak seřadit ICollection náhodně? Zkoušel jsem
orderBy('RAND()')
, ale to mi zařvalo.
Díky!
Editoval David Kregl (29. 1. 2017 11:16)
- Hanz25
- Člen | 38
Ahoj,
chtěl bych se zeptat jak se řeší kaskádové mazání ve vztahu m:n. Pokud mám vztah m:n a v definici entity uvedu
cascase[persist, remove]
a tato entita je právě mazána, tak mám pocit, že orm chce smazat entitu i na druhé straně, ovšem já bych potřeboval smazat pouze ten vztah.
Konkrétně: mám articles a tags. A jakmile smažu tag, tak chci zároveň odstranit spojení toho smazaného tagu s článkem, ovšem nechci pochopitelně smazat ten článek.
Zatím to mám vyřešené v databázi, ale chtěl bych tu logiku spíše směřovat k ORM.
A druhá věc: je nějaká možnost přes ORM z databáze vytáhnout hodnotu, která nebude definovaná jako properta entity? Prostě něco, co si spočítám v selectu, ať už by mělo jít o součet nějakých sloupců, nebo hodnotu selectnutou přes IF … THEN … ELSE, nebo jiným způsobem? Ono je sice super, že jakmile bohaté findBy/getBy metody selžou, člověk si může sestavit, nebo napsat vlastní SQL, ovšem vždycky se výsledek hydratuje na entitu, takže tam není šance propašovat nějakou vlastní hodnotu.
Díky.
Btw: jedu na nejnovější verzi a vše ok, je to super. viděl jsem commity na OR condition operator a už se těším na release (+1)
- medhi
- Generous Backer | 255
V aplikacích se velmi často musí ověřovat příslušnost entity
k jiné (například k userovi). Typicky pokud například edituji nebo mažu
článek, musím před akcí provést ověření. Nebylo by ideální to
ověřovat pomocí
$article = $user->articles->get()->getById($articleId);
?
Bohužel metoda getById()
na této kolekci není k dispozici.
Jak to děláte vy?
- Jan Tvrdík
- Nette guru | 2595
Můžeš použít
$user->articles->get()->get(['id' => $articleId])
Nicméně lepší je mít napsané rozumné ACL, takže třeba u nás máme na jednom projektu něco jako
$article = $this->articles->getById($articleId);
$this->requireEntity($article);
$this->requireAccess($article, Authorizator::ACTION_VIEW);
- hrach
- Člen | 1838
@Hanz25
- m:n
- nesmis mit cascade removed ⇒ u vztahu cascade vyjadruje kaskadu na entitu na druhe strane, ne na mezivazebni tabulku
- smazani tagu, kdyz k nemu uz nic nevede – to je funkcionalita remove orphan, zakladam issue: https://github.com/…m/issues/205; do implementace muzes pouzit nejaky event na entite, treba onPersist, mrknout, jestli je vztah modified, a pokud ano, provest patricnou logiku
- select hodnoty, ktera neni property – uplne moc nechapu usecase, nechces uvest priklad? Obecne ORM proste pracuje s entitami, pokud chces vyselectovat neco, co v nich neni, neni problem to rovnou selectnout DBALem.
@medhi
Honza myslel getBy, nicmene prijde mi to jako dobra vec, aby tam ta metoda
byla: https://github.com/…m/issues/204
- JZechy
- Člen | 161
Dá se nějak {enum} říct, že chci validovat hodnotu oproti všem konstantám ve třídě? Z příkladu lze zadávat třeba TYPE_*. Ale když těm konstantám žádný prefix nedávám a zkusím jenom hvězdičku:
/**
* @property type $name {enum EnumClass::*}
*/
class MyEntity extends Entity {
// ...
}
Tak mi validace neprojde. A vypisovat všechny hodnoty do enumu…
Editoval JZechy (19. 2. 2017 19:38)
- hrach
- Člen | 1838
@JZechy diky, fixed a oprava relased jako 2.2.3: https://github.com/…s/tag/v2.2.3 :)
- Hanz25
- Člen | 38
@hrach
select hodnoty, ktera neni property – uplne moc nechapu usecase, nechces uvest priklad? Obecne ORM proste pracuje s entitami, pokud chces vyselectovat neco, co v nich neni, neni problem to rovnou selectnout DBALem.
Aha, ano máš pravdu, tady jsem spíš řešil konkrétní problém, než funkčnost orm. No a jak bych se mohl dostat třeba ke QueryBuilderu? Dá se dostat nějak z ORM, nebo si ho budu muset nějak zaregistrovat v configu zvlášť a pak ho zvlášť žádat přes DI?
Protože třeba v doktríním entity manageru se dostaneš ke query builderu a můžeš si vytvořit vlastní dotaz, který se bude hydratovat na pole.
- hrach
- Člen | 1838
@Hanz25
- pokud chces query builder pro vlastni dotaz, ale pak z toho dostat entity, tak https://nextras.org/…s/2.2/mapper
- pokud chces query builder pro sestavovani vlastniho sql, pak si injectni
Dbal\Connection
a na ni mas metoducreateQueryBuilder()
: https://nextras.org/…uery-builder
- medhi
- Generous Backer | 255
K čemu slouží metoda remove($entity)
na hasMany vztahu? Lze
nějak využít k mazání entit nebo mám raději používat repozitář?
K tomu se vztahuje ještě jeden dotaz. Z laického pohledu mě pořád
přitahuje vyhýbat se repozitářům a občas pracovat pouze s entitami,
protože tam mám rovnou ověřeno, že entita má nějaký vztah. Například
pokud chci editovat článek přihlášeného autora, líbí se mi při editaci
načíst článek pomocí
$author->articles->get()->getBy(['id' => $articleId])
(zde by se mi ještě více líbilo něco jako
$author->articles->getById(['id' => $articleId])
, ale
nevím jestli to dává smysl).
Stejně tak při mazání článku ověřuji, že je autora, čili
$author->articles->remove($article)
(I když zde je asi
správný postup nejdříve ověřit zda mám právo článek smazat, viz
výše, a pak ho smazat rovnou přes
$articleRepository->removeAndFlush($article);
.
Dalším důvodem, proč mě to láká je, že v presentech nevidím na repositář, ale pouze na fasády. A přijde mi divné psát do fasád primitivní funkce, které nemají přidanou hodnotu a pouze kopírují funkci repositáře, například
public function getById($id)
{
return $this->addonRepository->getById($id);
}
Protože fasád mám více a toto bych pak musel opakovat v každé z nich, což není DRY. A BaseFacade se mi nehodí. Možná traitu? Budu rád když mě v tomto nasměrujete správným směrem.
Díky moc
- hrach
- Člen | 1838
K čemu slouží metoda remove($entity) na hasMany vztahu?
K rozpojeni vztahu, ne k smazani entity. Odstrani entitu ze vztahu.
$author->articles->get()->getBy([‚id‘ ⇒ $articleId])
ano, to je docela dobry pristup, sam casto pouzivam.
Stejně tak při mazání článku ověřuji, že je autora, čili $author->articles->remove($article)
ee, to je spatne, to mazes ten clanek ze vztahu, viz. vyse. ty by sis ho mel spravne vytahnout (klidne pres ten get, jak si psal), a pak ho smazat pres repository, model.
proč mě to láká je, že v presentech nevidím na repositář
Osobne nezavadim zadne fasady nad repozitarem, prijde mi to docela overhead. repozitar rovnou injektuji. Kdyz je slozitejsi logika, tak spis zavadim servisy, kterou tu celou slozitejsi logiku zapouzdri.
- Jan Tvrdík
- Nette guru | 2595
Ne, to mi smysl nedává. Spíš bych byl pro, aby author->articles mohlo vracet rovnou kolekci. Současné chování neumožňuje mít immutable relace.
- David Kregl
- Člen | 52
Ahoj,
možná řeším úplně banální věc velmi složitě, ale chci se zeptat na radu.
Mám několik entit: Article
, Person
, ‚Event‘ a
potřebuji pomocí jednoho search baru vyhledávat všechny tyto entity podle
jejich vlastností (title, fullName, desxription, …).
Jak a kde mám položit dotaz, který mi vrátí kolekci těchto všech
entit? Je to vůbec možné?
Jak řešíte vyhledávání ve více typech entit vy?
Díky!
Editoval David Kregl (7. 3. 2017 18:40)
- hrach
- Člen | 1838
@DavidKregl: orm neumoznuje vratit kolekci, ktera by mela vic typu entity. Vzdy muzes selectovat jen z jednoho repository. Nicmene to muzes podminit samozrejme jakymkoliv joinem, nejvic volnosti ziskas v mapperu, kde si muzes rovnou napsat sql. https://nextras.org/…s/2.2/mapper
Editoval hrach (7. 3. 2017 22:48)
- Jan Tvrdík
- Nette guru | 2595
Ono tohle zrovna nejde rozumně vybrat ani jedním SQL dotazem. Tj. stejně jako bych v čistém SQL poslal víc dotazů, tak bych i v ORM se zeptal více repositaru. Nebo změnil organizace dát v DB, aby to šlo rozumně jedním dotazem vytáhnout.
- Lizardor
- Člen | 35
Zdravím, snažím se rozběhat toto ORM a setkávám se s tímto
problémem:
Service 'orm.mappers.blocations': Service of type Nextras\Orm\Mapper\IMapper needed by Nextras\Orm\Repository\Repository::__construct() not found. Did you register it in configuration file?
I po registraci do configu to stále nejede. Držím se struktury z „dema“.
- David Kregl
- Člen | 52
@hrach @JanTvrdík Moc díky za postrčení. Nakonec jsem to
vyřešil následovně, jen by mě zajímalo, jestli existuje nějaký způsob,
jak bych mohl té query předat proměnou $query
, která obsahuje
vyhledáváný string jen jednou. Díky!
$result = $connection->query(
"SELECT a.id, at.title, at.content, 'accomplishment' AS type FROM accomplishment AS a
LEFT JOIN accomplishment_translation AS at ON a.id = at.accomplishment_id
WHERE at.title LIKE %_like_
OR at.content LIKE %_like_" .
" UNION SELECT a.id, at.title, at.content, 'article' AS type FROM article AS a
LEFT JOIN article_translation AS at ON a.id = at.article_id
WHERE at.title LIKE %_like_
OR at.content LIKE %_like_" .
" UNION SELECT m.id, mt.title, mt.content, 'meeting' AS type FROM meeting AS m
LEFT JOIN meeting_translation AS mt ON m.id = mt.meeting_id
WHERE mt.title LIKE %_like_
OR mt.content LIKE %_like_" .
" UNION SELECT p.id, pt.title, pt.content, 'principle' AS type FROM principle AS p
LEFT JOIN principle_translation AS pt ON p.id = pt.principle_id
WHERE pt.title LIKE %_like_
OR pt.content LIKE %_like_",
$query,
$query,
$query,
$query,
$query,
$query,
$query,
$query
);
Editoval David Kregl (9. 3. 2017 23:59)
- Hanz25
- Člen | 38
Ahoj,
řeším hrozně zvláštní problém s ukládáním entit ve vztahu m:n. Dělám ACL a k určité roli ukládám povolené actions, ovšem ten formulář funguje pouze pro jednu roli. V daném presenteru mám persistentní parametr $id, který vybírá určitou roli a zobrazuje k ní různé action, které se dají povolit/zakázat.
Výsledkem z toho formuláře je pole s idčky všech akcí, ke kterým by měl mít uživatel přístup (těch uložených i těch nových) A pak už se děje jen co píšu dole. Selectnu ty actions a uložím.
// Přijímá z formu $allowedActions = [9, 10, 11];
$allowedActionsCollection = $this->orm->actions->findById($allowedActions);
$this->loadRole->actions->set($allowedActionsCollection->fetchAll());
$this->orm->roles->persistAndFlush($this->loadRole);
Když ukládám dané actions k adminovi (specifický je jen idčkem 1 a tím, že je to role, ze které to zkouším), tak funguje vše dobře, ale na jakékoliv jiné roli to nejde. Přesněji řečeno, já té roli předám akce, které tam chci mít, ale z proklikávání laděnky to vypadá, že v RelationshipMapperManyHasMany jsou v jsou připraveny k odstranění/přidání naprosto špatné vztahy. Konečnou je exception Nextras\Dbal\UniqueConstraintViolationException kdy to selže kvůli unikátnímu klíči ve spojovací tabulce: Duplicate entry ‚10–8‘ for key ‚role_id_action_id‘, nebo v druhém případě vše proběhne bez chyby, ale žádný dotaz se na databázi nepošle.
Zkoušel jsem různě izolovat všechno možné, přihlásit se pod jinou rolí, ale vždy funguje pouze admin, což je dost zvláštní chování. Nenapadá vás něco? Díky
- medhi
- Generous Backer | 255
Lze nějak elegantně zkombinovat Nextras\ORM a dynamické snippety? Ty totiž fungují pouze, pokud cyklus, který je vypisuje, kteruje nad polem. Dle dokumentace, pokud chci jeden dynamický snippet překreslit, do takového pole umístím pouze ten jeden prvek a invaliduju obalovací snippet.
U ORM neiteruji při vykreslování nad polem, ale nad objektem relace a z něco nelze prvky odebrat a nechat tam pouze jeden.
- medhi
- Generous Backer | 255
hrach napsal(a):
@medhi to moc mi nedava smysl, dynamicke snippety vubec nejsou zavisle na tom, z ceho je generujes, potrebujes hlavne mit dynamic name, tam klidne muzes davat id z entity
{snippet row-$row->id}
by melo fungovat.
Nemyslel jsem generování, ale spíš invalidaci. Dle nápovědy se dělá takto:
public function handleUpdate($id)
{
$this->template->list = $this->isAjax()
? []
: $this->getTheWholeList();
$this->template->list[$id] = 'Updated item';
$this->redrawControl('itemsContainer');
}
Tedy z pole záznamů se udělá pole o jednom prvku a invaliduje se obalovací snippet.
Pokud používám Nextras\ORM, tak výše uvedená
$this->template->list
není obvykle pole, ale relace
OneHasMany
. Z té ovšem nelze tak jednoduše smazat všechny
„záznamy“ a ponechat pouze jeden. Musím ji převést na pole, což nevím
zda je správný postup.
- Lizardor
- Člen | 35
Chtěl bych se zeptat je možnost porovnávat na základě pouze cizího klíče? Aniž by se musel generovat další (pro mě zbytečný) dotaz? Jelikož mám Entitu kde mám vazbu na druhou Entitu (relace) a při porovnávání už se nepotřebuji znova dotazem ptát na druhou tabulku (chtěl bych to porovnat na základě ID), ale zatím se mi nepovedlo docílit toho aby se mi negeneroval zmíněný zbytečný dotaz. Pokud sem to napsal nějak nesrozumitelně tak se více rozepíšu i s příkladem. Díky.
- meridius
- Člen | 34
Ahoj. Chci se zeptat, jak správně řešit vícenásobné filtrování kolekce vlastními metodami v mapperu.
Mám například vyhledávací formulář s více textovými poli, kde
každé odpovídá jinému sloupci v tabulce. Pro každé vyplněné pole by
měla vzniknout LIKE
podmínka v dotazu. Více podmínek by se
mělo spojovat například AND
.
Problém je, že vlastní funkce v mapperu vrací novou koleci a na té kolekci nemůžu zavolat další funkci v mapperu. Vlastní funkce z mapperu lze tedy volat jen nad celou repository, což zabraňuje řetězení těchto funkcí.
Celý tento problém vznikl, protože do findBy
nad kolekcí lze
dát jen operátory =
, !=
, <=
,
<
, >=
a >
.
Přehlížím něco?
Předem díky