Completed
Push — develop ( ba4597...b6307e )
by Timothy
02:28
created

src/Aviat/AnimeClient/Model/Manga.php (4 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Hummingbird Anime Client
4
 *
5
 * An API client for Hummingbird to manage anime and manga watch lists
6
 *
7
 * @package     HummingbirdAnimeClient
8
 * @author      Timothy J. Warren
9
 * @copyright   Copyright (c) 2015 - 2016
10
 * @link        https://github.com/timw4mail/HummingBirdAnimeClient
11
 * @license     MIT
12
 */
13
14
namespace Aviat\AnimeClient\Model;
15
16
use GuzzleHttp\Cookie\Cookiejar;
17
use GuzzleHttp\Cookie\SetCookie;
18
19
use Aviat\Ion\Json;
20
use Aviat\AnimeClient\Model\API;
21
use Aviat\AnimeClient\Hummingbird\Transformer;
22
use Aviat\AnimeClient\Hummingbird\Enum\MangaReadingStatus;
23
24
/**
25
 * Model for handling requests dealing with the manga list
26
 */
27
class Manga extends API {
28
29
	const READING = 'Reading';
30
	const PLAN_TO_READ = 'Plan to Read';
31
	const DROPPED = 'Dropped';
32
	const ON_HOLD = 'On Hold';
33
	const COMPLETED = 'Completed';
34
35
	/**
36
	 * Map API constants to display constants
37
	 * @var array
38
	 */
39
	protected $const_map = [
40
		MangaReadingStatus::READING => self::READING,
41
		MangaReadingStatus::PLAN_TO_READ => self::PLAN_TO_READ,
42
		MangaReadingStatus::ON_HOLD => self::ON_HOLD,
43
		MangaReadingStatus::DROPPED => self::DROPPED,
44
		MangaReadingStatus::COMPLETED => self::COMPLETED
45
	];
46
47
	/**
48
	 * The base url for api requests
49
	 * @var string
50
	 */
51
	protected $base_url = "https://hummingbird.me/";
52
53
	/**
54
	 * Make an authenticated manga API call
55
	 *
56
	 * @param string $type - the HTTP verb
57
	 * @param string $url
58
	 * @param string|null $json
59
	 * @return array
60
	 */
61
	protected function _manga_api_call($type, $url, $json = NULL)
62
	{
63
		$token = $this->container->get('auth')
64
			->get_auth_token();
65
66
		// Set the token cookie, with the authentication token
67
		// from the auth class.
68
		$cookieJar = $this->cookieJar;
69
		$cookie_data = new SetCookie([
70
			'Name' => 'token',
71
			'Value' => $token,
72
			'Domain' => 'hummingbird.me'
73
		]);
74
		$cookieJar->setCookie($cookie_data);
75
76
		$config = [
77
			'cookies' => $cookieJar
78
		];
79
80
		if ( ! is_null($json))
81
		{
82
			$config['json'] = $json;
83
		}
84
85
		$result = $this->client->request(strtoupper($type), $url, $config);
86
87
		return [
88
			'statusCode' => $result->getStatusCode(),
89
			'body' => $result->getBody()
90
		];
91
	}
92
93
	/**
94
	 * Add a manga to the list
95
	 *
96
	 * @param array $data
97
	 */
98
	public function add($data)
99
	{
100
		$object = [
101
			'manga_library_entry' => [
102
				'status' => $data['status'],
103
				'manga_id' => $data['id']
104
			]
105
		];
106
107
		return $this->_manga_api_call('post', 'manga_library_entries', $object);
0 ignored issues
show
$object is of type array<string,array<strin...\"manga_id\":\"?\"}>"}>, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
108
	}
109
110
	/**
111
	 * Update the selected manga
112
	 *
113
	 * @param array $data
114
	 * @return array
115
	 */
116
	public function update($data)
117
	{
118
		$id = $data['id'];
119
120
		return $this->_manga_api_call(
121
			'put',
122
			"manga_library_entries/{$id}",
123
			['manga_library_entry' => $data]
0 ignored issues
show
array('manga_library_entry' => $data) is of type array<string,array,{"man...ibrary_entry":"array"}>, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
124
		);
125
	}
126
127
	/**
128
	 * Delete a manga entry
129
	 *
130
	 * @param  array $data
131
	 * @return array
132
	 */
133
	public function delete($data)
134
	{
135
		$id = $data['id'];
136
137
		return $this->_manga_api_call('delete', "manga_library_entries/{$id}");
138
	}
139
140
	/**
141
	 * Search for manga by name
142
	 *
143
	 * @param string $name
144
	 * @return array
145
	 */
146
	public function search($name)
147
	{
148
		$logger = $this->container->getLogger('default');
149
150
		$config = [
151
			'query' => [
152
				'scope' => 'manga',
153
				'depth' => 'full',
154
				'query' => $name
155
			]
156
		];
157
158
		$response = $this->get('search.json', $config);
159
160 View Code Duplication
		if ($response->getStatusCode() != 200)
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
161
		{
162
			$logger->warning("Non 200 response for search api call");
163
			$logger->warning($response->getBody());
164
165
			throw new RuntimeException($response->getEffectiveUrl());
166
		}
167
168
		return Json::decode($response->getBody(), TRUE);
169
	}
170
171
	/**
172
	 * Get the full set of anime lists
173
	 *
174
	 * @return array
175
	 */
176
	public function get_all_lists()
177
	{
178
		$data = $this->_get_list_from_api();
179
180
		foreach ($data as &$val)
181
		{
182
			$this->sort_by_name($val, 'manga');
183
		}
184
185
		return $data;
186
	}
187
188
	/**
189
	 * Get a category out of the full list
190
	 *
191
	 * @param string $status
192
	 * @return array
193
	 */
194
	public function get_list($status)
195
	{
196
		$data = $this->_get_list_from_api($status);
197
		$this->sort_by_name($data, 'manga');
198
199
		return $data;
200
	}
201
202
	/**
203
	 * Retrieve the list from the hummingbird api
204
	 *
205
	 * @param  string $status
206
	 * @return array
207
	 */
208
	protected function _get_list_from_api($status = "All")
209
	{
210
		$config = [
211
			'query' => [
212
				'user_id' => $this->config->get('hummingbird_username')
213
			],
214
			'allow_redirects' => FALSE
215
		];
216
217
		$response = $this->get('manga_library_entries', $config);
218
		$data = $this->_check_cache($response);
219
		$output = $this->map_by_status($data);
220
221
		return (array_key_exists($status, $output))
222
			? $output[$status]
223
			: $output;
224
	}
225
226
	/**
227
	 * Check the status of the cache and return the appropriate response
228
	 *
229
	 * @param \GuzzleHttp\Message\Response $response
230
	 * @codeCoverageIgnore
231
	 * @return array
232
	 */
233
	private function _check_cache($response)
234
	{
235
		// Bail out early if there isn't any manga data
236
		$api_data = Json::decode($response->getBody(), TRUE);
237
		if ( ! array_key_exists('manga', $api_data))
238
		{
239
			return [];
240
		}
241
242
		$cache_file = _dir($this->config->get('data_cache_path'), 'manga.json');
243
		$transformed_cache_file = _dir(
244
			$this->config->get('data_cache_path'),
245
			'manga-transformed.json'
246
		);
247
248
		$cached_data = file_exists($cache_file)
249
			? Json::decodeFile($cache_file)
250
			: [];
251
252 View Code Duplication
		if ($cached_data === $api_data && file_exists($transformed_cache_file))
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
253
		{
254
			return Json::decodeFile($transformed_cache_file);
255
		}
256
		else
257
		{
258
			Json::encodeFile($cache_file, $api_data);
259
260
			$zippered_data = $this->zipper_lists($api_data);
261
			$transformer = new Transformer\MangaListTransformer();
262
			$transformed_data = $transformer->transform_collection($zippered_data);
263
			Json::encodeFile($transformed_cache_file, $transformed_data);
264
			return $transformed_data;
265
		}
266
	}
267
268
	/**
269
	 * Map transformed anime data to be organized by reading status
270
	 *
271
	 * @param array $data
272
	 * @return array
273
	 */
274
	private function map_by_status($data)
275
	{
276
		$output = [
277
			self::READING => [],
278
			self::PLAN_TO_READ => [],
279
			self::ON_HOLD => [],
280
			self::DROPPED => [],
281
			self::COMPLETED => [],
282
		];
283
284
		foreach ($data as &$entry)
285
		{
286
			$entry['manga']['image'] = $this->get_cached_image(
287
				$entry['manga']['image'],
288
				$entry['manga']['slug'],
289
				'manga'
290
			);
291
			$key = $this->const_map[$entry['reading_status']];
292
			$output[$key][] = $entry;
293
		}
294
295
		return $output;
296
	}
297
298
	/**
299
	 * Combine the two manga lists into one
300
	 * @param  array $raw_data
301
	 * @return array
302
	 */
303
	private function zipper_lists($raw_data)
304
	{
305
		return (new Transformer\MangaListsZipper($raw_data))->transform();
306
	}
307
}
308
// End of MangaModel.php