GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( a7d97d...0097ae )
by Андрей
02:25
created

DoctrineWorkflowStory::createCurrentStep()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 32
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 32
rs 8.8571
cc 2
eloc 20
nc 2
nop 7
1
<?php
2
/**
3
 * @link    https://github.com/old-town/workflow-doctrine
4
 * @author  Malofeykin Andrey  <[email protected]>
5
 */
6
namespace OldTown\Workflow\Spi\Doctrine;
7
8
use DateTime;
9
use OldTown\PropertySet\PropertySetManager;
10
use OldTown\Workflow\Spi\Doctrine\Entity\HistoryStep;
11
use ReflectionClass;
12
use OldTown\Workflow\Query\WorkflowExpressionQuery;
13
use OldTown\Workflow\Spi\StepInterface;
14
use OldTown\Workflow\Spi\WorkflowStoreInterface;
15
use Doctrine\ORM\EntityManagerInterface;
16
use OldTown\Workflow\Spi\Doctrine\EntityManagerFactory\EntityManagerFactoryInterface;
17
use OldTown\Workflow\Spi\WorkflowEntryInterface;
18
use OldTown\Workflow\Spi\Doctrine\Entity\DefaultEntry;
19
use OldTown\Workflow\Spi\Doctrine\Entity\CurrentStep;
20
use OldTown\Workflow\Spi\Doctrine\EntityRepository\StepRepository;
21
use OldTown\Workflow\Spi\Doctrine\Entity\EntryInterface;
22
use OldTown\Workflow\Spi\Doctrine\Entity\CurrentStepInterface;
23
use OldTown\Workflow\Spi\Doctrine\Entity\HistoryStepInterface;
24
use SplObjectStorage;
25
use OldTown\Workflow\Spi\Doctrine\Entity\AbstractStep;
26
27
/**
28
 * Class DoctrineWorkflowStory
29
 *
30
 * @package OldTown\Workflow\Spi\Doctrine
31
 */
32
class DoctrineWorkflowStory implements WorkflowStoreInterface
33
{
34
    /**
35
     * Ключ по котормоу можно получить настройки для работы с фабрикой создающей менеджер сущностей
36
     *
37
     * @var string
38
     */
39
    const ENTITY_MANAGER_FACTORY = 'entityManagerFactory';
40
41
    /**
42
     * Ключ по котормоу можно получить имя класса фабрика создающей менеджер сущностей
43
     *
44
     * @var string
45
     */
46
    const ENTITY_MANAGER_FACTORY_NAME = 'name';
47
48
    /**
49
     * Ключ по котормоу можно получить параметры для создания менеджера сущностей
50
     *
51
     * @var string
52
     */
53
    const ENTITY_MANAGER_FACTORY_OPTIONS = 'options';
54
55
    /**
56
     * @var EntityManagerInterface
57
     */
58
    protected $entityManager;
59
60
    /**
61
     * @var EntityManagerFactoryInterface
62
     */
63
    protected $entityManagerFactory;
64
65
    /**
66
     * @var string
67
     */
68
    protected $entityManagerFactoryName;
69
70
    /**
71
     * @var array
72
     */
73
    protected $entityManagerFactoryOptions = [];
74
75
    /**
76
     * @var array
77
     */
78
    protected $entityMap = [
79
        'entry'       => DefaultEntry::class,
80
        'currentStep' => CurrentStep::class,
81
        'historyStep' => HistoryStep::class,
82
83
    ];
84
85
    /**
86
     * Инициализация хранилища
87
     *
88
     * @param array $props
89
     *
90
     * @throws Exception\InvalidArgumentException
91
     * @throws Exception\RuntimeException
92
     */
93
    public function init(array $props = [])
94
    {
95
        $this->entityManagerFactory = null;
96
        $this->entityManager = null;
97
98
        if (!array_key_exists(static::ENTITY_MANAGER_FACTORY, $props)) {
99
            $errMsg = sprintf('Option %s not found', static::ENTITY_MANAGER_FACTORY);
100
            throw new Exception\InvalidArgumentException($errMsg);
101
        }
102
103
        $emFactoryOptions = $props[static::ENTITY_MANAGER_FACTORY];
104 View Code Duplication
        if (!is_array($emFactoryOptions)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
105
            $errMsg = sprintf('Option %s is not array', static::ENTITY_MANAGER_FACTORY);
106
            throw new Exception\InvalidArgumentException($errMsg);
107
        }
108
109
110 View Code Duplication
        if (!array_key_exists(static::ENTITY_MANAGER_FACTORY_NAME, $emFactoryOptions)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
111
            $errMsg = sprintf('Option %s->%s not found', static::ENTITY_MANAGER_FACTORY, static::ENTITY_MANAGER_FACTORY_NAME);
112
            throw new Exception\InvalidArgumentException($errMsg);
113
        }
114
        $this->entityManagerFactoryName = $emFactoryOptions[static::ENTITY_MANAGER_FACTORY_NAME];
115
116
        if (array_key_exists(static::ENTITY_MANAGER_FACTORY_OPTIONS, $emFactoryOptions) && is_array($emFactoryOptions[static::ENTITY_MANAGER_FACTORY_OPTIONS])) {
117
            $this->entityManagerFactoryOptions = $emFactoryOptions[static::ENTITY_MANAGER_FACTORY_OPTIONS];
118
        }
119
    }
120
121
    /**
122
     * @return EntityManagerFactoryInterface
123
     *
124
     * @throws Exception\InvalidArgumentException
125
     * @throws Exception\RuntimeException
126
     */
127
    public function getEntityManagerFactory()
128
    {
129
        if ($this->entityManagerFactory) {
130
            return $this->entityManagerFactory;
131
        }
132
133
        if (!class_exists($this->entityManagerFactoryName)) {
134
            $errMsg = sprintf('Entity manager factory "%s" not found', $this->entityManagerFactoryName);
135
            throw new Exception\RuntimeException($errMsg);
136
        }
137
138
        $r = new ReflectionClass($this->entityManagerFactoryName);
139
        if (!$r->implementsInterface(EntityManagerFactoryInterface::class)) {
140
            $errMsg = sprintf('Factory not implement %s', EntityManagerFactoryInterface::class);
141
            throw new Exception\RuntimeException($errMsg);
142
        }
143
144
        /** @var EntityManagerFactoryInterface $factory */
145
        $this->entityManagerFactory = $r->newInstance();
146
147
        return $this->entityManagerFactory;
148
    }
149
150
    /**
151
     * @return EntityManagerInterface
152
     *
153
     * @throws Exception\DoctrineRuntimeException
154
     */
155
    public function getEntityManager()
156
    {
157
        if ($this->entityManager) {
158
            return $this->entityManager;
159
        }
160
161
        try {
162
            $em = $this->getEntityManagerFactory()->factory($this->entityManagerFactoryOptions);
163
164
            if (!$em instanceof EntityManagerInterface) {
165
                $errMsg = sprintf('EntityManager not implement %s', EntityManagerInterface::class);
166
                throw new Exception\RuntimeException($errMsg);
167
            }
168
            $this->entityManager = $em;
169
        } catch (\Exception $e) {
170
            throw new Exception\DoctrineRuntimeException($e->getMessage(), $e->getCode(), $e);
171
        }
172
173
        return $this->entityManager;
174
    }
175
176
177
    /**
178
     * @param string $workflowName
179
     *
180
     * @return WorkflowEntryInterface
181
     *
182
     * @throws Exception\DoctrineRuntimeException
183
     * @throws  Exception\InvalidArgumentException
184
     */
185
    public function createEntry($workflowName)
186
    {
187
        $entryClassName = $this->getEntityClassName('entry');
188
189
        $r = new ReflectionClass($entryClassName);
190
191
        $workflowEntry = $r->newInstance();
192
        $workflowEntry->setWorkflowName($workflowName);
193
        $workflowEntry->setState(WorkflowEntryInterface::CREATED);
194
195
        $em = $this->getEntityManager();
196
        $em->persist($workflowEntry);
197
        $em->flush();
198
199
        return $workflowEntry;
200
    }
201
202
    /**
203
     * @param int $entryId
204
     * @param int $state
205
     *
206
     * @throws Exception\DoctrineRuntimeException
207
     * @throws Exception\InvalidArgumentException
208
     */
209
    public function setEntryState($entryId, $state)
210
    {
211
        $em = $this->getEntityManager();
212
213
        $entryClassName = $this->getEntityClassName('entry');
214
215
        /** @var EntryInterface $entry */
216
        $entry = $em->getRepository($entryClassName)->find($entryId);
217
        $entry->setState($state);
218
219
        $em->flush();
220
    }
221
222
    /**
223
     *
224
     *
225
     * @param int      $entryId
226
     * @param int      $stepId
227
     * @param string   $owner
228
     * @param DateTime $startDate
229
     * @param DateTime $dueDate
230
     * @param string   $status
231
     * @param array    $previousIds
232
     *
233
     * @return StepInterface|void
234
     *
235
     * @throws Exception\DoctrineRuntimeException
236
     * @throws \OldTown\Workflow\Spi\Doctrine\EntityRepository\Exception\RuntimeException
237
     * @throws \OldTown\Workflow\Spi\Doctrine\Entity\Exception\InvalidArgumentException
238
     * @throws  Exception\InvalidArgumentException
239
     */
240
    public function createCurrentStep($entryId, $stepId, $owner = null, DateTime $startDate, DateTime $dueDate = null, $status, array $previousIds = [])
241
    {
242
        $em = $this->getEntityManager();
243
244
        $entryClassName = $this->getEntityClassName('entry');
245
246
        /** @var EntryInterface $entry */
247
        $entry = $em->getRepository($entryClassName)->find($entryId);
248
249
        $currentStepClassName = $this->getEntityClassName('currentStep');
250
        $r = new ReflectionClass($currentStepClassName);
251
252
        /** @var CurrentStepInterface $currentStep */
253
        $currentStep = $r->newInstance($currentStepClassName);
254
        $currentStep->setEntry($entry);
255
        $currentStep->setStepId($stepId);
256
        $currentStep->setOwner($owner);
257
        $currentStep->setStartDate($startDate);
258
        $currentStep->setDueDate($dueDate);
259
        $currentStep->setStatus($status);
260
261
        if (count($previousIds) > 0) {
262
            /** @var StepRepository $previousStepRepository */
263
            $previousStepRepository = $em->getRepository(AbstractStep::class);
264
            $previousSteps = $previousStepRepository->findByIds($previousIds);
265
            $currentStep->setPreviousSteps($previousSteps);
266
        }
267
        $em->persist($currentStep);
268
        $em->flush();
269
270
        return $currentStep->getId();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $currentStep->getId(); (integer) is incompatible with the return type declared by the interface OldTown\Workflow\Spi\Wor...face::createCurrentStep of type OldTown\Workflow\Spi\StepInterface.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
271
    }
272
273
    /**
274
     * @param int $entryId
275
     *
276
     * @return \Doctrine\Common\Collections\ArrayCollection|Entity\CurrentStepInterface[]
277
     *
278
     * @return StepInterface[]
279
     *
280
     * @throws Exception\DoctrineRuntimeException
281
     * @throws Exception\InvalidArgumentException
282
     */
283
    public function findCurrentSteps($entryId)
284
    {
285
        $em = $this->getEntityManager();
286
287
        $entryClassName = $this->getEntityClassName('entry');
288
289
        /** @var EntryInterface $entry */
290
        $entry = $em->getRepository($entryClassName)->find($entryId);
291
292
        $currentSteps = $entry->getCurrentSteps();
293
        $result = new SplObjectStorage();
294
        foreach ($currentSteps as $currentStep) {
295
            $result->attach($currentStep);
296
        }
297
298
        return $result;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $result; (SplObjectStorage) is incompatible with the return type declared by the interface OldTown\Workflow\Spi\Wor...rface::findCurrentSteps of type OldTown\Workflow\Spi\StepInterface[].

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
299
    }
300
301
302
    /**
303
     * @param integer $entryId
304
     *
305
     * @return WorkflowEntryInterface
306
     *
307
     * @throws Exception\DoctrineRuntimeException
308
     * @throws Exception\InvalidArgumentException
309
     */
310
    public function findEntry($entryId)
311
    {
312
        $em = $this->getEntityManager();
313
314
        $entryClassName = $this->getEntityClassName('entry');
315
316
        return $em->getRepository($entryClassName)->find($entryId);
317
    }
318
319
    /**
320
     * Помечает шаг как выполенный
321
     *
322
     * @param StepInterface $step
323
     * @param int           $actionId
324
     * @param DateTime      $finishDate
325
     * @param string        $status
326
     * @param string        $caller
327
     *
328
     * @return StepInterface
329
     *
330
     * @throws Exception\DoctrineRuntimeException
331
     */
332
    public function markFinished(StepInterface $step, $actionId, DateTime $finishDate, $status, $caller)
333
    {
334
        $step->setActionId($actionId);
335
        $step->setFinishDate($finishDate);
336
        $step->setStatus($status);
337
        $step->setCaller($caller);
338
339
        $em = $this->getEntityManager();
340
341
        $em->persist($step);
342
        $em->flush();
343
344
        return $step;
345
    }
346
347
348
    /**
349
     * Перемещает шаг в архив
350
     *
351
     * @param StepInterface $step
352
     *
353
     * @return $this|void
354
     *
355
     * @throws Exception\InvalidArgumentException
356
     * @throws \OldTown\Workflow\Spi\Doctrine\Exception\DoctrineRuntimeException
357
     * @throws \OldTown\Workflow\Spi\Doctrine\Entity\Exception\InvalidArgumentException
358
     */
359
    public function moveToHistory(StepInterface $step)
360
    {
361
        if (!$step instanceof CurrentStepInterface) {
362
            $errMsg = sprintf('Step not implement %s', CurrentStepInterface::class);
363
            throw new Exception\InvalidArgumentException($errMsg);
364
        }
365
        $entry = $step->getEntry();
366
367
        $entry->getCurrentSteps()->removeElement($step);
368
369
        $historyStepClassName = $this->getEntityClassName('historyStep');
370
371
        $r = new ReflectionClass($historyStepClassName);
372
        /** @var HistoryStepInterface $historyStep */
373
        $historyStep = $r->newInstance($step);
374
        $entry->addHistoryStep($historyStep);
375
376
        $em = $this->getEntityManager();
377
        $em->persist($historyStep);
378
        $em->remove($step);
379
        $em->flush();
380
    }
381
382
    /**
383
     * Поиск уже пройденных шагов для процесса workflow с заданным id
384
     *
385
     * @param $entryId
386
     *
387
     * @return \OldTown\Workflow\Spi\StepInterface[]|\Doctrine\ORM\PersistentCollection|void
388
     *
389
     * @throws \OldTown\Workflow\Spi\Doctrine\Exception\DoctrineRuntimeException
390
     * @throws Exception\InvalidArgumentException
391
     */
392
    public function findHistorySteps($entryId)
393
    {
394
        $em = $this->getEntityManager();
395
396
        $entryClassName = $this->getEntityClassName('entry');
397
398
        /** @var EntryInterface $entry */
399
        $entry = $em->getRepository($entryClassName)->find($entryId);
400
401
        return $entry->getHistorySteps();
0 ignored issues
show
Bug Compatibility introduced by
The expression $entry->getHistorySteps(); of type Doctrine\Common\Collecti...\HistoryStepInterface[] adds the type Doctrine\Common\Collections\ArrayCollection to the return on line 401 which is incompatible with the return type declared by the interface OldTown\Workflow\Spi\Wor...rface::findHistorySteps of type OldTown\Workflow\Spi\Ste...face[]|SplObjectStorage.
Loading history...
402
    }
403
404
    /**
405
     * @todo Реализовать функционал
406
     *
407
     * @param WorkflowExpressionQuery $query
408
     *
409
     * @return array|void
410
     *
411
     * @throws Exception\RuntimeException
412
     */
413
    public function query(WorkflowExpressionQuery $query)
414
    {
415
        $errMsg = sprintf('Method %s not supported', __METHOD__);
416
        throw new Exception\RuntimeException($errMsg);
417
    }
418
419
    /**
420
     * @todo Покрыть тестами и отрефакторить
421
     *
422
     * @param int $entryId
423
     *
424
     * @return \OldTown\PropertySet\PropertySetInterface
425
     * @throws \OldTown\PropertySet\Exception\RuntimeException
426
     */
427
    public function getPropertySet($entryId)
428
    {
429
        return PropertySetManager::getInstance('memory');
430
    }
431
432
    /**
433
     * Получение сущности по ее псевдониму
434
     *
435
     * @param $alias
436
     *
437
     * @return mixed
438
     *
439
     * @throws  Exception\InvalidArgumentException
440
     */
441
    public function getEntityClassName($alias)
442
    {
443
        if (array_key_exists($alias, $this->entityMap)) {
444
            return $this->entityMap[$alias];
445
        }
446
447
        $errMsg = sprintf('Invalid entity name: %s', $alias);
448
        throw new Exception\InvalidArgumentException($errMsg);
449
    }
450
451
    /**
452
     * @param $alias
453
     * @param $className
454
     *
455
     * @return $this
456
     */
457
    public function setEntityClassName($alias, $className)
458
    {
459
        $this->entityMap[$alias] = $className;
460
461
        return $this;
462
    }
463
}
464