Giter VIP home page Giter VIP logo

storyblok-php-client's Introduction

Storyblok Logo

Storyblok PHP Client

This is the official Storyblok PHP client to easily access the Content Delivery API and Management API.

Join the Storyblok Discord Community Follow @Storyblok
Follow @Storyblok

๐Ÿš€ Usage

With the Storyblok PHP client you can integrate two kinds of Storyblok APIs:

  • Management API: typically used for managing data, like creating data, blocks, settings etc.
  • Content Delivery API: typically used for retrieving data, for example when you want to build your public Web application.

In this README file you will find information for using the Storyblok PHP client, like:

Installing the Storyblok PHP client

You can install the Storyblok PHP Client via composer. Storyblok's PHP client requires PHP version 7.3 to 8.3. The suggestion is to use an actively supported version of PHP (8.2 and 8.3).

If you want to install the stable release of Storyblok PHP client you can launch:

composer require storyblok/php-client

If you want to install the current development release, you can add the version dev-master:

composer require storyblok/php-client dev-master

For executing the command above, you need to have composer installed on your development environment. If you need to install Composer, you can follow the official Composer documentation:

We suggest using the latest version of PHP.

Management API

The Storyblok\ManagementClient instance

Now we are going to see how to initialize the Storyblok Management Client for the Management API](https://www.storyblok.com/docs/api/management) with your Personal OAuth Token. The Personal OAuth token is taken from the "My Account" section. This token is used for read and write operations. The class for using the Management API is the Storyblok\ManagementClient class. When you are going to instance a new ManagementClient object you can use the Personal OAuth Token as a parameter.

<?php
// Require composer autoload
require 'vendor/autoload.php';
// Use the Storyblok\ManagementClient class
use Storyblok\ManagementClient;
// Use the ManagementClient class
$managementClient = new ManagementClient('your-storyblok-oauth-token');

Now, you have the ManagementClient object ($managementClient), you can start to manage your Storyblok data.

Retrieve data, get() method

If you need to retrieve data, you have to perform an HTTP request with the GET method. The ManagementClient provides a get() method for performing the HTTP request. The mandatory parameter is the path of the API ( for example spaces/<yourSpaceId/stories). The path defines which endpoint you want to use.

For retrieving a list of Stories:

$spaceId = 'YOUR_SPACE_ID';
$result = $managementClient->get('spaces/' . $spaceId . '/stories')->getBody();
print_r($result['stories']);

With getBody() method you can access the body response, and then access the stories key, to access the story list.

Create data, post() method

If you need to create data, you have to perform an HTTP request with the POST method. The ManagementClient provides a post() method for performing the HTTP request. The mandatory parameter is the path of the API ( for example spaces/<yourSpaceId/stories/), and the Story payload. For creating a new story:

$spaceId = 'YOUR_SPACE_ID';
$story = [
    "name" => "New Page",
    "slug" => "page-1",
    "content" =>  [
        "component" =>  "page",
        "body" =>  []
    ]
];
$result = $managementClient->post(
    'spaces/' . $spaceId . '/stories/',
    [ 'story' => $story ]
    )->getBody();
print_r($result);

Update data, put() method

If you need to update data, you have to perform an HTTP request with PUT method. The ManagementClient provides a put() method for performing the HTTP request. The mandatory parameter is the path of the API ( for example spaces/<yourSpaceId/stories/<storyId>), and the Story payload. For updating the story:

$spaceId = 'YOUR_SPACE_ID';
$storyId= 'theStoryId';
$story = [
    "name" => "Update Home Page"
];
$result = $managementClient->put(
    'spaces/' . $spaceId . '/stories/' . $storyId,
    [ 'story' => $story ]
    )->getBody();
print_r($result);

Delete data, delete() method

If you need to delete data, you have to perform an HTTP request with the DELETE method. The ManagementClient provides a delete() method for performing the HTTP request. The mandatory parameter is the path of the API, defining also the identifier of the entry you want to delete ( for example spaces/<yourSpaceId/stories/<storyId>). For deleting a story:

$spaceId = 'YOUR_SPACE_ID';
$storyId = 'YOUR_STORY_ID';
$result = $managementClient->delete('spaces/' . $spaceId . '/stories/' . $storyId)->getbody();
print_r($result);

Using spaces created in other regions for Management API

When creating a Space, you can select the EU, US, CA, AP region. The default region is EU.

EU: api.storyblok.com
US: api-us.storyblok.com
CA: api-ca.storyblok.com
AP: api-ap.storyblok.com

For example: If you want to access a Space created in US region, you need to define the apiEndpoint parameter with api-us.storyblok.com value, and forcing the ssl option for using HTTPS protocol:

use Storyblok\ManagementClient;

$client = new ManagementClient(
    apiKey: 'your-storyblok-oauth-token',
    apiEndpoint: "api-us.storyblok.com",
    ssl : true
);

Now you have the Storyblok\ManagementClient instance, you can start managing data.

Content Delivery API

The Storyblok\Client instance

Now we are going to see how to initialize the Storyblok Client class for consuming the Content Delivery API V2, with the Access Token. You can retrieve the access token from the "Settings > Access Tokens" tab (in your space, in Stroyblok UI).

<?php
// Require composer autoload
require 'vendor/autoload.php';
// Use the Storyblok\Client class
use Storyblok\Client;
// Use the Client class
$client = new Client('your-storyblok-readonly-accesstoken');

If you want to use an alias to refer to the Storyblok\Client class, you can use the use ... as ... statement:

require 'vendor/autoload.php';
use Storyblok\Client as StoryblokClient;
// Use the Storyblok\Client class via alias
$client = new StoryblokClient('your-storyblok-readonly-accesstoken');

Using spaces created in the other regions

When you create a Space, you can select the region: EU, US, CA or AP.

For example: If you want to access a Space created in US region, you need to define the apiRegion parameter with the us value (or US):

use Storyblok\Client;

$client = new Client(
    apiKey: 'your-storyblok-readonly-accesstoken',
    apiRegion: 'us'
);

If you are still using PHP 7.x, you have to use the old notation (without named arguments):

use Storyblok\Client;
$client = new Client(
    'your-storyblok-readonly-accesstoken',
    null,
    'v2',
    false,
    'us'
);

Now you have the Storyblok\Client instance you can start consuming data.

Load a Story by slug

require 'vendor/autoload.php';
use Storyblok\Client as StoryblokClient; // you can use also an alias
$client = new StoryblokClient('your-storyblok-readonly-accesstoken');
$data = $client->getStoryBySlug('home')->getBody();
// access to the body response...
print_r($data["story"]);
echo $data["cv"] . PHP_EOL;
print_r($data["rels"]);
print_r($data["links"]);

Load a Story by slug for a specific language

If are using the field-level translation, you can retrieve a story for a specific language via the language() method. The language method requires a string as a parameter with the code of the language.

require 'vendor/autoload.php';
use Storyblok\Client;
$client = new Client('your-storyblok-readonly-accesstoken');
$client->language('it');
$data = $client->getStoryBySlug('home')->getBody();
// access to the body response...
print_r($data["story"]);

Load Space information

If you need to access some space information like space identifier, space name, the latest version timestamp, or the list of configured languages you can use the spaces endpoint.

<?php

require 'vendor/autoload.php';

use Storyblok\Client as StoryblokClient; // you can use also an alias

$client = new StoryblokClient('your-storyblok-readonly-accesstoken');
$space = $client->get('spaces/me/' , $client->getApiParameters());
$data = $space->getBody();
print_r($data);
// Array of the language codes:
print_r($data["space"]["language_codes"]);
// The latest version timestamp:
echo "Last timestamp : " . $data["space"]["version"] . PHP_EOL;
// The space name:
echo "Space name : " . $data["space"]["name"] . PHP_EOL;
// The space id:
echo "Space id : " . $data["space"]["id"] . PHP_EOL;

Because the PHP Client, with the current version, doesn't provide an helper for retrieving data from the space endpoint you can use the get() method for accessing the spaces/me path of the Content Delivery API. The only thing you need to remember is to set the second parameter for the get() method injecting the API parameters. Even if you didn't set any parameters, you have to send getApiParameters() as the second parameter for the get() method because the PHP client manages for you some core parameters like the token. This is because you are using the low-level method get().

Load a Story by UUID

require 'vendor/autoload.php';
use Storyblok\Client as StoryblokClient; // you can use also an alias
$client = new StoryblokClient('your-storyblok-readonly-accesstoken');
$client->getStoryByUuid('0c092d14-5cd4-477e-922c-c7f8e330aaea');
$data = $client->getBody();

The structure of the data returned by the getBody() of the getStoryByUuid() method, has the same structure of the getStoryBySlug() so: story, cv, rels, links.

Load a list of Stories

If you need to retrieve a list of stories you can use the getStories() method. You can use the parameter to filter the stories. For example, if you want to retrieve all entries from a specific folder you can use starts_with option in this way:

$client = new \Storyblok\Client('your-storyblok-readonly-accesstoken');
// Get all Stories from the article folder
$client->getStories(['starts_with' => 'article']);
$data = $client->getBody();
print_r($data["stories"]);
echo $data["cv"] . PHP_EOL;
print_r($data["rels"]);
print_r($data["links"]);

Under the hood, the starts_with option, filters entries by full_slug.

Load all entries

Because the response from Storyblok API could be paginated, you should walk through all the pages to collect all the entries. The Storyblok PHP Client provides you a helper named getAll() for retrieving all the entries. Under the hood, the getAll() method performs all the API call according to the pagination data (total, per page etc).

Example, retrieving all stories:

$client = new Client('your-storyblok-readonly-accesstoken');
$options = $client->getApiParameters();
$options['per_page'] = 3;
$stories = $client->getAll('stories/', $options);

If you want to retrieve the array of the responses for each call:

$client = new Client('your-storyblok-readonly-accesstoken');
$options = $client->getApiParameters();
$options['per_page'] = 3;
$response = $client->getAll('stories/', $options, true);

Load a list of datasource entries

With the Storyblok\Client you have also the getDatasourceEntries() method for retrieving the list of key/values of the datasource:

$client = new \Storyblok\Client('your-storyblok-readonly-accesstoken');
// Get category entries from datasource
$client->getDatasourceEntries('categories');
// will return as ['name']['value'] Array for easy access
$nameValueArray = $client->getAsNameValueArray();
// instead, if you want to retrieve the whole response, you can use getBody() method:
$data = $client->getBody();

If you want to receive also the dimension values besides the default values in one datasource entry you can use the option dimension when you call getDatasourceEntries() method. You could use dimensions for example when you are using datasource for storing a list of values and you want a translation for the values. In this case, you should create one dimension for each language.

$client = new \Storyblok\Client('your-storyblok-readonly-accesstoken');
// Get product entries with dimension 'de-at'
$client->getDatasourceEntries('products', ['dimension'=> 'de-at']);
// show the dimension values:
foreach ($client->getBody()['datasource_entries'] as $key => $value) {
    echo $value['dimension_value'] . PHP_EOL;
}

Load a list of tags

$client = new \Storyblok\Client('your-storyblok-readonly-accesstoken');
// Get all Tags
$client->getTags();
// will return the whole response
$data = $client->getBody();
// will return as ['tagName1', 'tagName2'] Array for easy access
$stringArray = $client->getAsStringArray();

Access to the Responses Headers

When you perform a request to Content Delivery API, you can access the headers of the HTTP response. For example, after you call the getStories() method (for retrieving a list of stories) you can access to the HTTP response headers via getHeaders() method:

$client = new \Storyblok\Client('your-storyblok-readonly-accesstoken');
$result = $client->getStories();
$headersData = $client->getHeaders();
print_r($headersData);

Retrieving Draft or Published content

In a Web application where the query string is available, the content delivery client checks automatically the GET parameters:

  • _storyblok to get the draft version of a specific story
  • _storyblok_published to clear the cache.

If you want to override this "default" behavior, or you are in a non-web context (for example you are implementing a command line tool), to retrieve the draft content (for example a not yet published story) you have to use the editMode() method. If you want to retrieve the published content (for example a published story) you have to use the editMode(false) method with false parameter.

require 'vendor/autoload.php';
use Storyblok\Client as StoryblokClient; // you can use also an alias
$client = new StoryblokClient('your-storyblok-readonly-accesstoken');
$client->editMode(); // forcing draft mode
$data = $client->getStoryBySlug('home')->getBody();
// access to the body response...
print_r($data["story"]);
echo $data["cv"] . PHP_EOL;
print_r($data["rels"]);
print_r($data["links"]);

Managing cache

When you perform an API request you can use the caching mechanism provided by the Storyblok PHP client. When you initialize the Storyblok\Client you can set the cache provider. For example, using the setCache() method you can define the provider (for example filesystem) and an array of options. In case you are using the filesystem as storage of cache items, you can set the path with path option:

$client = new \Storyblok\Client('your-storyblok-readonly-accesstoken');
$client->setCache('filesystem', [ 'path' => 'cache']);
$result = $client->getStories();
print_r($result);

You can set a TTL value for the cache via default_lifetime option.

$client = new \Storyblok\Client('your-storyblok-readonly-accesstoken');
$client->setCache('filesystem',
    [
        'path' => 'cache',
        'default_lifetime' => 3600
    ]);
$result = $client->getStories();
print_r($result);

The caching mechanism uses under the hood the Symfony Cache package. So, you can use the Adapter supported the Symfony Cache. For example, for using a MySql database as cache storage, you can setup the connection via the PHP PDO class:

$client = new \Storyblok\Client('your-storyblok-readonly-accesstoken');
$pdo = new PDO('mysql:host=127.0.0.1;dbname=db_php-client;charset=utf8mb4;', "root");
$client->setCache('mysql', ['pdo' => $pdo]);
$result = $client->getStories();
print_r($result);

Clearing the cache (Optionally if using setCache)

In order to flush the cache when the user clicks publish, you need to listen to the published event in javascript or define a webhook in the space settings that clears the cache on your server.

<script type="text/javascript" src="//app.storyblok.com/f/storyblok-latest.js"></script>
<script type="text/javascript">
    storyblok.init()

    storyblok.on('published', function() {
        $.ajax({
            url: '/clear.php'
        })
    })
</script>

In clear.php:

$client = new \Storyblok\Client('your-storyblok-readonly-accesstoken');
$client->setCache('filesystem', array('path' => 'cache'));
// Flush the whole cache when a story has been published
$client->flushCache();
// Or empty the cache for one specific item only
$client->deleteCacheBySlug('home');

Generate a navigation tree

$tree = $client->editMode()->getLinks()->getAsTree();

echo '<ul>';
foreach ($tree as $item) {
    echo '<li>' . $item['item']['name'];

    if (!empty($item['children'])) {
        echo '<ul>';
        foreach ($item['children'] as $item2) {
            echo '<li>' . $item2['item']['name'] . '</li>';
        }
        echo '</ul>';
    }

    echo '</li>';
}
echo '</ul>';

Nginx SSI - Server Side Includes

Use the following script if you have Nginx SSI enabled and experience issues with printing the _editable html comments directly to manually parse the Storyblok HTML editable comments: https://gist.github.com/DominikAngerer/ca61d41bae3afcc646cfee286579ad36

Relationships and Links Resolving

In order to resolve relations you can use the resolveRelations method of the client passing a comma separated list of fields:

$client = new \Storyblok\Client('your-storyblok-readonly-accesstoken');

$client->resolveRelations('component_name1.field_name1,component_name2.field_name2')
$client->getStoryBySlug('home');

Another example:

use Storyblok\Client;
$client = new Client('your-storyblok-readonly-accesstoken');
$client->resolveRelations('popular-articles.articles');
$result = $client->getStoryBySlug("home")->getBody();

In order to resolve links, you can use the resolveLinks method passing the specific type of resolving you want to perform among url, story or link:

$client = new \Storyblok\Client('your-storyblok-readonly-accesstoken');

$client->resolveLinks('url')
$client->getStoryBySlug('home');

When using the CDN API V1, you can't resolve relationships of resolved entries and the resolved entries are injected in the field of the relationship. The same happens with links resolving. When using the CDN API V2 you can resolve also nested relationships in the resolved entries (just 2 levels deep), but the resolved entries are not injected in the fields, they are inserted in an array called rels which is in the root object. The resolved links will be placed in an array called links. In case you are using the API V2, to keep a consistent behaviour with the API V1, this client will inject the resolved entries and links inside the fields for you.

Code Quality

The package includes tools for tests and code formatting:

composer run all-check

that executes:

  • vendor/bin/php-cs-fixer fix
  • vendor/bin/pest

๐Ÿ”— Related Links

โ„น๏ธ More Resources

Support

Contributing

Please see our contributing guidelines and our code of conduct. This project use semantic-release for generate new versions by using commit messages and we use the Angular Convention to naming the commits. Check this question about it in semantic-release FAQ.

License

This repository is published under the MIT license.

storyblok-php-client's People

Contributors

andyjaar avatar axkirillov avatar brentmullen avatar christianzoppi avatar cmuench avatar dominikangerer avatar filecage avatar hoersamu avatar joaokamun avatar ltdanwithlegs avatar lucadegasperi avatar onefriendaday avatar oskarstark avatar pscheit avatar riclep avatar roberto-butti avatar snstamml avatar tomaj avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

storyblok-php-client's Issues

PHP8 bug

Hello,

thanks for providing this storyblok php client. It is very useful!

I'm afraid, since we upgraded to PHP8 we have to make a little change regarding to latest bug reports.

According to this blog post: https://php.watch/versions/8.0/deprecate-required-param-after-optional

I encounter exceptions, when using latest version of this library.

What we could change is in StoryBlok\Client, the following:

  • Line 584
    from private function _generateTree($parent = 0, $items),
    to private function _generateTree($items, $parent = 0)

  • Line 581
    from return $this->_generateTree(0, $tree);
    to return $this->_generateTree($tree);

  • Line 597
    from $this->_generateTree($item['id'], $items);
    to $this->_generateTree($items, $item['id']);

And exception would be gone.

Could you fix this and tag a new release?

Thanks in advance

Error on Management Client

On the latest release, there is an error on the Management Client. When trying to use the Management Client, the following error occurs:
image
The error is resolved once the responseHandler function in ManagementClient.php is updated to have same attributes as the one from BaseClient.php class.

Thanks

Make level of relation resolving overwritable

We have a data model with deep nested blocks and those blocks are used in rich text fields. The SDK stops at level 5 resolving relations however this limit is reached quite soon when a nestable block is used in a rich text field.

For getting things to work in our case we have to overwrite this limit.

As a workaround we are overwrite the Client quite ugly:

use Storyblok\Client as StoryblokClient;

class Client extends StoryblokClient
{
    public function enrichContent($data, $level = -2)
    {
        return parent::enrichContent($data, $level);
    }
}

Level does not start with the defaul 0 but in our case -2. At the end we have a limit of 6 (-2 up to 4).
isStopResolving is declared as private so we could not overwrite it directly.


Expected Behavior

Every relation in our rich text could be resolved.

  • Either we are able to set this level as parameter for the client
  • or the private method isStopResolving is made protected so we are able to overwrite it in a child class

Current Behavior

Deep nested relations are not resolved.

Steps to Reproduce

Create some content type with deep nested blocks and try to resolve all relations.

Language is not respected if a single story should be fetched

If I fetch a single story, the language parameter is omitted.

Example:

$client->language('de'); // fallback language is default
$client->getStoryBySlug("some-slug-with-multi-language-content");

Language is not added because of:

\Storyblok\Client::getStory

if($byUuid) {
    if ($this->language) {
        $options['language'] = $this->language;
    }

    if ($this->fallbackLanguage) {
        $options['fallback_lang'] = $this->fallbackLanguage;
    }
}

I manually removed the if statement and let the client aways send the language and fallback_lang parameter. This fixes the issue.

Function getStoryContent is undefined

This function is called on line 85 of Client.php

$response = $client->getStoryContent();

resulting in

Uncaught Error: Call to undefined method Storyblok\Client::getStoryContent()

Improve exception handling

Exception can be enhanced by passing causing exception as $previous when ApiException is created. Now it's impossible to get API response body from ApiException.


Expected Behavior

I'd like to get API response body from ApiException $e.

try {
...
} catch (ApiException $e)
(string) $e->getPrevious()->getResponse()->getBody();
}

Current Behavior

It's impossible to call $e->getPrevious()->... because previous exception is't stored.

Steps to Reproduce

  1. Set editMode: $client->editMode(true);
  2. Call $client->getStories($options)->getBody(); Now ApiException is thrown, because client is unauthorized.
  3. Try to obtain serious information what's going on from ApiException $e.

Infinite recursion in V2, when _storyblok_published is set

Hey there,

I have an issue, when using a cache and using "show published version" from the storyblok menu.

The link is then like:
https://mydomain.tld/mystories/?_storyblok_published=129972584

This leads to an non-stopping recursion and then of course hitting time or memory limits.

The recursion goes like this:
\Storyblok\Client::getStoryBySlug is called from my controller
\Storyblok\Client::getStory
\Storyblok\Client::reCacheOnPublish
\Storyblok\Client::setCacheVersion (since $_GET['...] is true)
\Storyblok\Client::getStories
\Storyblok\Client::reCacheOnPublish
\Storyblok\Client::setCacheVersion (since $_GET[...] is true)
\Storyblok\Client::getStories
\Storyblok\Client::reCacheOnPublish
\Storyblok\Client::setCacheVersion

etc etc.

Idea for a fix would be to not fetch an arbitrary story to get the cv value, but to invalidate the cv, fetch the first story (that we are fetching anyway, cause we've been called to summon a story/stories) and set the cv from there (maybe). I guess the problem here is, that the cv is missing for non-published?

or even do it afterwards, when the cv wasnt retrieved with the story.

We should add a test for that, i didnt blame it all the way, but i guess this was introduced in building v2? I updated the client and got this error immediately

Why is this repository so neglected? Is contribution still appreciated?

I created a pull request on Sep 16 2022 and it did not receive even any feedback. The PHP Storyblok Clients lacks so many features that the NodeJS implementation provides.

If this project is abandoned, a notice about this would be 'great' to see. It is so frustating trying to improve the repository by contributing only to see that the repository seems to be neglected.

Please note that this criticism is not meant to blame you. You have probably a lot of tasks and I do not want to complain about how long it takes for a PR to be managed. Instead I just want to clarify if further effort by users is appreciated and likely to be considered by the respective persons.

Does it Support Api V2 Already?

I constructed my client with the apiVersion as "v2"

But then I get:

resulted in a 422 Unknown response:
{"error":"Parameter(s) cache_version not allowed"}

I am confused :)

Am I supposed to use the old v1 ?

I want to set the language parameter and cant see it.

Thank You!

Client->getStories not respecting release

Currently the getStories function does not set the from_release query parameter for requests to Storyblok if the release was set using the setRelease function.

Expected Behavior

The getStories function should add the release parameter correctly if $this->release is set.

Current Behavior

The release parameter is not set when calling the getStories function if it isn't provided using the options parameter

Steps to Reproduce

Not working

$client = new Client('api-key-###');
$client->setRelease('123456');
$options = [
  'by_slugs' => 'test'
];
$client->getStories($options);

Workaround

$client = new Client('api-key-###');
$options = [
  'by_slugs'     => 'test',
  'from_release' => '123456'
];
$client->getStories($options);

I don't mind creating a fix PR, but your contributing guidelines said to open an issue first.

Compatibility with PHP 8.2

Update the code for PHP 8.2 compatibility

Expected Behavior

The code has to be compatible with PHP 8.2

Current Behavior

In the code there are some deprecation with PHP 8.2 like:
Creation of dynamic property Storyblok\Client::$resolvedRelations is deprecated

Steps to Reproduce

  1. run composer run test

I 'm going to assign to me the Issue an I will create a PR.

The getLinks method takes options which are not keyed in the cache

This means calling Client::getLinks(['starts_with' => 'x']) and Client::getLinks(['starts_with' => 'y']) will currently be cached under the same cache key despite probably having different data.

See https://github.com/storyblok/php-client/blob/master/src/Storyblok/Client.php#L512

If this method is going to be exposed as public it needs all its params to be in the cache key.

The only way round this at the moment is to always flush the whole cache before calling getLinks with options set.

I created a PR here #55

Thanks for taking the time to look at this.

Resolve Relations: sorting issue


Expected Behavior

If you try to resolve relations with (for example):

    $client->resolveRelations('popular-articles.articles');
    $story = $client->getStoryBySlug('home')->getBody();

The API provides you with the related stories in the 'rels' section (with a specific order).
In the main story, you have just the UUID of the related stories (so if you are using the API directly, you have to replace the UUIDs with the related story with the same UUID, found in the 'rels' section). The PHP client makes this for you.
The PHP client, under the hood, takes the related stories and injects them into the right place in the main story.
In this case parsing the main story (the home' in the example, you will find the related stories, and you can access them, for example, with:

$story['story']['content']['body'][1]['articles']

Current Behavior

If you have an array of related stories, the order could be not the same of the UUID listed in the main story.
This is because the UUID is used as key of the array while the library injects the related story, but the UUID as key is used as integer.

Steps to Reproduce

test('v2: check sorting for relations', function () {
    $client = new Client('test', null, $version = 'v2');
    // $client->editMode(false);
    $client->mockable([
        mockResponse('stories-relations', ['x-test' => 1], $version),
    ]);
    $client->resolveRelations('popular-articles.articles');
    $story = $client->getStoryBySlug('home')->getBody();
    $this->assertArrayHasKey('story', $story);
    $this->assertArrayHasKey('rels', $story);
    foreach (range(0, 2) as $i) {
        $this->assertEquals(
            $story['story']['content']['body'][1]['articles'][$i]['uuid'],
            $story['rels'][$i]['uuid'],
            'checking the ' . $i . ' relations'
        );
    }
});

Storyblok can't be used with PHP 8.1 and Symfony v6 (or rather `symfony/cache` >=6.0)

Hi,

I was trying to integrate the Storyblok PHP Client into a prototype Symfony application in order to evaluate all of its features for an upcoming project.

Naturally, I was bootstrapping a new barebone Symfony 6 project using the Symfony CLI and the microservice/API (no --webapp argument passed) template.

The following composer.json file will be created:

{
    "type": "project",
    "license": "proprietary",
    "minimum-stability": "stable",
    "prefer-stable": true,
    "require": {
        "php": ">=8.1",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "symfony/console": "6.1.*",
        "symfony/dotenv": "6.1.*",
        "symfony/flex": "^2",
        "symfony/framework-bundle": "6.1.*",
        "symfony/runtime": "6.1.*",
        "symfony/yaml": "6.1.*"
    },
    "require-dev": {
    },
    "config": {
        "allow-plugins": {
            "composer/package-versions-deprecated": true,
            "symfony/flex": true,
            "symfony/runtime": true
        },
        "optimize-autoloader": true,
        "preferred-install": {
            "*": "dist"
        },
        "sort-packages": true
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "App\\Tests\\": "tests/"
        }
    },
    "replace": {
        "symfony/polyfill-ctype": "*",
        "symfony/polyfill-iconv": "*",
        "symfony/polyfill-php72": "*",
        "symfony/polyfill-php73": "*",
        "symfony/polyfill-php74": "*",
        "symfony/polyfill-php80": "*",
        "symfony/polyfill-php81": "*"
    },
    "scripts": {
        "auto-scripts": {
            "cache:clear": "symfony-cmd",
            "assets:install %PUBLIC_DIR%": "symfony-cmd"
        },
        "post-install-cmd": [
            "@auto-scripts"
        ],
        "post-update-cmd": [
            "@auto-scripts"
        ]
    },
    "conflict": {
        "symfony/symfony": "*"
    },
    "extra": {
        "symfony": {
            "allow-contrib": false,
            "require": "6.1.*"
        }
    }
}

Now, when trying to install the latest version of storyblok/php-client โ€” as instructed by the README.md โ€” it'll fail due to apix/cache not being compatible with newer versions of psr/cache and PHP.

Both dependencies will create a hard failure and thus leads to not being able to integrate this lib into a modern Symfony 6 stack.

โฏ composer require storyblok/php-client dev-master
./composer.json has been updated
Running composer update storyblok/php-client
Loading composer repositories with package information
Updating dependencies
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - apix/cache[1.3.0, ..., 1.3.4] require php ^5.3.2|^7.0 -> your php version (8.1.9) does not satisfy that requirement.
    - apix/cache 1.3.5 requires psr/cache ^1.0 -> found psr/cache[1.0.0, 1.0.1] but the package is fixed to 3.0.0 (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command.
    - storyblok/php-client dev-master requires apix/cache ^1.3 -> satisfiable by apix/cache[1.3.0, ..., 1.3.5].
    - Root composer.json requires storyblok/php-client dev-master -> satisfiable by storyblok/php-client[dev-master].

Use the option --with-all-dependencies (-W) to allow upgrades, downgrades and removals for packages currently locked to specific versions.

Installation failed, reverting ./composer.json and ./composer.lock to their original content.
โฏ composer why psr/cache
symfony/cache           v6.1.3 requires psr/cache (^2.0|^3.0) 
symfony/cache-contracts v3.1.1 requires psr/cache (^3.0)    

I haven't heard of the apix/cache package before, but it looks like they have currently no plans to move forward with their PHP support to add support for PHP 8 โ€” though I may be mistaken, I've just checked their open issues and PRs.

Since Symfony provides a lot of components and is the defacto standard and infrastructure for most PHP-based frameworks out there, I'd like to propose switching over to symfony/cache instead of apix/cache. It's also based on the same PSR-6 Cache standard and has tons of adapters and configuration options available, so that we should be easily able to replicate all of apix/cache's functionality.

What do you think?

Cheers,
Kai

Allow $editModeEnabled override to be set in constructor

After working with the JS storyblok client and the REST API directly, I came to expect the ?version=draft flag to be set manually (mostly by my own environment variables).

$editModeEnabled is a private variable and can only be set to true by including _storyblok in the request. I am hoping for the ability to set the $editModeEnabled in the class constructor. eg:

$client = new \Storyblok\Client('my-token', editMode: TRUE);

Alternatively, this could be set at each request, like in getStoryBySlug();

Huge memory consumption in v2

Preconditions (*)

  1. Version 2.x

Steps to reproduce (*)

  1. Instanciate a \Storyblok\Client object
  2. Call the "resolveLinks" method with the "story" parameter passed to it
  3. Call the "resolveRelations" method and add some relations to the client
  4. Call the "getStoryByUuid" method to fetch a story by its UUID
  5. Watch your RAM while the whole process is running

Expected result (*)

  1. The query is made with no issue and the RAM isn't consumed more than it should

Actual result (*)

  1. The RAM decreases until it is completely consumed by the process
  2. The process gets killed

Explanation

The bug seems to have appeared in the commit #adb52f212e5f3ff38457ea4c7781a85f9f99e460 after adding the calls to the enrichStory method in the responseHandler method.

When I put a dump() in the enrichContent method, it parses way more data than when the resolveRelations method isn't called.

Add "default_lifetime" cache option for start using defaultLifetime param for caching

Add ttl option in the Caching options for managing the TTL.
for example as second parameter of setCache you can set an array of options.

$client->setCache('filesytem', ['path' => 'cache']);

Add an option named default_lifetime option for setting the default lifetime in the cache adapter

$client->setCache('filesytem', ['path' => 'cache', 'default-lifetime' => 3600]);

v2 breaks support of PHP<8 due to symphony dependency

By the introduction of the Symfony\Component\Cache\CacheItem
which is using a union-type declaration inside it will break the support for PHP lower than 8 already with v2.

Current Behavior

Using >=2.0 it will break with a php lower than v8 - so we are still on v7.4 and the union type of the symphony dependency breaks our code. You should update the min. stability to php>=8.0.0 thereby https://github.com/storyblok/storyblok-php-client/blob/master/composer.json#L9

Request: Brach for Guzzle v6

Hey I maintain a Magento 2 integration for Storyblok that relies on this module to interact with the SB API.

Unfortunately Magento uses Guzzle as well but they lock it at v6, this hasn't been an issue because I've used the 1.3 version of the client but we've ran into a couple issues recently that are fixed by updating to the latest 1.5 version.

Would it be possible for you to create a branch from master that uses "guzzlehttp/guzzle": "^6.0" as a dependency instead? We tried creating a fork and requiring that instead but that doesn't work.

Support symfony/cache 7.0

Symfony released v7 in November 2023. This project is currently limited to using symfony/cache up to v6.


It is possible to add symfony/cache:7 to allowed versions?

Storyblok\Client::$responseCode sometimes empty

The issue seems to be that Storyblok\Client::_assignState($response) does not retain $response->httpResponseCode.

Suggested fix to src/Storyblok/Client.php:

private function _assignState($response) {
    $this->responseBody = $response->httpResponseBody;
    $this->responseHeaders = $response->httpResponseHeaders;
    $this->responseCode = $response->httpResponseCode; // retain responseCode 
}

Feature: increase PHPstan at level 3

PHPStan level 3

Expected Behavior

Fix all warnings for reaching level 3
In phpstan.neon increase the level to level: 3
Execute composer phpstan and fix all warnings related to return types and types assigned to properties.

Current Behavior

Now PHPstan is set to level 2

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.