JobController   A
last analyzed

Complexity

Total Complexity 24

Size/Duplication

Total Lines 157
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 24
lcom 1
cbo 10
dl 0
loc 157
ccs 0
cts 114
cp 0
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
B overviewAction() 0 44 5
C detailsAction() 0 57 11
B retryJobAction() 0 30 6
A getEm() 0 4 1
1
<?php
2
3
namespace JMS\JobQueueBundle\Controller;
4
5
use Doctrine\Common\Util\ClassUtils;
6
use Doctrine\ORM\EntityManager;
7
use JMS\JobQueueBundle\Entity\Job;
8
use JMS\JobQueueBundle\Entity\Repository\JobManager;
9
use JMS\JobQueueBundle\View\JobFilter;
10
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
11
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
12
use Symfony\Component\HttpFoundation\RedirectResponse;
13
use Symfony\Component\HttpFoundation\Request;
14
use Symfony\Component\HttpKernel\Exception\HttpException;
15
16
class JobController extends AbstractController
17
{
18
    private $jobManager;
19
20
    public function __construct(JobManager $jobManager)
21
    {
22
        $this->jobManager = $jobManager;
23
    }
24
25
    /**
26
     * @Route("/", name = "jms_jobs_overview")
27
     */
28
    public function overviewAction(Request $request)
29
    {
30
        $jobFilter = JobFilter::fromRequest($request);
31
32
        $qb = $this->getEm()->createQueryBuilder();
33
        $qb->select('j')->from('JMSJobQueueBundle:Job', 'j')
34
            ->where($qb->expr()->isNull('j.originalJob'))
35
            ->orderBy('j.id', 'desc');
36
37
        $lastJobsWithError = $jobFilter->isDefaultPage() ? $this->jobManager->findLastJobsWithError(5) : [];
38
        foreach ($lastJobsWithError as $i => $job) {
39
            $qb->andWhere($qb->expr()->neq('j.id', '?' . $i));
40
            $qb->setParameter($i, $job->getId());
41
        }
42
43
        if (!empty($jobFilter->command)) {
44
            $qb->andWhere($qb->expr()->orX(
45
                $qb->expr()->like('j.command', ':commandQuery'),
46
                $qb->expr()->like('j.args', ':commandQuery')
47
            ))
48
                ->setParameter('commandQuery', '%' . $jobFilter->command . '%');
49
        }
50
51
        if (!empty($jobFilter->state)) {
52
            $qb->andWhere($qb->expr()->eq('j.state', ':jobState'))
53
                ->setParameter('jobState', $jobFilter->state);
54
        }
55
56
        $perPage = 50;
57
58
        $query = $qb->getQuery();
59
        $query->setMaxResults($perPage + 1);
60
        $query->setFirstResult(($jobFilter->page - 1) * $perPage);
61
62
        $jobs = $query->getResult();
63
64
        return $this->render('@JMSJobQueue/Job/overview.html.twig', array(
65
            'jobsWithError' => $lastJobsWithError,
66
            'jobs' => array_slice($jobs, 0, $perPage),
67
            'jobFilter' => $jobFilter,
68
            'hasMore' => count($jobs) > $perPage,
69
            'jobStates' => Job::getStates(),
70
        ));
71
    }
72
73
    /**
74
     * @Route("/{id}", name = "jms_jobs_details")
75
     */
76
    public function detailsAction(Job $job)
77
    {
78
        $relatedEntities = array();
79
        foreach ($job->getRelatedEntities() as $entity) {
80
            $class = ClassUtils::getClass($entity);
81
            $relatedEntities[] = array(
82
                'class' => $class,
83
                'id' => json_encode($this->getDoctrine()->getManagerForClass($class)->getClassMetadata($class)->getIdentifierValues($entity)),
84
                'raw' => $entity,
85
            );
86
        }
87
88
        $statisticData = $statisticOptions = array();
89
        if ($this->getParameter('jms_job_queue.statistics')) {
90
            $dataPerCharacteristic = array();
91
            foreach ($this->getDoctrine()->getManagerForClass(Job::class)->getConnection()->query("SELECT * FROM jms_job_statistics WHERE job_id = " . $job->getId()) as $row) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Doctrine\Persistence\ObjectManager as the method getConnection() does only exist in the following implementations of said interface: Doctrine\ORM\Decorator\EntityManagerDecorator, Doctrine\ORM\EntityManager.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
92
                $dataPerCharacteristic[$row['characteristic']][] = array(
93
                    // hack because postgresql lower-cases all column names.
94
                    array_key_exists('createdAt', $row) ? $row['createdAt'] : $row['createdat'],
95
                    array_key_exists('charValue', $row) ? $row['charValue'] : $row['charvalue'],
96
                );
97
            }
98
99
            if ($dataPerCharacteristic) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $dataPerCharacteristic of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
100
                $statisticData = array(array_merge(array('Time'), $chars = array_keys($dataPerCharacteristic)));
101
                $startTime = strtotime($dataPerCharacteristic[$chars[0]][0][0]);
102
                $endTime = strtotime($dataPerCharacteristic[$chars[0]][count($dataPerCharacteristic[$chars[0]]) - 1][0]);
103
                $scaleFactor = $endTime - $startTime > 300 ? 1 / 60 : 1;
104
105
                // This assumes that we have the same number of rows for each characteristic.
106
                for ($i = 0, $c = count(reset($dataPerCharacteristic)); $i < $c; $i++) {
107
                    $row = array((strtotime($dataPerCharacteristic[$chars[0]][$i][0]) - $startTime) * $scaleFactor);
108
                    foreach ($chars as $name) {
109
                        $value = (float) $dataPerCharacteristic[$name][$i][1];
110
111
                        switch ($name) {
112
                            case 'memory':
113
                                $value /= 1024 * 1024;
114
                                break;
115
                        }
116
117
                        $row[] = $value;
118
                    }
119
120
                    $statisticData[] = $row;
121
                }
122
            }
123
        }
124
125
        return $this->render('@JMSJobQueue/Job/details.html.twig', array(
126
            'job' => $job,
127
            'relatedEntities' => $relatedEntities,
128
            'incomingDependencies' => $this->jobManager->getIncomingDependencies($job),
129
            'statisticData' => $statisticData,
130
            'statisticOptions' => $statisticOptions,
131
        ));
132
    }
133
134
    /**
135
     * @Route("/{id}/retry", name = "jms_jobs_retry_job")
136
     */
137
    public function retryJobAction(Job $job)
138
    {
139
        $state = $job->getState();
140
141
        if (
142
            Job::STATE_FAILED !== $state &&
143
            Job::STATE_TERMINATED !== $state &&
144
            Job::STATE_INCOMPLETE !== $state
145
        ) {
146
            throw new HttpException(400, 'Given job can\'t be retried');
147
        }
148
149
        $retryJob = clone $job;
150
151
        //@see https://github.com/schmittjoh/JMSJobQueueBundle/issues/189
152
        $relatedEntities = $job->getRelatedEntities();
153
154
        if (!empty($relatedEntities)) {
155
            foreach ($relatedEntities as $relatedEntity) {
156
                $retryJob->addRelatedEntity($relatedEntity);
157
            }
158
        }
159
160
        $this->getEm()->persist($retryJob);
161
        $this->getEm()->flush();
162
163
        $url = $this->generateUrl('jms_jobs_details', ['id' => $retryJob->getId()]);
164
165
        return new RedirectResponse($url, 201);
166
    }
167
168
    private function getEm(): EntityManager
169
    {
170
        return $this->getDoctrine()->getManagerForClass(Job::class);
171
    }
172
}
173