2011-08-15 17 views
6

¿Cómo realizo consultas en una entidad?Doctrine 2, query inside entities

namespace Entities\Members; 

/** 
* @Entity(repositoryClass="\Entities\Member\MembersRepository") 
* @Table(name="Members") 
* @HasLifecycleCallbacks 
*/ 
class Members extends \Entities\AbstractEntity 
{ 
    /** 
    * @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="\Entities\Users\Wall", mappedBy="entry", cascade={"persist"}) 
    */ 
    protected $commententries; 

    public function __construct() 
    { 
     $this->commententries = new \Doctrine\Common\Collections\ArrayCollection(); 
    } 
} 

ejemplo, me gustaría tener una función dentro de esta entidad llamada: filter() y quiero ser capaz de filtrar la colección commententries. Debería devolver una colección con una determinada condición, como id=1. Básicamente debería estar filtrando los datos recibidos de la consulta de unión.

Así que algo como esto:

$this->commententries->findBy(array('id' => 1)); 

Pero, obviamente, esto no funciona.

+0

Puede hacer esto exactamente usando un repositorio personalizado que ya definió usando @Entity (repositoryClass = "\ Entities \ Member \ MembersRepository"). Verifique mi respuesta a continuación: http://stackoverflow.com/a/19319313/1697459 – Wilt

Respuesta

12

En general, no debe hacer esto.

Las entidades, como regla general, no deberían saber sobre el entitymanager (directamente, o a través de algún objeto intermediario).

La razón de esto es principalmente la capacidad de prueba, pero en mi experiencia, ayuda a mantener las cosas organizadas de otras maneras.

Lo abordaré diseñando una clase de servicio que maneje las búsquedas para usted. Su controlador (o lo que sea) conducirían así:

<?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); 

Mientras pista en el comentario, si más adelante decide que desea agregar el almacenamiento en caché (a través de memcached, por ejemplo) para evitar consultas frecuentes, que' Hago eso cerca o en esta clase de servicio. Esto mantiene sus entidades agradables y simples, y fácilmente comprobables. Como usted inyecta su entidad administrador en el servicio en tiempo de construcción, puede simular eso según sea necesario.

getFavoriteCommentaries() podría usar varias implementaciones. Una cosa trivial sería la de proxy a Member :: getFavoriteCommentaries(), que en realidad cargaría todo, y luego filtraría los "favoritos". Eso probablemente no se escalará particularmente bien, por lo que podría mejorar utilizando el EM para obtener solo los datos que necesita.

1

Su pregunta fue realmente difícil de entender, intente trabajar en cómo estructurar sus preguntas en el futuro. Por ejemplo, dices "devuelve el mismo resultado" pero "filtra", lo que podría significar cualquier cosa. ¿Desea utilizar el mismo conjunto de resultados (por qué diablos elegiría hacer eso), y simplemente utiliza array_filter o array_walk para filtrar los resultados o realmente quiere usar una unión condicional? Es increíblemente ambiguo.

De todos modos .. respuesta (después de leer su pregunta 4 veces).

$q = $qb->select ("m","w") 
      ->from ("Members", "m") 
      ->leftJoin("m.commententries","w",'WITH', 'w.id = :id') 
      ->setParameter ("id", $id) 
      ->getQuery(); 
+0

¿Puede usar array_filter en una clase de entidad? intenté: array_filter ($ a, array ("Miembros", "entryFilter")); pero la clase no se encuentra –

+0

Si devolviera su getResult() como matriz del modo de hidratación, podría hacerlo. Sería como pisar cualquier matriz. -> getQuery() -> getResult (2) lo haría. – Layke

21

Su ArrayCollection ya implementa un método de filtro(), necesita pasar un Cierre para que funcione sus entidades (aquí, las entradas de comentarios).

$idsToFilter = array(1,2,3,4); 

$member->getComments()->filter(
    function($entry) use ($idsToFilter) { 
     if (in_array($entry->getId(), $idsToFilter)) { 
      return true; 
     } 

     return false; 
    } 
); 

(no probado)

Tenga en cuenta que dicho método se repetirá y carga ansiosa sobre todos sus comentarios, por lo que en caso de que un usuario tiene mucho que puede ser un gran cuello de botella;

En la mayoría de los casos, desea utilizar repositorios personalizados, donde puede ubicar dicha lógica.

Como sugirió timdev, puede crear un MemberService que ajustará dicha llamada teniendo en cuenta EntityManager.

Separar las entidades de la capa de Peristance es una gran mejora con respecto a Doctrine 1, y no debe romper esa regla.

+1

en el cierre de php, debe incluir las variables entre paréntesis, p. use ($ idsToFilter) – falinsky

+0

@falinsky gracias, editado. –

1

Estoy de acuerdo con "timdev". No debe definir la consulta en su clase de entidades. Mi manera de definir un soporte de clase de servicio es que las entidades son clases de repositorio. Por ejemplo: Usuario (entidad - YourBundle/Entity/User.php) tendrá UserRepository (clase de servicio - YourBundle/Repository/UserRepository.php). Su método de "filtro" debería estar aquí. Solo necesita asignar esta clase de servicio en su clase de entidad. En su controlador, siempre puede acceder al "filtro" a través de su repositorio. Está documentado muy detalladamente en la Symfony2 book

4

Utilice un repositorio personalizado para consultas

No debe escribir consultas en sus entidades, pero debería utilizar un repositorio para eso. Esto también se explica en la documentación de la doctrina 7.8.8 Custom Repositories. Le permitirá construir sus consultas personalizadas en un lugar central y mantener limpias las definiciones de su entidad.

Utilizar criterios para las colecciones de filtro:

Pero si desea filtrar dentro de su colección en un método que puede utilizar getCriteria. Puede leer sobre cómo usar Criteria en la documentación de Doctrine 8.8 Filtering collections. Para filtrar como usted quiere hacer sería algo como esto:

declarar en la parte superior de su clase de entidad de la clase Criteria

use Doctrine\Common\Collections\Criteria 

En el método de getCommentEntries utilizar la clase para filtrar:

public function getCommentEntries() 
{ 
    $criteria = Criteria::create() 
     ->where(Criteria::expr()->eq('id', 1)); 

    $filteredCommentEntries = $this->commententries->matching($criteria); 

    return $filteredCommentEntries; 
} 
+0

@deanjase Terminé aquí nuevamente después de mucho tiempo y noté que anteriormente no estaba claro que quisiera filtrar su colección. Actualicé mi respuesta porque definitivamente es posible filtrar en su colección 'get'. – Wilt

+0

Aunque la respuesta es antigua, encontré esta pequeña nota en la documentación de Filtrado de colecciones que dice "Puede mover el acceso de secciones de colecciones a métodos dedicados de una entidad. Por ejemplo, Group # getTodaysBirthdayUsers(). Lo que obviamente hace que EntityManager sea visible para su entidad. ¿Eso no infringe la regla? " –

+0

@AftabNaveed No expondrá el EntityManager al agregar este método a su entidad. En el ejemplo, primero obtienen el '$ group' para recopilar' $ userCollection'. En la entidad de su grupo, simplemente puede hacer '$ this-> getUsers();' dentro de su getter personalizado y luego agregar la lógica de los criterios. – Wilt