Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
12 / 12
CRAP
100.00% covered (success)
100.00%
84 / 84
TweetRepository
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
12 / 12
20
100.00% covered (success)
100.00%
84 / 84
 getWithUsers
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
11 / 11
 getWithUsersAndMediasQuery
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
10 / 10
 getWithUsersAndMedias
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
6 / 6
 getTweetId
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
10 / 10
 getPreviousTweetId
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 getNextTweetId
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 countPendingTweets
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
8 / 8
 getLastTweet
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
5 / 5
 getTweetsLessThanId
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
12 / 12
 removeOrphanMedias
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
3 / 3
 removeTweet
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
8 / 8
 deleteAndHideTweetsLessThanId
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
9 / 9
1<?php
2
3namespace AlexisLefebvre\Bundle\AsyncTweetsBundle\Entity;
4
5use Doctrine\ORM\EntityRepository;
6use Doctrine\ORM\QueryBuilder;
7
8/**
9 * TweetRepository.
10 *
11 * This class was generated by the Doctrine ORM. Add your own custom
12 * repository methods below.
13 *
14 * @extends \Doctrine\ORM\EntityRepository<Tweet>
15 */
16class TweetRepository extends EntityRepository
17{
18    const TWEETS_PER_PAGE = 5;
19
20    /**
21     * @return int|mixed|string
22     */
23    public function getWithUsers(int $page = 1)
24    {
25        $firstResult = (($page - 1) * self::TWEETS_PER_PAGE);
26
27        $qb = $this->createQueryBuilder('t');
28
29        $query = $qb
30            ->select('t, user, rt, rt_user')
31            ->innerJoin('t.user', 'user')
32            ->leftJoin('t.retweeted_status', 'rt')
33            ->leftJoin('rt.user', 'rt_user')
34
35            // Ignore tweets that were only retweeted
36            ->where($qb->expr()->eq('t.in_timeline', 'true'))
37
38            ->orderBy('t.id', 'DESC')
39
40            ->setFirstResult($firstResult)
41            ->setMaxResults(self::TWEETS_PER_PAGE);
42
43        return $query->getQuery()->getResult();
44    }
45
46    private function getWithUsersAndMediasQuery(QueryBuilder $qb): QueryBuilder
47    {
48        $query = $qb
49            ->select('t, user, medias, rt, rt_user')
50            ->innerJoin('t.user', 'user')
51            ->leftJoin('t.medias', 'medias')
52            ->leftJoin('t.retweeted_status', 'rt')
53            ->leftJoin('rt.user', 'rt_user')
54
55            // Ignore tweets that were only retweeted
56            ->where($qb->expr()->eq('t.in_timeline', 'true'))
57
58            ->orderBy('t.id', 'ASC')
59
60            ->setFirstResult(0)
61            ->setMaxResults(self::TWEETS_PER_PAGE);
62
63        return $query;
64    }
65
66    /**
67     * @return array<Tweet>
68     */
69    public function getWithUsersAndMedias(?int $firstTweetId = null): array
70    {
71        $qb = $this->createQueryBuilder('t');
72
73        $query = $this->getWithUsersAndMediasQuery($qb);
74
75        if (!is_null($firstTweetId)) {
76            $query = $query->andWhere(
77                $qb->expr()->gte('t.id', $firstTweetId)
78            );
79        }
80
81        return $query->getQuery()->getResult();
82    }
83
84    private function getTweetId(string $condition, string $order, int $tweetId): ?int
85    {
86        $qb = $this->createQueryBuilder('t')
87            ->select('t.id')
88
89            ->where('t.id '.$condition.' :tweetId')
90            ->setParameter(':tweetId', $tweetId)
91
92            ->andWhere('t.in_timeline = true')
93
94            ->orderBy('t.id', $order)
95
96            ->setFirstResult(self::TWEETS_PER_PAGE - 1)
97            ->setMaxResults(1);
98
99        $result = $qb->getQuery()->getOneOrNullResult();
100
101        return is_array($result) ? $result['id'] : null;
102    }
103
104    public function getPreviousTweetId(int $tweetId): ?int
105    {
106        return $this->getTweetId('<', 'DESC', $tweetId);
107    }
108
109    public function getNextTweetId(int $tweetId): ?int
110    {
111        return $this->getTweetId('>', 'ASC', $tweetId);
112    }
113
114    public function countPendingTweets(?int $lastTweetId = null): int
115    {
116        /** @var \Doctrine\ORM\QueryBuilder $qb */
117        $qb = $this->createQueryBuilder('t');
118
119        $query = $qb
120            ->add('select', $qb->expr()->count('t.id'))
121            // Ignore tweets that were only retweeted
122            ->where(
123                $qb->expr()->eq('t.in_timeline', 'true')
124            );
125
126        if (!is_null($lastTweetId)) {
127            $query = $query->andWhere(
128                $qb->expr()->gte('t.id', $lastTweetId)
129            );
130        }
131
132        // return result of "COUNT()" query
133        return $query->getQuery()->getSingleScalarResult();
134    }
135
136    public function getLastTweet(): ?Tweet
137    {
138        $qb = $this->createQueryBuilder('t')
139            ->addOrderBy('t.id', 'DESC')
140            ->setFirstResult(0)
141            ->setMaxResults(1);
142
143        return $qb->getQuery()->getOneOrNullResult();
144    }
145
146    /**
147     * @return array<Tweet>
148     */
149    private function getTweetsLessThanId(int $tweetId): array
150    {
151        $qb = $this->createQueryBuilder('t')
152            ->select('t, m')
153            ->leftJoin('t.medias', 'm')
154            ->where('t.id < :tweetId')
155            ->setParameter(':tweetId', $tweetId)
156
157            // Get retweeted tweets (it would break foreign keys)
158            //  http://stackoverflow.com/questions/15087933/how-to-do-left-join-in-doctrine/15088250#15088250
159            ->leftJoin(
160                'AsyncTweetsBundle:Tweet',
161                't2',
162                'WITH',
163                't.id = t2.retweeted_status'
164            )
165
166            ->orderBy('t.id', 'DESC');
167
168        return $qb->getQuery()->getResult();
169    }
170
171    /**
172     * Remove Media not associated to any Tweet.
173     */
174    private function removeOrphanMedias(Media $media): void
175    {
176        if (count($media->getTweets()) == 0) {
177            $this->_em->remove($media);
178        }
179    }
180
181    /**
182     * Remove the tweet and return 1 is the deleted tweet is not a
183     *  retweet.
184     */
185    protected function removeTweet(Tweet $tweet): int
186    {
187        $count = 0;
188
189        foreach ($tweet->getMedias() as $media) {
190            $tweet->removeMedia($media);
191            $this->removeOrphanMedias($media);
192        }
193
194        // Don't count tweets that were only retweeted
195        if ($tweet->isInTimeline()) {
196            $count = 1;
197        }
198
199        $this->_em->remove($tweet);
200
201        return $count;
202    }
203
204    /**
205     * Delete tweets and return the number of deleted tweets (excluding
206     *  retweeted-only tweets).
207     */
208    public function deleteAndHideTweetsLessThanId(int $tweetId): int
209    {
210        $count = 0;
211
212        $tweets = $this->getTweetsLessThanId($tweetId);
213
214        foreach ($tweets as $tweet) {
215            if ($tweet->mustBeKept($tweetId)) {
216                // The Tweet is still in the timeline, it can only be hidden
217                $tweet->setInTimeline(false);
218                $this->_em->persist($tweet);
219            } else {
220                // The Tweet has not been retweeted, it can be removed
221                $count += $this->removeTweet($tweet);
222            }
223        }
224
225        $this->_em->flush();
226
227        return $count;
228    }
229}