Passed
Push — 0.x ( fc5333...ca02fa )
by Pavel
03:09
created

VacancyRepository::applyPaginationHints()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 7
dl 0
loc 16
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 2
1
<?php
2
3
/*
4
 * This file is part of the Veslo project <https://github.com/symfony-doge/veslo>.
5
 *
6
 * (C) 2019 Pavel Petrov <[email protected]>.
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 *
11
 * @license https://opensource.org/licenses/GPL-3.0 GPL-3.0
12
 */
13
14
declare(strict_types=1);
15
16
namespace Veslo\AnthillBundle\Entity\Repository;
17
18
use Doctrine\Common\Collections\Criteria;
19
use Doctrine\ORM\Cache;
20
use Doctrine\ORM\QueryBuilder;
21
use Knp\Component\Pager\Pagination\AbstractPagination;
22
use Knp\Component\Pager\PaginatorInterface;
23
use Symfony\Component\OptionsResolver\OptionsResolver;
24
use Veslo\AnthillBundle\Entity\Vacancy;
25
use Veslo\AnthillBundle\Entity\Vacancy\Category;
26
use Veslo\AppBundle\Dto\Paginator\CriteriaDto as PaginationCriteria;
27
use Veslo\AppBundle\Entity\Repository\BaseEntityRepository;
28
use Veslo\AppBundle\Entity\Repository\PaginateableInterface;
29
30
/**
31
 * Vacancy repository
32
 */
33
class VacancyRepository extends BaseEntityRepository implements PaginateableInterface
34
{
35
    /**
36
     * A hint for the pagination building process to include a specific category match statement
37
     *
38
     * Usage example:
39
     * ```
40
     * $paginationCriteria->addHint(VacancyRepository::PAGINATION_HINT_CATEGORY, $category);
41
     * ```
42
     *
43
     * @const string
44
     */
45
    public const PAGINATION_HINT_CATEGORY = 'category';
46
47
    /**
48
     * Modifies vacancy search query to provide data in small bunches
49
     *
50
     * @var PaginatorInterface
51
     */
52
    private $paginator;
53
54
    /**
55
     * Options for vacancy repository, ex. page cache time
56
     *
57
     * @var array
58
     */
59
    private $options;
60
61
    /**
62
     * {@inheritdoc}
63
     */
64
    public function setPaginator(PaginatorInterface $paginator): void
65
    {
66
        $this->paginator = $paginator;
67
    }
68
69
    /**
70
     * Sets options for vacancy repository
71
     *
72
     * @param array $options Options for vacancy repository, ex. page cache time
73
     *
74
     * @return void
75
     */
76
    public function setOptions(array $options): void
77
    {
78
        $optionsResolver = new OptionsResolver();
79
        $optionsResolver->setDefaults(
80
            [
81
                'cache_result_namespace' => md5(__CLASS__),
82
                'cache_result_lifetime'  => 300,
83
            ]
84
        );
85
86
        $this->options = $optionsResolver->resolve($options);
87
    }
88
89
    /**
90
     * Returns vacancy by specified SEO-friendly identifier
91
     *
92
     * @param string $slug SEO-friendly vacancy identifier
93
     *
94
     * @return Vacancy|null
95
     */
96
    public function findBySlug(string $slug): ?Vacancy
97
    {
98
        $criteria = new Criteria();
99
        $criteria->andWhere($criteria->expr()->eq('e.slug', $slug));
100
101
        return $this->getQuery($criteria)->getOneOrNullResult();
102
    }
103
104
    /**
105
     * Returns vacancy by roadmap name and identifier on external job website
106
     *
107
     * @param string $roadmapName        Roadmap name
108
     * @param string $externalIdentifier Identifier on external job website
109
     *
110
     * @return Vacancy|null
111
     */
112
    public function findByRoadmapNameAndExternalIdentifier(string $roadmapName, string $externalIdentifier): ?Vacancy
113
    {
114
        $criteria = new Criteria();
115
        $criteria
116
            ->andWhere($criteria->expr()->eq('e.roadmapName', $roadmapName))
117
            ->andWhere($criteria->expr()->eq('e.externalIdentifier', $externalIdentifier))
118
        ;
119
120
        $query = $this->getQuery($criteria);
121
122
        return $query->getOneOrNullResult();
123
    }
124
125
    /**
126
     * {@inheritdoc}
127
     *
128
     * Prevents exposition of database layer details to other application layers
129
     * All doctrine-related things (ex. criteria building) remains encapsulated
130
     */
131
    public function getPagination(PaginationCriteria $criteria): AbstractPagination
132
    {
133
        $queryBuilder = $this->createQueryBuilder('e');
134
        $queryBuilder
135
            // fetch joins for caching.
136
            ->innerJoin('e.company', 'c')
137
            ->addSelect('c')
138
            // inner join for company is required; due to fixtures loading logic there are some cases
139
            // when a deletion date can be set in company and not present in related vacancies at the same time.
140
            // it leads to inconsistent state in test environment; normally, a soft delete logic should be
141
            // properly applied for all relations at once.
142
            ->leftJoin('e.categories', 'ct')
143
            ->addSelect('ct')
144
            ->orderBy('e.id', Criteria::DESC)
145
        ;
146
147
        $queryBuilder = $this->applyPaginationHints($queryBuilder, $criteria);
148
149
        list($page, $limit, $options) = [$criteria->getPage(), $criteria->getLimit(), $criteria->getOptions()];
150
151
        $query = $queryBuilder->getQuery();
152
        $query
153
            ->setCacheable(true)
154
            ->setCacheMode(Cache::MODE_NORMAL)
155
        ;
156
157
        /** @var AbstractPagination $pagination */
158
        $pagination = $this->paginator->paginate($query, $page, $limit, $options);
159
160
        return $pagination;
161
    }
162
163
    /**
164
     * Returns a query builder instance modified according to the pagination hints, provided by criteria
165
     *
166
     * @param QueryBuilder       $queryBuilder A query builder instance
167
     * @param PaginationCriteria $criteria     Pagination criteria
168
     *
169
     * @return QueryBuilder
170
     */
171
    private function applyPaginationHints(QueryBuilder $queryBuilder, PaginationCriteria $criteria): QueryBuilder
172
    {
173
        $paginationHints = $criteria->getHints();
174
175
        // Hint: category.
176
        if (array_key_exists(self::PAGINATION_HINT_CATEGORY, $paginationHints)) {
177
            /** @var Category $category */
178
            $category = $paginationHints[self::PAGINATION_HINT_CATEGORY];
179
180
            $queryBuilder
181
                ->andWhere($queryBuilder->expr()->isMemberOf(':category', 'e.categories'))
182
                ->setParameter(':category', $category)
183
            ;
184
        }
185
186
        return $queryBuilder;
187
    }
188
}
189