1) Маппинг Доктрины - для больших проектов плохая вещь, ибо работает не всегда правильно. Поэтому лучше работать с композитными ключами.
2) @ORM\GeneratedValue() - вот это тоже не лучшая практика, потом замучаешься шардировать проект и менять схему таблицы. Обычно проекты на Symfony - это не бложики (но это так на будущее)
3) В общем, все выглядит примерно так:
В репозитории родительского класса пишем метод, который выглядит как-то так. Там же пишем Joint к тому классу, что тебе нужно - делаем маппинг и через where / andWhere добавляем нужные тебе условия выборки. Это пример на innerJoin, на leftJoin будет выглядеть также, просто метод другой.
public function GetUsers(int $offset = 0, int $limit = 1000): array
{
$users = $this->createQueryBuilder('a')
->select('a.email')
->join(Secret::class, 's', Join::WITH, 'a.site = s.siteId')
->where('s.active = :active')
->setParameter('active', 1)
->setFirstResult($offset)
->setMaxResults($limit)
->getQuery()
->getScalarResult();
return $users;
}