Symfony + Doctrina: ¿Cómo encontrar a todos los usuarios sin un valor específico de muchos a uno?
A Symfony
+ Doctrine
proyecto basado utiliza un User
llamadas para gestionar usuarios registrados. Otros UserMetadata
clase se utiliza para manejar todo tipo de información adicional (por ejemplo, la última fecha de inicio de sesión, ha visitado página xy, etc.) sobre un usuario.
UserMetadata
tiene una relación unidireccional de muchos a uno User
(un usuario) puede tienen muchos metadatos vinculados. User
la clase no sabe sobre los metadatos).
class UserMetadata {
...
/**
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\User")
* @ORM\JoinColumn(name="user_id", referencedColumnName="id", onDelete="CASCADE")
*/
protected $user;
protected $key;
protected $value;
Una entrada de metadatos se almacena como par clave/valor. Por ejemplo, la fecha que un usuario visitó por última vez una determinada página se almacena como key='somePageName', value='2020-12-01'
se añade. Si no hay entrada con key='somePageName'
existe, el usuario no ha visitado esta página todavía.
Objetivo 1: Crear una consulta que seleccione a todos los usuarios que NO han visitado una determinada página.
Encontrar a los usuarios que han visitado una página no es problema usando un LEFT JOIN
:
$qb = $this->em->createQueryBuilder();
$qb->select('u')
->from('AppBundle\Entity\User', 'u')
->andWhere('u.someValue = :filter1')
->setParameter('filer1', 'someFilterValueInUserEntity')
->leftJoin(UserSettingsEntry::class, 's', 'WITH', 's.user=u.id')
->andWhere('s.key = :key')
->setParameter('key', 'somePageName');
¿Pero cómo encontrar usuarios sin tal entrada?
Objetivo 2: Crear una consulta que seleccione a todos los usuarios que NO han visitado una determinada página, O donde la página fue visitada hace más de 6 semanas.
¿Es posible en la única consulta?
Pregunta hecha hace 3 años, 4 meses, 28 días - Por quantumquill
4 Respuestas:
-
Puedes usar
->notIn()
método:Obtenga sus resultados de la consulta original:
$myUsers = $qb->select('u') ->from('AppBundle\Entity\User', 'u') ->andWhere('u.someValue = :filter1') ->setParameter('filer1', 'someFilterValueInUserEntity') ->leftJoin(UserSettingsEntry::class, 's', 'WITH', 's.user=u.id') ->andWhere('s.key = :key') ->setParameter('key', 'somePageName') ->getQuery() ->getResult();
Entonces puede utilizar su resultado para filtrar una nueva consulta:
$qb->select('u') ->from('AppBundle\Entity\User', 'u') ->where($qb->expr()->notIn('u.id', $myUsers)) ->getQuery() ->getResult();
Puedes comprobarlo. esta pregunta y Referencias doctrinales
Respondida el Dec 18, 2020 a las 11:39 - por techtactician
Votos positivos: 0 | Votos negativos: 0 -
Ya que está usando una unión izquierda, simplemente puede comprobar si la entrada de la mesa lateral derecha está nula, asegúrese de mover la
s.key = :key
condición dentro de la condición de unión sin embargo.$qb = $this->em->createQueryBuilder(); $qb ->select('u') ->from('AppBundle\Entity\User', 'u') ->andWhere('u.someValue = :filter1') ->setParameter('filer1', 'someFilterValueInUserEntity') ->leftJoin(UserSettingsEntry::class, 's', 'WITH', $qb->expr()->andX( 's.user = u.id', 's.key = :key' )) ->setParameter('key', 'allo') ->andWhere($qb->expr()->isNull('s.id')) ;
Respondida el Dec 18, 2020 a las 11:47 - por codemaster89
Votos positivos: 0 | Votos negativos: 0 -
Para lograr el Objetivo 1 de encontrar a todos los usuarios que NO han visitado una determinada página, puedes modificar tu consulta de la siguiente manera:
php $qb = $this->em->createQueryBuilder(); $qb->select('u')
- >from('AppBundle\Entity\User', 'u')
- >leftJoin('AppBundle\Entity\UserMetadata', 'm', 'WITH', 'm.user = u.id AND m.key = :key')
- >andWhere($qb->expr()->isNull('m.key'))
- >setParameter('key', 'somePageName');
En esta consulta, se está haciendo un LEFT JOIN con la entidad
UserMetadata
utilizando la condición de que elkey
sea igual asomePageName
. Luego, se agrega una condición para encontrar los usuarios donde elkey
es nulo, lo que significa que no han visitado la página.Para el Objetivo 2, puedes añadir otra condición a la consulta existente para verificar si la página fue visitada hace más de 6 semanas. Sin embargo, Symfony y Doctrine no tienen soporte nativo para operaciones de fechas en las consultas DQL, por lo que es posible que necesites utilizar funciones DQL personalizadas o hacer el cálculo fuera de la consulta.
No obstante, podrías intentar algo así:
php $qb = $this->em->createQueryBuilder(); $qb->select('u')
- >from('AppBundle\Entity\User', 'u')
- >leftJoin('AppBundle\Entity\UserMetadata', 'm', 'WITH', 'm.user = u.id AND m.key = :key')
- >andWhere($qb->expr()->orX(
- >setParameter('key', 'somePageName')
- >setParameter('sixWeeksAgo', new \DateTime('-6 weeks'));
En esta nueva consulta, se agrega otra condición con un
OR
para verificar si la página fue visitada hace más de 6 semanas utilizando la funciónlt
(menor que) de Doctrine.Recuerda que estas consultas son solo un ejemplo y es posible que necesites ajustarlas según la estructura y lógica de tu aplicación.
Respondida el Dec 19, 2020 a las 11:35 - por Gpt
Votos positivos: 0 | Votos negativos: 0 -
Objetivo 1: Encontrar usuarios que NO han visitado una página determinada
SELECT u FROM AppBundle\Entity\User u LEFT JOIN AppBundle\Entity\UserMetadata s WITH s.user = u.id WHERE s.key IS NULL
Esta consulta utiliza un
LEFT JOIN
para unir opcionalmente la tablaUserMetadata
con la tablaUser
basada en la columnauser_id
. El filtroWHERE s.key IS NULL
seleccionará solo los usuarios que no tienen una entrada de metadatos con la clave especificada.Objetivo 2: Encontrar usuarios que NO han visitado una página determinada, O donde la página fue visitada hace más de 6 semanas
Sí, es posible lograr ambos objetivos en una sola consulta utilizando una combinación de
LEFT JOIN
y condicionesWHERE
:SELECT u FROM AppBundle\Entity\User u LEFT JOIN AppBundle\Entity\UserMetadata s WITH s.user = u.id WHERE (s.key IS NULL) OR (s.key = 'somePageName' AND s.value < DATE_SUB(NOW(), INTERVAL 6 WEEK))
Esta consulta utiliza el operador lógico
OR
para combinar dos condiciones:(s.key IS NULL)
: Selecciona usuarios que no tienen una entrada de metadatos con la clave especificada.(s.key = 'somePageName' AND s.value < DATE_SUB(NOW(), INTERVAL 6 WEEK))
: Selecciona usuarios que tienen una entrada de metadatos con la clave especificada, pero que fue visitada hace más de 6 semanas.Respondida el Dec 19, 2020 a las 11:48 - por Gemini
Votos positivos: 0 | Votos negativos: 0