RecordsManager::searchDistant()   A
last analyzed

Complexity

Conditions 3
Paths 2

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12
Metric Value
dl 0
loc 13
ccs 0
cts 12
cp 0
rs 9.4285
cc 3
eloc 8
nc 2
nop 1
crap 12
1
<?php
2
3
namespace Fmaj\LaposteDatanovaBundle\Manager;
4
5
use Fmaj\LaposteDatanovaBundle\Model\Download;
6
use Fmaj\LaposteDatanovaBundle\Model\Search;
7
use Fmaj\LaposteDatanovaBundle\Client\ClientInterface;
8
use Fmaj\LaposteDatanovaBundle\Service\Downloader;
9
use Fmaj\LaposteDatanovaBundle\Service\Finder;
10
use Fmaj\LaposteDatanovaBundle\Parser\ParserInterface;
11
use Psr\Log\LoggerInterface;
12
13
class RecordsManager
14
{
15
    /**
16
     * @var ClientInterface
17
     */
18
    private $client;
19
20
    /**
21
     * @var Downloader
22
     */
23
    private $downloader;
24
25
    /**
26
     * @var Finder
27
     */
28
    private $finder;
29
30
    /**
31
     * @var ParserInterface[]
32
     */
33
    private $parsers;
34
35
    /** @var LoggerInterface */
36
    private $logger;
37
38
    /**
39
     * @param ClientInterface $client
40
     * @param Downloader $downloader
41
     * @param Finder $finder
42
     */
43
    public function __construct(ClientInterface $client, Downloader $downloader, Finder $finder)
44
    {
45
        $this->parsers = array();
46
        $this->client = $client;
47
        $this->downloader = $downloader;
48
        $this->finder = $finder;
49
    }
50
51
    /**
52
     * {@inheritdoc}
53
     */
54
    public function setLogger(LoggerInterface $logger)
55
    {
56
        $this->logger = $logger;
57
    }
58
59
    /**
60
     * @param ParserInterface $parser
61
     */
62
    public function addParser(ParserInterface $parser)
63
    {
64
        $this->parsers[] = $parser;
65
    }
66
67
    /**
68
     * @param Search $search
69
     *
70
     * @return array|false
71
     */
72
    public function search(Search $search)
73
    {
74
        //look locally (parser)
75
        $data = $this->searchLocally($search);
76
        //query remote api
77
        if (false === $data) {
78
            $data = $this->searchDistant($search);
79
        }
80
81
        return $data;
82
    }
83
84
    /**
85
     * Look locally (parsers)
86
     *
87
     * @param Search $search
88
     *
89
     * @return array|false
90
     */
91
    private function searchLocally(Search $search)
92
    {
93
        $this->log('debug', sprintf('Search locally for %s dataset', $search->getDataset()), $search->getParameters());
94
        $result = false;
95
        foreach ($this->parsers as $parser) {
96
            $parsed = $parser->parse($search->getDataset());
97
            if ($parsed) {
98
                $result = $this->searchInArrayData($parsed, $search);
99
                $this->sortLocalData($result, $search);
100
                $this->log(
101
                    'debug',
102
                    sprintf(
103
                        'Local dataset %s found (%s)',
104
                        $search->getDataset(),
105
                        get_class($parser)
106
                    ),
107
                    $search->getParameters()
108
                );
109
                break;
110
            }
111
        }
112
113
        return $result;
114
    }
115
116
    /**
117
     * @param string $level
118
     * @param string $message
119
     * @param array $context
120
     */
121
    private function log($level, $message, $context = array())
122
    {
123
        if ($this->logger) {
124
            $this->logger->log($level, $message, $context);
125
        }
126
    }
127
128
    /**
129
     * @param array $parsed
130
     * @param Search $search
131
     *
132
     * @return array
133
     */
134
    private function searchInArrayData(array $parsed, Search $search)
135
    {
136
        $data = array();
137
        foreach ($parsed as $index => $line) {
138
            if ($search->getStart() > $index) {
139
                continue;
140
            }
141
            if ($this->matchToSearch($search, $line)) {
142
                $data[] = $line;
143
            }
144
            if ($search->getRows() == count($data)) {
145
                break;
146
            }
147
        }
148
149
        return $data;
150
    }
151
152
    /**
153
     * @param Search $search
154
     * @param array $line
155
     *
156
     * @return bool
157
     */
158
    private function matchToSearch(Search $search, $line)
159
    {
160
        $match = false;
161
        if (null !== $search->getFilter()) {
162
            $column = $search->getFilterColumn();
163
            $value = $search->getFilterValue();
164
            if (!empty($column)) {
165
                if (array_key_exists($column, $line) && $value == $line[$column]) {
166
                    $match = true;
167
                }
168
            } else {
169
                foreach ($line as $data) {
170
                    if ($this->isLike($data, $value)) {
171
                        $match = true;
172
                        break;
173
                    }
174
                }
175
            }
176
        } else {
177
            $match = true;
178
        }
179
180
        return $match;
181
    }
182
183
    /**
184
     * @param string $data
185
     * @param string $term
186
     * @param bool $sensitive
187
     *
188
     * @return bool
189
     */
190
    private function isLike($data, $term, $sensitive = false)
191
    {
192
        $match = false;
193
        if (false === $sensitive) {
194
            $term = strtolower($term);
195
            $data = strtolower($data);
196
        }
197
        if (false !== strpos($data, $term)) {
198
            $match = true;
199
        }
200
201
        return $match;
202
    }
203
204
    /**
205
     * @param array $parsed
206
     * @param Search $search
207
     *
208
     * @return array
209
     */
210
    private function sortLocalData(array &$parsed, Search $search)
211
    {
212
        $sortKey = $search->getSort();
213
        if (!empty($sortKey)) {
214
            $sorter = function ($key) {
215
                return function ($elt1, $elt2) use ($key) {
216
                    return strnatcmp($elt1[$key], $elt2[$key]);
217
                };
218
            };
219
            usort($parsed, $sorter($sortKey));
220
        }
221
222
        return $parsed;
223
    }
224
225
    /**
226
     * Look locally (parsers)
227
     *
228
     * @param Search $search
229
     *
230
     * @return array|false
231
     */
232
    private function searchDistant(Search $search)
233
    {
234
        $data = array();
235
        $result = $this->client->get('search', $search->getParameters());
236
        if ($result) {
237
            $result = json_decode($result, true);
238
            foreach ($result['records'] as $record) {
239
                $data[] = $record['fields'];
240
            }
241
        }
242
243
        return $data;
244
    }
245
246
    /**
247
     * @param Download $download
248
     * @param bool $forceUpdate
249
     *
250
     * @return false|string
251
     */
252
    public function download(Download $download, $forceUpdate = false)
253
    {
254
        return $this->downloader->download(
255
            $download->getDataset(),
256
            $download->getFormat(),
257
            $download->getFilter(),
258
            $forceUpdate
259
        );
260
    }
261
262
    /**
263
     * @param Download $download
264
     *
265
     * @return null|string
266
     */
267
    public function getLocalDatasetContent(Download $download)
268
    {
269
        $content = null;
270
        $filepath = $this->finder->findDataset(
271
            $download->getDataset(),
272
            $download->getFormat(),
273
            $download->getFilter()
274
        );
275
        if (false !== $filepath) {
276
            $content = $this->finder->getContent($filepath);
277
        }
278
279
        return $content;
280
    }
281
}
282