statamic / eloquent-driver

Provides support for storing your Statamic data in a database, rather than flat files.
https://statamic.dev/tips/storing-content-in-a-database
MIT License
104 stars 71 forks source link

Serialization of 'PDO' is not allowed, when using eloquent assets in a no cache region #291

Closed byron-roots closed 1 month ago

byron-roots commented 1 month ago

Bug description

I've been trying to migrate our assets to eloquent, but when using assets in a nocache region we get an error Serialization of 'PDO' is not allowed

Below is the template we use and removing nocache tags fixes the issue

{{ nocache }}
    <div class="c-basket">
        <button class="c-basket__button" @click="open('basket')">
            {{ svg src="basket" }}
                <span class="c-basket__count">
                    {{ sc:cart:quantityTotal }}
                </span>
        </button>
        <template x-teleport="body" >
            <div class="c-basket__portal"
                 x-show="isOpen('basket')"
                 x-transition.500ms
                 x-cloak
                 @click.outside="close()"
                 x-trap.noscroll="isOpen('basket') && isMobile()">
                <div class="c-basket-modal">
                    <div class="c-basket-modal__container">
                        {{ if { sc:cart:quantityTotal } !== 0 }}
                            <div class="c-basket-modal__items">
                                {{ sc:cart:items }}
                                    {{ partial:products.partials.basket }}
                                {{ /sc:cart:items }}
                                <div class="c-basket-modal__totals">
                                    <span class="c-basket-modal__total">{{ trans:newby.total }}</span>
                                    <span class="c-basket-modal__total">{{ sc:cart:total }}</span>
                                </div>
                                <div class="c-basket-modal__actions">
                                    <a href="/cart" class="c-button c-button--purple c-button--large">
                                        {{ trans:newby.view_cart }} ({{ sc:cart:total }})
                                    </a>
                                    <a href="/checkout" class="c-button c-button--orange c-button--large">
                                        {{ trans:newby.continue_checkout }}
                                        {{ svg src="arrow-right" }}
                                    </a>
                                </div>
                            </div>
                        {{ else }}
                            <div class="c-basket-modal__empty">
                                {{ trans:newby.empty_basket }}
                            </div>
                            <div class="c-basket-modal__actions">
                                <a href="{{ mount_url handle="products" }}" class="c-button c-button--orange c-button--large">
                                    {{ trans:newby.shop_products }}
                                    {{ svg src="arrow-right" }}
                                </a>
                            </div>
                        {{ /if }}
                    </div>
                </div>
            </div>
        </template>
    </div>
{{ /nocache }}

How to reproduce

  1. Setup new project
  2. Use eloquent assets
  3. Create template which uses assets and a nocache tag

Logs

https://flareapp.io/share/NPGYZMn5

Environment

Environment
Application Name: Newby Leisure
Laravel Version: 10.48.11
PHP Version: 8.2.19
Composer Version: 2.7.6
Environment: local
Debug Mode: ENABLED
URL: newbyleisure.test/
Maintenance Mode: OFF

Cache
Config: NOT CACHED
Events: NOT CACHED
Routes: NOT CACHED
Views: CACHED

Drivers
Broadcasting: pusher
Cache: redis
Database: mysql
Logs: stack / single
Mail: smtp
Queue: redis
Session: file

Locales
Installed
Locales Version: 1.9.0
Protected

Localization
Installed: en, es
LaravelLang\Attributes\Plugin: 2.10.5
LaravelLang\HttpStatuses\Plugin: 3.8.3
LaravelLang\Lang\Plugin: 13.12.0
Protected Locales: en
Publisher Version: 14.7.1

Simple Commerce
Currencies: GBP, EUR
Gateways: Stripe, Invoice
Repository: Customer: DuncanMcClean\SimpleCommerce\Customers\UserCustomerRepository
Repository: Order: App\Repositories\EloquentEntryOrderRepository
Repository: Product: DuncanMcClean\SimpleCommerce\Products\EntryProductRepository
Shipping Methods: Free Shipping
Tax Engine: DuncanMcClean\SimpleCommerce\Tax\BasicTaxEngine

Livewire
Livewire: v3.4.12

Statamic
Addons: 15
Sites: 2 (English,, Spanish)
Stache Watcher: Disabled
Static Caching: Disabled
Version: 5.4.0 PRO

Statamic Addons
appswithlove/statamic-one-click-content-translation: 5.2.1
duncanmcclean/simple-commerce: 7.2.0
jacksleight/statamic-bard-texstyle: 3.2.2
jonassiewertsen/statamic-livewire: 3.3.1
markkravchuk/statamic-activity-book: 1.0.1
nineteensquared/instagram: 1.2.0
rootstudio/cache-primer: 0.12
rootstudio/monitor: 0.3
rootstudio/mux-video: 0.6
rootstudio/turnstile: 0.3
statamic-rad-pack/meilisearch: 3.3.0
statamic-rad-pack/runway: 7.1.0
statamic/collaboration: 1.0.0
statamic/eloquent-driver: 4.0.0
stillat/relationships: 2.2.0

Statamic Eloquent Driver
Asset Containers: file
Assets: eloquent
Blueprints: file
Collection Trees: eloquent
Collections: file
Entries: eloquent
Forms: eloquent
Global Sets: file
Global Variables: eloquent
Navigation Trees: eloquent
Navigations: file
Revisions: eloquent
Taxonomies: file
Terms: eloquent

Additional details

No response

ryanmitchell commented 1 month ago

What makes you sure it's the assets? It seems youre using eloquent globals and they are in your template too - it could be them.

byron-roots commented 1 month ago

As soon as I swap assets to file based it works.

It could be a combination of both which I can have a go at testing

ryanmitchell commented 1 month ago

Are you able to add a dd($region) here so I can inspect the data its attempting to store?

https://github.com/statamic/cms/blob/df7412095459999e59b87eaeff07ab8f804acaae/src/StaticCaching/NoCache/Session.php#L153

byron-roots commented 1 month ago

So it looks like nocache regions without assets are being affected, and assets are not really causing the problem. Removing any glide tags or asset tags in the templates doesn't resolve the issues on those regions either.

I've tried setting globals to file and that hasn't fixed it either. Completely removing any content within the nocache regions also doesn't seem to fix it. Only changing assets back to the file driver fixes it.

Below are some examples of nocache regions which cause the page to error.

    {{ if consent:enable }}
            {{ consent:categories }}
            {{ if force }}
                {{ head_scripts }}
            {{ else }}
                <template id="{{ title | slugify }}-head" data-position="head">
                    {{ head_scripts }}
                </template>
            {{ /if }}
            {{ /consent:categories }}
        {{ /if }}
    {{ if consent:enable }}
            <template id="consent">
                <div class="c-consent js-consent" data-categories="{{ consent:categories }}{{ title | slugify}}{{ last ? '' : ',' }}{{ /consent:categories }}">
                    <div class="c-consent__container">
                        <div class="c-consent__content">
                            <h3 class="c-consent__title">
                                {{ consent:banner_title }}
                            </h3>
                            <div class="c-consent__copy is-quarantine">
                                {{ consent:banner_copy }}
                            </div>
                        </div>
                        <div class="c-consent__actions">
                            <button type="button" class="c-button c-button--orange js-consent__accept-all" aria-label="Accept all">
                                Confirm
                            </button>
                        </div>
                    </div>
                </div>
            </template>
            {{ consent:categories }}
                {{ if force }}
                    {{ scripts }}
                {{ else }}
                    <template id="{{ title | slugify }}-body" data-position="body">
                        {{ scripts }}
                    </template>
                {{ /if }}
            {{ /consent:categories }}
        {{ /if }}
    <div class="c-basket">
            <button class="c-basket__button" @click="open('basket')">
                {{ svg src="basket" }}
                    <span class="c-basket__count">
                        {{ sc:cart:quantityTotal }}
                    </span>
            </button>
            <template x-teleport="body" >
                <div class="c-basket__portal"
                     x-show="isOpen('basket')"
                     x-transition.500ms
                     x-cloak
                     @click.outside="close()"
                     x-trap.noscroll="isOpen('basket') && isMobile()">
                    <div class="c-basket-modal">
                        <div class="c-basket-modal__container">
                            {{ if { sc:cart:quantityTotal } !== 0 }}
                                <div class="c-basket-modal__items">
                                    {{ sc:cart:items }}
                                        {{ partial:products.partials.basket }}
                                    {{ /sc:cart:items }}
                                    <div class="c-basket-modal__totals">
                                        <span class="c-basket-modal__total">{{ trans:newby.total }}</span>
                                        <span class="c-basket-modal__total">{{ sc:cart:total }}</span>
                                    </div>
                                    <div class="c-basket-modal__actions">
                                        <a href="/cart" class="c-button c-button--purple c-button--large">
                                        </a>
                                        <a href="/checkout" class="c-button c-button--orange c-button--large">
                                            {{ trans:newby.continue_checkout }}
                                            {{ svg src="arrow-right" }}
                                        </a>
                                    </div>
                                </div>
                            {{ else }}
                                <div class="c-basket-modal__empty">
                                    {{ trans:newby.empty_basket }}
                                </div>
                                <div class="c-basket-modal__actions">
                                    <a href="{{ mount_url handle="products" }}" class="c-button c-button--orange c-button--large">
                                        {{ trans:newby.shop_products }}
                                        {{ svg src="arrow-right" }}
                                    </a>
                                </div>
                            {{ /if }}
                        </div>
                    </div>
                </div>
            </template>
        </div>
{{ if {cookie:has key="hide_newsletter"} != true }}
<div class="c-newsletter" x-data="Newsletter" x-show="!checkState(0)" :class="{ 'is-active': checkState(2)}" x-transition.opacity x-cloak>
    <div class="c-newsletter__modal" @click.outside="close()" x-trap.noscroll="checkState(2)">
        <button class="c-newsletter__close"  @click="downgrade()">
            {{ svg src="cross" }}
        </button>
        <header class="c-newsletter__tab" @click="upgrade()">
            <span class="c-newsletter__tab-title">
                {{ shared_content:newsletter_heading }}
            </span>
        </header>
        <div x-collapse x-show="checkState(2)">
            <form action="https://emailoctopus.com/lists/73eaf5b2-8b02-11eb-a3d0-06b4694bee2a/members/embedded/1.3s/add"
                  method="post"
                  data-message-success="Thanks for subscribing!"
                  data-message-missing-email-address="Your email address is required."
                  data-message-invalid-email-address="Your email address looks incorrect, please try again."
                  data-message-bot-submission-error="This doesn't look like a human submission."
                  data-message-consent-required="Please check the checkbox to indicate your consent."
                  data-message-invalid-parameters-error="This form has missing or invalid fields."
                  data-message-unknown-error="Sorry, an unknown error has occurred. Please try again later."
                  class="c-newsletter__container"
                  data-sitekey="6LdYsmsUAAAAAPXVTt-ovRsPIJ_IVhvYBBhGvRV6"
            >
                <h3 class="c-newsletter__title">
                    {{ shared_content:newsletter_title }}
                </h3>
                <div class="c-newsletter__copy">
                    {{ shared_content:newsletter_intro }}
                </div>
                <div class="c-newsletter__fields">
                    <div class="o-field o-field--center o-field--100">
                        <input id="field_0"
                               type="email"
                               name="field_0"
                               class="c-input o-field__control"
                               placeholder=" "
                               required
                        />
                        <label class="o-field__label" for="field_0">
                            Email
                        </label>
                    </div>
                    <div class="o-field o-field--center o-field--100">
                        <label class="c-toggle ">
                            <input type="checkbox" class="c-toggle__control" name="terms" required />
                            <span>{{ trans:newby.forms.terms url="{site_details:terms:url}" }}</span>
                        </label>
                    </div>
                </div>
                <div class="c-newsletter__actions">
                    <button type="submit" class="c-button">
                        {{ button_label ?? 'Submit'}}
                    </button>
                </div>
            </form>
        </div>
    </div>
</div>
{{ /if }}
{{ form:create form="{form|raw}" class="o-form" redirect="{redirect_url}" }}
        {{ if success }}
            <div class="o-form__row">
                {{ if success_message }}
                    <div class="is-quarantine">
                        {{ success_message }}
                    </div>
                {{ else }}
                    <span class="c-status c-status--success">
                        Thank you for your submission
                    </span>
                {{ /if }}
            </div>
        {{ else }}
            <div class="o-form__fields">
                {{ fields }}
                    {{ partial:components.field }}
                {{ /fields }}
                {{ turnstile }}
       </div>
            {{ if honeypot }}
            <div class="u-sr">
                <label for="{{ honeypot }}">Do not fill</label>
                <input type="text" name="{{ honeypot }}" id="{{ honeypot }}"/>
            </div>
            {{ /if }}
            <div class="o-form__toggles">
                <label class="c-toggle">
                    <input type="checkbox" class="c-toggle__control" name="terms" required />
                    <span>{{ trans:newby.forms.terms url="{site_details:terms:url}" }}</span>
                </label>
                <label class="c-toggle">
                    <input type="checkbox" class="c-toggle__control" name="mailing" id="mailing" />
                    <span>{{ trans:newby.forms.mailing }}</span>
                </label>
            </div>
            <div class="o-form__actions">
                <button type="submit" class="c-button">
                    {{ button_label ?? 'Submit'}}
                </button>
            </div>
        {{ /if }}
    {{ /form:create }}
ryanmitchell commented 1 month ago

What about adding select="zzz|yyy" to the region so you only store the data you need?

byron-roots commented 1 month ago

That doesn't have an effect. Even a region without any variables throws the error

Statamic\StaticCaching\NoCache\StringRegion {#18163 ▼ // vendor/statamic/cms/src/StaticCaching/NoCache/Session.php:153
  #key: "14264ae7f4a8d2243427655ee00d70c17e3c567e5d48317a123dd7fb6e3c333cfc3bf221"
  #context: []
  #session: 
Statamic\StaticCaching\NoCache
\
Session {#3236 ▶}
  #content: "<p>No content</p>"
  #extension: "antlers.html"
}
ryanmitchell commented 1 month ago

I can't see any reason for this - @duncanmcclean do you have any ideas?

duncanmcclean commented 1 month ago

I don't have any ideas off the top of my head. I'd probably need to have a dig around to figure out what's causing it.

@byron-roots Are you able to share access to the repo or reproduce the issue on a fresh Statamic site?

byron-roots commented 1 month ago

I can't add you to the repository, could I send it as a support ticket?

duncanmcclean commented 1 month ago

I can't add you to the repository, could I send it as a support ticket?

Sure, you can send it in as a support request: https://statamic.com/support

duncanmcclean commented 1 month ago

I've done a little bit of digging. From what I can tell, it seems like it has something to do with the media field on your shared_content global.

In the blueprint, the field looks like this, just a normal assets field:

-
  handle: media
  field:
    mode: list
    container: assets
    restrict: false
    allow_uploads: true
    show_filename: true
    show_set_alt: true
    type: assets
    display: Media
    icon: assets
    listable: hidden
    instructions_position: above
    visibility: visible
    hide_display: false

In the hotlinks/default.antlers.html partial, it's looping through the hotlinks field on the shared_content global:

{{ shared_content:hotlinks }}
    <div class="c-hotlinks__item">
        {{ partial:general.article article_class="c-article--center" :article_image="media" }}
    </div>
{{ /shared_content:hotlinks }}

If I comment out the {{ partial }} bit, or even just remove the article_image parameter that's being passed in, the error goes away and the page loads normally.

When I ray() the context before it gets passed into the nocache region here, this is what the view array looks like:

CleanShot 2024-05-23 at 17 17 38

I need to head off now but wanted to leave some notes here for future me or someone else who ends up looking into this.

byron-roots commented 1 month ago

Thanks for looking into that, much appreciated. I'll have another dig around and see if I can find out anything else

byron-roots commented 1 month ago

Setting the max_files option to 1 on the asset field appears to fix the issue