Práce s entitama v Database\Context
- Tirus91
- Člen | 199
Měl jsem ten nápad jak napsat správně entitu a zároveň aby sem s ní mohl pracovat. Co jsem koukal, tak asi k tomu je potřeba nějaký Mapper. Může mi tu někdo ve stručnosti poradit co a jak? popř. nakopnout správným směrem?
Co taková entita by měla správně obsahovat (atributy a metody)?
Mám nastřelenou např. tuto entitu
<?php
namespace Tirus\Entity;
class UserEntity {
/**
* @var int Identificator of user
*/
protected $id;
/**
* @var string User login
*/
protected $username;
/**
* @var string User password
*/
protected $password;
public function getId() {
return $this->id;
}
public function getUsername() {
return $this->username;
}
public function getPassword() {
return $this->password;
}
public function setUsername($username) {
$this->username = $username;
}
public function setPassword($password) {
$this->password = $password;
}
}
Když se podívám na Model: Entity-Repository-Mapper tak z toho nejsem nějak moc moudrej :(
Editoval Tirus91 (30. 7. 2014 18:30)
- Šaman
- Člen | 2666
Zvláštní, teď jsem zkusmo pustil pár projektů, jak na čistém Nette a
NDBT, tak nad Dibi a LeanMapperem.
Čisté Nette zabírá podle Tracy okolo 5MB, Dibi + LM 6,5MB.
Jinak NDB(T) skutečné entity nezná, to co vrací je jen chytřejší pole.
Není to ORM, je to databázová vrstva.
To, co potřebuješ ty, je ORM, zkus pohledat.
Za sebe doporučuji Dibi a LeanMapper, jeho hlavním kamenem úrazu je
v současné době… chvilka napětí… ano, dokumentace. Na to jsme si ale
asi už zvykli…
- Tirus91
- Člen | 199
Šaman: tak zkouším jak si poradil.. no ale problém je v tom, že když do konstruktoru svého repozitáře chci předat instanci \DibiConnection tak mi to zařve
Argument 1 passed to LeanMapper\Repository::__construct() must be an instance of LeanMapper\Connection, instance of DibiConnection given, called in C:\wwwroot\htdocs\nette_tirus_eu\app\FrontModule\presenters\HomepagePresenter.php on line 26 and defined
a jako druhý parametr je IMapper, to v dokumentaci taktéž chybí, takže netuším co s tím…
Jinak, v projektu mám toto:
"require": {
"php": ">= 5.3.7",
"nette/nette": "~2.1.0",
"kdyby/translation": "@dev",
"dg/texy": "2.4",
"dg/dibi": "2.1.1",
"o5/grido": "~2.0",
"tharos/leanmapper": "2.0.1"
}
Casem vyhodím Grido, ale v tuto chvíli u mne na localhostu to bere 13.76MB
Editoval Tirus91 (30. 7. 2014 22:23)
- Šaman
- Člen | 2666
Nad aktuálním Nette, Dibi a LM mám tenhle projekt, ale už
není určený ke studijnim účelům, takže si zkouším experimentovat třeba
s adresářovou strukturou a umistěním config souborů. Ukázku konfigurace
najdeš
tady, samotné pole %database%
je v lokálním
configu. Mapper a ostatní třídy jsou v /app/model/data/ORM. Ty
potřebuješ ale jen ten mapper (ctí stejná pravidla, jako NDB) a pokud chceš
lehce magický repository, tak ten (umí vygenerovat magicky getByXxx(),
findByFooAndBar() metody, pokud je uvedeš v anotaci). Entity a Collection jsou
ve fázi pokusů, skoro bych ti doporučil dědit od originálních LM\Entity
(v tom případě v repository zakomentuj hned první metodu createCollection,
ta se váže k té mé vlastní Collection).
Druhá možnost je stáhnout LM 1.3, pro kterou je psaná dokumentace. Na základní psaní a vyzkoušení ORM stačí a pokud pak budeš chtít nově přidané (a nezdokumentované) fičury, tak budeš mít už nějakou motivaci k upgrade. Zpětná kompatibilita je poměrně silná, takže přepsat projekt ze starého na nový LM je docela v pohodě.
P.S. Kdyby se v tom projektu snažil rozkoukat, jak jediná větší změna je, že všechny šablony jsou hned u svého presenteru, layout se hledá taky tam pak u všech předků. Následně se tedy mohou přesouvat celé adresáře presenterů i se svými šablonami libovolně kamkoliv, kam vidí RobotLoader. Stará struktura ale funguje taky, takže si to klidně popřesouvej podle zvyklostí v Sandboxu. Stejně tak ty configy můžeš prostě zkopírovat do jednoho.
P.P.S. Jo a lokální config je v ignorelistu, ten si vytvoř překopírováním a přejmenováním z toho example. A v databázi chybí uživatelé, zatím neni jak je přidat. Prostě je přidej v Adminerovi a v authenticatoru zakomentuj kontrolu hesla.
- Tirus91
- Člen | 199
@Šaman ok, díky moc, zítra na to mrknu, momentálně jsem si to aktualizoval na Nette 2.2.2 a nahrál klasické poslední Dibi a leanMapper a zbavuji se Kdyby/translate (po odebrání to má už jen 8,5MB v paměti – ach ten žrout).
Mrknu na to. Defakto ani nevadí, když klasické DibiConnection nevyužiju, ale budu už jen využívat LeanMapper\Connection?
A zda to ještě chápu.
Repository – jen obsluhuje to propojení Connection a ostatního
Entity – entita objektu
Mapper – namapuje DB objekt do entity
?
- Šaman
- Člen | 2666
Ty to DibiConnection potrebuješ, LM na něm staví. Jenom nevytváříš přímo instanci DibiConnestion, ale mírně rozšířeného potomka. Pokud bys někam jinam potřeboval to DibiConnection, klidně tam předej to LM\Connection.
Repository – práce s db, ale i specializované vyhledávací a filtrovací dotazy. Nad obyčejnou NDBT vypadá třeba takhle.
Entita je plnohodnotná entota z ER diagramu. I ona ale může na pozadí
komunikovat s db, když si donačítá vazby (mám-li už načtenou entitu
autora, tak volání $author->getBooks()
mi položí další
dotaz).
Mapper řeší pravidla převodu objektového PHP na konvence relační
databáze. Takže výše uvedená vazba je třeba realizována pomocí sloupce
id_author
v tabulce book
. A to ví mapper. Pokud
budeš používat jiné konvence, uprav jen mapper, ostatním je to většinou
jedno (teda, pokud nevytváříš nějaké nízkoúrovňové hacky –
i z repository i z entity teoreticky můžeš volat i čistou query).
Ta moje Collection je kolekce vrácených entit (třeba všech knih autora), originál LM to dřív vracel v poli, dnes nevím, jestli si nezavedl vlastní objektovou kolekci. Upravit se to dá snadno.
- Tirus91
- Člen | 199
@Šaman tak si troufám říct, že se mi to podařilo pochopit jen na nižší úrovni.
Zkusil jsem si to sepsat do vlastního a jedu zatím tabulky Topic a
Article
Když do presenteru dám
use \Tirus\TInjectTopicRepository;
use \Tirus\TInjectArticleRepository;
tak po následném zavolání metody get na articleRepository dostanu Exception
Trait method injectUserRepository has not been applied, because there are collisions with other trait methods on App\Presenters\HomepagePresenter
také v Netbeans to nezná trait ani use v objektu. Co dělám špatně?
Našel jsem to :) špatně jsem si pojmenoval metody v těch Inject repositařích :)
@Šaman prosím jak na findBy ? zkoušel jsem ji předat pole array(‚username=%s‘,‚tirus‘) ale to nefunguje
Editoval Tirus91 (31. 7. 2014 19:09)
- Šaman
- Člen | 2666
- traity – tohle není nutné, ale mě to přijde přehledné. Obsah traity můžeš překopirovat do Presenteru, nebo, pokud ti nevadí public property, můžeš repozitáře injectovat rovnou přes anotaci @inject. Nesouvisí s LM.
- Metodu findBy()
si definuji v obecném repository, samo LM také nic takového nezná. Už ale
nejsme na úrovni databáze, takže se předává přímo pole
findBy(['username'=>'tirus']);
. Případně si rovnou v repozitáři vytvoříš zkratkufindByUsername($username)
, pak ti ji to bude i napovídat.
Ještě bych rád podotkl, že to moje Repository vrací u get
jediný záznam, nebo NULL (a pokud jich najde víc, tak hlásí chybu) a
find
vrací kolekci, resp. pole entit, i kdyby byla jen jedna,
nebo žádná. Jiná konvence používá třeba find()
a
findOne()
, vyber si co ti vyhovuje. Z toho mého repozitáře
stačí vzít jen Mapper a ukázku konfigurace, pak máš čistý LM a nebudou
tě mást moje nezdokumentované vychytávky.
P.S. Pomocí magie stačí přidat anotaci
a metodu findByUsername($username)
můžeš rovnou používat. Jako
klíč se bere řetězec za findBy a hodnotu předáš parametrem. Aby to ale
nebyla černá magie, tak je vyžadována ta anotace, pomocí které zvládá
IDE napovídat a z kódu je jasné, jaké metody ten který repozitář umí
zpracovat.
Editoval Šaman (31. 7. 2014 23:01)
- Tirus91
- Člen | 199
@Šaman
super, už mi to dost frčí :)
jen se tu peru s určitýma ptákovinama, kterýma si myslím že bych to
mohl zacyklit.
Mám články, témata a komentáře.
Jak mám napsat entitu aby mi to vybralo k článku i téma i všechny jeho
komentáře? Téma jsem dokázal suprově, ale komentáře ne.
Nebo jak mám vyhledat všechny články k danému tématu když nevím ID
ale název?
Jak mohu dát podmínku na getBy kde datum začátku je <= aktuálnímu? vždy mi to dává rovno.
- Šaman
- Člen | 2666
- Podle toho, jak vypadá ER diagram. Jestli se komentáře vážou k článku, tak by to měla být vazba 1..n, tu máš třeba tady a pokud budeš chtít najít i článek podle komentáře, tak popiš i druhou stranu vazby. To už jsi ale v LeanMapperu a to je zdokumentované.
- Nezapomeň, že pracuješ objektově. Zapomeň na databázi, jsi na objektové straně ORM. Takže články hledáš přes objekt téma a objekt téma si už podle názvu najdeš. Proto jsou u mě repozitáře i entity prakticky prázdné. Nepotřebuju selectit články podle jména tématu, využívám vazbu články-téma a metody get a find jen pro vrácení základní entity, od které se odpíchneš. Jestli ti to připadá neefektivní, tak věz, že to je cena za možnost pracovat jen s objekty a zapomenout na SQL. A není to tak neefektivní, protože LM dokáže dotazy optimalizovat. Pokud by opravdu bylo potřeba optimalizovat na čas, nebo počet dotazů, pak je lepší psát si repository ručně a nepoužívat ORM. Ale potřeba to není, dokud nepíšeš třeba xchat.
Editoval Šaman (31. 7. 2014 23:39)
- Šaman
- Člen | 2666
Napiš si vlastní matodu v repository. Pomocí té mé to neuděláš, protože ta předává do where jen jeden parametr (klidně pole) a ty potřebuješ dva.
<?php
/**
* Vrátí kolekci entit podle …
*
* @param DateTime $date
* @return Collection
*/
public function findOlderThen($date)
{
$entities = $this->connection->select('*')
->from($this->getTable())
->where('[date] <= %d', $date) # nebo jak se to napíše v Dibi fluent
->fetchAll();
return $this->createEntities($entities); # tohle jen pokud používáš ty moje kolekce, v čistém LM to tam nepatří
}
?>
Editoval Šaman (1. 8. 2014 0:22)
- Šaman
- Člen | 2666
Tak jsem provedl kompletní revizi kódů modelu. Mapper bez problémů, ty ostatní třídy nefungovaly, jak měly. Výsledek je v repozitáři.
Dále jsem napsal jednoduchou extension. Na ní je důležité, že umí
zapínat profiler, tedy dumpováni sql dotazů. Pokud ji použiješ, tak
pozor – změnil se config.local.neon, uprav ho podle examplu.
Extension umí i nastavit mapper a entityFactory, pokud je uvedeš. tahle
část je u mě v configu modelu.
Co se týče toho předávání do where
více parametrů, tak
to půjde zrealizovat až v PHP 5.6, takže snad brzy. Do té doby to nechám
tak, jak je.
Jo a zjistil jsem, že anotace pro magické metody není potřeba, ale důrazně ji doporučuji jednak kvůli napovídání, druhak aby třída netajila své API a za třetí to asi časem dopíšu a pak by magické metody pro které není napsaná anotace přestaly fungovat.
- Šaman
- Člen | 2666
Měnil jsem:
- Collection: odstranil jsem _toArray, protože není funkční jak má, a zrušil jsem fetchPairs, kteá se ale může hodit. Nicméně není potřeba ji aktualizovat, stará funguje bez problémů.
- Entity, Repository: zrušil jsem createCollection, tahle metoda patří do EntityFactory.
- Vytvořil jsem EntityFactory. Tím se i na aktuálním LM skutečně vytváří arrayObjekt Collection místo obyč pole. Tohle s novým LM nefungovalo.
- Repository: zrušil jsem simulaci toho, že dědí od Nette\Object. Takže pokud bys v repozitáři používal Nette\Object fičury, tak si je tam zase vrať. Ale pro běžnou práci nejsou nutné a to vykopirování půlky N\Object kódu a vlepení do Repository nebylo vůbec hezké. Teď nemá žádnou závislost na Nette.
- Extension – přesně, je to vykopírovaná DibiExtension. Není potřeba ji používat, starý ruční přístup funguje v pohodě, ale extension je hezčí a hlavně umí zapnout db profiler.
P.S. Dočasně je v Entity zrušená možnost předávat místo navázané
entity její $id. Takže třeba $book->author = 10;
Je to proto,
že v novém LM mi kód téhle fičury nefungoval. Položil jsem dotaz
na DibiFórum, až mi @Tharos odepíše, tak tuhle věc vrátím. Docela
ji používám.
Obecně – jestli ti fungoval kód i se včerejšími soubory, bude nejspíš fungovat i dnes, protože jsem zrušil jen věci, které stejně nefungovaly nad aktuálním LM a buď jsem je opravil, nebo prohlásil býti balastem. Ten projekt je už totiž docela starý a ty base třídy pamatují ještě LM 1.3.
P.P.S. Asi udělám LM Sandbox, abych dalším zájemcům usnadnil začátky a nemuseli se prokousávat projektem, co má vlastní strukturu.
- Šaman
- Člen | 2666
@Tirus91 Další update, tentokrát jen přidává fičury.
- Entity opět umí pracovat s id navázanych entit (takže je možné psát
$book->author = 1;
namísto$book->author = $author;
). - Entita i kolekce má metodu
toArray()
pro přehledné dumpování. Místo rekurzivního vypisování navázaných entit (které by snadno vedlo k zacyklení) se vypisují idčka entit.
To je prozatím všechno, tím považuji třídy v adresáři ORM(*) za vyzkoušené a finální. Připravuji composer balíček, další změny bych směřoval tam.
(*) Odkazuji na nový projekt, Skeleton, který používá vnořené namespace. Původní projekt má všechny své třídy v jediném NS.
P.S. Možná dojde ještě k jedné aktualizaci – teď natvrdo považuji
za primární klíč 'id'
. Chci to nahradit něčím, co si ten PK
zjistí a pak by mohl mít i jiný tvar. API se už ale měnit nebude.
Editoval Šaman (5. 8. 2014 5:33)
- Šaman
- Člen | 2666
Collection
je jen ArrayObject
nad vrácenými daty
a nyní navíc umí toArray()
. Můžeš si do ni dopsat další
metody, třeba podporu řazení, filtrování, nebo jsem tam měl třeba
fetchPairs() pro naplnění selectboxů.
Originál LM vrací rovnou pole entit, takže se s výsledkem nedá dělat nic,
než jej iterovat.
P.S. Moje toArray()
nevytvoří pole entit, ale převede celou
Collection i s entitama na čitelné pole vhodné k dumpování.
Kdybys Collection používat nechtěl, tak jen upravíš EntityFactory, resp. použiješ původní.
- Matey
- Člen | 142
ďakujem za vysvetlenie
pýtal som sa preto, že pri entite Article ($article->toArray()) mi to vracalo mix všetkého možného, tak som myslel že to zle používam, ale chyba bola v tom že som síce do Entity vložil tvoju metodu toArray(), ale nezmenil som v nej alias pre LeanMapper\Entity as LeanEntity
už môžem spokojne plniť editačné formuláre :)
- Matey
- Člen | 142
@Šaman ako riešiš kontrolu či metoda find* niečo vrátila?
upravil som si tvoju EntityFactory createCollection, je to na tejto úrovni správne?
<?php
/**
* @param Entity[] $entities
* @return Collection
*/
public function createCollection(array $entities) {
return (empty($entities) ? NULL : Collection::from($entities));
}
?>
- Šaman
- Člen | 2666
Není. Kolekce přece může být prázdná. Jestli chceš testovat, zda v ní něco je, tak si ji countni. Ale i prázdná je iterovatelná, countnutelná, dumpnutelná (fuj, to jsou výrazy). :)
S tím NULL si jen přiděláváš problémy. U Entity je to něco jiného – buď je to entita (objekt), nebo to musí být NULL.
Jinak jestli ještě chvilku počkáš, tak právě připravuji composer balíček, odhadem za hodinu by mohl být venku, už jenom testuji.
- Šaman
- Člen | 2666
Takže composer balíček je venku. Zdrojáky jsou na GitHubu.
Pokud používáte composer, stačí přidat řádek:
"require": {
"saman/leanmodel": "~1.0"
}
Veškeré závislosti na Nette (samozřejmě kromě použití extension) jsou odstraněny, balíček je komentován a anotován anglicky, licence je newBSD. LeanMapper není potřeba v composer.json uvádět, automaticky se přidá nejnovější stabilní kompatibilní. Kaskádově se pak přidá i Dibi, které je kompatibilní s tím LM.
Ukázka použití je třeba v tom Todolistu, u kterého to
celé začalo. BC break je nyní nutnost dědit entity a repozitáře od
LeanModel\Entity
, resp. LeanModel\Repository
.
Prakticky stačí do všeh entit a repozitářů vložit use sekci. Trochu se
změnily i konfigurační soubory a přibyla možnost nastavit
$defaultEntityNamespace
přímo v configu.
Otázka na Tharose: Nemáš nic proti zvolenému názvu?
ping @Matey, @Tharos
// edit: změněná verze z ~1.0.0 na ~1.0 – budu držet kompatibilitu v rámci jednotek.
Editoval Šaman (11. 8. 2014 23:41)
- Matey
- Člen | 142
jasné, zistíš čo ti v tom chýba a na to spolieham, že to bude úprava LM s bonusmi pre použitie v Nette :) ja sa zatiaľ bez fetchPairs obídem, lebo som sa pustil do úpravy toho čo už mám hodne škaredo napísané..
a mám taký dotaz k LM :)
nahodil som si LeanModel a teraz postupne prepisujem to čo už mám hotové
z pôvodného LeanQuery(priamo v presenteroch/komponentách)/Repository mixu,
niekde som používal LeanQuery podľa Microsite a inde zas *Repository
práve som takéto niečo prehodil do repository Microsite metóda loadPages()
<?php
// v komponente som získaval stránky takto:
$pages = $this->getPages();
/**
* @return Page[]
*/
private function getPages() {
return $this->domainQueryFactory->createQuery()
->select('p')
->from('App\Domain\Page', 'p')
->where('p.lang = %s', $this->locale)
->getEntities();
}
// prerobil som to a presunul získavanie stránok z repository
$pages = $this->pageRepository->findByLang($this->lang);
//repository:
/**
* @method Page[] findByLang($lang)
*/
class PageRepository extends \LeanModel\Repository {}
?>
skončí to erorom, tabuľka page nemá stĺpec lang
áno nemá, lebo má lang_id, znamená to teda že Repository nemá prístup k definícii Entity?
a tým pádom musím použiť buď niečo takéto:
/**
* @method Page[] findByLang_id($lang)
*/
class PageRepository extends \LeanModel\Repository {}
alebo si metódú napísať normálne:
class PageRepository extends \LeanModel\Repository {
public function findByLang($lang) {
$entities = $this->connection->select('*')
->from($this->getTable())
->where('lang_id = %s', $lang)
->fetchAll();
return $this->createEntities($entities);
}
}
pardón ak je to hlúpa otázka, ale akosi som si myslel že ten Repository berie na vedomie definiciu Entity (pozeral som video o Nextras\Orm a tam bolo práve niečo také spomenuté, je pravda že u videa o LeanMapper som to nezapočul)
- Matey
- Člen | 142
v tom prípade nemám potuchy kde robím chybu :/
áno entita Page má property lang
/**
* @property Lang $lang m:hasOne
*/
class Page extends Entity {}
a entita Lang
/**
* @property string $id
*/
class Lang extends Entity {}
všetky entity aj repository mám v rovnakom namespace
class PageRepository extends Repository {
public function findByLang($lang) {
$entities = $this->connection->select('*')
->from($this->getTable())
->where('lang = %s', $lang) // DibiDriverException #1054 - Unknown column 'lang' in 'where clause'
//->where('lang_id = %s', $lang) je vporiadku
->fetchAll();
return $this->createEntities($entities);
}
}
Editoval Matey (9. 8. 2014 19:24)
- Šaman
- Člen | 2666
Podle jiné entity se nedá takhle vyhledávat (@Tharos, nekecám?).
Správný přístup by byl načíst si entitu Lang a pak volat
$lang->pages
.
@method Page[] findByLang_id($lang)
taky funguje, jen tam holt
musíš znát název sloupce.
Z jiného soudku: vytvořil jsem composer balíček
saman/skeleton
, ve kterém je použitá stejná kostra jako
v Todolistu a taky obsahuje LeanModel. Takže stačí
composer create-project saman/skeleton
- vytvořit si databázi a v ní spustit .sql skript
z adresáře
app/model/data
- zkopírovat
config.local.example.neon
naconfig.local.neon
a vyplnit v něm klíčky k databázi
P.S. Pokud budete verzovat, tak config.local.neon je v .gitignore a nechte ho
tam. Jen pokud budete dělat nějaké změny v tomto souboru, tak je
nezapomeňte dopsat i do verzovaného
config.local.example
, jak se často stává mě.
P.P.S. Samozřejmě si databázi můžete udělat vlastní, pak jen upravte
entitu User a případně Authenticator a přihlašovací formulář.
Editoval Šaman (10. 8. 2014 7:36)
- Tirus91
- Člen | 199
@Šaman
Nastal mi menší problém.
Mám definované 2 entity
Tým
zápasy
V zápasech je tým buď domácí nebo jako host. Potřeboval bych ale vybrat entitu týmu a k tomu vypsat všechny zápasy kde tým byl buď jako host nebo domácí. Jak toto provést?
Tým je takto
/**
* @property int $id
* @property string $name
* @property string $trainer
* @property string $trainerphone
* @property string $trainermail
* @property Countries $country m:hasOne
* @property int $playercount
* @property int $accepted
*
* @property Ebtournament $tournament m:hasOne
* @property Ebaccommodation $accommodation m:hasOne
* @property Ebcategory $category m:hasOne
*
* @method Ebteam findBySeasonId($tournament) Vrátí tymy k dane sezone
*/
Zápas takto
/**
* @property int $id
* @property Ebteam $hometeam m:hasOne(hometeam)
* @property Ebteam $visitorteam m:hasOne(visitorteam)
* @property \DateTime|null $matchtime
* @property int $homescore
* @property int $visitorscore
* @property Ebtournament $tournament m:hasOne
*
* @method Ebmatch findByEbtournament_id($tournament) Vrátí zapasy dle turnaje
*/
- Šaman
- Člen | 2666
Tak to už jsou otázky na @Tharose. Já jsem rád, že zvládám běžné použití. Ale mělo by to jít tak, že si dopíšeš metodu třeba getAllMatches(), kde si vytáhneš obě ty property, spojíš je (kolekce by se měla chovat jako pole, i když nevím, jestli budou fungovat array_ funkce, třeba právě ta array_merge) a z nich vytvoříš jednu kolekci.
- Tirus91
- Člen | 199
@Šaman
Zkusil jsem to tak jak si psal předtím. No asi nejsem moudrej :(
/**
* @property int $id
* @property string $name
* @property string $trainer
* @property string $trainerphone
* @property string $trainermail
* @property Countries $country m:hasOne
* @property int $playercount
* @property int $accepted
*
* @property Ebmatch $visitorteam m:belongsToMany(visitorteam)
* @property Ebmatch $hometeam m:belongsToMany(hometeam)
* @property Ebtournament $tournament m:hasOne
* @property Ebaccommodation $accommodation m:hasOne
* @property Ebcategory $category m:hasOne
*
* @method Ebteam findBySeasonId($tournament) Vrátí tymy k dane sezone
*/
a dostanu
Property 'visitorteam' with HasMany or BelongsToMany in entity Tirus\Entity\Ebteam relationship must contain collection
Dám si rovnou facku :)
vyřešeno, má to být
/**
* @property int $id
* @property string $name
* @property string $trainer
* @property string $trainerphone
* @property string $trainermail
* @property Countries $country m:hasOne
* @property int $playercount
* @property int $accepted
*
* @property Ebmatch[] $visitorteam m:belongsToMany(visitorteam)
* @property Ebmatch[] $hometeam m:belongsToMany(hometeam)
* @property Ebtournament $tournament m:hasOne
* @property Ebaccommodation $accommodation m:hasOne
* @property Ebcategory $category m:hasOne
*
* @method Ebteam findBySeasonId($tournament) Vrátí tymy k dane sezone
*/
Editoval Tirus91 (11. 8. 2014 23:12)
- Tirus91
- Člen | 199
@Tharos
nijak jsem si s tím nehrál, protože jsem nijak extra do toho zatím
nepronikl.
Jinak
-- Adminer 4.1.0 MySQL dump
SET NAMES utf8;
SET time_zone = '+00:00';
SET foreign_key_checks = 0;
SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO';
DROP TABLE IF EXISTS `countries`;
CREATE TABLE `countries` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`countrycode` varchar(2) NOT NULL DEFAULT '',
`countryname` varchar(100) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `ebaccommodation`;
CREATE TABLE `ebaccommodation` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(200) NOT NULL,
`description` text NOT NULL,
`active` enum('0','1') NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `ebcategory`;
CREATE TABLE `ebcategory` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(200) NOT NULL,
`description` text NOT NULL,
`active` tinyint(4) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `ebmatch`;
CREATE TABLE `ebmatch` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`hometeam` int(10) unsigned NOT NULL,
`visitorteam` int(10) unsigned NOT NULL,
`matchtime` datetime NOT NULL,
`homescore` int(5) DEFAULT NULL,
`visitorscore` int(5) DEFAULT NULL,
`ebtournament_id` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `hometeam` (`hometeam`),
KEY `visitorteam` (`visitorteam`),
KEY `ebtournament_id` (`ebtournament_id`),
CONSTRAINT `ebmatch_ibfk_3` FOREIGN KEY (`ebtournament_id`) REFERENCES `ebtournament` (`id`),
CONSTRAINT `ebmatch_ibfk_1` FOREIGN KEY (`hometeam`) REFERENCES `ebteam` (`id`),
CONSTRAINT `ebmatch_ibfk_2` FOREIGN KEY (`visitorteam`) REFERENCES `ebteam` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `ebteam`;
CREATE TABLE `ebteam` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(250) NOT NULL,
`trainer` varchar(250) NOT NULL,
`trainerPhone` varchar(45) DEFAULT NULL,
`trainerMail` varchar(100) DEFAULT NULL,
`playercount` int(11) NOT NULL,
`countries_id` int(10) unsigned NOT NULL DEFAULT '1',
`accepted` tinyint(4) DEFAULT NULL,
`ebtournament_id` int(10) unsigned NOT NULL,
`ebcategory_id` int(10) unsigned NOT NULL,
`ebaccommodation_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `ebteam_fk_1` (`ebtournament_id`),
KEY `ebcategory_id` (`ebcategory_id`),
KEY `ebaccommodation_id` (`ebaccommodation_id`),
CONSTRAINT `ebteam_ibfk_1` FOREIGN KEY (`ebcategory_id`) REFERENCES `ebcategory` (`id`),
CONSTRAINT `ebteam_ibfk_2` FOREIGN KEY (`ebaccommodation_id`) REFERENCES `ebaccommodation` (`id`),
CONSTRAINT `ebteam_ibfk_3` FOREIGN KEY (`ebtournament_id`) REFERENCES `ebtournament` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `ebtournament`;
CREATE TABLE `ebtournament` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL DEFAULT '0',
`startdate` datetime DEFAULT NULL,
`enddate` datetime DEFAULT NULL,
`active` tinyint(4) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `year` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 2014-08-12 15:30:24
Editoval Tirus91 (12. 8. 2014 19:13)
- Šaman
- Člen | 2666
Extense v LeanModelu v 1.0.3 umí registrovat filtry, pokud splňují jednoduché rozhraní (*) Ukázka v Todolistu.
(*) Protože nevím, jak v extension ověřit 'instanceof'
, tak
ono rozhrani splňovat nemusí. Musí mít jen metodu
registerFilters($connection)
, která se zavolá pro každou třídu
filtrů. Pokud tam nebude, vyskočí laděnka. Ale doporučuji to rozhraní
používat, protože se tam ta chybějící podmínka může později objevit,
abych mohl vypsat srozumitelnou chybovou hlášku.
P.S. Pro ještě jednodušší použití filtrů existuje třída LeanModel\DefaultFilters
,
která obsahuje prozatím filtry limit(int $count)
,
where(array $by)
, is(string $column) a not(string $column). Pokud
bych si ji zaregistroval, tak nemusím psát vlastní filtr a filtrování
aktivních tasků bych provedl pomocí 'm:filter(not#done)'
.
Doplnění: Při registrování tříd filtrů už není potřeba uvádět název – dokonce je možné kombinovat pojmenované a nepojmenované třídy (od verze 1.0.4)
leanModel:
defaultEntityNamespace: Todolist
mapper: LeanModel\Mapper
entityFactory: LeanModel\EntityFactory
filters:
- Todolist\TaskFilters
- def: LeanModel\DefaultFilters
Editoval Šaman (14. 8. 2014 13:22)
- Tharos
- Člen | 1030
@Tirus91: Pokud potřebuješ spojit kolekce ze dvou
vazeb, nejsnazší je udělat to ve vlastním getteru pomocí
array_merge
nebo podobně.
Pokud bys to chtěl spojit už na úrovni databázového dotazu, taky to lze, ale musíš si pak při tom traverzování položit vlastní dotaz. Tady se řešilo něco podobného, ale bohužel to vyžaduje určitý vhled do toho, jak LM vnitřně funguje :(. Asi napíšu ještě nějakou jednodušší ukázku…
- Tharos
- Člen | 1030
@Tirus91: Já jsem věděl, že už jsem něco podobného dříve s někým řešil… :)
https://github.com/…er/issues/53
Jak je vidět z těch řešení, nejsnazší je opravdu spojit si to (a eventuálně i seřadit) v paměti. Já osobně bych volil „databázové“ řešení až v momentě, kdy by to přinášelo nějakou zásadní výhodu.
- Tirus91
- Člen | 199
@Tharos @Šaman
Mám na vás ještě jeden dotaz. Narazil jsem na situaci kdy bych potřeboval
udělat vazbu N:M
Definice pomocí anotací
Našel jsem jen M:N 1:N a N:1 a 1:1
M:N mi udělá následující query
SELECT `user`.*
FROM `user`
SELECT `user_role`.*
FROM `user_role`
WHERE `user_role`.`user_id` IN (1)
SELECT `role`.*
FROM `role`
WHERE `role`.`id` IN (1, 2, 3, 4)
Editoval Tirus91 (17. 8. 2014 19:46)
- Šaman
- Člen | 2666
N:M je totéž, jako M:N, jenom je z pohledu té druhé tabulky potřeba
explicitně říct přes jakou tabulku se bude hledat. Nebo to nastavit ručně
v mapperu podle toho, ze které strany hledáš.
Mapper totiž při traverzování od usera správně určí spojivací tabulku
‚user_role‘ , ale při traverzování od role určí špatně
‚role_user‘.
- Tirus91
- Člen | 199
@Šaman
díky, blbě jsem si přečetl dokumentaci. Už mi to funguje :)
pro ty co by to také potřebovali
/**
*
* @property int $id
* @property string $name
* @property string $code
* @property User[] $users m:hasMany(role_id:user_role:user_id:user)
* @property string $canbedeleted
*/
Editoval Tirus91 (18. 8. 2014 9:02)
- Tirus91
- Člen | 199
@Šaman @Tharos
Narazil jsem ještě na jednu věc, co není v dokumentaci.
Hodlám udělat něco jako rozpracované články a zde bych rád udělal aby
když si vyberu entitu témat, která má v sobě kolekci článků, abych zde
mohl vybrat jen ty, co nejsou rozpracované.
Vím že když chci získat nově kolekci článků, tak stačí si napsat vlastní findBy a nebo využít např. findByActive(1), ale jak to brát z jiné entity?
- Šaman
- Člen | 2666
Na to slouží filtry. Dokumentace někde v tom dlooooouhém vlákně :)
Ale jestli chceš jenom tohle, tak na to jsou připravené moje DefaultFilters.
Pokud bys měl u článku třeba proprerty $active, tak když píšeš tu vazbu
z jiné entity, přidáš nakonec definice ještě
'm:filter(is#active)'
. K dispozici je is, not, where a limit. Jen
si ty filtry musíš zaregistrovat v extension, viz můj skeleton,
nebo todolist (tam je ukázka vlastního filtru).
Editoval Šaman (18. 8. 2014 23:01)