Giter Site home page Giter Site logo

riclep / laravel-storyblok Goto Github PK

View Code? Open in Web Editor NEW
54.0 3.0 14.0 495 KB

Make Laravel and Storyblok work together beautifully.

Home Page: https://ls.sirric.co.uk/

License: MIT License

PHP 98.73% Blade 1.27%
laravel laravel-5-package storyblok storyblok-client storyblok-api content-management-system content-management headless-cms php headless

laravel-storyblok's Introduction

Use Storyblok’s amazing headless CMS with Laravel

Latest Version on Packagist Total Downloads

GitHub Workflow Status Packagist PHP Version

Build Quality Score

ko-fi Twitter

This package allows you to use fantastic Storyblok headless CMS with the amazing Laravel PHP framework. It’s designed to try and feel natural to Laravel developers and part of the ecosystem whilst also converting Storyblok’s API JSON responses into something powerful with minimal effort.

Key Features

  • Pages from Storyblok mapped to PHP Pages classes giving access to the nested content (Blocks) and meta data for SEO, OpenGraph and more.
  • Quickly and easily resolve relations and inverse relations between content.
  • Each Storyblok component is automatically transformed into a PHP class using a simple naming convention - just match your class and component names.
  • Fields in your components are converted to a Field PHP class allowing you to manipulate their data. The package automatically detects common types like richtext fields, assets and markdown. Easily cast fields to classes.
  • Asset fields are converted to Assets and Image classes allowing you to manipulate them as required. Images can be easily transformed using Storyblok’s Asset CDN or external services like Imgix.
  • Blocks and fields know where they sit in relation to their ancestors and CSS classes can be created to help your styling.
  • The structure of the JSON data is preserved but super powered making it simple to loop over in your views.
  • It’s simple to link to the Storyblok visual composer by including one view and printing a string in a Block’s Blade template.
  • Request ‘Folders’ of content such as a list of articles or a team of people complete with support for pagination.
  • Richer Typography using a supporting package utilising PHP Typography.

Documentation

Read the full docs

Contribute to the docs

Other Packages

Laravel Storyblok Embed

Embed all types of media in your Storyblok site using only their URL. Package Docs

Laravel Storyblok Typography

Improve your content’s typography. Package Docs

Laravel Storyblok Layout & CSS

Helpers for layout, block positioning and CSS class name generation. Package Docs

Laravel Storyblok Form builder (BETA)

Build forms with Storyblok complete with Laravel’s validation. Package Docs

Laravel Storyblok CLI

Useful Artisan commands to help manage your content? Check out my Laravel Storyblok CLI package

Testing

The tests are mostly up-to-date and cover the majority of the code. A few areas that would require hitting the Storyblok API are not tested. If you have experience mocking API please feel free to contribute tests.

Changelog

See it here

Contributing

Please feel free to help expand and improve this project.

Security

If you discover any security related issues, please email [email protected] instead of using the issue tracker.

Credits

img

License

The MIT License (MIT). Please see License File for more information.

Laravel Package Boilerplate

This package was generated using the Laravel Package Boilerplate.

laravel-storyblok's People

Contributors

brentmullen avatar chaseconey avatar czuniga9 avatar marvinosswald avatar renovate[bot] avatar riclep avatar roberto-butti avatar rversantos avatar wl1981 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  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

laravel-storyblok's Issues

File Permission Issue with Console Commands

Overview

It appears that there is an issue when using the console commands that are using the replace method on files where it will actually update file permissions unnecessarily.

Steps to Reproduce

  1. Create a component in Storyblok
  2. Create class representation for said component php artisan ls:block <component>
  3. Add a new field to the component
  4. Run php artisan ls:sync

Expected Outcome

The new field will show up as a property for that block's class. File permissions should remain 644 (rw, r, r).

Actual Outcome

The new field will show up as a property for that block's class. File permissions are updated to 755 (rwx, rx, rx).

Artisan command to create block template uses folder …/components while Block.php searches in …/blocks

Hey there, thanks for the package!

While following the documentation about Blocks i ran into an issue with my blocks not being rendered properly.

php artisan ls:block MyComponent -b
Creates a template in ressources/views/storyblok/components/my-component.blade.php.
This will not be found by the Block Class automatically. It expects the templates to reside in ressources/views/storyblok/blocks/ see

public function views() {

Once i renamed the folder the template gets used.

Question about serializing blocks

Question

Hey there 👋 .

This is more of a question than a feature request or bug ticket.

Let's say you have a block that you are going to serialize (via json_encode or the like). This package uses the magic __get() function to get attributes out of the private $fields array behind the scenes, so the JSON encoded object only has a few public properties (resolve relations and whatnot).

Is there a native way to get the full class serialized with all of the Storyblok attributes included? If there isn't, would that be something you want this package to handle?

Background

A lot of times we are using Storyblok to render our data, but then there are some frontend manipulations that we want to do as well. To accomplish our goals, sometimes we need to encode the Storyblok object into javascript to be used on the client.

Example

If I use the laravel @json directive on any component, the output will look similar to this:

{"_autoResolveRelations":false,"_resolveRelations":[],"_filterRelations":true,"_componentPath":["page","page","full-section","button"]}

But if I @dd that same instance, I get something like this:

App\Storyblok\Blocks\Button {[#1588 ▼]
  +_autoResolveRelations: false
  +_resolveRelations: []
  +_filterRelations: true
  +_componentPath: array:4 [[▶]()]
  #_casts: []
  -_fields: Illuminate\Support\Collection {[#1589 ▼]()
    #items: array:15 [[▼]()
      "url" => Riclep\Storyblok\Fields\UrlLink {[#1592 ▶]()}
      "size" => "normal"
      "text" => "Rawr"
    ]
    #escapeWhenCastingToString: false
    // other attributes
  }
}

Notice that the component has data like size and text in it, but it doesn't show up in the serialized instance.

Call to undefined method App\Storyblok\Block::flatten()

Overview

Looks like the latest version broke something in the editor-bridge. I see a function called flatten was removed, but not seeing any obvious references.

Error

Error
Call to undefined method App\Storyblok\Block::flatten() (View: /.../projectname/vendor/riclep/laravel-storyblok/src/resources/views/editor-bridge.blade.php)

Steps to Reproduce

  • Upgrade from previous version to version 2.6.0
  • Create a new piece of content inside Storyblok (or anything that enables the editor bridge)

Cache ignores $page in paginated fetching

Thanks for the awesome library.

Referring to line: https://github.com/RicLeP/laravel-storyblok/blob/master/src/Folder.php#L165

If we have the following two pages we get the same results back, the second from the cache.

// example.org/some-content?page=1
$requestSomeContent = new Folder();
$requestSomeContent->slug('some-content');
$requestSomeContent->perPage(10);
$requestSomeContent->settings([
    'is_startpage' => false,
    'filter_query' => [],
]);

// yields cache key:
// folder-some-content-4893ccccb8f122d63a21febbbb000000

// example.org/some-content?page=2
$requestSomeContent = new Folder();
$requestSomeContent->slug('some-content');
$requestSomeContent->perPage(10);
$requestSomeContent->settings([
  'is_startpage' => false,
  'filter_query' => [],
]);

// yields cache key:
// folder-some-content-4893ccccb8f122d63a21febbbb000000

Note on the second request (page=2), the cache key constructed doesn't account for the current page which gives it the same key, meaning we get the cached data back.

I believe the cache key should include $page in the cache key if set.

If I disable the cache (STORYBLOK_CACHE=false) it works as expected.
Looking through telescope I can confirm the cache keys aren't accounting for $page.

Enable (rendering of) inline components within the RichText field

Hey, again thanks for the nice package!

Storyblok allows to insert components within the RichText field [1]. But from what i understood the provided storyblok/richtext-resolver for PHP does not handle them. Anyways, i needed to render inline components and came up with the following code. Maybe its a common enough use-case to be added to the core of this package.

I found no way to overwrite the existing RichText Field Class, so i added a Custom Field and named it accordingly in Storyblok. Maybe it would be nice if one could also overwrite the built-in fields to apply changes globally based on the field type vs the field name.

<?php

namespace App\Storyblok\Fields;

use Riclep\Storyblok\Fields\RichText as BaseRichText;
use Storyblok\RichtextRender\Resolver;
use Riclep\Storyblok\Block;

class Richtextfield extends BaseRichText
{
	/**
	 * Converts the data to HTML when printed
	 *
	 * @return string
	 */
	public function __toString()
	{
        $richtextResolver = new Resolver();

        $out = "";
        // Loop through all nodes from the RichText Field
        // Either Render them normally
        // Or if type is 'blok' create a block an add the rendered content to ouput
        foreach ($this->content['content'] as $node) {
            if ($node['type'] == 'blok' && isset($node['attrs']['body']) && is_array($node['attrs']['body'])){
                foreach ($node['attrs']['body'] as $blockContent) {
                    $block = new Block($blockContent, $this->block()->parent());
                    $out .= $block->render();
                }
            } else {
                $out .= $richtextResolver->render(["content" => [$node]]);
            }
        }
        return $out;
        // Original
		// return $richtextResolver->render($this->content);
	}
}

[1] https://www.storyblok.com/docs/richtext-field#components-inside-the-richtext-field

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Detected dependencies

composer
composer.json
  • php ^8.1|^8.2|^8.3
  • barryvdh/reflection-docblock ^2.0
  • embed/embed ^3.4|^4
  • illuminate/support ^9.0|^10.0|^11.0
  • imgix/imgix-php ^3.3|^4.0
  • ivopetkov/html5-dom-document-php 2.*
  • league/commonmark ^2.0
  • spatie/schema-org ^3.3
  • storyblok/php-client ^2.3
  • storyblok/richtext-resolver ^2.2
  • mockery/mockery ^1.2
  • orchestra/testbench ^8.0|^9.0
  • phpunit/phpunit ^10
  • spatie/laravel-ignition ^2.0
github-actions
.github/workflows/tests.yml
  • actions/checkout v3
  • php-actions/composer v6
  • php-actions/phpunit v3

  • Check this box to trigger a request for Renovate to run again on this repository

Detecting broken images to prevent calling transform()

A bit of an edge case here, but I've got a StoryBlok instance where we imported thousands of articles into the system, and we have a ton of broken images.

The client's gradually going through and fixing them, but we've gone live anyway - so there's a lot of older pages that just error out when we use $image->transform().

My current method of detecting if an image exists or not is to do a check on $story->image->filename.

However with broken images imported into Storyblok, there's still a filename (which is helping my client to figure out which images need to be added back in).

I could probably do a check on the $image->copyright or $image->alt fields - these are null on broken images, and if not utilised are empty.

But I thought it might be a better option to be able to check the transformer for the extension type - which will return null on these images.

As I said, very much an edge case, but I'd be interested to hear your thoughts on a solve.

Scaffolding Empty Components Errors

Overview

When generating new components that have no items in the schema, the command errors out.

Steps to Reproduce

  1. Create new component in Storyblok with no fields (an empty schema)
  2. Run command to scaffold component like so:
php artisan ls:block EmptyComponent

Expected

I expect a class representation of the component, but with no available attributes listed.

Actual

Command errors out with the following stack:

   ErrorException

  Trying to access array offset on value of type null

  at vendor/riclep/laravel-storyblok/src/Console/BlockSyncCommand.php:154
    150▕
    151▕ 			$component = $components->firstWhere('name', $name);
    152▕
    153▕ 			$fields = [];
  ➜ 154▕ 			foreach ($component['schema'] as $name => $data) {
    155▕ 				if ( ! $this->isIgnoredType($data['type'])) {
    156▕ 					$fields[$name] = $this->convertToPhpType($data['type']);
    157▕ 				}
    158▕ 			}

      +29 vendor frames
  30  artisan:37
      Illuminate\Foundation\Console\Kernel::handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))

Fine tuning default configuration (public api key)

In a clean Laravel project if I try to install laravel-storyblok package i receive an error:

Storyblok\BaseClient::setApiKey(): Argument #1 ($apiKey) must be of type string, null given, called in /Users/roberto/Sites/composable-example/vendor/storyblok/php-client/src/Storyblok/BaseClient.php on line 81

I think this issue could be solved by setting the default as an empty string in the config/storyblok.php:

'api_public_key' => env('STORYBLOK_PUBLIC_API_KEY', ''),

Instead of:

'api_public_key' => env('STORYBLOK_PUBLIC_API_KEY'),

Undefined Variable after Generating Block

Looks like a recent change to the generator command stub changed the variable in the blade file from $content to $block. Now when generating a fresh block with the latest version of the library, you get this error:

Undefined variable: block (View: /projects/www.bankrate.com/resources/views/storyblok/blocks/teaser.blade.php)

Having trouble with the basics

This is almost certainly down to a lack of experience with Laravel, but I'm having trouble getting started.

I've got a StoryBlok space setup with a ton of content imported into it.

I've got laravel and laravel-storyblok installed, and serving on localhost.

Environment variables are set, and by the errors I'm getting, it's definitely trying to hit the storyblok API. I've got the catch-all router in place.

I've gone back to basics, created the file /resources/views/storyblok/pages/page.blade.php which looks like this:

<main>
<header>
    <h1>@{{ $story->title }}</h1>
</header>

<section>
<?php dd($story); ?>
</section>
</main>

I've created a page called 'test' at the root of Storyblok, using the 'page' component, which has two fields defined - title and text.

But the @{{ $story->title }} just displays {{ $story->title}} in the browser.

The dd does give me a result:

App\Storyblok\Page {#1180 ▼
  +_componentPath: array:1 [▶]
  -block: App\Storyblok\Block {#1090 ▶}
  -story: array:22 [▼
    "name" => "test"
    "created_at" => "2021-11-17T10:41:40.966Z"
    "published_at" => "2021-11-17T10:42:25.116Z"
    "alternates" => []
    "id" => 86019624
    "uuid" => "761d11ac-c8bd-4eee-ae1f-6e3dbdc920bf"
    "content" => array:4 [▼
      "_uid" => "fbb0f60a-3c57-456e-84c3-8b86f02aeae0"
      "body" => array:1 [▼
        0 => array:4 [▼
          "_uid" => "62cd6c66-4d78-4ee5-af0f-e17b231fabda"
          "title" => "My big test"
          "component" => "text"
          "_editable" => "<!--#storyblok#{"name": "text", "space": "127485", "uid": "62cd6c66-4d78-4ee5-af0f-e17b231fabda", "id": "86019624"}-->"
        ]
      ]
      "component" => "page"
      "_editable" => "<!--#storyblok#{"name": "page", "space": "127485", "uid": "fbb0f60a-3c57-456e-84c3-8b86f02aeae0", "id": "86019624"}-->"
    ]
    "slug" => "test"
    "full_slug" => "test"
    "default_full_slug" => null
    "sort_by_date" => null
    "position" => -40
    "tag_list" => []
    "is_startpage" => false
    "parent_id" => 0
    "meta_data" => null
    "group_id" => "c3210eaf-a185-46ea-b7c5-a54424b3fcc9"
    "first_published_at" => "2021-11-17T10:42:25.116Z"
    "release_id" => null
    "lang" => "default"
    "path" => null
    "translated_slugs" => []
  ]
  +liveContent: []
  #_meta: array:5 [▶]
}

Only slugs in Storyblok root produce this kind of result, everything else errors out completely - usually because the API call is using the slug without the 'starts_with'. For example:

Storyblok\ApiException
An HTTP Error has occurred! - An HTTP Error has occurred! - Client error: `GET http://api.storyblok.com/v1/cdn/stories/blog?token=xxxxx&version=draft&cache_version=1637149023` resulted in a `404 Not Found` response: {"stories":["This record could not be found"]}

If I hit the API directly in the browser with a 'starts_with=blog/' parameter, then I get a full API response.

So definitely I'm missing something here, and if the answer is "learn more Laravel", then that's perfectly acceptable answer!

[Bug] Multi-option Parsing

Overview

Hello there 👋 .

We seem to be encountering a bug in one of the recent updates of your package. We upgraded from 2.15.0 to 2.19.2 and parsing of multi-option fields is no longer working.

After doing a bit more research the bug seems to have been introduced in 2.17.0.

Steps to Reproduce

  • Create a page
  • Add a new field that is a muti-option with a source as self
  • Add some options
  • Add field to page
  • Inspect draft response, and you should see something like:
{
    "story": {
        "content": {
            "_uid": "1515771c-3ec4-4922-8921-9607eda522e6",
            "body": [
                {
                    "_uid": "29914472-2a98-428d-868c-56c30207d608",
                    "component": "test-component",
                    "test_field": [
                        "20",
                        "10",
                        "30"
                    ],
                }
            ]
        }
    }
}

Expected Result

I expect in the component TestComponent to have access to a field called test_field that would be an array.

Actual Result

When accessing test_field, null is returned.

Dealing with pagination

Hi @RicLeP, I was wondering how you'd deal with paginating through the StoryBlok API when there's more than 100 articles in a 'collection'.

E.G. my client has a blog with several hundred articles - I couldn't see a process for iterating through all of the blog posts - just the first 100.

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.