-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathadd-joomla-articles-from-streamed-csv-url.php
159 lines (134 loc) · 4.45 KB
/
add-joomla-articles-from-streamed-csv-url.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
<?php
declare(strict_types=1);
/**
* Add Joomla! articles from streamed csv url (with POST)
*
* @author Alexandre ELISÉ <[email protected]>
* @copyright (c) 2009 - present. Alexandre ELISÉ. All rights reserved.
* @license GNU Affero General Public License version 3 (AGPLv3)
* @link https://apiadept.com
*/
// Public url of the sample csv used in this example (CHANGE WITH YOUR OWN CSV URL IF YOU WISH)
$csvUrl = 'https://docs.google.com/spreadsheets/d/e/2PACX-1vTtV7Bnj-E3mwnBgXbkZlzS476aHVp6vtZ7qdI5vxlPUUqNe_85S3ozT5_gzNkBDih4dL1f8uDeeh_g/pub?gid=522567559&single=true&output=csv';
// HTTP Verb
$httpVerb = 'POST';
// Your Joomla! 4.x website base url
$baseUrl = 'https://example.org';
$basePath = 'api/index.php/v1';
// This time we need endpoint to be a function to make it more dynamic
$endpoint = fn(string $givenBaseUrl, string $givenBasePath, int $givenResourceId = 0): string => $givenResourceId ? sprintf('%s/%s/%s/%d', $givenBaseUrl, $givenBasePath, 'content/articles', $givenResourceId)
: sprintf('%s/%s/%s', $givenBaseUrl, $givenBasePath, 'content/articles');
$timeout = 10;
// Add custom fields support (shout-out to Marc DECHÈVRE : CUSTOM KING)
// The keys are the columns in the csv with the custom fields names (that's how Joomla! Web Services Api work as of today)
// For the custom fields to work they need to be added in the csv and to exists in the Joomla! site.
$customFieldKeys = []; //['with-coffee','with-dessert','extra-water-bottle'];
// Your Joomla! 4.x Api Token (DO NOT STORE IT IN YOUR REPO USE A VAULT OR A PASSWORD MANAGER)
$token = '';
// PHP Generator to efficiently read the csv file
$generator = function (string $url, array $keys = []): Generator {
if (empty($url))
{
yield new RuntimeException('Url MUST NOT be empty', 422);
}
$defaultKeys = [
'title',
'alias',
'catid',
'articletext',
'language',
'metadesc',
'metakey',
'state',
'featured',
];
$mergedKeys = array_unique(array_merge($defaultKeys, $keys));
$resource = fopen($url, 'r');
if ($resource === false)
{
yield new RuntimeException('Could not read csv file', 500);
}
try
{
//NON-BLOCKING I/O (Does not wait before processing next line.)
stream_set_blocking($resource, false);
do
{
$currentLine = stream_get_line(
$resource,
0,
"\r\n"
);
if (empty($currentLine))
{
yield new RuntimeException('Current line MUST NOT be empty', 422);
}
$extractedContent = str_getcsv($currentLine);
// Remove first element of csv line as it is usually the id of the article (since for POST it's not used, we remove it)
array_shift($extractedContent);
if ($mergedKeys != $extractedContent)
{
$encodedContent = json_encode(array_combine($mergedKeys, $extractedContent));
yield $encodedContent;
}
yield new RuntimeException('Current line seem to be invalid', 422);
} while (!feof($resource));
} finally
{
fclose($resource);
}
};
// Read CSV in a PHP Generator using streams in non-blocking I/O mode
$streamCsv = $generator($csvUrl, $customFieldKeys);
// Process data returned by the PHP Generator
$process = function (string $givenHttpVerb, string $endpoint, string $dataString, array $headers, int $timeout, $transport) {
curl_setopt_array($transport, [
CURLOPT_URL => $endpoint,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => 'utf-8',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => $timeout,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_2TLS,
CURLOPT_CUSTOMREQUEST => $givenHttpVerb,
CURLOPT_POSTFIELDS => $dataString,
CURLOPT_HTTPHEADER => $headers,
]
);
$response = curl_exec($transport);
// Might slow down the script but at least shows what's going on
echo $response . PHP_EOL;
return $response;
};
foreach ($streamCsv as $dataString)
{
if (!is_string($dataString))
{
continue;
}
$curl = curl_init();
try
{
// HTTP request headers
$headers = [
'Accept: application/vnd.api+json',
'Content-Type: application/json',
'Content-Length: ' . mb_strlen($dataString),
sprintf('X-Joomla-Token: %s', trim($token)),
];
$output = $process($httpVerb, $endpoint($baseUrl, $basePath, 0), $dataString, $headers, $timeout, $curl);
// Continue even on partial failure
if (empty($output) || array_key_exists('errors', json_decode($output, true)))
{
continue;
}
}
catch (Throwable $e)
{
echo $e->getMessage() . PHP_EOL;
continue;
} finally
{
curl_close($curl);
}
}