Completed
Push — master ( 717c8b...cd6864 )
by Pascal
10:32
created

SetInfos::crawlNextPage()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 15
ccs 0
cts 9
cp 0
rs 9.4286
cc 2
eloc 8
nc 2
nop 2
crap 6
1
<?php namespace MtGTutor\Console\Commands;
2
3
use Goutte\Client;
4
use Symfony\Component\Console\Command\Command;
5
use Symfony\Component\Console\Helper\Table;
6
use Symfony\Component\Console\Input\InputArgument;
7
use Symfony\Component\Console\Input\InputInterface;
8
use Symfony\Component\Console\Input\InputOption;
9
use Symfony\Component\Console\Output\OutputInterface;
10
use Symfony\Component\DomCrawler\Crawler;
11
12
/**
13
 * Class SetInfos
14
 * @package MtGTutor\Console\Commands
15
 */
16
class SetInfos extends Command
17
{
18
    /**
19
     * @var string
20
     */
21
    protected $url = 'http://magic.wizards.com/en/game-info/products/card-set-archive';
22
23
    /**
24
     * @var string
25
     */
26
    protected $imagePath = 'http://magic.wizards.com/sites/mtg/files/';
27
28
    /**
29
     * @var bool
30
     */
31
    protected $exists = false;
32
33
    /**
34
     * @var array
35
     */
36
    protected $data = [
37
        'code'   => null,
38
        'name'   => null,
39
        'block'  => null,
40
        'number' => null,
41
        'date'   => null,
42
        'logo'   => null,
43
        'icon'   => null,
44
    ];
45
46
    /**
47
     * Configure command
48
     */
49 12
    protected function configure()
50
    {
51 12
        $this->setName('set:info')
52 12
            ->addArgument(
53 12
                'set',
54 12
                InputArgument::REQUIRED,
55
                'Which set do you want to select. Use the official three-letter code.'
56 12
            )
57 12
            ->addOption('full-path', null, InputOption::VALUE_NONE, 'Add the full path to the images')
58 12
            ->addOption('date-format', null, InputOption::VALUE_OPTIONAL, 'Date Format', 'Y-m-d')
59 12
            ->addOption('json', null, InputOption::VALUE_NONE, 'Output result as json')
60 12
            ->setDescription('Get some basic information about a specific set')
61 12
            ->setHelp(
62
                <<<EOT
63
Gets the following information about a set:
64
 * Official Three-Letter Code
65
 * Set Name
66
 * Block
67
 * Number of Cards
68
 * Release Date
69
 * Logo
70
 * Icon
71
72
Usage:
73
Default usage to get a set (here Alara Reborn)
74
<info>mtgtutor-console set:info ARB</info>
75
76
You can specify a date format via the --date-format option
77
<info>mtgtutor-console set:info ARB --date-format=d.m.Y</info>
78
79
You can change the output format to json with the --json option
80
<info>mtgtutor-console set:info ARB --json</info>
81
82
You can add the full path to the files with --full-path
83
<info>mtgtutor-console set:info ARB --full-path</info>
84
85
You can combine all these options
86
<info>mtgtutor-console set:info ARB --date-format=d.m.Y --json --full-path</info>
87
EOT
88 12
            );
89 12
    }
90
91
    /**
92
     * Get set infos
93
     * @throws \InvalidArgumentException if no set is found
94
     * @param \Symfony\Component\Console\Input\InputInterface   $input
95
     * @param \Symfony\Component\Console\Output\OutputInterface $output
96
     * @return void
97
     */
98
    protected function execute(InputInterface $input, OutputInterface $output)
99
    {
100
        // init vars
101
        $this->data['code'] = $input->getArgument('set');
102
103
        // Fetch WotC website
104
        $client = new Client();
105
        $crawler = $client->request('GET', $this->url);
106
107
        $crawler->filter('.card-set-archive-table > ul > li > a > span.logo > img')->each(function (Crawler $node) use (
108
            &$client
109
        ) {
110
            $logo = $node->attr('src');
111
            $this->exists = true;
112
113
            if ($this->setExists($logo)) {
114
                // Get Logo + Icon + Name
115
                $siblings = $node->parents()->first()->siblings();
116
117
                $this->data['logo'] = str_replace($this->imagePath, '', $logo);
118
                $this->data['icon'] = str_replace($this->imagePath, '', $siblings->eq(0)->children()->attr('src'));
119
                $this->data['name'] = trim($siblings->eq(1)->text());
120
121
                // go to next page
122
                $crawler = $this->crawlNextPage($node, $client);
123
124
                // Fetch block, number of cards and release date
125
                $crawler->filter('.tab-content.current > p')->each(function (Crawler $node, $i) {
126
                    if ($i % 2) {
127
                        $this->fetchBlockCardsAndDate($node, $i);
128
                    }
129
                });
130
            }
131
        });
132
133
        // Could not find set
134
        if (!$this->exists) {
135
            throw new \InvalidArgumentException('Could not find set with the following code: ' . $this->data['code']);
136
        }
137
138
        // Format result
139
        $this->formatResult($input);
140
141
        // Ouput result
142
        $this->output($input, $output);
143
    }
144
145
    /**
146
     * @param string $url
147
     * @return bool|null
148
     */
149
    protected function setExists($url)
150
    {
151
        return strpos(strtolower($url), strtolower($this->data['code'])) !== false;
152
    }
153
154
    /**
155
     * @param \Symfony\Component\DomCrawler\Crawler $crawler
156
     * @param \Goutte\Client                        $client
157
     * @return \Symfony\Component\DomCrawler\Crawler
158
     */
159
    protected function crawlNextPage(Crawler $crawler, Client $client)
160
    {
161
        // Go to details page
162
        $link = $crawler->parents()->parents()->link();
163
        $crawler = $client->click($link);
164
165
        // Go to to info page (if exists)
166
        $anchor = $crawler->selectLink('Info');
167
        if ($anchor->count()) {
168
            $link = $anchor->link();
169
            $crawler = $client->click($link);
170
        }
171
172
        return $crawler;
173
    }
174
175
    /**
176
     * @param \Symfony\Component\DomCrawler\Crawler $node
177
     * @param integer                               $i
178
     */
179
    protected function fetchBlockCardsAndDate(Crawler $node, $i)
180
    {
181
        // Get block and number of cards
182
        if ($i == 1) {
183
            preg_match('~<strong>Block:<\\/strong>.*<em>(.+?)</~m', $node->html(), $block);
184
            preg_match('~<strong>Number of Cards:\s*<\/strong>[^\d]*(\d+)<?~m', $node->html(), $number);
185
186
            $this->data['block'] = $block[1];
187
            $this->data['number'] = (int)$number[1];
188
        }
189
190
        // get release date
191
        if ($i == 5) {
192
            preg_match('~<strong>Release Date:<\/strong>.+?([\w|,|\s]+)<~im', $node->html(), $release);
193
            $this->data['date'] = $release[1];
194
        }
195
    }
196
197
    /**
198
     * Format result
199
     * @param \Symfony\Component\Console\Input\InputInterface $input
200
     * @return void
201
     */
202
    protected function formatResult(InputInterface $input)
203
    {
204
        // change date format
205
        $date = new \DateTime(date('Y-m-d H:i:s', strtotime($this->data['date'])));
206
        $this->data['date'] = $date->format($input->getOption('date-format'));
207
208
        // add full path to url
209
        if ($input->getOption('full-path')) {
210
            $this->data['logo'] = $this->imagePath . $this->data['logo'];
211
            $this->data['icon'] = $this->imagePath . $this->data['icon'];
212
        }
213
    }
214
215
    /**
216
     * Write result to console
217
     * @param \Symfony\Component\Console\Input\InputInterface   $input
218
     * @param \Symfony\Component\Console\Output\OutputInterface $output
219
     */
220
    protected function output(InputInterface $input, OutputInterface $output)
221
    {
222
        // json output
223
        if ($input->getOption('json')) {
224
            $output->writeln(json_encode($this->data, JSON_PRETTY_PRINT));
225
        }
226
227
        // table output
228
        if (!$input->getOption('json')) {
229
            $table = new Table($output);
230
            $table
231
                ->setHeaders(['Code', 'Name', 'Block', 'Number', 'Release Date', 'Logo', 'Icon'])
232
                ->setRows([$this->data]);
233
            $table->render();
234
        }
235
    }
236
}
237