Получить «ранг» с помощью Doctrine 2 ORM и OneToOne в репозитории - PullRequest
1 голос
/ 20 октября 2019

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

Моя цель состоит в том, чтобы извлекать / получать каждый рейтинг «проектов» на основе их «баллов».

Рассматривать "Score" как int и со значениями, такими как 1,2,6,4,8,10,200 и т. Д.

Ранг будет выглядеть следующим образом:

Ранг- Оценка

  1. 200
  2. 10
  3. 8
  4. 6

Для моего вопроса, чтобы быть простым иочистить, насколько это возможно, я переименовал мои фактические таблицы / сущности, как показано ниже:

MainEntity (main_table):

/**
* @ORM\Id
* @ORM\Column(name="id")
* @ORM\GeneratedValue
*/
protected $id;

// other fields, un-related to this question

/**
* @ORM\OneToOne(targetEntity="Application\Entity\SecondTable", mappedBy="second_table_data")
*/
protected $second_table;

/**
* @ORM\OneToOne(targetEntity="Application\Entity\ThirdTable", mappedBy="third_table_data")
* 
*/
protected $third_table;

SecondEntity (second_table):

/**
* @ORM\Id
* @ORM\Column(name="id")
* @ORM\GeneratedValue
*/
protected $id;

// other fields, un-related to this question

/**
* @ORM\OneToOne(targetEntity="Application\Entity\SecondTable", inversedBy="second_table")
* @ORM\JoinColumn(name="project_id", referencedColumnName="id")
*/
private $second_table_data;

ThirdEntity (third_table):

/**
* @ORM\Id
* @ORM\Column(name="id")
* @ORM\GeneratedValue
*/
protected $id;

// other fields, un-related to this question

/** 
 * @ORM\Column(name="score")  
 */
protected $score;

/**
* @ORM\OneToOne(targetEntity="Application\Entity\ThirdTable", inversedBy="third_table")
* @ORM\JoinColumn(name="project_id", referencedColumnName="id")
*/
private $third_table_data;

И функция хранилища для выбора «всех проектов» в порядке их оценки:

public function findAllProjects()
{
    $entityManager = $this->getEntityManager();

    $queryBuilder = $entityManager->createQueryBuilder();

    $queryBuilder->select('u')
       ->from(MainEntity::class, 'u')
       ->leftJoin('u.third_table', 't')
       ->orderBy('t.score', 'DESC');

    return $queryBuilder->getQuery()->getResult();
}

Это прекрасно работает (я считаю), поскольку я получаю все "проекты" из main_table + second_table + third_table на основе их "project_id".

Однако проблема в том, что я не могу найти способ правильно рассчитать или получить номер ранга каждого проекта. Я также попытался использовать «foreach» и использовать «index» в качестве «rank», но это не будет работать должным образом, потому что я использую ORMPaginator , поэтому каждый раз, когда вы нажимаете «страницу», которая «foreach» »index "будет сброшен с 0.

Я надеюсь, что вопрос достаточно ясен и дает вам четкое понимание моей проблемы.

Пожалуйста, посоветуйте, как мне этого добиться, и если весь мой подход кэто неправильно, пожалуйста, укажите.

Каждый совет / подсказка / решение высоко ценится.

1 Ответ

0 голосов
/ 24 октября 2019

Прежде всего, обратите особое внимание на типы столбцов. Все ваши столбцы создаются доктриной как varchar(255), что не является идеальным, если это те числа, для которых вы хотите сделать order by. Это фрагмент вывода vendor/bin/doctrine-module orm:schema-tool:create --dump-sql:

CREATE TABLE main_entity (
  id VARCHAR(255) NOT NULL,
  PRIMARY KEY(id)
) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;

Итак, первое, что вы должны сделать, это добавить типы столбцов в ваши столбцы:

/**
 * @ORM\Id
 * @ORM\Column(name="id", type="integer")
 * @ORM\GeneratedValue
 */
protected $id;

Я использовал эти данные:

third_table                      main_entity
+----+------------+-------+      +----+
| id | project_id | score |      | id |
+----+------------+-------+      +----+
| 1  | 1          | 8     |      | 1  |
| 2  | 2          | 10    |      | 2  |
| 3  | 3          | 6     |      | 3  |
| 4  | 4          | 200   |      | 4  |
+----+------------+-------+      +----+

Что касается ранга, который по существу является номером строки, SQL-запрос:

SELECT 
   ROW_NUMBER() OVER (ORDER BY t.score DESC) AS r_rank,
   t.score AS score 
FROM main_entity m 
LEFT JOIN third_table t ON m.id = t.project_id 
ORDER BY t.score DESC
-- LIMIT 2 OFFSET 2 -- Limits for pagination
without limits           with limits
+--------+-------+       +--------+-------+
| r_rank | score |       | r_rank | score |
+--------+-------+       +--------+-------+
| 1      | 200   |       | 3      | 8     |
| 2      | 10    |       | 4      | 6     |
| 3      | 8     |       +--------+-------+
| 4      | 6     |
+--------+-------+

К сожалению, ROW_NUMBER не реализован для SQL-запросов. Если вы попробуете это:

$queryBuilder->select(['ROW_NUMBER() OVER (ORDER BY t.score DESC)','t.score'])
        ->from(MainEntity::class, 'u')
        ->leftJoin('u.third_table', 't')
        ->orderBy('t.score', 'DESC');
$queryBuilder->getQuery()->getResult();

Вы получите следующее сообщение об ошибке: [Syntax Error] line 0, col 7: Error: Expected known function, got 'ROW_NUMBER' Альтернативой может быть:

$sql = 'SELECT 
            ROW_NUMBER() OVER (ORDER BY t.score DESC) AS r_rank,
            t.score AS score 
        FROM main_entity m 
        LEFT JOIN third_table t ON m.id = t.project_id 
        ORDER BY t.score DESC';
$results = $this->entityManager->getConnection()->executeQuery($sql);

Я вижу, что что-то похожее в ходунках-пагинаторах(?! , даже с нумератором страниц.

Если вы знаете размер страницы и текущую страницу, вы можете рассчитать ранг:

$pageSize = 2;
$currentPage = 1;
$queryBuilder = $this->entityManager->createQueryBuilder();
$queryBuilder->select('t.score') // Here I use only the t.score, but you could put the whole class
        ->from(MainEntity::class, 'u')
        ->leftJoin('u.third_table', 't')
        ->orderBy('t.score', 'DESC');
$queryBuilder->setMaxResults($pageSize);
$queryBuilder->setFirstResult(($currentPage - 1) * $pageSize);

$paginator = new \Doctrine\ORM\Tools\Pagination\Paginator($queryBuilder->getQuery());
$adapter = new \DoctrineORMModule\Paginator\Adapter\DoctrinePaginator($paginator);

$results = [];
$currentItem = 1 + ($currentPage - 1) * $pageSize;
foreach($adapter->getPaginator()->getIterator()->getArrayCopy() as $result){
    $results[] = [
        'rank' => $currentItem++,
        'item' => $result
    ];
}
var_dump($results);

Результат:

$pageSize = 2;                                        $pageSize = 2;
$currentPage = 1;                                     $currentPage = 2;

Array                                                 Array
(                                                     (
    [0] => Array                                          [0] => Array
        (                                                     (
            [rank] => 1                                           [rank] => 3
            [item] => Array                                       [item] => Array
                (                                                     (
                    [score] => 200                                        [score] => 8
                )                                                     )

        )                                                     )

    [1] => Array                                          [1] => Array
        (                                                     (
            [rank] => 2                                           [rank] => 4
            [item] => Array                                       [item] => Array
                (                                                     (
                    [score] => 10                                         [score] => 6
                )                                                     )

        )                                                     )

)                                                     )
...