Test Setup Failed
Push — master ( 1d88cc...3d481f )
by Alexey
02:54
created

PostFactory::createFromDTO()   D

Complexity

Conditions 10
Paths 8

Size

Total Lines 44
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 110

Importance

Changes 0
Metric Value
dl 0
loc 44
ccs 0
cts 26
cp 0
rs 4.8196
c 0
b 0
f 0
cc 10
eloc 28
nc 8
nop 1
crap 110

1 Method

Rating   Name   Duplication   Size   Complexity  
C PostFactory::findOrCreateFromDTOWithTagsAndFiles() 0 33 7

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Skobkin\Bundle\PointToolsBundle\Service\Factory\Blogs;
4
5
use Doctrine\ORM\EntityManagerInterface;
6
use Psr\Log\LoggerInterface;
7
use Skobkin\Bundle\PointToolsBundle\DTO\Api\MetaPost;
8
use Skobkin\Bundle\PointToolsBundle\DTO\Api\Post as PostDTO;
9
use Skobkin\Bundle\PointToolsBundle\DTO\Api\PostsPage;
10
use Skobkin\Bundle\PointToolsBundle\Entity\Blogs\Post;
11
use Skobkin\Bundle\PointToolsBundle\Entity\Blogs\PostTag;
12
use Skobkin\Bundle\PointToolsBundle\Entity\User;
13
use Skobkin\Bundle\PointToolsBundle\Exception\Factory\Blog\InvalidDataException;
14
use Skobkin\Bundle\PointToolsBundle\Repository\Blogs\PostRepository;
15
use Skobkin\Bundle\PointToolsBundle\Exception\Api\InvalidResponseException;
16
use Skobkin\Bundle\PointToolsBundle\Service\Factory\AbstractFactory;
17
use Skobkin\Bundle\PointToolsBundle\Service\Factory\UserFactory;
18
19
class PostFactory extends AbstractFactory
20
{
21
    /**
22
     * @var EntityManagerInterface
23
     */
24
    private $em;
25
26
    /**
27
     * @var PostRepository
28
     */
29
    private $postRepository;
30
31
    /**
32
     * @var UserFactory
33
     */
34
    private $userFactory;
35
36
    /**
37
     * @var FileFactory
38
     */
39
    private $fileFactory;
40
41
    /**
42
     * @var CommentFactory
43
     */
44
    private $commentFactory;
45
46
    /**
47
     * @var TagFactory
48
     */
49
    private $tagFactory;
50
51
52
    public function __construct(
53
        LoggerInterface $logger,
54
        EntityManagerInterface $em,
55
        PostRepository $postRepository,
56
        UserFactory $userFactory,
57
        FileFactory $fileFactory,
58
        CommentFactory $commentFactory,
59
        TagFactory $tagFactory
60
    ) {
61
        parent::__construct($logger);
62
        $this->em = $em;
63
        $this->postRepository = $postRepository;
64
        $this->userFactory = $userFactory;
65
        $this->fileFactory = $fileFactory;
66
        $this->commentFactory = $commentFactory;
67
        $this->tagFactory = $tagFactory;
68
    }
69
70
    /**
71
     * Creates posts and return status of new insertions
72
     *
73
     * @todo refactor
74
     *
75
     * @throws InvalidResponseException
76
     */
77
    public function createFromPageDTO(PostsPage $page): bool
78
    {
79
        $posts = [];
80
81
        $hasNew = false;
82
83
        foreach ((array) $page->getPosts() as $postData) {
84
            try {
85
                if (null === $this->postRepository->find($postData->getPost()->getId())) {
86
                    $hasNew = true;
87
                }
88
89
                $post = $this->findOrCreateFromDTOWithTagsAndFiles($postData);
90
                $posts[] = $post;
91
            } catch (\Exception $e) {
92
                $this->logger->error('Error while processing post DTO', [
93
                    'post_id' => $postData->getPost()->getId(),
94
                    'exception' => get_class($e),
95
                    'message' => $e->getMessage(),
96
                    'file' => $e->getFile(),
97
                    'line' => $e->getLine(),
98
                ]);
99
            }
100
        }
101
102
        foreach ($posts as $post) {
103
            if ($this->em->getUnitOfWork()->isScheduledForInsert($post)) {
104
                $hasNew = true;
105
            }
106
        }
107
108
        return $hasNew;
109
    }
110
111
    /**
112
     * @todo Implement full post processing with comments
113
     *
114
     * @throws InvalidDataException
115
     */
116
    public function findOrCreateFromDTOWithTagsAndFiles(MetaPost $metaPost): Post
117
    {
118
        if (!$metaPost->isValid()) {
119
            throw new InvalidDataException('Invalid post data');
120
        }
121
122
        $postData = $metaPost->getPost();
123
124
        try {
125
            $author = $this->userFactory->findOrCreateFromDTO($metaPost->getPost()->getAuthor());
126
        } catch (\Exception $e) {
127
            $this->logger->error('Error while creating user from DTO');
128
            throw $e;
129
        }
130
131
        $post = $this->findOrCreateFromDto($postData, $author);
1 ignored issue
show
Bug introduced by
It seems like $postData defined by $metaPost->getPost() on line 122 can be null; however, Skobkin\Bundle\PointTool...::findOrCreateFromDto() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
132
133
        try {
134
            $this->updatePostTags($post, $postData->getTags() ?: []);
135
        } catch (\Exception $e) {
136
            $this->logger->error('Error while updating post tags');
137
            throw $e;
138
        }
139
140
        try {
141
            $this->updatePostFiles($post, $postData->getFiles() ?: []);
142
        } catch (\Exception $e) {
143
            $this->logger->error('Error while updating post files');
144
            throw $e;
145
        }
146
147
        return $post;
148
    }
149
150
    private function findOrCreateFromDto(PostDTO $postData, User $author): Post
151
    {
152
        if (null === ($post = $this->postRepository->find($postData->getId()))) {
153
            // Creating new post
154
            $post = new Post(
155
                $postData->getId(),
156
                $author,
157
                new \DateTime($postData->getCreated()),
158
                $postData->getType() ?: Post::TYPE_POST
159
            );
160
            $this->postRepository->add($post);
161
        }
162
163
        $post
164
            ->setText($postData->getText())
165
            ->setPrivate($postData->getPrivate())
166
        ;
167
168
        return $post;
169
    }
170
171
    /**
172
     * @param Post $post
173
     * @param string[] $tagsStrings
174
     */
175
    private function updatePostTags(Post $post, array $tagsStrings): void
176
    {
177
        $tags = $this->tagFactory->createFromStringsArray($tagsStrings);
178
179
        // Hashing tags strings
180
        $tagStringsHash = [];
181
        foreach ($tagsStrings as $tagsString) {
182
            $tagStringsHash[mb_strtolower($tagsString)] = $tagsString;
183
        }
184
185
        // Hashing current post tags
186
        $newTagsHash = [];
187
        foreach ($tags as $tag) {
188
            $newTagsHash[mb_strtolower($tag->getText())] = $tag;
189
        }
190
191
        // Hashing old post tags (from DB)
192
        $oldTagsHash = [];
193
        foreach ($post->getPostTags() as $postTag) {
194
            $oldTagsHash[mb_strtolower($postTag->getOriginalTagText())] = $postTag;
195
        }
196
197
        // Adding missing tags
198
        foreach ($tags as $tag) {
199
            if (!array_key_exists(mb_strtolower($tag->getText()), $oldTagsHash)) {
200
                $tmpPostTag = (new PostTag($tag))
201
                    ->setText($tagStringsHash[mb_strtolower($tag->getText())])
202
                ;
203
                $post->addPostTag($tmpPostTag);
204
            }
205
        }
206
207
        // Removing deleted tags
208
        foreach ($post->getPostTags() as $postTag) {
209
            if (!array_key_exists(mb_strtolower($postTag->getOriginalTagText()), $newTagsHash)) {
210
                $post->removePostTag($postTag);
211
            }
212
        }
213
    }
214
215
    /**
216
     * @param Post $post
217
     * @param array $urls
218
     */
219
    private function updatePostFiles(Post $post, array $urls): void
220
    {
221
        $files = $this->fileFactory->createFromUrlsArray($urls);
222
223
        // Adding missing files
224
        foreach ($files as $file) {
225
            if (!$post->getFiles()->contains($file)) {
226
                $post->addFile($file);
227
            }
228
        }
229
230
        // Removing deleted files
231
        foreach ($post->getFiles() as $file) {
232
            if (!in_array($file, $files, true)) {
233
                $post->removeFile($file);
234
            }
235
        }
236
    }
237
}