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?

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.

5
  • 1
    Haha, 1 minute behind but great minds :)
    – Andy
    May 18 at 9:46
  • @Andy You're too fast for me ^^
    – MoritzLost
    May 18 at 9:47
  • The only correction I'd make to your first option is to get the field layout for the entry type, not just the entry.
    – Andy
    May 18 at 9:51
  • @Andy Thanks for the tip, I've updated my example code. One thing that's bugging me is that EntryType::getFieldLayout() isn't documented at all. Probably because the field layout is attached as a behavior. Though it feels icky to use undocumented methods like that.
    – MoritzLost
    May 18 at 10:06
  • Perfect, exactly what I was looking for!
    – Mark Lloyd
    May 18 at 23:59
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.
}
1
  • This is exactly what I was after, thanks!
    – Mark Lloyd
    May 18 at 23:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.