statamic / cms

The core Laravel CMS Composer package
https://statamic.com
Other
3.88k stars 519 forks source link

Empty select fieldtypes not returning null when passed through partial #7952

Closed taylorcammack closed 1 month ago

taylorcammack commented 1 year ago

Bug description

I have a select fieldtype that doesn't have a value set. The field doesn't appear in the entry markdown file.

  -
    handle: site_type
    field:
      options:
        Airport: null
        Commercial: null
        Greenfield: null
        Industrial: null
        Office: null
      clearable: true
      multiple: false
      searchable: true
      taggable: false
      push_tags: false
      cast_booleans: false
      type: select
      listable: hidden
      display: 'Site Type'
      if:
        property_type: 'equals Site'
      width: 50
      instructions_position: above
      visibility: visible

I'm passing that value into a partial

{{ partial:property/components/item label="Site Type" :item="site_type" }}

And inside that partial, checking for a value:

{{ if item }}
    {{ label }} - {{ item }}
{{ /if }}

Rather than show nothing (the expected behavior) because the value isn't set, it shows the label that I passed in with no item value.

However, if I do this, it works as expected.

{{ if site_type }}
  {{ partial:property/components/item label="Site Type" :item="site_type" }}
{{ /if }}

When I checked what is being returned, this is what I get:

Screenshot 2023-04-19 at 11 37 03 AM

By contrast, an empty text fieldtype simply returns null. It seems that this should be the expected behavior for empty fields.

How to reproduce

  1. Create a select fieldtype.
  2. Don't set a value on it.
  3. Pass it into a partial with a check to see if the item is set

Logs

No response

Environment

Environment
Application Name: Statamic Core
Laravel Version: 9.52.4
PHP Version: 8.1.16
Composer Version: 2.6-dev+4e1774518002b0be4365fe62ce220b2011c2b4bf
Environment: local
Debug Mode: ENABLED
URL: core.test
Maintenance Mode: OFF

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

Drivers
Broadcasting: log
Cache: statamic
Database: mysql
Logs: stack / single
Mail: log
Queue: sync
Session: file

Statamic
Addons: 9
Antlers: runtime
Stache Watcher: Enabled
Static Caching: Disabled
Version: 3.4.7 PRO

Statamic Addons
aryehraber/statamic-captcha: 1.9.1
encore/encore: dev-main
goldnead/statamic-collapse-fieldtype: 1.0.4
jonassiewertsen/statamic-livewire: 2.11.0
mitydigital/iconamic: 1.1.2
rias/statamic-data-import: 1.2.2
simonhamp/statamic-heroicons: 1.1.0
spatie/statamic-responsive-images: 3.1.3
webographen/statamic-dynamic-token: 1.0.0

Installation

Other (please explain)

Antlers Parser

runtime (new)

Additional details

No response

taylorcammack commented 1 year ago

Whipped up a modifier as a work-around

<?php

namespace App\Modifiers;

use Statamic\Fields\LabeledValue;
use Statamic\Modifiers\Modifier;

class IsActuallyEmpty extends Modifier
{

    protected static $handle = 'is_actually_empty';

    /**
     * Modify a value.
     *
     * @param mixed  $value    The value to be modified
     * @param array  $params   Any parameters used in the modifier
     * @param array  $context  Contextual values
     * @return mixed
     */

    public function index($value)
    {   
      if ($value instanceof LabeledValue) {
        if (empty(array_filter($value->toArray()))) {
          return false;
        }
      }

      return true;
    }
}
jasonvarga commented 1 year ago

Good find. Those fieldtypes give you a "labeled value" even if they're null. Probably should just give you null.

Try doing this instead: {{ if item:value }}

taylorcammack commented 1 year ago

Yep, had tried that and it works for that scenario, but it doesn't work if you're using the same partial to pass through fields that returns simple values or null (text fields, etc.).

At this point, seems like you'd need to have a separate partial just to change that one line or go the modifier route.

jasonvarga commented 1 year ago

Then pass :item="sprinkler_system:item" to the partial.

taylorcammack commented 1 year ago

Oh gotcha, assuming you mean :item="sprinkler_system:value" - yep, that works

jasonvarga commented 1 year ago

Oh yeah, value. 😄

taylorcammack commented 1 year ago

Looks like I was wrong on thinking this was also a button_group field issue, just seeing this on select. Suppose that same pattern may be used on other fieldtypes that I just haven't used yet. Updated code example and title.

jasonvarga commented 1 year ago

Yeah it's any that use those "labeled value" types. Button groups, selects, radios, checkboxes, etc.

caseydwyer commented 6 months ago

Piggybacking here, as I think I'm running into the same underlying issue. Currently upgrading a site from 3.x to 4.x, and it seems like templating a multiple-enabled select field (ie, via a loop) no longer works, similar to what Taylor outlined above—no value set in the content markdown. This used to do the trick, and antlers would just skip it if empty:

{{ organizations }}
    {{ partial:components/tag-bubble }}
{{ /organizations }}

In 4.x, however, it's rendering that partial even when organizations (the multiple-enabled select field) is empty. Which, in this case, causes the title of the article to show up as a tag, presumably from the cascade.

Workaround above does the trick (ie, wrapping in an {{ if }})—just wanted to share this scenario, in case it's helpful for restoring that v3 behavior somewhere down the road, so the loop works as expected by not rendering when null.

duncanmcclean commented 1 month ago

From what I can tell, both of these issues seem to be fixed on the latest version of Statamic. 🎉