Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
9 / 9
CRAP
100.00% covered (success)
100.00%
71 / 71
StatusesHomeTimelineCommand
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
9 / 9
15
100.00% covered (success)
100.00%
71 / 71
 configure
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
9 / 9
 execute
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
11 / 11
 setAndDisplayLastTweet
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
9 / 9
 getContent
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
8 / 8
 displayContentNotArrayError
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
6 / 6
 addAndDisplayTweets
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
10 / 10
 setProgressBar
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
4 / 4
 setTable
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
6 / 6
 iterateTweets
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
8 / 8
1<?php
2
3namespace AlexisLefebvre\Bundle\AsyncTweetsBundle\Command;
4
5use Abraham\TwitterOAuth\TwitterOAuth;
6use AlexisLefebvre\Bundle\AsyncTweetsBundle\Entity\Tweet;
7use AlexisLefebvre\Bundle\AsyncTweetsBundle\Entity\TweetRepository;
8use AlexisLefebvre\Bundle\AsyncTweetsBundle\Utils\PersistTweet;
9use Symfony\Component\Console\Helper\ProgressBar;
10use Symfony\Component\Console\Helper\Table;
11use Symfony\Component\Console\Input\InputInterface;
12use Symfony\Component\Console\Input\InputOption;
13use Symfony\Component\Console\Output\OutputInterface;
14
15class StatusesHomeTimelineCommand extends BaseCommand
16{
17    /** @var bool */
18    private $displayTable;
19    /** @var Table */
20    private $table;
21    /** @var ProgressBar */
22    private $progress;
23
24    /**
25     * @var array<bool|int>
26     *
27     * @see https://dev.twitter.com/rest/reference/get/statuses/home_timeline
28     */
29    private $parameters = [
30        'count'           => 200,
31        'exclude_replies' => true,
32    ];
33
34    protected function configure(): void
35    {
36        parent::configure();
37
38        $this
39            ->setName('statuses:hometimeline')
40            ->setDescription('Fetch home timeline')
41            // http://symfony.com/doc/2.3/cookbook/console/console_command.html#automatically-registering-commands
42            ->addOption(
43                'table',
44                null,
45                InputOption::VALUE_NONE,
46                'Display a table with tweets'
47            );
48    }
49
50    protected function execute(InputInterface $input, OutputInterface $output): int
51    {
52        $this->setAndDisplayLastTweet($output);
53
54        $content = $this->getContent($input);
55
56        if (!is_array($content)) {
57            $this->displayContentNotArrayError($output, $content);
58
59            return 1;
60        }
61
62        $numberOfTweets = count($content);
63
64        if ($numberOfTweets == 0) {
65            $output->writeln('<comment>No new tweet.</comment>');
66
67            return 0;
68        }
69
70        $this->addAndDisplayTweets($input, $output, $content, $numberOfTweets);
71
72        return 0;
73    }
74
75    protected function setAndDisplayLastTweet(OutputInterface $output): void
76    {
77        /** @var TweetRepository $tweetRepository */
78        $tweetRepository = $this->em
79            ->getRepository(Tweet::class);
80
81        // Get the last tweet
82        $lastTweet = $tweetRepository->getLastTweet();
83
84        // And use it in the request if it exists
85        if ($lastTweet) {
86            $this->parameters['since_id'] = $lastTweet->getId();
87
88            $comment = 'last tweet = '.$this->parameters['since_id'];
89        } else {
90            $comment = 'no last tweet';
91        }
92
93        $output->writeln('<comment>'.$comment.'</comment>');
94    }
95
96    /**
97     * @return array<\stdClass>|object
98     */
99    protected function getContent(InputInterface $input)
100    {
101        $connection = new TwitterOAuth(
102            (string) $this->container->getParameter('twitter_consumer_key'),
103            (string) $this->container->getParameter('twitter_consumer_secret'),
104            (string) $this->container->getParameter('twitter_token'),
105            (string) $this->container->getParameter('twitter_token_secret')
106        );
107
108        return $connection->get(
109            'statuses/home_timeline',
110            $this->parameters
111        );
112    }
113
114    /**
115     * @param array<string>|object $content
116     */
117    protected function displayContentNotArrayError(
118        OutputInterface $output,
119        $content
120    ): void {
121        $formatter = $this->getHelper('formatter');
122
123        $errorMessages = ['Error!', 'Something went wrong, $content is not an array.'];
124        $formattedBlock = $formatter->formatBlock($errorMessages, 'error');
125        $output->writeln($formattedBlock);
126        $output->writeln(print_r($content, true));
127    }
128
129    /**
130     * @param array<\stdClass> $content
131     */
132    protected function addAndDisplayTweets(
133        InputInterface $input,
134        OutputInterface $output,
135        array $content,
136        int $numberOfTweets
137    ): void {
138        $output->writeln('<comment>Number of tweets: '.$numberOfTweets.'</comment>');
139
140        // Iterate through $content in order to add the oldest tweet first,
141        //  if there is an error the oldest tweet will still be saved
142        //  and newer tweets can be saved next time the command is launched
143        $tweets = array_reverse($content);
144
145        $this->setProgressBar($output, $numberOfTweets);
146        $this->setTable($input, $output);
147        $this->iterateTweets($tweets);
148
149        $this->progress->finish();
150        $output->writeln('');
151
152        if ($this->displayTable) {
153            $this->table->render();
154        }
155    }
156
157    protected function setProgressBar(
158        OutputInterface $output,
159        int $numberOfTweets
160    ): void {
161        $this->progress = new ProgressBar($output, $numberOfTweets);
162        $this->progress->setBarCharacter('<comment>=</comment>');
163        $this->progress->start();
164    }
165
166    protected function setTable(
167        InputInterface $input,
168        OutputInterface $output
169    ): void {
170        $this->displayTable = (bool) $input->getOption('table');
171
172        // Display
173        if ($this->displayTable) {
174            $this->table = new Table($output);
175            $this->table
176                ->setHeaders(['Datetime', 'Text excerpt', 'Name']);
177        }
178    }
179
180    /**
181     * @param array<\stdClass> $tweets
182     */
183    protected function iterateTweets(array $tweets): void
184    {
185        $persistTweet = new PersistTweet(
186            $this->em,
187            $this->displayTable,
188            $this->table
189        );
190
191        foreach ($tweets as $tweetTmp) {
192            $persistTweet->addTweet($tweetTmp, true);
193
194            $this->progress->advance();
195        }
196    }
197}