FieldConfigBase.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. <?php
  2. namespace Drupal\Core\Field;
  3. use Drupal\Core\Config\Entity\ConfigEntityBase;
  4. use Drupal\Core\Entity\EntityStorageInterface;
  5. use Drupal\Core\Entity\FieldableEntityInterface;
  6. use Drupal\Core\Field\TypedData\FieldItemDataDefinition;
  7. /**
  8. * Base class for configurable field definitions.
  9. */
  10. abstract class FieldConfigBase extends ConfigEntityBase implements FieldConfigInterface {
  11. /**
  12. * The field ID.
  13. *
  14. * The ID consists of 3 parts: the entity type, bundle and the field name.
  15. *
  16. * Example: node.article.body, user.user.field_main_image.
  17. *
  18. * @var string
  19. */
  20. protected $id;
  21. /**
  22. * The field name.
  23. *
  24. * @var string
  25. */
  26. protected $field_name;
  27. /**
  28. * The field type.
  29. *
  30. * This property is denormalized from the field storage for optimization of
  31. * the "entity and render cache hits" critical paths. If not present in the
  32. * $values passed to create(), it is populated from the field storage in
  33. * postCreate(), and saved in config records so that it is present on
  34. * subsequent loads.
  35. *
  36. * @var string
  37. */
  38. protected $field_type;
  39. /**
  40. * The name of the entity type the field is attached to.
  41. *
  42. * @var string
  43. */
  44. protected $entity_type;
  45. /**
  46. * The name of the bundle the field is attached to.
  47. *
  48. * @var string
  49. */
  50. protected $bundle;
  51. /**
  52. * The human-readable label for the field.
  53. *
  54. * This will be used as the title of Form API elements for the field in entity
  55. * edit forms, or as the label for the field values in displayed entities.
  56. *
  57. * If not specified, this defaults to the field_name (mostly useful for fields
  58. * created in tests).
  59. *
  60. * @var string
  61. */
  62. protected $label;
  63. /**
  64. * The field description.
  65. *
  66. * A human-readable description for the field when used with this bundle.
  67. * For example, the description will be the help text of Form API elements for
  68. * this field in entity edit forms.
  69. *
  70. * @var string
  71. */
  72. protected $description = '';
  73. /**
  74. * Field-type specific settings.
  75. *
  76. * An array of key/value pairs. The keys and default values are defined by the
  77. * field type.
  78. *
  79. * @var array
  80. */
  81. protected $settings = [];
  82. /**
  83. * Flag indicating whether the field is required.
  84. *
  85. * TRUE if a value for this field is required when used with this bundle,
  86. * FALSE otherwise. Currently, required-ness is only enforced at the Form API
  87. * level in entity edit forms, not during direct API saves.
  88. *
  89. * @var bool
  90. */
  91. protected $required = FALSE;
  92. /**
  93. * Flag indicating whether the field is translatable.
  94. *
  95. * Defaults to TRUE.
  96. *
  97. * @var bool
  98. */
  99. protected $translatable = TRUE;
  100. /**
  101. * Default field value.
  102. *
  103. * The default value is used when an entity is created, either:
  104. * - through an entity creation form; the form elements for the field are
  105. * prepopulated with the default value.
  106. * - through direct API calls (i.e. $entity->save()); the default value is
  107. * added if the $entity object provides no explicit entry (actual values or
  108. * "the field is empty") for the field.
  109. *
  110. * The default value is expressed as a numerically indexed array of items,
  111. * each item being an array of key/value pairs matching the set of 'columns'
  112. * defined by the "field schema" for the field type, as exposed in
  113. * hook_field_schema(). If the number of items exceeds the cardinality of the
  114. * field, extraneous items will be ignored.
  115. *
  116. * This property is overlooked if the $default_value_callback is non-empty.
  117. *
  118. * Example for a integer field:
  119. * @code
  120. * array(
  121. * array('value' => 1),
  122. * array('value' => 2),
  123. * )
  124. * @endcode
  125. *
  126. * @var array
  127. */
  128. protected $default_value = [];
  129. /**
  130. * The name of a callback function that returns default values.
  131. *
  132. * The function will be called with the following arguments:
  133. * - \Drupal\Core\Entity\FieldableEntityInterface $entity
  134. * The entity being created.
  135. * - \Drupal\Core\Field\FieldDefinitionInterface $definition
  136. * The field definition.
  137. * It should return an array of default values, in the same format as the
  138. * $default_value property.
  139. *
  140. * This property takes precedence on the list of fixed values specified in the
  141. * $default_value property.
  142. *
  143. * @var string
  144. */
  145. protected $default_value_callback = '';
  146. /**
  147. * The field storage object.
  148. *
  149. * @var \Drupal\Core\Field\FieldStorageDefinitionInterface
  150. */
  151. protected $fieldStorage;
  152. /**
  153. * The data definition of a field item.
  154. *
  155. * @var \Drupal\Core\Field\TypedData\FieldItemDataDefinition
  156. */
  157. protected $itemDefinition;
  158. /**
  159. * Array of constraint options keyed by constraint plugin ID.
  160. *
  161. * @var array
  162. */
  163. protected $constraints = [];
  164. /**
  165. * Array of property constraint options keyed by property ID. The values are
  166. * associative array of constraint options keyed by constraint plugin ID.
  167. *
  168. * @var array[]
  169. */
  170. protected $propertyConstraints = [];
  171. /**
  172. * {@inheritdoc}
  173. */
  174. public function id() {
  175. return $this->entity_type . '.' . $this->bundle . '.' . $this->field_name;
  176. }
  177. /**
  178. * {@inheritdoc}
  179. */
  180. public function getName() {
  181. return $this->field_name;
  182. }
  183. /**
  184. * {@inheritdoc}
  185. */
  186. public function getType() {
  187. return $this->field_type;
  188. }
  189. /**
  190. * {@inheritdoc}
  191. */
  192. public function getTargetEntityTypeId() {
  193. return $this->entity_type;
  194. }
  195. /**
  196. * {@inheritdoc}
  197. */
  198. public function getTargetBundle() {
  199. return $this->bundle;
  200. }
  201. /**
  202. * {@inheritdoc}
  203. */
  204. public function calculateDependencies() {
  205. parent::calculateDependencies();
  206. // Add dependencies from the field type plugin. We can not use
  207. // self::calculatePluginDependencies() because instantiation of a field item
  208. // plugin requires a parent entity.
  209. /** @var $field_type_manager \Drupal\Core\Field\FieldTypePluginManagerInterface */
  210. $field_type_manager = \Drupal::service('plugin.manager.field.field_type');
  211. $definition = $field_type_manager->getDefinition($this->getType());
  212. $this->addDependency('module', $definition['provider']);
  213. // Plugins can declare additional dependencies in their definition.
  214. if (isset($definition['config_dependencies'])) {
  215. $this->addDependencies($definition['config_dependencies']);
  216. }
  217. // Let the field type plugin specify its own dependencies.
  218. // @see \Drupal\Core\Field\FieldItemInterface::calculateDependencies()
  219. $this->addDependencies($definition['class']::calculateDependencies($this));
  220. // Create dependency on the bundle.
  221. $bundle_config_dependency = $this->entityManager()->getDefinition($this->entity_type)->getBundleConfigDependency($this->bundle);
  222. $this->addDependency($bundle_config_dependency['type'], $bundle_config_dependency['name']);
  223. return $this;
  224. }
  225. /**
  226. * {@inheritdoc}
  227. */
  228. public function onDependencyRemoval(array $dependencies) {
  229. $changed = parent::onDependencyRemoval($dependencies);
  230. $field_type_manager = \Drupal::service('plugin.manager.field.field_type');
  231. $definition = $field_type_manager->getDefinition($this->getType());
  232. if ($definition['class']::onDependencyRemoval($this, $dependencies)) {
  233. $changed = TRUE;
  234. }
  235. return $changed;
  236. }
  237. /**
  238. * {@inheritdoc}
  239. */
  240. public function postCreate(EntityStorageInterface $storage) {
  241. parent::postCreate($storage);
  242. // If it was not present in the $values passed to create(), (e.g. for
  243. // programmatic creation), populate the denormalized field_type property
  244. // from the field storage, so that it gets saved in the config record.
  245. if (empty($this->field_type)) {
  246. $this->field_type = $this->getFieldStorageDefinition()->getType();
  247. }
  248. }
  249. /**
  250. * {@inheritdoc}
  251. */
  252. public function postSave(EntityStorageInterface $storage, $update = TRUE) {
  253. // Clear the cache.
  254. $this->entityManager()->clearCachedFieldDefinitions();
  255. // Invalidate the render cache for all affected entities.
  256. $entity_type = $this->getFieldStorageDefinition()->getTargetEntityTypeId();
  257. if ($this->entityManager()->hasHandler($entity_type, 'view_builder')) {
  258. $this->entityManager()->getViewBuilder($entity_type)->resetCache();
  259. }
  260. }
  261. /**
  262. * {@inheritdoc}
  263. */
  264. public function getLabel() {
  265. return $this->label();
  266. }
  267. /**
  268. * {@inheritdoc}
  269. */
  270. public function setLabel($label) {
  271. $this->label = $label;
  272. return $this;
  273. }
  274. /**
  275. * {@inheritdoc}
  276. */
  277. public function getDescription() {
  278. return $this->description;
  279. }
  280. /**
  281. * {@inheritdoc}
  282. */
  283. public function setDescription($description) {
  284. $this->description = $description;
  285. return $this;
  286. }
  287. /**
  288. * {@inheritdoc}
  289. */
  290. public function isTranslatable() {
  291. // A field can be enabled for translation only if translation is supported.
  292. return $this->translatable && $this->getFieldStorageDefinition()->isTranslatable();
  293. }
  294. /**
  295. * {@inheritdoc}
  296. */
  297. public function setTranslatable($translatable) {
  298. $this->translatable = $translatable;
  299. return $this;
  300. }
  301. /**
  302. * {@inheritdoc}
  303. */
  304. public function getSettings() {
  305. return $this->settings + $this->getFieldStorageDefinition()->getSettings();
  306. }
  307. /**
  308. * {@inheritdoc}
  309. */
  310. public function setSettings(array $settings) {
  311. $this->settings = $settings + $this->settings;
  312. return $this;
  313. }
  314. /**
  315. * {@inheritdoc}
  316. */
  317. public function getSetting($setting_name) {
  318. if (array_key_exists($setting_name, $this->settings)) {
  319. return $this->settings[$setting_name];
  320. }
  321. else {
  322. return $this->getFieldStorageDefinition()->getSetting($setting_name);
  323. }
  324. }
  325. /**
  326. * {@inheritdoc}
  327. */
  328. public function setSetting($setting_name, $value) {
  329. $this->settings[$setting_name] = $value;
  330. return $this;
  331. }
  332. /**
  333. * {@inheritdoc}
  334. */
  335. public function isRequired() {
  336. return $this->required;
  337. }
  338. /**
  339. * [@inheritdoc}
  340. */
  341. public function setRequired($required) {
  342. $this->required = $required;
  343. return $this;
  344. }
  345. /**
  346. * {@inheritdoc}
  347. */
  348. public function getDefaultValue(FieldableEntityInterface $entity) {
  349. // Allow custom default values function.
  350. if ($callback = $this->getDefaultValueCallback()) {
  351. $value = call_user_func($callback, $entity, $this);
  352. }
  353. else {
  354. $value = $this->getDefaultValueLiteral();
  355. }
  356. // Allow the field type to process default values.
  357. $field_item_list_class = $this->getClass();
  358. return $field_item_list_class::processDefaultValue($value, $entity, $this);
  359. }
  360. /**
  361. * {@inheritdoc}
  362. */
  363. public function getDefaultValueLiteral() {
  364. return $this->default_value;
  365. }
  366. /**
  367. * {@inheritdoc}
  368. */
  369. public function setDefaultValue($value) {
  370. if (!is_array($value)) {
  371. if ($value === NULL) {
  372. $value = [];
  373. }
  374. $key = $this->getFieldStorageDefinition()->getPropertyNames()[0];
  375. // Convert to the multi value format to support fields with a cardinality
  376. // greater than 1.
  377. $value = [
  378. [$key => $value],
  379. ];
  380. }
  381. $this->default_value = $value;
  382. return $this;
  383. }
  384. /**
  385. * {@inheritdoc}
  386. */
  387. public function getDefaultValueCallback() {
  388. return $this->default_value_callback;
  389. }
  390. /**
  391. * {@inheritdoc}
  392. */
  393. public function setDefaultValueCallback($callback) {
  394. $this->default_value_callback = $callback;
  395. return $this;
  396. }
  397. /**
  398. * Implements the magic __sleep() method.
  399. *
  400. * Using the Serialize interface and serialize() / unserialize() methods
  401. * breaks entity forms in PHP 5.4.
  402. * @todo Investigate in https://www.drupal.org/node/2074253.
  403. */
  404. public function __sleep() {
  405. // Only serialize necessary properties, excluding those that can be
  406. // recalculated.
  407. $properties = get_object_vars($this);
  408. unset($properties['fieldStorage'], $properties['itemDefinition'], $properties['original']);
  409. return array_keys($properties);
  410. }
  411. /**
  412. * {@inheritdoc}
  413. */
  414. public static function createFromItemType($item_type) {
  415. // Forward to the field definition class for creating new data definitions
  416. // via the typed manager.
  417. return BaseFieldDefinition::createFromItemType($item_type);
  418. }
  419. /**
  420. * {@inheritdoc}
  421. */
  422. public static function createFromDataType($type) {
  423. // Forward to the field definition class for creating new data definitions
  424. // via the typed manager.
  425. return BaseFieldDefinition::createFromDataType($type);
  426. }
  427. /**
  428. * {@inheritdoc}
  429. */
  430. public function getDataType() {
  431. return 'list';
  432. }
  433. /**
  434. * {@inheritdoc}
  435. */
  436. public function isList() {
  437. return TRUE;
  438. }
  439. /**
  440. * {@inheritdoc}
  441. */
  442. public function getClass() {
  443. // Derive list class from the field type.
  444. $type_definition = \Drupal::service('plugin.manager.field.field_type')
  445. ->getDefinition($this->getType());
  446. return $type_definition['list_class'];
  447. }
  448. /**
  449. * {@inheritdoc}
  450. */
  451. public function getConstraints() {
  452. return \Drupal::typedDataManager()->getDefaultConstraints($this) + $this->constraints;
  453. }
  454. /**
  455. * {@inheritdoc}
  456. */
  457. public function getConstraint($constraint_name) {
  458. $constraints = $this->getConstraints();
  459. return isset($constraints[$constraint_name]) ? $constraints[$constraint_name] : NULL;
  460. }
  461. /**
  462. * {@inheritdoc}
  463. */
  464. public function getItemDefinition() {
  465. if (!isset($this->itemDefinition)) {
  466. $this->itemDefinition = FieldItemDataDefinition::create($this)
  467. ->setSettings($this->getSettings());
  468. // Add any custom property constraints, overwriting as required.
  469. $item_constraints = $this->itemDefinition->getConstraint('ComplexData') ?: [];
  470. foreach ($this->propertyConstraints as $name => $constraints) {
  471. if (isset($item_constraints[$name])) {
  472. $item_constraints[$name] = $constraints + $item_constraints[$name];
  473. }
  474. else {
  475. $item_constraints[$name] = $constraints;
  476. }
  477. $this->itemDefinition->addConstraint('ComplexData', $item_constraints);
  478. }
  479. }
  480. return $this->itemDefinition;
  481. }
  482. /**
  483. * {@inheritdoc}
  484. */
  485. public function getConfig($bundle) {
  486. return $this;
  487. }
  488. /**
  489. * {@inheritdoc}
  490. */
  491. public function setConstraints(array $constraints) {
  492. $this->constraints = $constraints;
  493. return $this;
  494. }
  495. /**
  496. * {@inheritdoc}
  497. */
  498. public function addConstraint($constraint_name, $options = NULL) {
  499. $this->constraints[$constraint_name] = $options;
  500. return $this;
  501. }
  502. /**
  503. * {@inheritdoc}
  504. */
  505. public function setPropertyConstraints($name, array $constraints) {
  506. $this->propertyConstraints[$name] = $constraints;
  507. // Reset the field item definition so the next time it is instantiated it
  508. // will receive the new constraints.
  509. $this->itemDefinition = NULL;
  510. return $this;
  511. }
  512. /**
  513. * {@inheritdoc}
  514. */
  515. public function addPropertyConstraints($name, array $constraints) {
  516. foreach ($constraints as $constraint_name => $options) {
  517. $this->propertyConstraints[$name][$constraint_name] = $options;
  518. }
  519. // Reset the field item definition so the next time it is instantiated it
  520. // will receive the new constraints.
  521. $this->itemDefinition = NULL;
  522. return $this;
  523. }
  524. /**
  525. * {@inheritdoc}
  526. */
  527. public function isInternal() {
  528. // Respect the definition, otherwise default to TRUE for computed fields.
  529. if (isset($this->definition['internal'])) {
  530. return $this->definition['internal'];
  531. }
  532. return $this->isComputed();
  533. }
  534. }