Просто так что у меня уже есть какая-то запись. У меня есть поле даты «создано», и теперь я хочу получить следующую и предыдущую запись по дате.

Получил работу:

    $qb = $this->createQueryBuilder('a');

    $next = $qb->expr()->gt('a.created', ':date');
    $prev = $qb->expr()->lt('a.created', ':date');

    $prev = $qb->select('partial a.{id,title,created}')
        ->where($prev)
        ->setParameter('date', $date)
        ->orderBy('a.created', 'DESC')
        ->setMaxResults(1)
        ->getQuery()
        ->getArrayResult();

    $next = $qb->select('partial a.{id,title,created}')
        ->where($next)
        ->setParameter('date', $date)
        ->orderBy('a.created', 'DESC')
        ->setMaxResults(1)
        ->getQuery()
        ->getArrayResult();

Он работает очень хорошо. Но это 2 вопроса к базе данных. Мне нужна одна. Я могу сделать это, просто присоединившись и т. Д., Но когда нет следующего или предыдущего, у меня просто пустой массив.

Любая идея?

6
Artur Schroeder 23 Фев 2015 в 12:44

3 ответа

Лучший ответ

Ответ Александра близок. Когда вы запрашиваете id < 2 LIMIT 1, он возвращает 1, но если вы запрашиваете id < 5 LIMIT 1, это также возвращает 1. Это потому, что он возвращает 1, 2, 3, 4 и принимает первый элемент, которым является 1, а не необходимый 4.

Просто добавьте ORDER BY id DESC, чтобы получить предыдущий элемент. Это вернет 4, 3, 2, 1, а LIMIT 1 вернет 4 или предыдущий элемент.

$query = $em->createNativeQuery('SELECT id FROM users WHERE
        id = (SELECT id FROM users WHERE id > 2 LIMIT 1)
        OR
        id = (SELECT id FROM users WHERE id < 2 ORDER BY id DESC LIMIT 1)', $rsm);
6
blackbishop 9 Апр 2015 в 19:34

Вы можете сделать это с помощью собственных запросов:

/** @var EntityManager $em */
$em = $this->getDoctrine()->getManager();
$rsm = new ResultSetMapping();
$rsm->addScalarResult('id', 'id');

$query = $em->createNativeQuery('SELECT id FROM users WHERE
        id = (SELECT id FROM users WHERE id > 2 LIMIT 1)
    OR
        id = (SELECT id FROM users WHERE id < 2 LIMIT 1)', $rsm);
$users = $query->execute();

В переменной $ users будет следующий массив:

[
    ['id' => 1]
    ['id' => 3]
]

Подробнее здесь http://doctrine-orm.readthedocs.org/ ru / latest / reference / native-sql.html

2
Alexandr Lensky 14 Мар 2015 в 00:08

Альтернативный подход к собственному SQL при использовании репозитория сущностей ORM.

namespace EntityNamespace;

use Doctrine\ORM\EntityRepository;

class MyEntityRepository extends EntityRepository
{

    /**
     * @param int $id
     * @return array|int[]
     */
    public function filterNextPrevious($id)
    {
        $expr = $this->_em->getExpressionBuilder();
        $qbNext = $this->createQueryBuilder('a')
           ->select(['MIN(a.id)'])
           ->where($expr->gt('a.id', ':id'));
        $qbPrevious = $this->createQueryBuilder('b')
            ->select(['MAX(b.id)'])
            ->where($expr->lt('b.id', ':id'));
        $query = $this->createQueryBuilder('m')
            ->select(['m.id'])
            ->where($expr->orX(
                $expr->eq('m.id', '(' . $qbNext->getDQL() . ')'),
                $expr->eq('m.id', '(' . $qbPrevious->getDQL() . ')')
            ))
            ->setParameter('id', $id)
            ->addOrderBy('m.id', 'ASC')
            ->getQuery();
       //optionally enable caching
       //$query->useQueryCache(true)->useResultCache(true, 3600);    

        return $query->getScalarResult();
    }
}

Результирующий DQL:

SELECT m.id 
FROM EntityNamespace\Entity m 
WHERE m.id = (
    SELECT MIN(a.id) 
    FROM EntityNamespace\Entity a 
    WHERE a.id > :id
) 
OR m.id = (
    SELECT MAX(b.id) 
    FROM EntityNamespace\Entity b 
    WHERE b.id < :id
) 
ORDER BY m.id ASC

(необязательно использовать вместо построителя запросов через $this->_em->createQuery($DQL))

Результирующий набор данных:

array(2) {
  [0]=>
  array(1) {
    ["id"]=>
    string(4) "5869"
  }
  [1]=>
  array(1) {
    ["id"]=>
    string(4) "5871"
  }
}

Другой альтернативой использованию условия WHERE IN() является использование подзапросов SELECT и, возможно, добавление итогового COUNT() в базовую таблицу.

Это позволит вам присвоить результирующее значение соответствующему ключу и отказаться от предложения ORDER BY.

$expr = $this->_em->getExpressionBuilder();
$next = $this->createQueryBuilder('a')
    ->select($expr->min('a.id'))
    ->where($expr->gt('a.id', ':id'));
$previous = $this->createQueryBuilder('b')
    ->select($expr->max('b.id'))
    ->where($expr->lt('b.id', ':id'));
$query = $this->createQueryBuilder('o')
    ->select('COUNT(o.id) as total')
    ->addSelect('(' . $previous->getDQL() . ') as previous')
    ->addSelect('(' . $next->getDQL() . ') as next')
    ->setParameter('id', $id)
    ->getQuery();

/* optionally enable caching
 * $query->useQueryCache(true)->useResultCache(true, 3600);
 */

return $query->getSingleResult();

Результат DQL:

SELECT 
    COUNT(o.id) as total, 
    (
     SELECT MAX(b.id) 
     FROM EntityNamespace\Entity b 
     WHERE b.id < :id
    ) as previous, 
    (
     SELECT MIN(a.id) 
     FROM EntityNamespace\Entity a 
     WHERE a.id > :id
    ) as next 
FROM EntityNamespace\Entity o

Результирующий набор данных:

array(3) {
    ["total"]=>
    string(4) "63156"
    ["previous"]=>
    string(4) "5869"
    ["next"]=>
    string(4) "5871"
}
5
Will B. 5 Апр 2019 в 19:16