Kdyby/Doctrine best practice

- Oli
 - Člen | 1215
 
Ahoj,
snažím se přejít z Nette database k Doctrine 2 (Kdyby/Doctrine). Něco
jsem si o tom přečetl:
- Doctrine 2 od Honzy Tichýho
 - Bezbolestná integrace Doctrine 2 ORM do Nette – Kdyby/Doctrine
 - https://github.com/…/en/index.md
 - Pět vrstev modelu.
 
Mám v tom ale docela hokej.
Například v Nette database bych si napsal něco jako:
public function getBySlug($slug)
{
	return $this->connection->table('article')->where('slug = ?', $slug)->fetch();
}
Kam a jak napsat tohle pomocí Doctrine 2? Používá se na to standardně
->findBy(array()); nebo se to něčím někde obaluje? Kde bych
měl například napsat tu metodu getBySlug? Přímo do tý entity?
Logické mě přijde podědit \Kdyby\Doctrine\EntityDao a dat tu
metodu tam, ale HosipLan píše, že by se DAO
nemělo dědit.
Z toho plyne otázka kde a jak pracovat s entitou. Mělo by se s ní pracovat v presenteru?
public function actionDefault()
{
	$articles = $this->em->getDao(\App\Article::getClassName());
	$article = new \App\Article();
	$article->title = "The Tigger Movie";
	$articles->save($article);
}
Kde potom určím, kam se bude entita ukládat (např. cache)?
Zatím to mám takhle (ale nepřijde mě, že to je
správně):
config
doctrine:
	user: *
	password: *
	dbname: *
	metadata:
		App: %appDir%
		Article: annotations(%appDir%/model/entity)
services:
	entityManager: @Kdyby\Doctrine\EntityManager
	article: App\Article(@doctrine.dao(App\Article))
Article
namespace App;
use Doctrine\ORM\Mapping as ORM;
/**
 * @ORM\Entity
 */
class Article extends \Kdyby\Doctrine\Entities\IdentifiedEntity
{
	/**
	* @ORM\Column(type="string")
	*/
	protected $title;
	/**
	*
	* @ORM\Column(type="string")
	*/
	protected $slug;
	/**
	* @ORM\Column(type="string")
	*/
	protected $text;
}
Homapage
private $em;
public function __construct(\Kdyby\Doctrine\EntityManager $em)
{
	$this->em = $em;
}
public function actionDefault()
{
	$articles = $this->em->getDao(\App\Article::getClassName());
	$article = new \App\Article();
	$article->title = "The Tigger Movie";
	$article->slug = 'the-tigger-movie';
	$articles->save($article);
	$article = $articles->findBy(array('slug' => 'the-tigger-movie')); // Místo tohodle bych rád zavolal např. $articles->getBySlug($slug);
	dump($article[0]->title);
}
Moc nevím jak to mám uchopit. Díky moc za nějaký nakopnutí jakým směrem se pustit k best practice. :-)

- Filip Procházka
 - Moderator | 4668
 
Oli napsal(a):
Například v Nette database bych si napsal něco jako… Kam a jak napsat tohle pomocí Doctrine 2?
Můžeš použít DAO v presenteru a zavolat si
$article = $articlesDao->findOneBy(array('slug' => $slug));
Nebo si napíšeš facade třídu, do které to schováš. Obecně doporučuji klidně to ze začátku psát do presenteru a v momentě kdy budeš mít kus funkčnosti hotový tak refaktorovat. Tedy tohle bych napsal jednoduše pomocí dao a pak si napsal třídu a přesunul to do jejich metod.
Přímo do tý entity?
To rozhodně ne, entita nemůže sama sebe někde hledat.
Logické mě přijde podědit
\Kdyby\Doctrine\EntityDaoa dat tu metodu tam, ale HosipLan píše, že by se DAO nemělo dědit.
Správně, DAO nedědit, vytvářel bys god class
doctrine: metadata: App: %appDir% Article: annotations(%appDir%/model/entity)
Ten article namespace v metadatech tam máš zbytečný, stačí ten App, pokud ten namespace budeš používat. Popřípadě to můžeš na něco změnit.
services: entityManager: @Kdyby\Doctrine\EntityManager article: App\Article(@doctrine.dao(App\Article))
Tohle je úplně zbytečné, kdekoliv si řekneš o
EntityManager, tak se ti tam doplní pomocí autowire, dělat
aliasy nepotřebuješ, protože ta služba už
v DIC je registrovaná.
private $em; public function __construct(\Kdyby\Doctrine\EntityManager $em) { $this->em = $em; }
Na tohle by se ti mohl hodit autowired
Moc nevím jak to mám uchopit. Díky moc za nějaký nakopnutí jakým směrem se pustit k best practice. :-)
Co takhle si napsat službu
class ArticlesFacade extends Nette\Object
{
	private $articles;
	public function __construct(\Kdyby\Doctrine\EntityManager $em)
	{
		$this->articles = $em->getDao(\App\Article::getClassName());
	}
	public function findBySlug($slug)
	{
		return $this->articles->findOneBy(array('slug' => $slug));
	}
}
Službu zaregistruješ
services:
	- ArticlesFacade() # netřeba ani pojmenovávat
Necháš si injectnout do presenteru a používáš.
Malá vsuvka, findBy metody na repozitáři ti většinou stačit nebou a jsou spíše na prototypování a postupem času budeš muset psát místo nich DQL dotazy, protože je to efektivnější, ale prozatím je klidně používej :)

- Jiří Nápravník
 - Člen | 710
 
Filip Procházka napsal(a):
Malá vsuvka, findBy metody na repozitáři ti většinou stačit nebou a jsou spíše na prototypování a postupem času budeš muset psát místo nich DQL dotazy, protože je to efektivnější, ale prozatím je klidně používej :)
Kam bys poradil ty DQL psát? Vím, že Kdyby\Doctrine má ty QueryAble, ale nějak se mi nechce kvůli každému DQL dělat speciální třídu. A dát to přímo do Facade se nabízí, ale přeci jen neměla by Facade být tenká a delegovat jinam, než pokládat dotazy tady?

- Filip Procházka
 - Moderator | 4668
 
QueryObjecty jsou určtěné do presenterů a komponent, tam kde stránkuješ a vypisuješ hodně záznamů. Pokud nestránkuješ, neřadíš jenom chceš vrátit nějakej omezenej set záznamů, piš s klidem servisní/facade třídy a měj DQL v nich. Ale opět platí pravidlo nedědit DAO, pouze kompozice.
V momentě kdy ti bude dávat smysl využít funkce QueryObjektu, tak si ho prostě napíšeš :)

- Jiří Nápravník
 - Člen | 710
 
Díky za reakci a upřesnění, já právě na základě článku bral, že se mají používat, kde píšu nějaký DQL