Source of file CaseInsensitiveFieldAccessor.php
Size: 6,123 Bytes - Last Modified: 2021-12-23T10:31:47+00:00
/var/www/docs.ssmods.com/process/src/src/Util/CaseInsensitiveFieldAccessor.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 | <?php namespace SilverStripe\GraphQL\Util; use SilverStripe\GraphQL\FieldAccessorInterface; use SilverStripe\ORM\DataObject; use SilverStripe\Core\ClassInfo; use SilverStripe\View\ViewableData; use InvalidArgumentException; /** * Infer original field name casing from case insensitive field comparison. * Useful counterpart to {@link \Convert::upperCamelToLowerCamel()}. * * SilverStripe is using a mix of case sensitive and case insensitive checks, * due to the nature of PHP (case sensitive for properties and array keys, * case insensitive for methods). * * Caution: Assumes fields have been whitelisted through GraphQL type definitions already. * Does not perform any canView() checks or further validation. * * @see http://www.php.net/manual/en/functions.user-defined.php * @see http://php.net/manual/en/function.array-change-key-case.php */ class CaseInsensitiveFieldAccessor implements FieldAccessorInterface { const HAS_METHOD = 'HAS_METHOD'; const HAS_FIELD = 'HAS_FIELD'; const HAS_SETTER = 'HAS_SETTER'; const DATAOBJECT = 'DATAOBJECT'; /** * @param ViewableData $object The parent resolved object * @param string $fieldName Name of the field/getter/method * @param array $opts Map of which lookups to use (class constants to booleans). * Example: [ViewableDataCaseInsensitiveFieldMapper::HAS_METHOD => true] * @param bool $asObject If true, return the DBField instance instead of the scalar value. * @return mixed */ public function getValue(ViewableData $object, $fieldName, $opts = [], $asObject = false) { $opts = $opts ?: []; $opts = array_merge([ self::HAS_METHOD => true, self::HAS_FIELD => true, self::HAS_SETTER => false, self::DATAOBJECT => true, ], $opts); $objectFieldName = $this->getObjectFieldName($object, $fieldName, $opts); if (!$objectFieldName) { throw new InvalidArgumentException(sprintf( 'Field name or method "%s" does not exist on %s', $fieldName, (string)$object )); } // Correct case for methods (e.g. canView) if ($object->hasMethod($objectFieldName)) { return $asObject ? $object->obj($objectFieldName) : $object->{$objectFieldName}(); } // Correct case (and getters) if ($object->hasField($objectFieldName)) { return $asObject ? $object->obj($objectFieldName) : $object->{$objectFieldName}; } return null; } /** * @param ViewableData $object The parent resolved object * @param string $fieldName Name of the field/getter/method * @param mixed $value * @param array $opts Map of which lookups to use (class constants to booleans). * Example: [ViewableDataCaseInsensitiveFieldMapper::HAS_METHOD => true] * @return mixed */ public function setValue(ViewableData $object, $fieldName, $value, $opts = []) { $opts = $opts ?: []; $opts = array_merge([ self::HAS_METHOD => true, self::HAS_FIELD => true, self::HAS_SETTER => true, self::DATAOBJECT => true, ], $opts); $objectFieldName = $this->getObjectFieldName($object, $fieldName, $opts); if (!$objectFieldName) { throw new InvalidArgumentException(sprintf( 'Field name "%s" does not exist on %s', $fieldName, (string)$object )); } if ($object->hasMethod($objectFieldName)) { // Correct case for methods (e.g. canView) $object->{$objectFieldName}($value); } elseif ($object->hasField($objectFieldName)) { // Correct case (and getters) $object->{$objectFieldName} = $value; } elseif ($object instanceof DataObject) { // Infer casing $object->setField($objectFieldName, $value); } return null; } /** * @param ViewableData $object The object to resolve a name on * @param string $fieldName Name in different casing * @param array $opts Map of which lookups to use (class constants to booleans). * Example: [ViewableDataCaseInsensitiveFieldMapper::HAS_METHOD => true] * @return null|string Name in actual casing on $object */ public function getObjectFieldName(ViewableData $object, $fieldName, $opts = []) { $opts = $opts ?: []; $opts = array_merge([ self::HAS_METHOD => true, self::HAS_FIELD => true, self::HAS_SETTER => true, self::DATAOBJECT => true, ], $opts); $optFn = function ($type) use (&$opts) { return (in_array($type, $opts) && $opts[$type] === true); }; // Correct case (and getters) if ($optFn(self::HAS_FIELD) && $object->hasField($fieldName)) { return $fieldName; } // Infer casing from DataObject fields if ($optFn(self::DATAOBJECT) && $object instanceof DataObject) { $parents = ClassInfo::ancestry($object, true); foreach ($parents as $parent) { $fields = DataObject::getSchema()->databaseFields($parent); foreach ($fields as $objectFieldName => $fieldClass) { if (strcasecmp($objectFieldName, $fieldName) === 0) { return $objectFieldName; } } } } // Setters // TODO Support for Object::$extra_methods (case sensitive array key check) $setterName = "set" . ucfirst($fieldName); if ($optFn(self::HAS_SETTER) && $object->hasMethod($setterName)) { return $setterName; } // Correct case for methods (e.g. canView) - method_exists() is case insensitive if ($optFn(self::HAS_METHOD) && $object->hasMethod($fieldName)) { return $fieldName; } return null; } } |