3

I've been pulling my hair out trying to work this one out.

I'm building a module to auto-populate a super-table on EVENT_BEFORE_SAVE, the module works exactly the way I want it to, everything seems to be working fine.

Except Craft throws a "Invalid field handle:" exception if the custom field doesn't exist on that entry.

I've tried to check if the field exists with a few different methods, but anything that directly references the fieldhandle seems to throw an exception.

What's the proper way of checking if a field exists and running the code if it does?

Mark Lloyd
  • 93
  • 3

3 Answers3

2

Without modifying your code too much, you could simply add an exception handler with a try/catch block.

Another method would be to retrieve the field handles for the entry type's field layout, and check for a match:

Getting the $entryType as necessary,

$etFieldHandles = [];
$fields = $entryType->getFieldLayout()->getFields();
foreach ($fields as $field) {
    $etFieldHandles[] = $field->handle;
}

if (in_array('yourfieldhandle', $etFieldHandles) ) { // the field exists in this entry type's layout. }

Wallace
  • 611
  • 3
  • 8
2

I've also run into this problem. I've checked the API documentation both for craft\elements\Entry and craft\base\Element and neither seem to have a simple, straightforward method to check if a field exists. Here are a couple of options I have found:

Getting the list of fields

You can get the list of fields by going through the field layout:

// this works with any element
$elementFields = $element->getFieldLayout()->getFields();

// this works only with entries $entryFields = $entry->getType()->getFieldLayout()->getFields();

// transform the array of Field objects into an array of field handles for convenience $entryFieldHandles = array_column($entryFields, 'handle');

// check if the entry has my_custom_field $entryHasMyCustomField = in_array('my_custom_field', $entryFieldHandles);

This also works well in Twig:

{% set entry_field_handles = entry.getFieldLayout().getFields()|column('handle') %}
{% set entry_has_field = 'my_custom_field' in entry_field_handles %}

Downside: May be slow, is a bit verbose, not sure how it interacts with different entry types in a section.

Going through the CustomFieldBehavior

Craft 3 compiles all your fields to the class CustomFieldBehavior, which is then attached to the Element object as a behavior. This class has properties for each custom field and a method canGetProperty which you can use to check if a particular property exists:

$entryHasMyCustomField = $entry->getBehavior('customFields')
                               ->canGetProperty('my_custom_field');

Downsides: Uses undocumented methods / behavior which might change. There might also be some edge-cases if the CustomFieldBehavior class has different properties that aren't fields but match any of your field names, though that's pretty unlikely.

Catching the error

Simple but effective:

try {
    $fieldValue = $entry->getFieldValue('my_custom_field');
} catch (\craft\errors\InvalidFieldException $error) {
    // field doesn't exist
}

Downside: Really ugly and verbose.

MoritzLost
  • 11,215
  • 5
  • 22
1

Update for Craft 4

$entry->getFieldLayout()->getFieldByHandle('fieldHandle');

https://docs.craftcms.com/api/v4/craft-models-fieldlayout.html#public-methods

comtyler
  • 15
  • 4