private function extractAssociatedEntities(array $entities)
    {
        $linkIds = $associatedEntities = [];

        foreach ($entities as $entity) {
            $reflect = new \ReflectionClass($entity);

            //If Widget is already in cache, extract only its Criterias (used outside Widget rendering)
            $widgetCached = ($entity instanceof Widget && $this->widgetHelper->isCacheEnabled($entity));

            //If Widget has LinkTrait, store the entity link id
            if (!$widgetCached && $this->hasLinkTrait($reflect) && $entity->getLink()) {
                $linkIds[] = $entity->getLink()->getId();
            }

            //Pass through all entity associations
            $metaData = $this->em->getClassMetadata(get_class($entity));
            foreach ($metaData->getAssociationMappings() as $association) {
                $targetClass = $association['targetEntity'];

                //Skip already set WidgetMap association
                if ($targetClass == WidgetMap::class) {
                    continue;
                }

                //If Widget has OneToOne or ManyToOne association, store target entity id to construct
                //a single query for this entity type
                if ($metaData->isSingleValuedAssociation($association['fieldName'])
                    && !$widgetCached
                ) {
                    //If target Entity is not null, treat it
                    if ($targetEntity = $this->accessor->getValue($entity, $association['fieldName'])) {
                        $associatedEntities[$targetClass]['id']['unsorted']['entitiesToWarm'][] = new AssociatedEntityToWarm(
                            AssociatedEntityToWarm::TYPE_MANY_TO_ONE,
                            $entity,
                            $association['fieldName'],
                            $targetEntity->getId()
                        );
                    }
                }

                //If Widget has OneToMany association, store owner entity id and mappedBy value
                //to construct a single query for this entity type
                elseif ($metaData->isCollectionValuedAssociation($association['fieldName'])) {

                    //Even if Widget is cached, we need its Criterias used before cache call
                    if (!$widgetCached || $targetClass === Criteria::class) {

                        //If Collection is not null, treat it
                        if ($this->accessor->getValue($entity, $association['fieldName'])) {

                            //Don't use Collection getter directly and override Collection
                            //default behaviour to avoid useless query
                            $getter = 'get'.ucwords($association['fieldName']);
                            $entity->$getter()->setDirty(false);
                            $entity->$getter()->setInitialized(true);
                            $orderByToken = 'unsorted';

                            if (isset($association['orderBy'])) {
                                $orderByToken = implode($association['orderBy']);
                                $associatedEntities[$targetClass][$association['mappedBy']][$orderByToken]['orderBy'] = $association['orderBy'];
                            }

                            $associatedEntities[$targetClass][$association['mappedBy']][$orderByToken]['entitiesToWarm'][] = new AssociatedEntityToWarm(
                                AssociatedEntityToWarm::TYPE_ONE_TO_MANY,
                                $entity,
                                $association['fieldName'],
                                $entity->getId()
                            );
                        }
                    }
                }
            }
        }

        $newEntities = $this->setAssociatedEntities($associatedEntities);
        $this->setPagesForLinks($linkIds);

        //Recursive call if previous has return new entities to warm
        if ($newEntities) {
            $this->extractAssociatedEntities($newEntities);
        }
    }