News
связаны друг с другом с использованием подхода «один ко многим» со ссылками на себя (одна новость является родительской и может иметь много потомков). Более того, каждый News
имеет нормальные (не ссылающиеся на себя) отношения один к одному с Event
и Gallery
. Когда я запускаю простой DQL:
SELECT n FROM App\Entity\News n WHERE n.parent = :id
, а затем обновляю результаты методом getResults
со значением по умолчанию HYDRATION_OBJECT
, дополнительные запросы выполняются где-то внутри метода getResults
.
SELECT t0.* FROM event t0 WHERE t0.news_id = 2 AND ((t0.deleted_at IS NULL));
SELECT t0.* FROM gallery t0 WHERE t0.news_id = 2 AND ((t0.deleted_at IS NULL));
SELECT t0.* FROM event t0 WHERE t0.news_id = 1 AND ((t0.deleted_at IS NULL));
SELECT t0.* FROM gallery t0 WHERE t0.news_id = 1 AND ((t0.deleted_at IS NULL));
Где news_id = 1
и news_id = 2
- дочерние элементы новостей, выбранных первым запросом.
News
также имеет не ссылающиеся на себя отношения «один ко многим» (я их здесь проигнорировал), но гидратация не вызывает дополнительных запросов о них. Проблема возникает только тогда, когда в операторе where
задействовано отношение parent
.
Как воспроизвести
// news Entity
/**
* @ORM\Entity(repositoryClass="App\Repository\NewsRepository")
* @ORM\Table(uniqueConstraints={@UniqueConstraint(name="news_slug_deleted", columns={"slug","deleted_at"})})
*/
class News {
use SoftDeleteableEntity;
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
*/
private $title;
/**
* @Gedmo\Slug(fields={"title"})
* @ORM\Column(length=128)
*/
private $slug;
/**
* @ORM\OneToOne(targetEntity="App\Entity\Gallery", mappedBy="news", cascade={"persist", "remove"}, orphanRemoval=true)
*
* @var Gallery
*/
private $gallery;
/**
* @ORM\OneToOne(targetEntity="App\Entity\Event", mappedBy="news", cascade={"persist", "remove"}, orphanRemoval=true)
*
* @var Event
*/
private $event;
/**
* One News has Many News.
* @ORM\OneToMany(targetEntity="News", mappedBy="parent")
*/
private $children;
/**
* Many News have One News.
* @ORM\ManyToOne(targetEntity="News", inversedBy="children")
* @ORM\JoinColumn(name="parent_id", referencedColumnName="id", nullable=true)
*/
private $parent;
}
/**
* @ORM\Entity(repositoryClass="App\Repository\EventRepository")
* @Gedmo\SoftDeleteable()
*/
class Event
{
use SoftDeleteableEntity;
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\OneToOne(targetEntity="App\Entity\News", inversedBy="event", cascade={"persist"})
* @ORM\JoinColumn(nullable=false)
*/
private $news;
Gallery
сущность похожа на Event
, поэтому я проигнорировал ее здесь.
// News controller
public function index(NewsRepository $newsRepository, $slug)
{
$news = $newsRepository->findOneBy(['slug' => $slug]);
$newsRepository->getConnectedNews($news->getId());
}
// news repository
class NewsRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, News::class);
}
public function getConnectedNews($newsId) {
$query = $this->createQueryBuilder('n')->andWhere('n.parent = :id')->setParameter('id', $newsId);
return $query->getQuery()->getResult(AbstractQuery::HYDRATE_OBJECT);
}
}
Гидратация news
, у которого есть 20 дочерних элементов, заканчивается: 20 * 2 + 1 (n * r + 1) запросов, где:
- (n) 20 - это количество детей
- (r) 2 - это количество отношений один к одному
- 1 - это базовый c запрос
Я хочу предотвратить это лишнее запросы к взаимно однозначным отношениям, сделанным Doctrine. Это ошибка Doctrine или нежелательное поведение, или я где-то допустил ошибку?
В общем, я хочу просто получить всех детей, ссылающихся на себя, без взаимно однозначных отношений, поскольку я не просил получить их, поэтому он должен использовать только один запрос для получения всех дочерних news
без дополнительных запросов для каждого извлеченного объекта 'news' и каждого из них один к одному.