Test Setup Failed
Push — master ( 7c5f1f...6e7280 )
by Alexey
02:40
created

PostFactory::validateMetaPost()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 14
ccs 0
cts 12
cp 0
rs 9.4285
cc 3
eloc 8
nc 3
nop 1
crap 12
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\PostsPage;
9
use Skobkin\Bundle\PointToolsBundle\Entity\Blogs\Post;
10
use Skobkin\Bundle\PointToolsBundle\Entity\Blogs\PostTag;
11
use Skobkin\Bundle\PointToolsBundle\Exception\Factory\Blog\InvalidDataException;
12
use Skobkin\Bundle\PointToolsBundle\Repository\Blogs\PostRepository;
13
use Skobkin\Bundle\PointToolsBundle\Exception\Api\InvalidResponseException;
14
use Skobkin\Bundle\PointToolsBundle\Service\Factory\AbstractFactory;
15
use Skobkin\Bundle\PointToolsBundle\Service\Factory\UserFactory;
16
17
class PostFactory extends AbstractFactory
18
{
19
    /**
20
     * @var EntityManagerInterface
21
     */
22
    private $em;
23
24
    /**
25
     * @var PostRepository
26
     */
27
    private $postRepository;
28
29
    /**
30
     * @var UserFactory
31
     */
32
    private $userFactory;
33
34
    /**
35
     * @var FileFactory
36
     */
37
    private $fileFactory;
38
39
    /**
40
     * @var CommentFactory
41
     */
42
    private $commentFactory;
43
44
    /**
45
     * @var TagFactory
46
     */
47
    private $tagFactory;
48
49
50
    public function __construct(
51
        LoggerInterface $logger,
52
        EntityManagerInterface $em,
53
        PostRepository $postRepository,
54
        UserFactory $userFactory,
55
        FileFactory $fileFactory,
56
        CommentFactory $commentFactory,
57
        TagFactory $tagFactory
58
    ) {
59
        parent::__construct($logger);
60
        $this->em = $em;
61
        $this->postRepository = $postRepository;
62
        $this->userFactory = $userFactory;
63
        $this->fileFactory = $fileFactory;
64
        $this->commentFactory = $commentFactory;
65
        $this->tagFactory = $tagFactory;
66
    }
67
68
    /**
69
     * Creates posts and return status of new insertions
70
     *
71
     * @throws InvalidResponseException
72
     */
73
    public function createFromPageDTO(PostsPage $page): bool
74
    {
75
        $posts = [];
76
77
        $hasNew = false;
78
79
        foreach ($page->getPosts() as $postData) {
0 ignored issues
show
Bug introduced by
The expression $page->getPosts() of type array<integer,object<Sko...DTO\Api\MetaPost>>|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
80
            try {
81
                if (null === $this->postRepository->find($postData->getPost()->getId())) {
82
                    $hasNew = true;
83
                }
84
85
                $post = $this->createFromDTO($postData);
86
                $posts[] = $post;
87
            } catch (\Exception $e) {
88
                $this->logger->error('Error while processing post DTO', [
89
                    'post_id' => $postData->getPost()->getId(),
90
                    'exception' => get_class($e),
91
                    'message' => $e->getMessage(),
92
                    'file' => $e->getFile(),
93
                    'line' => $e->getLine(),
94
                ]);
95
            }
96
        }
97
98
        foreach ($posts as $post) {
99
            if ($this->em->getUnitOfWork()->isScheduledForInsert($post)) {
100
                $hasNew = true;
101
            }
102
        }
103
104
        return $hasNew;
105
    }
106
107
    /**
108
     * @todo Implement full post processing with comments
109
     *
110
     * @throws InvalidDataException
111
     */
112
    private function createFromDTO(MetaPost $postData): Post
113
    {
114
        if (!$postData->isValid()) {
115
            throw new InvalidDataException('Invalid post data');
116
        }
117
118
        try {
119
            $user = $this->userFactory->findOrCreateFromDTO($postData->getPost()->getAuthor());
120
        } catch (\Exception $e) {
121
            $this->logger->error('Error while creating user from DTO');
122
            throw $e;
123
        }
124
125
        if (null === ($post = $this->postRepository->find($postData->getPost()->getId()))) {
126
            // Creating new post
127
            $post = new Post($postData->getPost()->getId());
128
            $this->postRepository->add($post);
129
        }
130
131
        // Updating data
132
        $post
133
            ->setAuthor($user)
134
            ->setCreatedAt((new \DateTime($postData->getPost()->getCreated())) ?: null)
135
            ->setType($postData->getPost()->getType() ?: Post::TYPE_POST)
136
            ->setText($postData->getPost()->getText())
137
            ->setPrivate($postData->getPost()->getPrivate())
138
        ;
139
140
        try {
141
            $this->updatePostTags($post, $postData->getPost()->getTags() ?: []);
142
        } catch (\Exception $e) {
143
            $this->logger->error('Error while updating post tags');
144
            throw $e;
145
        }
146
147
        try {
148
            $this->updatePostFiles($post, $postData->getPost()->getFiles() ?: []);
149
        } catch (\Exception $e) {
150
            $this->logger->error('Error while updating post files');
151
            throw $e;
152
        }
153
154
        return $post;
155
    }
156
157
    /**
158
     * @param Post $post
159
     * @param string[] $tagsStrings
160
     */
161
    private function updatePostTags(Post $post, array $tagsStrings): void
162
    {
163
        $tags = $this->tagFactory->createFromStringsArray($tagsStrings);
164
165
        // Hashing tags strings
166
        $tagStringsHash = [];
167
        foreach ($tagsStrings as $tagsString) {
168
            $tagStringsHash[mb_strtolower($tagsString)] = $tagsString;
169
        }
170
171
        // Hashing current post tags
172
        $newTagsHash = [];
173
        foreach ($tags as $tag) {
174
            $newTagsHash[mb_strtolower($tag->getText())] = $tag;
175
        }
176
177
        // Hashing old post tags (from DB)
178
        $oldTagsHash = [];
179
        foreach ($post->getPostTags() as $postTag) {
180
            $oldTagsHash[mb_strtolower($postTag->getOriginalTagText())] = $postTag;
181
        }
182
183
        // Adding missing tags
184
        foreach ($tags as $tag) {
185
            if (!array_key_exists(mb_strtolower($tag->getText()), $oldTagsHash)) {
186
                $tmpPostTag = (new PostTag($tag))
187
                    ->setText($tagStringsHash[mb_strtolower($tag->getText())])
188
                ;
189
                $post->addPostTag($tmpPostTag);
190
            }
191
        }
192
193
        // Removing deleted tags
194
        foreach ($post->getPostTags() as $postTag) {
195
            if (!array_key_exists(mb_strtolower($postTag->getOriginalTagText()), $newTagsHash)) {
196
                $post->removePostTag($postTag);
197
            }
198
        }
199
    }
200
201
    /**
202
     * @param Post $post
203
     * @param array $urls
204
     */
205
    private function updatePostFiles(Post $post, array $urls): void
206
    {
207
        $files = $this->fileFactory->createFromUrlsArray($urls);
208
209
        // Adding missing files
210
        foreach ($files as $file) {
211
            if (!$post->getFiles()->contains($file)) {
212
                $post->addFile($file);
213
            }
214
        }
215
216
        // Removing deleted files
217
        foreach ($post->getFiles() as $file) {
218
            if (!in_array($file, $files, true)) {
219
                $post->removeFile($file);
220
            }
221
        }
222
    }
223
}