Pergunta

I have a user entity and an Event entity. the Users can notify that they want to go to some events. Those users also have friends who are themselves users (self referencing many to many relation).

I'm looking to retrieve from my database the user' friends who fulfill the following conditions: They want to go to the same event. they are nearby each others.

While the second condition is fine, I'm trying to build the best query possible to make sure the first condition is respected.

Here is what I have done so far:

$qb = $this->getEntityManager()->createQueryBuilder();


        $qb->select( 'USER', 'FRIENDS' ) // Selecting Me, the user who is asking to retrieve the users matched, and the friends who fill the criterias below
            ->from( 'Entity\User',  'USER' ) // we start with the user
            ->where( 'USER.id = :userId' ) // And I want the user the be me
            ->leftJoin('USER.friends', 'FRIENDS') // I'm going to take all of his friends
            ->innerJoin( 'FRIENDS.eventNotified', 'EVENT' ) //but ONLY the ones that have notified Events ( array collection ov EVENT entity )
            ->leftJoin( 'EVENT.categories', 'c' ) //take events category in order to avoid doing any request when getting the name of the category
            ->leftJoin( 'EVENT.place', 'p' ) // same with  the place where the event occured
            ->andWhere( 
                $qb->expr()->andX(
                    $qb->expr()->between('FRIENDS.latitude', ':minLat', ':maxLat'), //in a defined area
                    $qb->expr()->between('FRIENDS.longitude', ':minLng', ':maxLng'),
                    $qb->expr()->eq('FRIENDS.eventNotified.id', 'USER.eventNotified.id') //I want to select ONLY the friends who have notified they want to go to the same events than me (I have notified the same events as well)
                )
            )
            ->add( 'orderBy', $qb->expr()->sum( 'FRIENDS.latitude - :lat', 'FRIENDS.longitude - :lng' ) );

Unfortunately for me, this is not doing what I am looking to do. And I wonder why. So once I select FRIENDS, I basically want to select ONLY the friends who have notified they want to go to the same events as the ones I have notified too.

So I'm doing an innerJoin to check if the friends (they are user as well) have notified they want to go to events. And then in those friends, I want to select ONLY the ones who are going to the same events as mine $qb->expr()->eq('FRIENDS.eventNotified.id', 'USER.eventNotified.id').

I think there is my problem ... since eventNotified is a collection of the EVENT entity from which, I am looking to select ONLY the events we both have notified, can I just get rid of the .id ? also, I have the feeling this is selecting all of the events when I just want to retrieve the eventsnotified we have in commun. So I believe I should do an innerJoin here somewhere but I can find the right synthax.

Any advice ?

Thanks

Bonus: should I also specify I want to retrieve 'c' and 'p' in the SELECT statement ?

EDIT:

I tried the solution below, but I am getting this error I don't understand. How can I retrieve the complete error message ? (some parts are missing ! )

Code I have tried :

//We only want to return users nearby who are available OR who $qb = $this->getEntityManager()->createQueryBuilder();

    $qb->select( 'USER', 'FRIENDS' ) // Selecting Me, the user who is asking to retrieve the users matched, and the friends who fill the criterias below
        ->from( 'Entity\User',  'USER' ) // we start with the user
        ->where( 'USER = :user' ) // And I want the user the be me
        ->leftJoin('USER.friends', 'FRIENDS', 'WITH', 'USER.eventNotified = FRIENDS.eventNotified');

    $array = array(
         'user'   => $user
    );

    $qb->setParameters( $array );

    $usersMatched = $qb->getQuery()->getResult();

error:

Fatal error: Uncaught exception 'Doctrine\ORM\Query\QueryException' with message 'SELECT USER, FRIENDS FROM Entity\User USER LEFT JOIN USER.friends FRIENDS WITH USER.eventNotified = FRIENDS.eventNotified WHERE USER = :user' in /Users/YohannM/Sites/meetmyfriends-back/application/libraries/Doctrine/ORM/Query/QueryException.php:39Stack trace:#0 /Users/YohannM/Sites/meetmyfriends-back/application/libraries/Doctrine/ORM/Query/Parser.php(429): Doctrine\ORM\Query\QueryException::dqlError('SELECT USER, FR...')#1 /Users/YohannM/Sites/meetmyfriends-back/application/libraries/Doctrine/ORM/Query/Parser.php(686): Doctrine\ORM\Query\Parser->semanticalError('Invalid PathExp...', Array)#2 /Users/YohannM/Sites/meetmyfriends-back/application/libraries/Doctrine/ORM/Query/Parser.php(240): Doctrine\ORM\Query\Parser->_processDeferredPathExpressions(Object(Doctrine\ORM\Query\AST\SelectStatement))#3 /Users/YohannM/Sites/meetmyfriends-back/application/libraries/Doctrine/ORM/Query/Parser.php(304): Doctrine\ORM\Query\Parser->getAST()#4 /Users in /Users/YohannM/Sites/meetmyfriends-back/application/libraries/Doctrine/ORM/Query/QueryException.php on line 49

It seems that Doctrine2 doesn't like 'WITH', 'USER.eventNotified = FRIENDS.eventNotified' for some reason. I don't really understand why. without this bit of code, the code works fine but this condition is indeed not fulfilled.

Many Thanks for your help

EDIT2:

After trying what was in the third comment of the answer below, I still get a similar error message:

Fatal error: Uncaught exception 'Doctrine\ORM\Query\QueryException' with message 'SELECT USER, FRIENDS, EVENT, c, p FROM Entity\User USER LEFT JOIN USER.friends FRIENDS WHERE USER = :user AND USER.eventNotified = FRIENDS.eventNotified' in /Users/YohannM/Sites/meetmyfriends-back/application/libraries/Doctrine/ORM/Query/QueryException.php:39Stack trace:#0 /Users/YohannM/Sites/meetmyfriends-back/application/libraries/Doctrine/ORM/Query/Parser.php(429): Doctrine\ORM\Query\QueryException::dqlError('SELECT USER, FR...')#1 /Users/YohannM/Sites/meetmyfriends-back/application/libraries/Doctrine/ORM/Query/Parser.php(528): Doctrine\ORM\Query\Parser->semanticalError(''EVENT' is not ...', Array)#2 /Users/YohannM/Sites/meetmyfriends-back/application/libraries/Doctrine/ORM/Query/Parser.php(233): Doctrine\ORM\Query\Parser->_processDeferredIdentificationVariables()#3 /Users/YohannM/Sites/meetmyfriends-back/application/libraries/Doctrine/ORM/Query/Parser.php(304): Doctrine\ORM\Query\Parser->getAST()#4 /Users/YohannM/Sites/meetmyfrien in /Users/YohannM/Sites/meetmyfriends-back/application/libraries/Doctrine/ORM/Query/QueryException.php on line 49

I think we cannot do dynamic conditions. I don't know if there is a way to do it with doctrine otherwise I'll probably have to do that with PHP i guess ..

Here are the structure of my event and user entity:

User entity:

class User {
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer", nullable=false, unique=true)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

    /**
     * @var integer
     *
     * @ORM\Column(name="fb_id", type="bigint", nullable=false, unique=true)
     */
    private $fb_id;

    /**
     * @var string
     *
     * @ORM\Column(name="firstname", type="string", length=100, nullable=false, unique=false)
     */
    private $first_name;

    /**
     * @var string
     *
     * @ORM\Column(name="lastname", type="string", length=100, nullable=true, unique=false)
     */
    private $last_name;

    /**
     * @var string
     *
     * @ORM\Column(name="email", type="string", length=255, nullable=true, unique=true)
     */
    private $email;

    /**
     * @var integer
     *
     * @ORM\Column(name="notation", type="integer", nullable=true, unique=true)
     */
    private $notation;

    /**
     * Bidirectional - Many users have Many favorite comments (OWNING SIDE)
     *
     * @var \Doctrine\Common\Collections\Collection
     *
     * @ORM\ManyToMany(targetEntity="Entity\Category", inversedBy="userInterests")
     */
    private $interests;

    /**
     * @var \Doctrine\Common\Collections\Collection
     *
     * @ORM\ManyToMany(targetEntity="Entity\User", cascade={"persist"})
     * @ORM\JoinTable(name="friends",
     *      joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
     *      inverseJoinColumns={@ORM\JoinColumn(name="friend_user_id", referencedColumnName="id")}
     *      )
     **/
    private $friends;

    /**
     * @var \Doctrine\Common\Collections\Collection
     *
     * @ORM\OneToMany(targetEntity="Entity\Request", mappedBy="user", cascade={"remove"}, orphanRemoval=true)
     * @ORM\JoinColumn(nullable=true)
     */
    private $requests;

    /**
     * @var \Doctrine\Common\Collections\Collection
     *
     * @ORM\OneToMany(targetEntity="Entity\Request", mappedBy="friend", cascade={"remove"}, orphanRemoval=true)
     * @ORM\JoinColumn(nullable=true)
     */
    private $notifications;

    /**
     * Bidirectional - Many users have notified they want to go to different events (OWNING SIDE)
     *
     * @var \Doctrine\Common\Collections\Collection
     *
     * @ORM\ManyToMany(targetEntity="Entity\Event", inversedBy="userNotified", cascade={"persist"})
     */
    private $eventNotified;

    /**
     * @var integer
     *
     * @ORM\Column(name="age", type="integer", length=3, nullable=true, unique=false)
     */
    private $age;

    /**
     * @var string
     *
     * @ORM\Column(name="description", type="text", nullable=true, unique=false)
     */
    private $description;

    /**
     * @var string
     *
     * @ORM\Column(name="picture", type="string", length=300, nullable=true, unique=false)
     */
    private $picture;

    /**
     * @var string
     *
     * @ORM\Column(name="genre", type="string", length=10, nullable=true, unique=false)
     */
    private $genre;

    /**
     * @var boolean
     *
     * @ORM\Column(name="isregistered", type="boolean", length=1, nullable=false, unique=false)
     */
    private $registered;

    /**
     * @var string
     *
     * @ORM\Column(name="latitude", type="decimal", length=64, precision=25, scale=20, nullable=true, unique=false)
     */
    private $latitude;

    /**
     * @var string
     *
     * @ORM\Column(name="longitude", type="decimal", length=64, precision=25, scale=20, nullable=true, unique=false)
     */
    private $longitude;

    /**
     * @var \Entity\Security_Key
     *
     * @ORM\OneToOne(targetEntity="Entity\Security_Key", cascade={"persist","remove"}, orphanRemoval=true)
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="private_key_id", referencedColumnName="id", unique=true, onDelete="SET NULL")
     * })
     */
    private $private_key;

    /**
     * @var boolean
     *
     * @ORM\Column(name="isavailable", type="boolean", length=1, nullable=false, unique=false)
     */
    private $available = 0;

Event entity:

class Event
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer", precision=0, scale=0, nullable=false, unique=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="name", type="string", length=200, precision=0, scale=0, nullable=false, unique=false)
     */
    private $name;

    /**
     * @var \Doctrine\Common\Collections\Collection
     *
     * @ORM\ManyToMany(targetEntity="Entity\Category", inversedBy="events")
     * @ORM\JoinTable(name="categories_events")
     */
    private $categories;


    /**
     * @var \Entity\User
     *
     * @ORM\ManyToOne(targetEntity="Entity\User", inversedBy="events")
     * @ORM\JoinColumn(name="user_id", referencedColumnName="id")
     **/
    private $user;


    /**
     * @var \Entity\Admin
     *
     * @ORM\ManyToOne(targetEntity="Entity\Admin", inversedBy="events")
     * @ORM\JoinColumn(name="admin_id", referencedColumnName="id")
     **/
    private $admin;

    /**
     * @var \Entity\Place
     *
     * @ORM\ManyToOne(targetEntity="Entity\Place", inversedBy="events", cascade={"persist"})
     * @ORM\JoinColumn(nullable=false)
     */
    private $place;

    /**
     * @var string
     *
     * @ORM\Column(name="description", type="text", precision=0, scale=0, nullable=true, unique=false)
     */
    private $description;

    /**
     * @var string
     *
     * @ORM\Column(name="link", type="string", length=200, nullable=true, unique=false)
     */
    private $link;

    /**
     * @var string
     *
     * @ORM\Column(name="picture", type="string", length=100, precision=0, scale=0, nullable=true, unique=false)
     */
    private $picture;

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="date_created", type="datetime", precision=0, scale=0, nullable=false, unique=false)
     */
    private $dateCreated;

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="date_start", type="datetime", precision=0, scale=0, nullable=true, unique=false)
     */
    private $dateStart;

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="date_end", type="datetime", precision=0, scale=0, nullable=true, unique=false)
     */
    private $dateEnd;

    /**
     * @var boolean
     *
     * @ORM\Column(name="confirmed", type="boolean", length=1, nullable=false, unique=false)
     */
    private $confirmed;

     /**
     * Bidirectional - One-To-Many (INVERSE SIDE)
     *
     * @var \Doctrine\Common\Collections\Collection
     *
     * @ORM\OneToMany(targetEntity="Entity\Request", mappedBy="event")
     */
    private $invitations;

    /**
     * Bidirectional - Many users are intending many events (INVERSE SIDE)
     *
     * @ORM\ManyToMany(targetEntity="User", mappedBy="eventNotified", cascade={"persist"})
     */
    private $userNotified;
Foi útil?

Solução

A few ideas:

You can try using something like $qb->expr()->eq('FRIENDS.eventNotified', 'USER.eventNotified') instead of $qb->expr()->eq('FRIENDS.eventNotified.id', 'USER.eventNotified.id'), because I suppose that eventNotified is someway a USER related entity which attribute name in your USER entity is eventNotified.

Anyhow, I would directly use a "with" in the join, so you limit your join results independently of your where clause:

  ->leftJoin('USER.friends', 'FRIENDS', 'WITH', 'FRIENDS.eventNotified = USER.eventNotified') // I'm going to take all of his friends

Bonus: should I also specify I want to retrieve 'c' and 'p' in the SELECT statement ? Answer : yes, if you want to retrieve them with your getters after without a new query to the database

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top