SearchService   A
last analyzed

Complexity

Total Complexity 18

Size/Duplication

Total Lines 142
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
wmc 18
lcom 1
cbo 5
dl 0
loc 142
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A analyzeRequest() 0 10 2
A search() 0 17 3
A searchRepository() 0 25 2
A searchUser() 0 15 1
B format() 0 43 9
1
<?php
2
3
namespace KI\CoreBundle\Service;
4
5
use Doctrine\ORM\EntityManager;
6
use Doctrine\ORM\EntityRepository;
7
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
8
9
class SearchService
10
{
11
    protected $manager;
12
    protected $userRepository;
13
14
    public function __construct(EntityManager $manager, EntityRepository $userRepository)
0 ignored issues
show
Bug introduced by
You have injected the EntityManager via parameter $manager. This is generally not recommended as it might get closed and become unusable. Instead, it is recommended to inject the ManagerRegistry and retrieve the EntityManager via getManager() each time you need it.

The EntityManager might become unusable for example if a transaction is rolled back and it gets closed. Let’s assume that somewhere in your application, or in a third-party library, there is code such as the following:

function someFunction(ManagerRegistry $registry) {
    $em = $registry->getManager();
    $em->getConnection()->beginTransaction();
    try {
        // Do something.
        $em->getConnection()->commit();
    } catch (\Exception $ex) {
        $em->getConnection()->rollback();
        $em->close();

        throw $ex;
    }
}

If that code throws an exception and the EntityManager is closed. Any other code which depends on the same instance of the EntityManager during this request will fail.

On the other hand, if you instead inject the ManagerRegistry, the getManager() method guarantees that you will always get a usable manager instance.

Loading history...
15
    {
16
        $this->manager        = $manager;
17
        $this->userRepository = $userRepository;
18
    }
19
20
    /**
21
     * @param  string $search La recherche complète venant de la requête
22
     * @return array  $category, $criteria
23
     * @throws BadRequestHttpException Si le format n'est pas bon
24
     */
25
    public function analyzeRequest($search)
26
    {
27
        $match = [];
28
29
        if (!preg_match('/(.*)\/(.*)/', $search, $match)) {
30
            throw new BadRequestHttpException('Syntaxe de la recherche erronée');
31
        }
32
33
        return [$match[1], $match[2]];
34
    }
35
36
    /**
37
     * Recherche au travers des repos suivant la catégorie voulue
38
     * @param  string $category La catégorie d'objets recherchés
39
     * @param  string $criteria Le critère de recherche
40
     * @return array            Un tableau de résultats formaté
41
     * @see $this->format()
42
     * @throws BadRequestHttpException Si la catégorie d'objets recherchés n'existe pas
43
     */
44
    public function search($category, $criteria)
45
    {
46
        switch ($category) {
47
        case 'User':
48
            return ['users' => $this->searchUser($criteria)];
49
        case '':
50
            return [
51
                'clubs'   => $this->searchRepository('KIUserBundle:Club', $criteria, 'fullName'),
52
                'courses' => $this->searchRepository('KIPublicationBundle:Course', $criteria),
53
                'files'   => $this->searchRepository('KIPonthubBundle:PonthubFile', $criteria),
54
                'posts'   => $this->searchRepository('KIPublicationBundle:Post', $criteria),
55
                'users'   => $this->searchUser($criteria),
56
            ];
57
        default:
58
            throw new BadRequestHttpException('Syntaxe de la recherche erronée');
59
        }
60
    }
61
62
    // On fouille un repo à la recherche d'entités correspondantes au nom
63
    protected function searchRepository($repositoryName, $criteria, $additionnalField = null) {
64
        $repository = $this->manager->getRepository($repositoryName);
65
        $qb = $repository->createQueryBuilder('e');
66
67
        // Si on a affaire au repo club on peut chercher sur le nom complet
68
        if ($additionnalField !== null) {
69
            $qb
70
                ->orWhere('SOUNDEX(e.'.$additionnalField.') = SOUNDEX(:search)')
71
                ->orwhere('e.'.$additionnalField.' LIKE :searchlike')
72
            ;
73
        }
74
75
        $results = $qb
76
            ->orwhere('SOUNDEX(e.name) = SOUNDEX(:search)')
77
            ->orwhere('e.name LIKE :searchlike')
78
            ->andwhere('e.name <> \'message\'')
79
            ->setParameter('search', $criteria)
80
            ->setParameter('searchlike', '%'.$criteria.'%')
81
            ->setMaxResults(10)
82
            ->getQuery()
83
            ->getResult()
84
        ;
85
86
        return $this->format($results, $criteria);
87
    }
88
89
    // La recherche d'user demande une fonction particulière (champs différents, acronyme...)
90
    protected function searchUser($criteria) {
91
        $qb = $this->userRepository->createQueryBuilder('e');
92
93
        $results = $qb
94
            ->orwhere('SOUNDEX(CONCAT(e.firstName, CONCAT(\' \', CONCAT(e.lastName, CONCAT(\' \', COALESCE(e.nickname, \'\')))))) = SOUNDEX(:search)')
95
            ->orwhere('CONCAT(e.firstName, CONCAT(\' \', CONCAT(e.lastName, CONCAT(\' \', COALESCE(e.nickname, \'\'))))) LIKE :searchlike')
96
            ->setParameter('search', $criteria)
97
            ->setParameter('searchlike', '%'.$criteria.'%')
98
            ->setMaxResults(10)
99
            ->getQuery()
100
            ->getResult()
101
        ;
102
103
        return $this->format($results, $criteria);
104
    }
105
106
    // On formate et ordonne les données pour le retour
107
    protected function format($results, $criteria) {
108
        $return = $score = [];
109
        $percent = 0;
110
111
        foreach ($results as $result) {
112
            $name = $result->getName();
113
            $class = preg_replace('/.*\\\/', '', get_class($result));
114
            $item = [
115
                'name' => $name,
116
                'slug' => $result->getSlug(),
117
                'type' => $class
118
            ];
119
120
            // On sort les objets non actifs
121
            if ($class == 'Course' && ($result->getActive() === null || !$result->getActive())) {
122
                continue;
123
            }
124
            // On précise des choses en plus pour les utilisateurs
125
            if ($class == 'User') {
126
                $item['balance'] = $result->getBalance();
127
                $item['promo'] = $result->getPromo();
128
            }
129
130
            // Pour les épisodes, on ajoute une référence à l'entité parent
131
            if ($class == 'Episode') {
132
                $item['parent'] = $result->getSerie()->getSlug();
133
            }
134
135
            // Si une image existe on la rajoute
136
            if (method_exists($result, 'imageUrl') && $result->imageUrl() !== null) {
137
                $item['image_url'] = $result->imageUrl();
138
            }
139
140
            $return[] = $item;
141
142
            // On trie par pertinence
143
            similar_text(strtolower($name), strtolower($criteria), $percent);
144
            $score[] = $percent;
145
        }
146
        array_multisort($score, SORT_DESC, $return);
147
148
        return $return;
149
    }
150
}
151