vendor/sonata-project/admin-bundle/src/Admin/BaseFieldDescription.php line 296

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4.  * This file is part of the Sonata Project package.
  5.  *
  6.  * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
  7.  *
  8.  * For the full copyright and license information, please view the LICENSE
  9.  * file that was distributed with this source code.
  10.  */
  11. namespace Sonata\AdminBundle\Admin;
  12. use Doctrine\Common\Inflector\Inflector;
  13. use Sonata\AdminBundle\Exception\NoValueException;
  14. /**
  15.  * A FieldDescription hold the information about a field. A typical
  16.  * admin instance contains different collections of fields.
  17.  *
  18.  * - form: used by the form
  19.  * - list: used by the list
  20.  * - filter: used by the list filter
  21.  *
  22.  * Some options are global across the different contexts, other are
  23.  * context specifics.
  24.  *
  25.  * Global options :
  26.  *   - type (m): define the field type (use to tweak the form or the list)
  27.  *   - template (o) : the template used to render the field
  28.  *   - name (o) : the name used (label in the form, title in the list)
  29.  *   - link_parameters (o) : add link parameter to the related Admin class when
  30.  *                           the Admin.generateUrl is called
  31.  *   - code : the method name to retrieve the related value
  32.  *   - associated_tostring : (deprecated, use associated_property option)
  33.  *                           the method to retrieve the "string" representation
  34.  *                           of the collection element.
  35.  *   - associated_property : property path to retrieve the "string" representation
  36.  *                           of the collection element.
  37.  *
  38.  * Form Field options :
  39.  *   - field_type (o): the widget class to use to render the field
  40.  *   - field_options (o): the options to give to the widget
  41.  *   - edit (o) : list|inline|standard (only used for associated admin)
  42.  *      - list : open a popup where the user can search, filter and click on one field
  43.  *               to select one item
  44.  *      - inline : the associated form admin is embedded into the current form
  45.  *      - standard : the associated admin is created through a popup
  46.  *
  47.  * List Field options :
  48.  *   - identifier (o): if set to true a link appear on to edit the element
  49.  *
  50.  * Filter Field options :
  51.  *   - options (o): options given to the Filter object
  52.  *   - field_type (o): the widget class to use to render the field
  53.  *   - field_options (o): the options to give to the widget
  54.  *
  55.  * @author Thomas Rabaix <thomas.rabaix@sonata-project.org>
  56.  */
  57. abstract class BaseFieldDescription implements FieldDescriptionInterface
  58. {
  59.     /**
  60.      * @var string the field name
  61.      */
  62.     protected $name;
  63.     /**
  64.      * @var string|int the type
  65.      */
  66.     protected $type;
  67.     /**
  68.      * @var string|int the original mapping type
  69.      */
  70.     protected $mappingType;
  71.     /**
  72.      * @var string the field name (of the form)
  73.      */
  74.     protected $fieldName;
  75.     /**
  76.      * @var array the ORM association mapping
  77.      */
  78.     protected $associationMapping;
  79.     /**
  80.      * @var array the ORM field information
  81.      */
  82.     protected $fieldMapping;
  83.     /**
  84.      * @var array the ORM parent mapping association
  85.      */
  86.     protected $parentAssociationMappings;
  87.     /**
  88.      * @var string the template name
  89.      */
  90.     protected $template;
  91.     /**
  92.      * @var array the option collection
  93.      */
  94.     protected $options = [];
  95.     /**
  96.      * @var AdminInterface|null the parent Admin instance
  97.      */
  98.     protected $parent;
  99.     /**
  100.      * @var AdminInterface the related admin instance
  101.      */
  102.     protected $admin;
  103.     /**
  104.      * @var AdminInterface the associated admin class if the object is associated to another entity
  105.      */
  106.     protected $associationAdmin;
  107.     /**
  108.      * @var string the help message to display
  109.      */
  110.     protected $help;
  111.     /**
  112.      * @var array[] cached object field getters
  113.      */
  114.     private static $fieldGetters = [];
  115.     public function setFieldName($fieldName)
  116.     {
  117.         $this->fieldName $fieldName;
  118.     }
  119.     public function getFieldName()
  120.     {
  121.         return $this->fieldName;
  122.     }
  123.     public function setName($name)
  124.     {
  125.         $this->name $name;
  126.         if (!$this->getFieldName()) {
  127.             $this->setFieldName(substr(strrchr('.'.$name'.'), 1));
  128.         }
  129.     }
  130.     public function getName()
  131.     {
  132.         return $this->name;
  133.     }
  134.     public function getOption($name$default null)
  135.     {
  136.         return isset($this->options[$name]) ? $this->options[$name] : $default;
  137.     }
  138.     public function setOption($name$value)
  139.     {
  140.         $this->options[$name] = $value;
  141.     }
  142.     public function setOptions(array $options)
  143.     {
  144.         // set the type if provided
  145.         if (isset($options['type'])) {
  146.             $this->setType($options['type']);
  147.             unset($options['type']);
  148.         }
  149.         // remove property value
  150.         if (isset($options['template'])) {
  151.             $this->setTemplate($options['template']);
  152.             unset($options['template']);
  153.         }
  154.         // set help if provided
  155.         if (isset($options['help'])) {
  156.             $this->setHelp($options['help']);
  157.             unset($options['help']);
  158.         }
  159.         // set default placeholder
  160.         if (!isset($options['placeholder'])) {
  161.             $options['placeholder'] = 'short_object_description_placeholder';
  162.         }
  163.         if (!isset($options['link_parameters'])) {
  164.             $options['link_parameters'] = [];
  165.         }
  166.         $this->options $options;
  167.     }
  168.     public function getOptions()
  169.     {
  170.         return $this->options;
  171.     }
  172.     public function setTemplate($template)
  173.     {
  174.         $this->template $template;
  175.     }
  176.     public function getTemplate()
  177.     {
  178.         if (null !== $this->template && !\is_string($this->template) && 'sonata_deprecation_mute' !== (\func_get_args()[0] ?? null)) {
  179.             @trigger_error(sprintf(
  180.                 'Returning other type than string or null in method %s() is deprecated since sonata-project/admin-bundle 3.65. It will return only those types in version 4.0.',
  181.                 __METHOD__
  182.             ), E_USER_DEPRECATED);
  183.         }
  184.         return $this->template;
  185.     }
  186.     public function setType($type)
  187.     {
  188.         $this->type $type;
  189.     }
  190.     public function getType()
  191.     {
  192.         return $this->type;
  193.     }
  194.     public function setParent(AdminInterface $parent)
  195.     {
  196.         $this->parent $parent;
  197.     }
  198.     public function getParent()
  199.     {
  200.         return $this->parent;
  201.     }
  202.     public function getAssociationMapping()
  203.     {
  204.         return $this->associationMapping;
  205.     }
  206.     public function getFieldMapping()
  207.     {
  208.         return $this->fieldMapping;
  209.     }
  210.     public function getParentAssociationMappings()
  211.     {
  212.         return $this->parentAssociationMappings;
  213.     }
  214.     public function setAssociationAdmin(AdminInterface $associationAdmin)
  215.     {
  216.         $this->associationAdmin $associationAdmin;
  217.         $this->associationAdmin->setParentFieldDescription($this);
  218.     }
  219.     public function getAssociationAdmin()
  220.     {
  221.         return $this->associationAdmin;
  222.     }
  223.     public function hasAssociationAdmin()
  224.     {
  225.         return null !== $this->associationAdmin;
  226.     }
  227.     public function getFieldValue($object$fieldName)
  228.     {
  229.         if ($this->isVirtual() || null === $object) {
  230.             return null;
  231.         }
  232.         $getters = [];
  233.         $parameters = [];
  234.         // prefer method name given in the code option
  235.         if ($this->getOption('code')) {
  236.             $getters[] = $this->getOption('code');
  237.         }
  238.         // parameters for the method given in the code option
  239.         if ($this->getOption('parameters')) {
  240.             $parameters $this->getOption('parameters');
  241.         }
  242.         if (\is_string($fieldName) && '' !== $fieldName) {
  243.             if ($this->hasCachedFieldGetter($object$fieldName)) {
  244.                 return $this->callCachedGetter($object$fieldName$parameters);
  245.             }
  246.             $camelizedFieldName Inflector::classify($fieldName);
  247.             $getters[] = 'get'.$camelizedFieldName;
  248.             $getters[] = 'is'.$camelizedFieldName;
  249.             $getters[] = 'has'.$camelizedFieldName;
  250.         }
  251.         foreach ($getters as $getter) {
  252.             if (method_exists($object$getter) && \is_callable([$object$getter])) {
  253.                 $this->cacheFieldGetter($object$fieldName'getter'$getter);
  254.                 return $object->{$getter}(...$parameters);
  255.             }
  256.         }
  257.         if (method_exists($object'__call')) {
  258.             $this->cacheFieldGetter($object$fieldName'call');
  259.             return $object->{$fieldName}(...$parameters);
  260.         }
  261.         if (isset($object->{$fieldName})) {
  262.             $this->cacheFieldGetter($object$fieldName'var');
  263.             return $object->{$fieldName};
  264.         }
  265.         throw new NoValueException(sprintf('Unable to retrieve the value of `%s`'$this->getName()));
  266.     }
  267.     public function setAdmin(AdminInterface $admin)
  268.     {
  269.         $this->admin $admin;
  270.     }
  271.     public function getAdmin()
  272.     {
  273.         return $this->admin;
  274.     }
  275.     public function mergeOption($name, array $options = [])
  276.     {
  277.         if (!isset($this->options[$name])) {
  278.             $this->options[$name] = [];
  279.         }
  280.         if (!\is_array($this->options[$name])) {
  281.             throw new \RuntimeException(sprintf('The key `%s` does not point to an array value'$name));
  282.         }
  283.         $this->options[$name] = array_merge($this->options[$name], $options);
  284.     }
  285.     public function mergeOptions(array $options = [])
  286.     {
  287.         $this->setOptions(array_merge_recursive($this->options$options));
  288.     }
  289.     public function setMappingType($mappingType)
  290.     {
  291.         $this->mappingType $mappingType;
  292.     }
  293.     public function getMappingType()
  294.     {
  295.         return $this->mappingType;
  296.     }
  297.     /**
  298.      * Camelize a string.
  299.      *
  300.      * NEXT_MAJOR: remove this method.
  301.      *
  302.      * @static
  303.      *
  304.      * @param string $property
  305.      *
  306.      * @return string
  307.      *
  308.      * @deprecated since sonata-project/admin-bundle 3.1. Use \Doctrine\Common\Inflector\Inflector::classify() instead
  309.      */
  310.     public static function camelize($property)
  311.     {
  312.         @trigger_error(
  313.             sprintf(
  314.                 'The %s method is deprecated since 3.1 and will be removed in 4.0. '.
  315.                 'Use \Doctrine\Common\Inflector\Inflector::classify() instead.',
  316.                 __METHOD__
  317.             ),
  318.             E_USER_DEPRECATED
  319.         );
  320.         return Inflector::classify($property);
  321.     }
  322.     /**
  323.      * Defines the help message.
  324.      *
  325.      * @param string $help
  326.      */
  327.     public function setHelp($help)
  328.     {
  329.         $this->help $help;
  330.     }
  331.     public function getHelp()
  332.     {
  333.         return $this->help;
  334.     }
  335.     public function getLabel()
  336.     {
  337.         $label $this->getOption('label');
  338.         if (null !== $label && false !== $label && !\is_string($label) && 'sonata_deprecation_mute' !== (\func_get_args()[0] ?? null)) {
  339.             @trigger_error(sprintf(
  340.                 'Returning other type than string, false or null in method %s() is deprecated since sonata-project/admin-bundle 3.65. It will return only those types in version 4.0.',
  341.                 __METHOD__
  342.             ), E_USER_DEPRECATED);
  343.         }
  344.         return $label;
  345.     }
  346.     public function isSortable()
  347.     {
  348.         return false !== $this->getOption('sortable'false);
  349.     }
  350.     public function getSortFieldMapping()
  351.     {
  352.         return $this->getOption('sort_field_mapping');
  353.     }
  354.     public function getSortParentAssociationMapping()
  355.     {
  356.         return $this->getOption('sort_parent_association_mappings');
  357.     }
  358.     public function getTranslationDomain()
  359.     {
  360.         return $this->getOption('translation_domain') ?: $this->getAdmin()->getTranslationDomain();
  361.     }
  362.     /**
  363.      * Return true if field is virtual.
  364.      *
  365.      * @return bool
  366.      */
  367.     public function isVirtual()
  368.     {
  369.         return false !== $this->getOption('virtual_field'false);
  370.     }
  371.     private function getFieldGetterKey($object, ?string $fieldName): ?string
  372.     {
  373.         if (!\is_string($fieldName)) {
  374.             return null;
  375.         }
  376.         if (!\is_object($object)) {
  377.             return null;
  378.         }
  379.         $components = [\get_class($object), $fieldName];
  380.         $code $this->getOption('code');
  381.         if (\is_string($code) && '' !== $code) {
  382.             $components[] = $code;
  383.         }
  384.         return implode('-'$components);
  385.     }
  386.     private function hasCachedFieldGetter($objectstring $fieldName): bool
  387.     {
  388.         return isset(
  389.             self::$fieldGetters[$this->getFieldGetterKey($object$fieldName)]
  390.         );
  391.     }
  392.     private function callCachedGetter($objectstring $fieldName, array $parameters = [])
  393.     {
  394.         $getterKey $this->getFieldGetterKey($object$fieldName);
  395.         if ('getter' === self::$fieldGetters[$getterKey]['method']) {
  396.             return $object->{self::$fieldGetters[$getterKey]['getter']}(...$parameters);
  397.         } elseif ('call' === self::$fieldGetters[$getterKey]['method']) {
  398.             return $object->{$fieldName}(...$parameters);
  399.         }
  400.         return $object->{$fieldName};
  401.     }
  402.     private function cacheFieldGetter($object, ?string $fieldNamestring $method, ?string $getter null): void
  403.     {
  404.         $getterKey $this->getFieldGetterKey($object$fieldName);
  405.         if (null !== $getterKey) {
  406.             self::$fieldGetters[$getterKey] = [
  407.                 'method' => $method,
  408.             ];
  409.             if (null !== $getter) {
  410.                 self::$fieldGetters[$getterKey]['getter'] = $getter;
  411.             }
  412.         }
  413.     }
  414. }