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 el key sea igual a somePageName. Luego, se agrega una condición para encontrar los usuarios donde el key 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(
    $qb->expr()->isNull('m.key'), $qb->expr()->lt('m.value', ':sixWeeksAgo') ))
    • >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ón lt (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 tabla UserMetadata con la tabla User basada en la columna user_id. El filtro WHERE 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 condiciones WHERE:

    
    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