diff --git a/src/Storyblok/BaseClient.php b/src/Storyblok/BaseClient.php index 5ba1dfe..99491de 100644 --- a/src/Storyblok/BaseClient.php +++ b/src/Storyblok/BaseClient.php @@ -63,7 +63,7 @@ class BaseClient * @param string $apiVersion * @param bool $ssl */ - function __construct($apiKey = null, $apiEndpoint = "api.storyblok.com", $apiVersion = "v1", $ssl = false) + function __construct($apiKey = null, $apiEndpoint = "api.storyblok.com", $apiVersion = "v2", $ssl = false) { $handlerStack = HandlerStack::create(new CurlHandler()); $handlerStack->push(Middleware::retry($this->retryDecider(), $this->retryDelay())); @@ -222,10 +222,9 @@ public function get($endpointUrl, $queryString = array()) if ($this instanceof ManagementClient) { $requestOptions[RequestOptions::HEADERS] = ['Authorization' => $this->apiKey]; } - $responseObj = $this->client->request('GET', $endpointUrl, $requestOptions); - return $this->responseHandler($responseObj); + return $this->responseHandler($responseObj, $queryString); } catch (\GuzzleHttp\Exception\ClientException $e) { throw new ApiException(self::EXCEPTION_GENERIC_HTTP_ERROR . ' - ' . $e->getMessage(), $e->getCode()); } @@ -233,10 +232,11 @@ public function get($endpointUrl, $queryString = array()) /** * @param \Psr\Http\Message\ResponseInterface $responseObj + * @param array $queryString * * @return \stdClass */ - public function responseHandler($responseObj) + public function responseHandler($responseObj, $queryString = array()) { $httpResponseCode = $responseObj->getStatusCode(); $data = (string) $responseObj->getBody(); @@ -247,6 +247,10 @@ public function responseHandler($responseObj) $result->httpResponseBody = $data && empty($jsonResponseData) ? $data : $jsonResponseData; $result->httpResponseCode = $httpResponseCode; $result->httpResponseHeaders = $responseObj->getHeaders(); + + if (is_array($result->httpResponseBody) && isset($result->httpResponseBody['story']) || isset($result->httpResponseBody['stories'])) { + $result->httpResponseBody = $this->enrichStories($result->httpResponseBody, $queryString); + } return $result; } @@ -302,4 +306,178 @@ public function getCode() { return $this->responseCode; } + + private function enrichStories($data, $queryString) { + $enrichedData = $data; + $this->getResolvedRelations($data, $queryString); + $this->getResolvedLinks($data, $queryString); + + if(isset($data['story'])) { + $enrichedData['story']['content'] = $this->enrichContent($data['story']['content']); + } else if(isset($data['stories'])) { + $stories = []; + foreach($data['stories'] as $index => $story) { + $story = $data['stories'][$index]; + $story['content'] = $this->enrichContent($story['content']); + $stories[] = $story; + } + $enrichedData['stories'] = $stories; + } + return $enrichedData; + } + + /** + * Enrich the Stories with resolved links and stories + * @param \stdClass + * + * @return \stdClass + */ + function enrichContent($data) { + $enrichedContent = $data; + + if (is_array($data) && isset($data['component'])) { + if(!isset($story['_stopResolving'])) { + foreach($data as $fieldName => $fieldValue) { + $enrichedContent[$fieldName] = $this->insertRelations($data['component'], $fieldName, $fieldValue); + $enrichedContent[$fieldName] = $this->insertLinks($enrichedContent[$fieldName]); + $enrichedContent[$fieldName] = $this->enrichContent($enrichedContent[$fieldName]); + } + } + } else if (is_array($data)) { + foreach($data as $key => $value) { + $enrichedContent[$key] = $this->enrichContent($value); + } + } + + return $enrichedContent; + } + + /** + * Retrieve or resolve the relations + * @param \stdClass $data + * @param array $queryString + * + * @return null + */ + function getResolvedRelations($data, $queryString) { + $this->resolvedRelations = []; + $relations = []; + + if(isset($data['rels'])) { + $relations = $data['rels']; + } else if(isset($data['rel_uuids'])) { + $relSize = count($data['rel_uuids']); + $chunks = []; + $chunkSize = 50; + + for ($i = 0; $i < $relSize; $i += $chunkSize) { + $end = min($relSize, $i + $chunkSize); + $chunks[] = array_slice($data['rel_uuids'], $i, $end); + } + + for ($chunkIndex = 0; $chunkIndex < count($chunks); $chunkIndex++) { + $relationsParams = array( + 'per_page' => $chunkSize, + 'by_uuids' => implode(',', $chunks[$chunkIndex]) + ); + if(isset($queryString['language'])) { + $relationsParams['language'] = $queryString['language']; + } + $relationsRes = $this->getStories($relationsParams); + + $relations = array_merge($relations, $relationsRes->responseBody['stories']); + } + } + + foreach($relations as $rel) { + $this->resolvedRelations[$rel['uuid']] = $rel; + } + } + + + /** + * Retrieve or resolve the Links + * @param \stdClass $data + * @param array $queryString + * + * @return null + */ + function getResolvedLinks($data) { + $this->resolvedLinks = []; + $links = []; + + if(isset($data['links'])) { + $links = $data['links']; + } else if(isset($data['link_uuids'])) { + $linksSize = count($data['link_uuids']); + $chunks = []; + $chunkSize = 50; + + for ($i = 0; $i < $linksSize; $i += $chunkSize) { + $end = min($linksSize, $i + $chunkSize); + $chunks[] = array_slice($data['link_uuids'], $i, $end); + } + + for ($chunkIndex = 0; $chunkIndex < count($chunks); $chunkIndex++) { + $linksRes = $this->getStories(array( + 'per_page' => $chunkSize, + 'language' => isset($queryString['language']) ? $queryString['language'] : 'default', + 'by_uuids' => implode(',', $chunks[$chunkIndex]) + )); + + $links = array_merge($links, $linksRes->responseBody['stories']); + } + } + foreach($links as $link) { + $this->resolvedLinks[$link['uuid']] = $link; + } + } + + /** + * Insert the resolved relations in a story + * @param string $component + * @param string $field + * @param string|array $value + * + * @return null + */ + private function insertRelations($component, $field, $value) { + $filteredNode = $value; + if(isset($this->_relationsList[$component]) && $field == $this->_relationsList[$component]) { + if(is_string($value)) { + if(isset($this->resolvedRelations[$value])) { + $filteredNode = $this->resolvedRelations[$value]; + $filteredNode['_stopResolving'] = true; + } + } else if(is_array($value)) { + $filteredNode = []; + foreach($value as $item) { + if(is_string($item) && isset($this->resolvedRelations[$item])) { + $story = $this->resolvedRelations[$item]; + $story['_stopResolving'] = true; + $filteredNode[] = $story; + } + } + } + } + return $filteredNode; + } + + /** + * Insert the resolved links in a story + * @param \stdClass $node + * + * @return \stdClass + */ + private function insertLinks($node) { + $filteredNode = $node; + if(isset($node['fieldtype']) && $node['fieldtype'] == 'multilink' && $node['linktype'] == 'story') { + if(isset($node['id']) && is_string($node['id']) && isset($this->resolvedLinks[$node['id']])) { + $filteredNode['story'] = $this->resolvedLinks[$node['id']]; + } else if(isset($node['uuid']) && is_string($node['uuid']) && isset($this->resolvedLinks[$node['uuid']])) { + $filteredNode['story'] = $this->resolvedLinks[$node['uuid']]; + } + } + return $filteredNode; + } } diff --git a/src/Storyblok/Client.php b/src/Storyblok/Client.php index f5ac13a..9c717ce 100644 --- a/src/Storyblok/Client.php +++ b/src/Storyblok/Client.php @@ -11,7 +11,7 @@ */ class Client extends BaseClient { - const CACHE_VERSION_KEY = "storyblok:cache_version"; + const CACHE_VERSION_KEY = "storyblok:cv"; const EXCEPTION_GENERIC_HTTP_ERROR = "An HTTP Error has occurred!"; /** @@ -60,7 +60,7 @@ class Client extends BaseClient * @param string $apiVersion * @param bool $ssl */ - function __construct($apiKey = null, $apiEndpoint = "api.storyblok.com", $apiVersion = "v1", $ssl = false) + function __construct($apiKey = null, $apiEndpoint = "api.storyblok.com", $apiVersion = "v2", $ssl = false) { parent::__construct($apiKey, $apiEndpoint, $apiVersion, $ssl); @@ -96,6 +96,30 @@ public function cacheNotFound($enabled = true) return $this; } + /** + * Returns the requested version of the content + * + * @return String + */ + public function getVersion() + { + return $this->editModeEnabled ? 'draft' : 'published'; + } + + /** + * Returns the commond API parameters + * + * @return Array + */ + public function getApiParameters() + { + return array( + 'token' => $this->getApiKey(), + 'version' => $this->getVersion(), + 'cv' => $this->getCacheVersion() + ); + } + /** * Set cache driver and optional the cache path * @@ -136,9 +160,9 @@ public function setCache($driver, $options = array()) break; } - $this->cacheVersion = $this->cache->load(self::CACHE_VERSION_KEY); + $this->cv = $this->cache->load(self::CACHE_VERSION_KEY); - if (!$this->cacheVersion) { + if (!$this->cv) { $this->setCacheVersion(); } @@ -208,9 +232,9 @@ private function reCacheOnPublish($key) public function setCacheVersion() { if ($this->cache) { - $timestamp = time(); - $this->cache->save($timestamp, self::CACHE_VERSION_KEY); - $this->cacheVersion = $timestamp; + $res = $this->getStories(Array('per_page' => 1, 'version' => 'published')); + $this->cv = $res->responseBody['cv']; + $this->cache->save($this->cv, self::CACHE_VERSION_KEY); } return $this; @@ -223,11 +247,11 @@ public function setCacheVersion() */ function getCacheVersion() { - if (empty($this->cacheVersion)) { - return time(); + if (empty($this->cv)) { + return ''; } - return $this->cacheVersion; + return $this->cv; } /** @@ -251,7 +275,7 @@ public function setRelease($release) * @throws ApiException */ public function getStoryBySlug($slug) - { + { return $this->getStory($slug); } @@ -279,29 +303,19 @@ public function getStoryByUuid($uuid) */ private function getStory($slug, $byUuid = false) { - $version = 'published'; - - if ($this->editModeEnabled) { - $version = 'draft'; - } - $key = 'stories/' . $slug; $cachekey = $this->_getCacheKey($key); $this->reCacheOnPublish($key); - if ($version === 'published' && $this->cache && $cachedItem = $this->cache->load($cachekey)) { + if ($this->getVersion() === 'published' && $this->cache && $cachedItem = $this->cache->load($cachekey)) { if ($this->cacheNotFound && $cachedItem->httpResponseCode == 404) { throw new ApiException(self::EXCEPTION_GENERIC_HTTP_ERROR, 404); } $this->_assignState($cachedItem); } else { - $options = array( - 'token' => $this->getApiKey(), - 'version' => $version, - 'cache_version' => $this->getCacheVersion() - ); + $options = $this->getApiParameters(); if ($byUuid) { $options['find_by'] = 'uuid'; @@ -310,7 +324,7 @@ private function getStory($slug, $byUuid = false) if ($this->resolveRelations) { $options['resolve_relations'] = $this->resolveRelations; } - + if ($this->resolveLinks) { $options['resolve_links'] = $this->resolveLinks; } @@ -318,10 +332,10 @@ private function getStory($slug, $byUuid = false) if ($this->release) { $options['from_release'] = $this->release; } - + try { $response = $this->get($key, $options); - $this->_save($response, $cachekey, $version); + $this->_save($response, $cachekey, $this->getVersion()); } catch (\Exception $e) { if ($this->cacheNotFound && $e->getCode() === 404) { $result = new \stdClass(); @@ -356,34 +370,29 @@ private function getStory($slug, $byUuid = false) */ public function getStories($options = array()) { - $version = 'published'; $endpointUrl = 'stories/'; - if ($this->editModeEnabled) { - $version = 'draft'; - } - $key = 'stories/' . serialize($this->_prepareOptionsForKey($options)); $cachekey = $this->_getCacheKey($key); $this->reCacheOnPublish($key); - if ($version === 'published' && $this->cache && $cachedItem = $this->cache->load($cachekey)) { + if ($this->getVersion() === 'published' && $this->cache && $cachedItem = $this->cache->load($cachekey)) { $this->_assignState($cachedItem); } else { - $options = array_merge($options, array( - 'token' => $this->getApiKey(), - 'version' => $version, - 'cache_version' => $this->getCacheVersion() - )); + $options = array_merge($options, $this->getApiParameters()); if ($this->resolveRelations) { $options['resolve_relations'] = $this->resolveRelations; } + + if ($this->resolveLinks) { + $options['resolve_links'] = $this->resolveLinks; + } $response = $this->get($endpointUrl, $options); - $this->_save($response, $cachekey, $version); + $this->_save($response, $cachekey, $this->getVersion()); } return $this; @@ -400,7 +409,11 @@ public function getStories($options = array()) public function resolveRelations($reference) { $this->resolveRelations = $reference; - + $this->_relationsList = []; + foreach(explode(',', $this->resolveRelations) as $relation) { + $relationVars = explode('.', $relation); + $this->_relationsList[$relationVars[0]] = $relationVars[1]; + } return $this; } @@ -430,30 +443,21 @@ public function resolveLinks($reference) */ public function getTags($options = array()) { - $version = 'published'; $endpointUrl = 'tags/'; - if ($this->editModeEnabled) { - $version = 'draft'; - } - $key = 'tags/' . serialize($options); $cachekey = $this->_getCacheKey($key); $this->reCacheOnPublish($key); - if ($version === 'published' && $this->cache && $cachedItem = $this->cache->load($cachekey)) { + if ($this->getVersion() === 'published' && $this->cache && $cachedItem = $this->cache->load($cachekey)) { $this->_assignState($cachedItem); } else { - $options = array_merge($options, array( - 'token' => $this->getApiKey(), - 'version' => $version, - 'cache_version' => $this->getCacheVersion() - )); + $options = array_merge($options, $this->getApiParameters()); $response = $this->get($endpointUrl, $options); - $this->_save($response, $cachekey, $version); + $this->_save($response, $cachekey, $this->getVersion()); } return $this; @@ -468,31 +472,23 @@ public function getTags($options = array()) */ public function getDatasourceEntries($slug, $options = array()) { - $version = 'published'; $endpointUrl = 'datasource_entries/'; - if ($this->editModeEnabled) { - $version = 'draft'; - } - $key = 'datasource_entries/' . $slug . '/' . serialize($options); $cachekey = $this->_getCacheKey($key); $this->reCacheOnPublish($key); - if ($version === 'published' && $this->cache && $cachedItem = $this->cache->load($cachekey)) { + if ($this->getVersion() === 'published' && $this->cache && $cachedItem = $this->cache->load($cachekey)) { $this->_assignState($cachedItem); } else { - $options = array_merge($options, array( - 'token' => $this->getApiKey(), - 'version' => $version, - 'cache_version' => $this->getCacheVersion(), - 'datasource' => $slug - )); + $options = array_merge($options, + array('datasource' => $slug), + $this->getApiParameters()); $response = $this->get($endpointUrl, $options); - $this->_save($response, $cachekey, $version); + $this->_save($response, $cachekey, $this->getVersion()); } return $this; @@ -511,27 +507,17 @@ public function getDatasourceEntries($slug, $options = array()) */ public function getLinks($options = array()) { - $version = 'published'; - $key = $this->linksPath; $cachekey = $this->_getCacheKey($key); - if ($this->editModeEnabled) { - $version = 'draft'; - } - - if ($version === 'published' && $this->cache && $cachedItem = $this->cache->load($cachekey)) { + if ($this->getVersion() === 'published' && $this->cache && $cachedItem = $this->cache->load($cachekey)) { $this->_assignState($cachedItem); } else { - $options = array_merge($options, array( - 'token' => $this->getApiKey(), - 'version' => $version, - 'cache_version' => $this->getCacheVersion() - )); + $options = array_merge($options, $this->getApiParameters()); $response = $this->get($key, $options); - $this->_save($response, $cachekey, $version); + $this->_save($response, $cachekey, $this->getVersion()); } return $this; @@ -645,7 +631,7 @@ private function _save($response, $key, $version) $version === 'published' && $response->httpResponseHeaders && $response->httpResponseCode == 200) { - + $this->cache->save($response, $key); } }