Doctrine 2, interrogation à l'intérieur des entités
comment effectuer des requêtes dans une entité?
namespace EntitiesMembers;
/**
* @Entity(repositoryClass="EntitiesMemberMembersRepository")
* @Table(name="Members")
* @HasLifecycleCallbacks
*/
class Members extends EntitiesAbstractEntity
{
/**
* @Id @Column(name="id", type="bigint",length=15)
* @GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @Column(name="userid", type="bigint", length=26, nullable=true)
*/
protected $userid;
/**
* @Column(name="fname", type="string", length=255,nullable=true)
*/
protected $fname;
/**
* @OneToMany(targetEntity="EntitiesUsersWall", mappedBy="entry", cascade={"persist"})
*/
protected $commententries;
public function __construct()
{
$this->commententries = new DoctrineCommonCollectionsArrayCollection();
}
}
exemple j'aimerais avoir une fonction à l'intérieur de cette entité appelée: filter()
et je veux pouvoir filtrer la collection commententries
. Il doit retourner une collection avec une certaine condition telle id=1
. Fondamentalement, il devrait être le filtrage des données reçues de la requête de jointure.
donc quelque chose comme ça:
$this->commententries->findBy(array('id' => 1));
mais évidemment ceci ne fonctionne pas.
5 réponses
en général, vous ne devriez pas faire ça.
les entités, en règle générale, ne doivent pas connaître entitymanager (directement, ou via un objet intermédiaire).
la raison en est principalement la testabilité, mais d'après mon expérience, cela aide à garder les choses organisées d'une autre manière.
Je l'approcherais en concevant une classe de service qui gère les recherches pour vous. Votre contrôleur (ou autre) le piloterait comme ceci:
<?php
// create a new service, injecting the entitymanager. if you later wanted
// to start caching some things, you might inject a cache driver as well.
$member = $em->find('Member',$member_id); //get a member, some how.
$svc = new MemberService($em);
$favoriteCommentaries = $svc->getFavoriteCommentaries($member);
comme je l'indique dans le commentaire, si vous décidez plus tard que vous voulez ajouter la mise en cache (via memcached, par exemple) pour éviter les recherches fréquentes, vous le feriez quelque part près ou dans cette classe de service. Cela permet à vos entités de rester simples et faciles à tester. Puisque vous injectez votre entitymanager dans le service au moment de la construction, vous pouvez vous en moquer au besoin.
getFavoriteCommentaries () pourrait utiliser diverses implémentations. Un trivial one serait de le proxy à Member:: getFavoriteCommentaries(), qui chargerait en fait tout, et filtrerait ensuite les "Favoris". Cela ne se fera probablement pas particulièrement bien à l'échelle, donc vous pouvez l'améliorer en utilisant L'EM pour récupérer juste les données dont vous avez besoin.
votre ArrayCollection implémente déjà une méthode filter (), vous devez passer une fermeture pour la faire fonctionner vos entities (ici, les commentEntries).
$idsToFilter = array(1,2,3,4);
$member->getComments()->filter(
function($entry) use ($idsToFilter) {
if (in_array($entry->getId(), $idsToFilter)) {
return true;
}
return false;
}
);
(non testé)
notez qu'une telle méthode sera itérate et eager load au-dessus de tous vos commentaires, donc dans le cas où un utilisateur a beaucoup, il peut être un gros goulot d'étranglement;
dans la plupart des cas, vous souhaitez utiliser un dépôt personnalisé, où vous pouvez place d'une telle logique.
comme timdev suggéré, vous pouvez créer un MemberService qui enveloppera un tel appel en étant conscient de L'EntityManager.
séparer les entités de la couche de la Peristance est une grande amélioration par rapport à la Doctrine 1, et vous ne devriez pas enfreindre cette règle.
utiliser un référentiel personnalisé pour les requêtes
vous ne devez pas écrire de requêtes dans vos entities, mais vous devez utiliser un dépôt pour cela. Cela est également expliqué dans la documentation de doctrine 7.8.8 dépôts personnalisés . Il vous permet de créer vos requêtes personnalisées sur une place centrale, et maintient votre définitions d'entités propres.
utiliser des critères pour filtrer collections:
mais si vous voulez filtrer à l'intérieur de votre collection dans une méthode get
vous pouvez utiliser Criteria
. Vous pouvez lire sur la façon d'utiliser Criteria
dans la Documentation de Doctrine 8.8 collections de filtrage . Filtrer comme vous voulez faire ressemblerait à quelque chose comme ceci:
déclarez en haut de votre catégorie d'entités la Criteria
classe
use Doctrine\Common\Collections\Criteria
dans votre méthode getCommentEntries
utilisez la classe pour filtrer:
public function getCommentEntries()
{
$criteria = Criteria::create()
->where(Criteria::expr()->eq('id', 1));
$filteredCommentEntries = $this->commententries->matching($criteria);
return $filteredCommentEntries;
}
Votre question est vraiment difficile à comprendre, essayer de travailler sur la façon dont vous structurez vos questions à l'avenir. Par exemple, vous dites "retourner le même résultat" mais "filtrer", ce qui peut signifier n'importe quoi. Voulez-vous utiliser le même jeu de résultats (pourquoi diable choisiriez-vous jamais de faire cela), et utilisez juste array_filter ou array_walk pour filtrer les résultats ou voulez-vous réellement utiliser une jointure conditionnelle? C'est incroyablement ambigu.
en tout cas.. réponse ( après avoir lu votre question 4 fois).
$q = $qb->select ( "m","w" )
->from ( "Members", "m" )
->leftJoin( "m.commententries","w",'WITH', 'w.id = :id')
->setParameter ( "id", $id )
->getQuery ();
je suis d'accord avec"timdev". Vous ne devriez pas définir query dans votre classe entities. Ma façon de définir un support de classe de service les entités sont des classes de dépôt. Par exemple: l'Utilisateur (entité -- YourBundle/Entité/Utilisateur.php) aura UserRepository (classe service -- YourBundle/Repository/UserRepository.php). Votre méthode de "filtrage" devrait être ici. Vous avez juste besoin de mapper cette classe de service dans votre classe entity. Dans votre contrôleur, vous pouvez toujours accéder au "filtre" via son dépôt. C'est documenté très en détail dans le Symfony2 Livre