Skip to content

Commit 52dcc75

Browse files
committed
Welcome to StoryBulkApi class
1 parent 2da53a5 commit 52dcc75

File tree

7 files changed

+505
-408
lines changed

7 files changed

+505
-408
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## 0.0.5 - WIP
4+
5+
- Re-structuring bulk operations and pagination with `StoryBulkApi` class
6+
37
## 0.0.4 - 2025-02-05
48

59
- Adding WorkflowStage Api class for handling workflow stages

README.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -210,13 +210,17 @@ $stories = $storyApi->page(
210210
### Filtering stories via query filters
211211

212212
Besides using query parameters to filter stories, you can leverage more powerful query filters.
213+
214+
> If you need to handle pagination (retrieving all stories across multiple pages) or create multiple stories at once, you can use the `StoryBulkApi` instead of the `StoryApi` class.
215+
213216
In this example, you will retrieve all stories where the "title" field is empty. (Stories with content types that do not include a "title" field in their schema will be skipped.)
214217

215218
```php
216219
use Storyblok\ManagementApi\QueryParameters\Filters\Filter;
217220
use Storyblok\ManagementApi\QueryParameters\Filters\QueryFilters;
218221

219-
$stories = $storyApi->all(
222+
$storyBulkApi = $client->storyBulkApi($spaceId);
223+
$stories = $storyBulkApi->all(
220224
filters: (new QueryFilters())->add(
221225
new Filter(
222226
"title",
@@ -298,7 +302,7 @@ $storyApi->publish($storyId);
298302

299303

300304
### Creating stories in bulk
301-
You can create multiple stories using the `createBulk()` method, which processes an array of stories while managing rate limits through a retry mechanism that respects the '429' status code.
305+
You can create multiple stories using the `createStories()` method (in the `StoryBulkApi` class), which processes an array of stories while managing rate limits through a retry mechanism that respects the '429' status code.
302306

303307
For example, if you have a CSV file containing content for new stories, you can use this method to efficiently create them.
304308

@@ -308,10 +312,10 @@ myslug-002;My Story 2 BULK;page
308312
myslug-003;My Story 3 BULK;page
309313
```
310314

311-
Next, you can implement a script to load and parse the CSV file. In this case, we use `SplFileObject` and then call the `createBulk` method to process the data:
315+
Next, you can implement a script to load and parse the CSV file. In this case, we use `SplFileObject` and then call the `createStories` method to process the data:
312316

313317
```php
314-
$storyApi = $client->storyApi($spaceId);
318+
$storyBulkApi = $client->storyBulkApi($spaceId);
315319
$file = new SplFileObject("stories.csv");
316320
$file->setFlags(SplFileObject::READ_CSV);
317321
$file->setCsvControl(separator: ";");
@@ -324,7 +328,7 @@ foreach ($file as $row) {
324328
$story->setContentType($contentType);
325329
$stories[] = $story;
326330
}
327-
$createdStories = iterator_to_array($storyApi->createBulk($stories));
331+
$createdStories = iterator_to_array($storyBulkApi->createStories($stories));
328332
```
329333

330334
## Handling users

src/Endpoints/StoryApi.php

Lines changed: 0 additions & 158 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,6 @@
2424
*/
2525
class StoryApi extends EndpointSpace
2626
{
27-
private const int DEFAULT_ITEMS_PER_PAGE = 25;
28-
29-
private const int DEFAULT_PAGE = 1;
30-
31-
private const int RATE_LIMIT_STATUS_CODE = 429;
32-
33-
private const int RETRY_DELAY = 1;
34-
35-
private const int MAX_RETRIES = 3;
36-
3727
/**
3828
* StoryApi constructor.
3929
*/
@@ -45,47 +35,6 @@ public function __construct(
4535
parent::__construct($httpClient, $spaceId);
4636
}
4737

48-
/**
49-
* Retrieves all stories using pagination
50-
*
51-
* @param int $itemsPerPage Number of items to retrieve per page
52-
* @return \Generator<StoryData>
53-
* @throws StoryblokApiException
54-
*/
55-
public function all(?StoriesParams $params = null, ?QueryFilters $filters = null, int $itemsPerPage = self::DEFAULT_ITEMS_PER_PAGE): \Generator
56-
{
57-
58-
$totalPages = null;
59-
$retryCount = 0;
60-
$page = new PaginationParams(self::DEFAULT_PAGE, $itemsPerPage);
61-
62-
do {
63-
try {
64-
$response = $this->page(
65-
params: $params,
66-
queryFilters: $filters,
67-
page: $page,
68-
);
69-
70-
if ($response->isOk()) {
71-
$totalPages = $this->handleSuccessfulResponse($response, $totalPages, $itemsPerPage);
72-
yield from $this->getStoriesFromResponse($response);
73-
$page->incrementPage();
74-
$retryCount = 0;
75-
} else {
76-
$this->handleErrorResponse($response, $retryCount);
77-
++$retryCount;
78-
}
79-
} catch (\Exception $e) {
80-
$this->logger->error('Error fetching stories', [
81-
'error' => $e->getMessage(),
82-
'page' => $page->page(),
83-
]);
84-
throw new StoryblokApiException('Failed to fetch stories: ' . $e->getMessage(), 0, $e);
85-
}
86-
} while ($page->page() <= $totalPages);
87-
}
88-
8938
/**
9039
* Retrieves a specific page of stories
9140
*/
@@ -280,114 +229,7 @@ public function unpublish(
280229
);
281230
}
282231

283-
/**
284-
* Creates multiple stories with rate limit handling and retries
285-
*
286-
* @param StoryData[] $stories Array of stories to create
287-
* @return \Generator<StoryblokDataInterface> Generated stories
288-
* @throws StoryblokApiException
289-
*/
290-
public function createBulk(array $stories): \Generator
291-
{
292-
$retryCount = 0;
293-
294-
foreach ($stories as $storyData) {
295-
while (true) {
296-
try {
297-
$response = $this->create($storyData);
298-
yield $response->data();
299-
$retryCount = 0;
300-
break;
301-
} catch (StoryblokApiException $e) {
302-
if ($e->getCode() === self::RATE_LIMIT_STATUS_CODE) {
303-
if ($retryCount >= self::MAX_RETRIES) {
304-
$this->logger->error('Max retries reached while creating story', [
305-
'story_name' => $storyData->name(),
306-
]);
307-
throw new StoryblokApiException(
308-
'Rate limit exceeded maximum retries',
309-
self::RATE_LIMIT_STATUS_CODE,
310-
);
311-
}
312-
313-
$this->logger->warning('Rate limit reached while creating story, retrying...', [
314-
'retry_count' => $retryCount + 1,
315-
'max_retries' => self::MAX_RETRIES,
316-
'story_name' => $storyData->name(),
317-
]);
318-
319-
$this->handleRateLimit();
320-
++$retryCount;
321-
continue;
322-
}
323-
324-
throw $e;
325-
}
326-
}
327-
}
328-
}
329-
330-
/**
331-
* Handles successful API response
332-
*/
333-
private function handleSuccessfulResponse(
334-
StoryblokResponseInterface $response,
335-
?int $totalPages,
336-
int $itemsPerPage,
337-
): int {
338-
if ($totalPages === null) {
339-
$totalPages = (int) ceil($response->total() / $itemsPerPage);
340-
$this->logger->info('Total stories found: ' . $response->total());
341-
}
342-
343-
return $totalPages;
344-
}
345-
346-
/**
347-
* Handles error responses from the API
348-
*
349-
* @throws StoryblokApiException
350-
*/
351-
private function handleErrorResponse(StoryblokResponseInterface $response, int $retryCount): void
352-
{
353-
if ($response->getResponseStatusCode() === self::RATE_LIMIT_STATUS_CODE) {
354-
if ($retryCount < self::MAX_RETRIES) {
355-
$this->handleRateLimit();
356-
} else {
357-
throw new StoryblokApiException('Rate limit exceeded maximum retries');
358-
}
359-
} else {
360-
$this->logger->error('API error', [
361-
'status' => $response->getResponseStatusCode(),
362-
'message' => $response->getErrorMessage(),
363-
]);
364-
throw new StoryblokApiException($response->getErrorMessage());
365-
}
366-
}
367-
368-
/**
369-
* Handles rate limiting by implementing a delay
370-
*/
371-
protected function handleRateLimit(): void
372-
{
373-
$this->logger->warning('Rate limit reached, waiting before retry...');
374-
sleep(self::RETRY_DELAY);
375-
}
376232

377-
/**
378-
* Extracts stories from the API response
379-
*
380-
* @return \Generator<int, StoryData>
381-
*/
382-
private function getStoriesFromResponse(StoryblokResponseInterface $response): \Generator
383-
{
384-
/** @var StoriesData $stories */
385-
$stories = $response->data();
386-
foreach ($stories as $story) {
387-
/** @var StoryData $story */
388-
yield $story;
389-
}
390-
}
391233

392234
/**
393235
* Validates pagination parameters

0 commit comments

Comments
 (0)