Kdyby/Doctrine best practice

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
Oli
Člen | 1215
+
0
-

Ahoj,
snažím se přejít z Nette database k Doctrine 2 (Kdyby/Doctrine). Něco jsem si o tom přečetl:

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
+
0
-

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\EntityDao a 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 :)

Oli
Člen | 1215
+
0
-

Super, díky! Takhle to všechno dává perfektní smysl. Já se furt snažil o nějakou „dokonalou složitost“ a přitom v tom plaval. ;-)

Jiří Nápravník
Člen | 710
+
0
-

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
+
0
-

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
+
0
-

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