Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save roberto-butti/019a0ae8a0a3edab9e829e9e37e56546 to your computer and use it in GitHub Desktop.

Select an option

Save roberto-butti/019a0ae8a0a3edab9e829e9e37e56546 to your computer and use it in GitHub Desktop.
Storyblok PHP Management API example: create a typed component schema, upload an asset, create a story, and move it to a workflow stage with a due date.
<?php
declare(strict_types=1);
/**
* Tutorial script: creates an `article-example` content type, uploads an image,
* creates a story using that component, and moves the story to the configured
* workflow stage with a due date when the stage exists.
*
* Requirements:
* - `vlucas/phpdotenv` installed, because this script loads configuration from `.env`.
* - `SECRET_KEY`: Storyblok Management API personal access token.
* - `STORYBLOK_SPACE_ID`: Storyblok space ID where the component, asset, and story are created.
* - `STORYBLOK_EXAMPLE_IMAGE_PATH`: optional path to the image to upload.
* - `STORYBLOK_WORKFLOW_STAGE_NAME`: optional workflow stage name, defaults to `Reviewing`.
* - `STORYBLOK_WORKFLOW_DUE_DATE`: optional due date, defaults to seven days from today.
*/
use Dotenv\Dotenv;
use Storyblok\ManagementApi\Data\Component;
use Storyblok\ManagementApi\Data\Fields\RichtextField;
use Storyblok\ManagementApi\Data\Fields\Schema\FieldAsset;
use Storyblok\ManagementApi\Data\Fields\Schema\FieldOption;
use Storyblok\ManagementApi\Data\Fields\Schema\FieldRichtext;
use Storyblok\ManagementApi\Data\Fields\Schema\FieldText;
use Storyblok\ManagementApi\Data\Story;
use Storyblok\ManagementApi\Data\StoryComponent;
use Storyblok\ManagementApi\Data\WorkflowStageChange;
use Storyblok\ManagementApi\Data\WorkflowStageData;
use Storyblok\ManagementApi\Endpoints\AssetApi;
use Storyblok\ManagementApi\Endpoints\ComponentApi;
use Storyblok\ManagementApi\Endpoints\StoryApi;
use Storyblok\ManagementApi\Endpoints\WorkflowStageChangeApi;
use Storyblok\ManagementApi\Endpoints\WorkflowStageApi;
use Storyblok\ManagementApi\ManagementApiClient;
require __DIR__ . "/../vendor/autoload.php";
// Load local environment variables from the test project .env file.
$dotenv = Dotenv::createImmutable(__DIR__ . "/../");
$dotenv->load();
function envString(string $key, ?string $default = null): string
{
if (array_key_exists($key, $_ENV) && $_ENV[$key] !== "") {
$value = $_ENV[$key];
return $value;
}
if ($default !== null) {
return $default;
}
fwrite(STDERR, "Missing required environment variable: {$key}" . PHP_EOL);
exit(1);
}
function findComponentByName(
ComponentApi $componentApi,
string $name,
): ?Component {
// The script should be idempotent for the tutorial: if the content type
// already exists, we stop before creating assets or stories.
foreach ($componentApi->all()->data() as $component) {
if ($component instanceof Component && $component->name() === $name) {
return $component;
}
}
return null;
}
function findWorkflowStageByName(
WorkflowStageApi $workflowStageApi,
string $name,
): ?WorkflowStageData {
// Workflow stage names are configured in the Storyblok UI. We search by
// name and then require an exact match before applying the stage change.
$stages = $workflowStageApi->list(search: $name)->data();
if (!$stages instanceof Traversable) {
return null;
}
foreach ($stages as $stage) {
if ($stage instanceof WorkflowStageData && $stage->name() === $name) {
return $stage;
}
}
return null;
}
// Configuration used by the tutorial. Required values come from .env; optional
// values have defaults so the script can be run without editing the code.
$token = envString("SECRET_KEY");
$spaceId = envString("STORYBLOK_SPACE_ID");
$imagePath = envString(
"STORYBLOK_EXAMPLE_IMAGE_PATH",
__DIR__ . "/../assets/creative-workspace-with-abstract-art.jpeg",
);
$storyName = envString("STORYBLOK_STORY_NAME", "Article Example Story");
$storySlug = envString(
"STORYBLOK_STORY_SLUG",
"article-example-story-" . date("YmdHis"),
);
$workflowStageName = envString("STORYBLOK_WORKFLOW_STAGE_NAME", "Reviewing");
$workflowDueDate = envString(
"STORYBLOK_WORKFLOW_DUE_DATE",
new DateTimeImmutable("+7 days")->format("Y-m-d"),
);
// Fail early if the image cannot be found. Asset upload is the first operation
// after the component is created, so this keeps errors easy to understand.
if (!is_file($imagePath)) {
fwrite(STDERR, "Image file not found: {$imagePath}" . PHP_EOL);
exit(1);
}
// Create the Management API client and the endpoint-specific APIs used below.
$client = new ManagementApiClient(
personalAccessToken: $token,
shouldRetry: true,
);
$componentApi = new ComponentApi($client, $spaceId);
$assetApi = new AssetApi($client, $spaceId);
$storyApi = new StoryApi($client, $spaceId);
$workflowStageApi = new WorkflowStageApi($client, $spaceId);
$componentName = "article-example";
// 1. Stop if the content type already exists.
if (findComponentByName($componentApi, $componentName) instanceof Component) {
echo "Component '{$componentName}' already exists. Nothing created." .
PHP_EOL;
exit(0);
}
// 2. Create the content type schema with the typed field helpers.
// Component::contentType() sets the Storyblok flags for a content type
// component, while appendFields() assigns the field positions in order.
echo "Creating component '{$componentName}'..." . PHP_EOL;
$component = Component::contentType($componentName)
->setDisplayName("Article Example")
->setPreviewField("headline")
->appendFields([
FieldText::make("headline")->setDisplayName("Headline")->setRequired(),
FieldAsset::make("image")->setDisplayName("Image"),
FieldRichtext::make("text")->setDisplayName("Text"),
FieldOption::make("category")
->setDisplayName("Category")
->addOption("Developers", "developers")
->addOption("Marketers", "marketers")
->addOption("Editors", "editors"),
]);
$createdComponent = $componentApi->create($component)->data();
echo "Created component: " . $createdComponent->name() . PHP_EOL;
// 3. Upload an image that will be assigned to the story asset field.
echo "Uploading image..." . PHP_EOL;
$asset = $assetApi->upload($imagePath)->data();
echo "Uploaded asset ID: " . $asset->id() . PHP_EOL;
// 4. Create the story content. StoryComponent::makeComponent() is used when
// building new content programmatically. The typed richtext helper creates the
// correct Storyblok richtext JSON shape.
echo "Creating story '{$storyName}'..." . PHP_EOL;
$content = StoryComponent::makeComponent($componentName)
->set("headline", "Automating Storyblok with PHP")
->setAsset("image", $asset)
->setRichtext(
"text",
RichtextField::paragraph(
"This story was created from the PHP Management API client.",
),
)
->set("category", "developers");
$story = new Story(name: $storyName, slug: $storySlug, content: $content);
$createdStory = $storyApi->create($story)->data();
echo "Created story ID: " . $createdStory->id() . PHP_EOL;
// 5. Move the story to the configured workflow stage when it exists.
// If your space does not have a "Reviewing" stage, the script leaves the story
// created and exits with a clear message.
$reviewingStage = findWorkflowStageByName(
$workflowStageApi,
$workflowStageName,
);
if (!$reviewingStage instanceof WorkflowStageData) {
echo "Workflow stage '{$workflowStageName}' not found. Story was created without workflow stage change." .
PHP_EOL;
exit(0);
}
// The due date is included in the workflow stage change payload.
echo "Moving story to workflow stage '{$workflowStageName}' with due date {$workflowDueDate}..." .
PHP_EOL;
$workflowStageChange = WorkflowStageChange::makeFromParams(
storyId: (int) $createdStory->id(),
workflowStageId: (int) $reviewingStage->id(),
dueDate: $workflowDueDate,
);
$workflowStageChangeApi = new WorkflowStageChangeApi($client, $spaceId);
$workflowStageChangeApi->create($workflowStageChange);
echo "Workflow stage change created." . PHP_EOL;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment