3

Background information: I'm trying to manipulate the available options of a list type text field, depending on the host bundle:

function wtbase_options_list_alter(array &$options, array $context) {
  $fieldDefinition = $context['fieldDefinition'];
  if ($fieldDefinition->getName() == 'field_aos_cell' && $fieldDefinition->bundle() != 'link') {
    foreach ($options as $key => $value) {
      if ($key == 'link-horizontally-opposing') {
        unset($options[$key]);
      }
    }
  }
}

The $fieldDefinition->bundle part is really confusing. There seems to be a variable $fieldDefinition->bundle (containing the expected bundle name link) and a function $fieldDefinition->bundle() (containing field_config). But I can't access the variable bundle, because this crashed with Cannot access protected property Drupal\field\Entity\FieldConfig::$bundle.

After some digging I found FieldConfigBase::getTargetBundle(), which did return the expected result and did not crash (yet).

So this leads to two questions:

What is the difference between $bundle, bundle() and getTargetBundle() and which one I'm supposed to use? I'd prefer using variables, because they are visible in Xdebug, while functions are not.

And in a more general point of view, are there some guidelines when to use functions and when to use variables? In the same example as above Xdebug printed the same weird results for e.g. entity_type vs entityTypeId vs getEntityType() vs getEntityTypeId().

--

Update, clarification for question 1:

I know the technical difference between a member property and a member function. But I'm deeply confused that a property $bundle and a function bundle() of the exact same name are returning values from a completely different entities/context. In my example $fieldDefinition->bundle returns a Paragraph bundle name link, while $fieldDefinition->bundle() seems to return the bundle name from FieldConfig. I know this is technically possible... but why would anyone write code like this? I've not yet experienced this behavior anywhere else in Drupal core, usually $entity->bundle and $entity->bundle() return the same result.

1 Answer 1

3

bundle is the property on the object. It's where the value is stored. Properties can be assigned one of three ways:

  • public: these properties can be directly set or read on the object. Example:

    $object->key = 'value';
    $value = $object->key;
    
  • private: These can only be accessed by methods (functions) of the class itself. Nothing else can access it. For example, this will throw an error:

    $value = $object->key;
    

    Whereas this would be allowed:

    class Example {
      private $key;
    
      function getPrivateValue() {
        return $this->key;
      }
    }
    
    $value = $object->getPrivateValue();
    

    $key cannot be accessed outside of the class, since it is private. This can be used to enforce rules on the types of data to be stored in the property. Checks can be run to ensure it's an allowed data type.

  • protected: These are like private properties, however if a class extends the class, the extended class can access the property. If the property is private, extended classes cannot access it.

One thing for you to understand is that $node->bundle and $node->bundle() are not the same things. The first is a property, the other is a method. This should help understand, as a (non-real) example:

class Node {
  protected $bundle;

  public function bundle() {
    return $this->bundle;
  }
}

The above class has the property $bundle, and the method (function) bundle(). The method returns the value stored in the property.

Look at the error you are seeing: Cannot access protected property Drupal\field\Entity\FieldConfig::$bundle. This is because you are trying to access the $bundle property, but it is protected, meaning you cannot access it from outside the class.

To get the bundle of a node, you need to call the bundle() method on it, which will give you the bundle type. This is the answer to your question about which to use.

And in a more general point of view, are there some guidelines when to use functions and when to use variables?

It's generally thought to be a best practice to not provide direct access to properties on objects, and instead create setter and getter methods (functions) on objects to set and retrieve the property values. That way you can say, enforce a pattern:

class Example {

  protected $username

  public function setUsername($username) {
    if (!is_string($username)) {
      throw new \Exception('Username must be a string');
    }
    elseif (!preg_match('/^[a-zA-Z0-9_-]{3,12}$/', $username)) {
      throw new \Exception('Username must be between 3 and 12 characters, and may contain only alphanumerics, underscores or hyphens');
    }

    $this->username = $username;
  }

  public function getUsername() {
    return $this->username;
  }
}

The above enforces that username is both a string, and is the right length and only contains allowed characters.

Edit: Additional comment from Leigh:

another reason to avoid accessing the property directly is when you have multiple values. e.g. if you have a field "my_field" on a node which has multiple values if you use $node->my_field->value you'll only get 1 value returned

8
  • 1
    Also just to add to this, another reason to avoid accessing the property directly is when you have multiple values. e.g. if you have a field "my_field" on a node which has multiple values if you use $node->my_field->value you'll only get 1 value returned
    – Leigh
    Commented Sep 23, 2019 at 11:08
  • Good point. I'll add it to my post.
    – Jaypan
    Commented Sep 23, 2019 at 11:11
  • I got the basic, technical difference between a member property and a member function... but why would anyone write code that returns semantically different things for those two? In my example above link is from Paragraphs, while field_config is from FieldConfig.
    – Hudri
    Commented Sep 23, 2019 at 11:13
  • bundle() will return a string containing the bundle name. Your original post doesn't really make sense in that regard. If it is returning anything other than a string containing the bundle name, you've either misdiagnosed the issue, or you've got a broken system, and some module/theme is doing wonky stuff breaking core functionality.
    – Jaypan
    Commented Sep 23, 2019 at 11:17
  • 1
    As a side note, want to mention that this is why a language aware IDE like PhpStorm is almost essential to avoid frustrations like this. Autocompleting and being able to see docblocks on the interfaces is a huge help.
    – mpdonadio
    Commented Sep 23, 2019 at 14:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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