From 60f4760af6b83f2cc2dae06b3e5097d5e1e6be31 Mon Sep 17 00:00:00 2001 From: Kore Nordmann Date: Fri, 7 Jul 2017 13:44:34 +0200 Subject: [PATCH 1/9] Prototyped PHP part of a custom field type --- .../Controller/DefaultController.php | 13 ++ .../DependencyInjection/Configuration.php | 29 +++++ .../KoreRatingFieldTypeExtension.php | 28 +++++ .../KoreRatingFieldTypeBundle.php | 9 ++ .../Resources/config/routing.xml | 10 ++ .../Resources/config/services.xml | 20 ++++ .../Resources/views/Default/index.html.twig | 1 + .../Storage/FieldType/Type.php | 113 ++++++++++++++++++ .../Storage/FieldType/Value.php | 15 +++ .../Storage/Legacy/Converter.php | 39 ++++++ 10 files changed, 277 insertions(+) create mode 100644 src/Kore/RatingFieldTypeBundle/Controller/DefaultController.php create mode 100644 src/Kore/RatingFieldTypeBundle/DependencyInjection/Configuration.php create mode 100644 src/Kore/RatingFieldTypeBundle/DependencyInjection/KoreRatingFieldTypeExtension.php create mode 100644 src/Kore/RatingFieldTypeBundle/KoreRatingFieldTypeBundle.php create mode 100644 src/Kore/RatingFieldTypeBundle/Resources/config/routing.xml create mode 100644 src/Kore/RatingFieldTypeBundle/Resources/config/services.xml create mode 100644 src/Kore/RatingFieldTypeBundle/Resources/views/Default/index.html.twig create mode 100644 src/Kore/RatingFieldTypeBundle/Storage/FieldType/Type.php create mode 100644 src/Kore/RatingFieldTypeBundle/Storage/FieldType/Value.php create mode 100644 src/Kore/RatingFieldTypeBundle/Storage/Legacy/Converter.php diff --git a/src/Kore/RatingFieldTypeBundle/Controller/DefaultController.php b/src/Kore/RatingFieldTypeBundle/Controller/DefaultController.php new file mode 100644 index 0000000000..9f8cd55b50 --- /dev/null +++ b/src/Kore/RatingFieldTypeBundle/Controller/DefaultController.php @@ -0,0 +1,13 @@ +render('KoreRatingFieldTypeBundle:Default:index.html.twig'); + } +} diff --git a/src/Kore/RatingFieldTypeBundle/DependencyInjection/Configuration.php b/src/Kore/RatingFieldTypeBundle/DependencyInjection/Configuration.php new file mode 100644 index 0000000000..c1ef4206ed --- /dev/null +++ b/src/Kore/RatingFieldTypeBundle/DependencyInjection/Configuration.php @@ -0,0 +1,29 @@ +root('kore_rating_field_type'); + + // Here you should define the parameters that are allowed to + // configure your bundle. See the documentation linked above for + // more information on that topic. + + return $treeBuilder; + } +} diff --git a/src/Kore/RatingFieldTypeBundle/DependencyInjection/KoreRatingFieldTypeExtension.php b/src/Kore/RatingFieldTypeBundle/DependencyInjection/KoreRatingFieldTypeExtension.php new file mode 100644 index 0000000000..6010ab639d --- /dev/null +++ b/src/Kore/RatingFieldTypeBundle/DependencyInjection/KoreRatingFieldTypeExtension.php @@ -0,0 +1,28 @@ +processConfiguration($configuration, $configs); + + $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('services.xml'); + } +} diff --git a/src/Kore/RatingFieldTypeBundle/KoreRatingFieldTypeBundle.php b/src/Kore/RatingFieldTypeBundle/KoreRatingFieldTypeBundle.php new file mode 100644 index 0000000000..2ae8c1a8b3 --- /dev/null +++ b/src/Kore/RatingFieldTypeBundle/KoreRatingFieldTypeBundle.php @@ -0,0 +1,9 @@ + + + + + + KoreRatingFieldTypeBundle:Default:index + + diff --git a/src/Kore/RatingFieldTypeBundle/Resources/config/services.xml b/src/Kore/RatingFieldTypeBundle/Resources/config/services.xml new file mode 100644 index 0000000000..bf7b563f77 --- /dev/null +++ b/src/Kore/RatingFieldTypeBundle/Resources/config/services.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + diff --git a/src/Kore/RatingFieldTypeBundle/Resources/views/Default/index.html.twig b/src/Kore/RatingFieldTypeBundle/Resources/views/Default/index.html.twig new file mode 100644 index 0000000000..980a0d5f19 --- /dev/null +++ b/src/Kore/RatingFieldTypeBundle/Resources/views/Default/index.html.twig @@ -0,0 +1 @@ +Hello World! diff --git a/src/Kore/RatingFieldTypeBundle/Storage/FieldType/Type.php b/src/Kore/RatingFieldTypeBundle/Storage/FieldType/Type.php new file mode 100644 index 0000000000..2c21baf93c --- /dev/null +++ b/src/Kore/RatingFieldTypeBundle/Storage/FieldType/Type.php @@ -0,0 +1,113 @@ + $inputValue]); + } + + return $inputValue; + } + + protected function checkValueStructure(CoreValue $value) + { + // @DISPACTH to Value? + if (!$value->rating) { + throw new eZ\Publish\Core\Base\Exceptions\InvalidArgumentType( + '$value->rating', + 'number', + $value->rating + ); + } + } + + public function getEmptyValue() + { + return new Value; + } + + public function validateValidatorConfiguration($validatorConfiguration = []) + { + // @EXTENSION-POINT + return []; + } + + public function validate(FieldDefinition $fieldDefinition, SPIValue $fieldValue) + { + // @EXTENSION-POINT + return []; + } + + public function getName(SPIValue $value) + { + // @DISPACTH to Value? + return (string) $value; + } + + protected function getSortInfo(CoreValue $value) + { + // @DISPACTH to Value? + return $this->getName($value); + } + + public function fromHash($hash) + { + // @DISPACTH to Value? + if ($hash === null) { + return $this->getEmptyValue(); + } + + return new Value($hash); + } + + public function toHash(SPIValue $value) + { + // @DISPACTH to Value? + return get_object_vars($value); + } + + /** + * @param \EzSystems\TweetFieldTypeBundle\eZ\Publish\FieldType\Tweet\Value $value + * @return \eZ\Publish\SPI\Persistence\Content\FieldValue + */ + public function toPersistenceValue(SPIValue $value): FieldValue + { + // @EXTENSION-POINT: Write to external storage + return new FieldValue( + array( + "data" => $this->toHash($value), + "sortKey" => $this->getSortInfo($value), + ) + ); + } + + /** + * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $fieldValue + * @return \EzSystems\TweetFieldTypeBundle\eZ\Publish\FieldType\Tweet\Value + */ + public function fromPersistenceValue(FieldValue $fieldValue) + { + // @EXTENSION-POINT: Read from external storage + if ($fieldValue->data === null) { + return $this->getEmptyValue(); + } + + return new Value($fieldValue->data); + } +} diff --git a/src/Kore/RatingFieldTypeBundle/Storage/FieldType/Value.php b/src/Kore/RatingFieldTypeBundle/Storage/FieldType/Value.php new file mode 100644 index 0000000000..23bbc6bf35 --- /dev/null +++ b/src/Kore/RatingFieldTypeBundle/Storage/FieldType/Value.php @@ -0,0 +1,15 @@ +rating; + } +} diff --git a/src/Kore/RatingFieldTypeBundle/Storage/Legacy/Converter.php b/src/Kore/RatingFieldTypeBundle/Storage/Legacy/Converter.php new file mode 100644 index 0000000000..95b519e763 --- /dev/null +++ b/src/Kore/RatingFieldTypeBundle/Storage/Legacy/Converter.php @@ -0,0 +1,39 @@ +dataText = $value->rating; + $storageFieldValue->sortKeyString = $value->sortKey; + } + + public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) + { + $fieldValue->rating = $value->dataText; + $fieldValue->sortKey = $value->sortKeyString; + } + + public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef) + { + + } + + public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) + { + + } + + public function getIndexColumn() + { + return 'sort_key_string'; + } +} From f26ef30cf0334c2c7f7a33217efca3d00c9027b0 Mon Sep 17 00:00:00 2001 From: Kore Nordmann Date: Mon, 10 Jul 2017 08:37:48 +0200 Subject: [PATCH 2/9] Fixed converter definition --- src/Kore/RatingFieldTypeBundle/Storage/Legacy/Converter.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Kore/RatingFieldTypeBundle/Storage/Legacy/Converter.php b/src/Kore/RatingFieldTypeBundle/Storage/Legacy/Converter.php index 95b519e763..20bd7da91d 100644 --- a/src/Kore/RatingFieldTypeBundle/Storage/Legacy/Converter.php +++ b/src/Kore/RatingFieldTypeBundle/Storage/Legacy/Converter.php @@ -12,13 +12,13 @@ class Converter implements ConverterInterface { public function toStorageValue(FieldValue $value, StorageFieldValue $storageFieldValue) { - $storageFieldValue->dataText = $value->rating; + $storageFieldValue->dataText = json_encode($value->data); $storageFieldValue->sortKeyString = $value->sortKey; } public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) { - $fieldValue->rating = $value->dataText; + $fieldValue->data = json_decode($value->dataText, true) ?: []; $fieldValue->sortKey = $value->sortKeyString; } From 69de1881467c4504c2b28e94f854a7d51c83a190 Mon Sep 17 00:00:00 2001 From: Kore Nordmann Date: Mon, 10 Jul 2017 08:38:10 +0200 Subject: [PATCH 3/9] Stubbed and registered templates --- .../KoreRatingFieldTypeExtension.php | 10 +++++++++- .../Resources/config/ezpublish.yml | 8 ++++++++ .../Resources/views/field_edit.html.twig | 1 + .../Resources/views/field_view.html.twig | 6 ++++++ .../Resources/views/fielddefinition_settings.html.twig | 1 + 5 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 src/Kore/RatingFieldTypeBundle/Resources/config/ezpublish.yml create mode 100644 src/Kore/RatingFieldTypeBundle/Resources/views/field_edit.html.twig create mode 100644 src/Kore/RatingFieldTypeBundle/Resources/views/field_view.html.twig create mode 100644 src/Kore/RatingFieldTypeBundle/Resources/views/fielddefinition_settings.html.twig diff --git a/src/Kore/RatingFieldTypeBundle/DependencyInjection/KoreRatingFieldTypeExtension.php b/src/Kore/RatingFieldTypeBundle/DependencyInjection/KoreRatingFieldTypeExtension.php index 6010ab639d..a6c7be9f9c 100644 --- a/src/Kore/RatingFieldTypeBundle/DependencyInjection/KoreRatingFieldTypeExtension.php +++ b/src/Kore/RatingFieldTypeBundle/DependencyInjection/KoreRatingFieldTypeExtension.php @@ -5,15 +5,23 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Config\FileLocator; use Symfony\Component\HttpKernel\DependencyInjection\Extension; +use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; use Symfony\Component\DependencyInjection\Loader; +use Symfony\Component\Yaml\Yaml; /** * This is the class that loads and manages your bundle configuration. * * @link http://symfony.com/doc/current/cookbook/bundles/extension.html */ -class KoreRatingFieldTypeExtension extends Extension +class KoreRatingFieldTypeExtension extends Extension implements PrependExtensionInterface { + public function prepend(ContainerBuilder $container) + { + $config = Yaml::parse(file_get_contents(__DIR__ . '/../Resources/config/ezpublish.yml')); + $container->prependExtensionConfig('ezpublish', $config); + } + /** * {@inheritdoc} */ diff --git a/src/Kore/RatingFieldTypeBundle/Resources/config/ezpublish.yml b/src/Kore/RatingFieldTypeBundle/Resources/config/ezpublish.yml new file mode 100644 index 0000000000..ab2522b584 --- /dev/null +++ b/src/Kore/RatingFieldTypeBundle/Resources/config/ezpublish.yml @@ -0,0 +1,8 @@ +system: + default: + fielddefinition_settings_templates: + - { template: "KoreRatingFieldTypeBundle::fielddefinition_settings.html.twig"} + field_edit_templates: + - { template: "KoreRatingFieldTypeBundle::field_edit.html.twig"} + field_templates: + - { template: "KoreRatingFieldTypeBundle::field_view.html.twig"} diff --git a/src/Kore/RatingFieldTypeBundle/Resources/views/field_edit.html.twig b/src/Kore/RatingFieldTypeBundle/Resources/views/field_edit.html.twig new file mode 100644 index 0000000000..2b8ddc6dbc --- /dev/null +++ b/src/Kore/RatingFieldTypeBundle/Resources/views/field_edit.html.twig @@ -0,0 +1 @@ +Edit? diff --git a/src/Kore/RatingFieldTypeBundle/Resources/views/field_view.html.twig b/src/Kore/RatingFieldTypeBundle/Resources/views/field_view.html.twig new file mode 100644 index 0000000000..5c52f4f05c --- /dev/null +++ b/src/Kore/RatingFieldTypeBundle/Resources/views/field_view.html.twig @@ -0,0 +1,6 @@ +{% block kore-rating_field %} + {% set field_value %} + {{ field.value.rating }} + {% endset %} + {{ block( 'simple_block_field' ) }} +{% endblock %} diff --git a/src/Kore/RatingFieldTypeBundle/Resources/views/fielddefinition_settings.html.twig b/src/Kore/RatingFieldTypeBundle/Resources/views/fielddefinition_settings.html.twig new file mode 100644 index 0000000000..86ab76a5c0 --- /dev/null +++ b/src/Kore/RatingFieldTypeBundle/Resources/views/fielddefinition_settings.html.twig @@ -0,0 +1 @@ +{% block kore-rating_settings %}{% endblock %} From cb5228d0b41ef7e9fd48d55837078375a9699f86 Mon Sep 17 00:00:00 2001 From: Kore Nordmann Date: Mon, 10 Jul 2017 09:41:31 +0200 Subject: [PATCH 4/9] Changed field type identifier to work with twig template blocks --- src/Kore/RatingFieldTypeBundle/Resources/config/services.xml | 4 ++-- .../Resources/views/field_view.html.twig | 2 +- .../Resources/views/fielddefinition_settings.html.twig | 4 +++- src/Kore/RatingFieldTypeBundle/Storage/FieldType/Type.php | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Kore/RatingFieldTypeBundle/Resources/config/services.xml b/src/Kore/RatingFieldTypeBundle/Resources/config/services.xml index bf7b563f77..72f2494a2b 100644 --- a/src/Kore/RatingFieldTypeBundle/Resources/config/services.xml +++ b/src/Kore/RatingFieldTypeBundle/Resources/config/services.xml @@ -8,13 +8,13 @@ - + - + diff --git a/src/Kore/RatingFieldTypeBundle/Resources/views/field_view.html.twig b/src/Kore/RatingFieldTypeBundle/Resources/views/field_view.html.twig index 5c52f4f05c..84c5443671 100644 --- a/src/Kore/RatingFieldTypeBundle/Resources/views/field_view.html.twig +++ b/src/Kore/RatingFieldTypeBundle/Resources/views/field_view.html.twig @@ -1,4 +1,4 @@ -{% block kore-rating_field %} +{% block koreRating_field %} {% set field_value %} {{ field.value.rating }} {% endset %} diff --git a/src/Kore/RatingFieldTypeBundle/Resources/views/fielddefinition_settings.html.twig b/src/Kore/RatingFieldTypeBundle/Resources/views/fielddefinition_settings.html.twig index 86ab76a5c0..0ed0944762 100644 --- a/src/Kore/RatingFieldTypeBundle/Resources/views/fielddefinition_settings.html.twig +++ b/src/Kore/RatingFieldTypeBundle/Resources/views/fielddefinition_settings.html.twig @@ -1 +1,3 @@ -{% block kore-rating_settings %}{% endblock %} +{% block koreRating_settings %} + No settings +{% endblock %} diff --git a/src/Kore/RatingFieldTypeBundle/Storage/FieldType/Type.php b/src/Kore/RatingFieldTypeBundle/Storage/FieldType/Type.php index 2c21baf93c..2ba9079ebb 100644 --- a/src/Kore/RatingFieldTypeBundle/Storage/FieldType/Type.php +++ b/src/Kore/RatingFieldTypeBundle/Storage/FieldType/Type.php @@ -12,7 +12,7 @@ class Type extends FieldType { public function getFieldTypeIdentifier() { - return 'kore-rating'; + return 'koreRating'; } protected function createValueFromInput($inputValue) From 2690fae01c766b07941492d72219d05dbfb80d70 Mon Sep 17 00:00:00 2001 From: Kore Nordmann Date: Mon, 10 Jul 2017 09:59:58 +0200 Subject: [PATCH 5/9] Removed unused files --- .../Controller/DefaultController.php | 13 ------------- .../Resources/config/routing.xml | 10 ---------- .../Resources/views/Default/index.html.twig | 1 - 3 files changed, 24 deletions(-) delete mode 100644 src/Kore/RatingFieldTypeBundle/Controller/DefaultController.php delete mode 100644 src/Kore/RatingFieldTypeBundle/Resources/config/routing.xml delete mode 100644 src/Kore/RatingFieldTypeBundle/Resources/views/Default/index.html.twig diff --git a/src/Kore/RatingFieldTypeBundle/Controller/DefaultController.php b/src/Kore/RatingFieldTypeBundle/Controller/DefaultController.php deleted file mode 100644 index 9f8cd55b50..0000000000 --- a/src/Kore/RatingFieldTypeBundle/Controller/DefaultController.php +++ /dev/null @@ -1,13 +0,0 @@ -render('KoreRatingFieldTypeBundle:Default:index.html.twig'); - } -} diff --git a/src/Kore/RatingFieldTypeBundle/Resources/config/routing.xml b/src/Kore/RatingFieldTypeBundle/Resources/config/routing.xml deleted file mode 100644 index a603347514..0000000000 --- a/src/Kore/RatingFieldTypeBundle/Resources/config/routing.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - KoreRatingFieldTypeBundle:Default:index - - diff --git a/src/Kore/RatingFieldTypeBundle/Resources/views/Default/index.html.twig b/src/Kore/RatingFieldTypeBundle/Resources/views/Default/index.html.twig deleted file mode 100644 index 980a0d5f19..0000000000 --- a/src/Kore/RatingFieldTypeBundle/Resources/views/Default/index.html.twig +++ /dev/null @@ -1 +0,0 @@ -Hello World! From 98a7c9c5c928874392279df8d938016373a874c7 Mon Sep 17 00:00:00 2001 From: Kore Nordmann Date: Mon, 10 Jul 2017 11:25:53 +0200 Subject: [PATCH 6/9] Added docs and removed unnecessary method overrides --- .../Storage/FieldType/Type.php | 184 +++++++++++++----- .../Storage/Legacy/Converter.php | 6 +- 2 files changed, 141 insertions(+), 49 deletions(-) diff --git a/src/Kore/RatingFieldTypeBundle/Storage/FieldType/Type.php b/src/Kore/RatingFieldTypeBundle/Storage/FieldType/Type.php index 2ba9079ebb..cd8be21ca9 100644 --- a/src/Kore/RatingFieldTypeBundle/Storage/FieldType/Type.php +++ b/src/Kore/RatingFieldTypeBundle/Storage/FieldType/Type.php @@ -10,14 +10,53 @@ class Type extends FieldType { + /** + * Returns the field type identifier for this field type. + * + * This identifier should be globally unique and the implementer of a + * FieldType must take care for the uniqueness. It is therefore recommended + * to prefix the field-type identifier by a unique string that identifies + * the implementer. A good identifier could for example take your companies + * main domain name as a prefix in reverse order. + * + * @return string + */ public function getFieldTypeIdentifier() { + // @EXT: Necessary return 'koreRating'; } + /** + * Inspects given $inputValue and potentially converts it into a dedicated + * value object. + * + * If given $inputValue could not be converted or is already an instance of + * dedicate value object, the method should simply return it. + * + * This is an operation method for {@see acceptValue()}. + * + * Example implementation: + * + * protected function createValueFromInput( $inputValue ) + * { + * if ( is_array( $inputValue ) ) + * { + * $inputValue = \eZ\Publish\Core\FieldType\CookieJar\Value( $inputValue ); + * } + * + * return $inputValue; + * } + * + * + * @param mixed $inputValue + * + * @return mixed The potentially converted input value. + */ protected function createValueFromInput($inputValue) { - // @DISPACTH to Value? + // @EXT: Default possible depending on value class, at least for + // trivial values if (is_string($inputValue)) { $inputValue = new Value(['rating' => $inputValue]); } @@ -25,9 +64,33 @@ protected function createValueFromInput($inputValue) return $inputValue; } + /** + * Throws an exception if value structure is not of expected format. + * + * Note that this does not include validation after the rules + * from validators, but only plausibility checks for the general data + * format. + * + * This is an operation method for {@see acceptValue()}. + * + * Example implementation: + * + * protected function checkValueStructure( Value $value ) + * { + * if ( !is_array( $value->cookies ) ) + * { + * throw new InvalidArgumentException( "An array of assorted cookies was expected." ); + * } + * } + * + * + * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. + * + * @param \eZ\Publish\Core\FieldType\Value $value + */ protected function checkValueStructure(CoreValue $value) { - // @DISPACTH to Value? + // @EXT: Default possible depending on value if (!$value->rating) { throw new eZ\Publish\Core\Base\Exceptions\InvalidArgumentType( '$value->rating', @@ -37,77 +100,102 @@ protected function checkValueStructure(CoreValue $value) } } + /** + * Returns the empty value for this field type. + * + * This value will be used, if no value was provided for a field of this + * type and no default value was specified in the field definition. It is + * also used to determine that a user intentionally (or unintentionally) + * did not set a non-empty value. + * + * @return \eZ\Publish\SPI\FieldType\Value + */ public function getEmptyValue() { - return new Value; - } - - public function validateValidatorConfiguration($validatorConfiguration = []) - { - // @EXTENSION-POINT - return []; - } - - public function validate(FieldDefinition $fieldDefinition, SPIValue $fieldValue) - { - // @EXTENSION-POINT - return []; + // @EXT: Default possible when we know about the value class + return new Value(); } + /** + * Returns a human readable string representation from the given $value. + * + * It will be used to generate content name and url alias if current field + * is designated to be used in the content name/urlAlias pattern. + * + * The used $value can be assumed to be already accepted by {@link * + * acceptValue()}. + * + * @deprecated Since 6.3/5.4.7, use \eZ\Publish\SPI\FieldType\Nameable + * @param \eZ\Publish\SPI\FieldType\Value $value + * + * @return string + */ public function getName(SPIValue $value) { - // @DISPACTH to Value? return (string) $value; } + /** + * Returns information for FieldValue->$sortKey relevant to the field type. + * + * Return value is mixed. It should be something which is sensible for + * sorting. + * + * It is up to the persistence implementation to handle those values. + * Common string and integer values are safe. + * + * For the legacy storage it is up to the field converters to set this + * value in either sort_key_string or sort_key_int. + * + * @param \eZ\Publish\Core\FieldType\Value $value + * + * @return mixed + */ protected function getSortInfo(CoreValue $value) { - // @DISPACTH to Value? return $this->getName($value); } + /** + * Converts an $hash to the Value defined by the field type. + * + * This is the reverse operation to {@link toHash()}. At least the hash + * format generated by {@link toHash()} must be converted in reverse. + * Additional formats might be supported in the rare case that this is + * necessary. See the class description for more details on a hash format. + * + * @param mixed $hash + * + * @return \eZ\Publish\SPI\FieldType\Value + */ public function fromHash($hash) { - // @DISPACTH to Value? if ($hash === null) { return $this->getEmptyValue(); } + // The default constructor at least works for the top level objects. + // For more complex values a manual conversion is necessary. return new Value($hash); } - public function toHash(SPIValue $value) - { - // @DISPACTH to Value? - return get_object_vars($value); - } - - /** - * @param \EzSystems\TweetFieldTypeBundle\eZ\Publish\FieldType\Tweet\Value $value - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function toPersistenceValue(SPIValue $value): FieldValue - { - // @EXTENSION-POINT: Write to external storage - return new FieldValue( - array( - "data" => $this->toHash($value), - "sortKey" => $this->getSortInfo($value), - ) - ); - } - /** - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $fieldValue - * @return \EzSystems\TweetFieldTypeBundle\eZ\Publish\FieldType\Tweet\Value + * Converts the given $value into a plain hash format. + * + * Converts the given $value into a plain hash format, which can be used to + * transfer the value through plain text formats, e.g. XML, which do not + * support complex structures like objects. See the class level doc block + * for additional information. See the class description for more details + * on a hash format. + * + * @param \eZ\Publish\SPI\FieldType\Value $value + * + * @return mixed */ - public function fromPersistenceValue(FieldValue $fieldValue) + public function toHash(SPIValue $value) { - // @EXTENSION-POINT: Read from external storage - if ($fieldValue->data === null) { - return $this->getEmptyValue(); - } - - return new Value($fieldValue->data); + // Simplest way to ensure a deep structure is cloned and converted into + // scalars and has maps. + return json_decode(json_encode($value), true); } } diff --git a/src/Kore/RatingFieldTypeBundle/Storage/Legacy/Converter.php b/src/Kore/RatingFieldTypeBundle/Storage/Legacy/Converter.php index 20bd7da91d..33943d51e8 100644 --- a/src/Kore/RatingFieldTypeBundle/Storage/Legacy/Converter.php +++ b/src/Kore/RatingFieldTypeBundle/Storage/Legacy/Converter.php @@ -34,6 +34,10 @@ public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefin public function getIndexColumn() { - return 'sort_key_string'; + // How to decide between sort_key_(int|string) + // + // How do we get access to the currently used value class to reason + // about this? + return 'sort_key_int'; } } From ffc35af786c92754c0bee66d73745797408c8ea3 Mon Sep 17 00:00:00 2001 From: Kore Nordmann Date: Mon, 10 Jul 2017 11:37:26 +0200 Subject: [PATCH 7/9] Added another note --- src/Kore/RatingFieldTypeBundle/Storage/FieldType/Type.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Kore/RatingFieldTypeBundle/Storage/FieldType/Type.php b/src/Kore/RatingFieldTypeBundle/Storage/FieldType/Type.php index cd8be21ca9..a539abf1db 100644 --- a/src/Kore/RatingFieldTypeBundle/Storage/FieldType/Type.php +++ b/src/Kore/RatingFieldTypeBundle/Storage/FieldType/Type.php @@ -57,6 +57,9 @@ protected function createValueFromInput($inputValue) { // @EXT: Default possible depending on value class, at least for // trivial values + // + // There is probably a connection with the edit template, which is + // missing in the tutoorial. if (is_string($inputValue)) { $inputValue = new Value(['rating' => $inputValue]); } From 6e1e190e8e6b4805757d1eb66a00900480885d6f Mon Sep 17 00:00:00 2001 From: Kore Nordmann Date: Tue, 11 Jul 2017 10:48:01 +0200 Subject: [PATCH 8/9] Implemented test for field type using base test class --- .../Storage/FieldType/Type.php | 18 +- .../Storage/Legacy/Converter.php | 5 +- .../RatingFieldTypeBundle/Tests/TypeTest.php | 248 ++++++++++++++++++ 3 files changed, 261 insertions(+), 10 deletions(-) create mode 100644 src/Kore/RatingFieldTypeBundle/Tests/TypeTest.php diff --git a/src/Kore/RatingFieldTypeBundle/Storage/FieldType/Type.php b/src/Kore/RatingFieldTypeBundle/Storage/FieldType/Type.php index a539abf1db..b710d8243a 100644 --- a/src/Kore/RatingFieldTypeBundle/Storage/FieldType/Type.php +++ b/src/Kore/RatingFieldTypeBundle/Storage/FieldType/Type.php @@ -7,6 +7,7 @@ use eZ\Publish\SPI\FieldType\Value as SPIValue; use eZ\Publish\Core\FieldType\Value as CoreValue; use eZ\Publish\SPI\Persistence\Content\FieldValue; +use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; class Type extends FieldType { @@ -60,11 +61,15 @@ protected function createValueFromInput($inputValue) // // There is probably a connection with the edit template, which is // missing in the tutoorial. - if (is_string($inputValue)) { - $inputValue = new Value(['rating' => $inputValue]); + if ($inputValue instanceof Value) { + return $inputValue; } - return $inputValue; + if (!is_numeric($inputValue)) { + return new Value(['rating' => false]); + } + + return new Value(['rating' => (int) $inputValue]); } /** @@ -95,10 +100,9 @@ protected function checkValueStructure(CoreValue $value) { // @EXT: Default possible depending on value if (!$value->rating) { - throw new eZ\Publish\Core\Base\Exceptions\InvalidArgumentType( - '$value->rating', - 'number', - $value->rating + throw new InvalidArgumentException( + '$value->rating', + 'Expected rating to be a number' ); } } diff --git a/src/Kore/RatingFieldTypeBundle/Storage/Legacy/Converter.php b/src/Kore/RatingFieldTypeBundle/Storage/Legacy/Converter.php index 33943d51e8..98d6afd5ef 100644 --- a/src/Kore/RatingFieldTypeBundle/Storage/Legacy/Converter.php +++ b/src/Kore/RatingFieldTypeBundle/Storage/Legacy/Converter.php @@ -14,22 +14,21 @@ public function toStorageValue(FieldValue $value, StorageFieldValue $storageFiel { $storageFieldValue->dataText = json_encode($value->data); $storageFieldValue->sortKeyString = $value->sortKey; + $storageFieldValue->sortKeyInt = $value->sortKey; } public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) { $fieldValue->data = json_decode($value->dataText, true) ?: []; - $fieldValue->sortKey = $value->sortKeyString; + $fieldValue->sortKey = $value->sortKeyInt ?? $value->sortKeyString; } public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef) { - } public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) { - } public function getIndexColumn() diff --git a/src/Kore/RatingFieldTypeBundle/Tests/TypeTest.php b/src/Kore/RatingFieldTypeBundle/Tests/TypeTest.php new file mode 100644 index 0000000000..1ad84bc66d --- /dev/null +++ b/src/Kore/RatingFieldTypeBundle/Tests/TypeTest.php @@ -0,0 +1,248 @@ + + * return array( + * array( + * new \stdClass(), + * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', + * ), + * array( + * array(), + * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', + * ), + * // ... + * ); + * + * + * @return array + */ + public function provideInvalidInputForAcceptValue() + { + return array( + array( + [], + 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', + ), + ); + } + + /** + * Data provider for valid input to acceptValue(). + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to acceptValue(), 2. The expected return value from acceptValue(). + * For example: + * + * + * return array( + * array( + * null, + * null + * ), + * array( + * __FILE__, + * new BinaryFileValue( array( + * 'path' => __FILE__, + * 'fileName' => basename( __FILE__ ), + * 'fileSize' => filesize( __FILE__ ), + * 'downloadCount' => 0, + * 'mimeType' => 'text/plain', + * ) ) + * ), + * // ... + * ); + * + * + * @return array + */ + public function provideValidInputForAcceptValue() + { + return array( + array( + null, + new Value(), + ), + array( + 1, + new Value(['rating' => 1]), + ), + array( + '2', + new Value(['rating' => 2]), + ), + ); + } + + /** + * Provide input for the toHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to toHash(), 2. The expected return value from toHash(). + * For example: + * + * + * return array( + * array( + * null, + * null + * ), + * array( + * new BinaryFileValue( array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) ), + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) + * ), + * // ... + * ); + * + * + * @return array + */ + public function provideInputForToHash() + { + return array( + array( + new Value(), + ['rating' => 3], + ), + array( + new Value(['rating' => 5]), + ['rating' => 5], + ), + ); + } + + /** + * Provide input to fromHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to fromHash(), 2. The expected return value from fromHash(). + * For example: + * + * + * return array( + * array( + * null, + * null + * ), + * array( + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ), + * new BinaryFileValue( array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) ) + * ), + * // ... + * ); + * + * + * @return array + */ + public function provideInputForFromHash() + { + return array( + array( + array(), + new Value(array()), + ), + array( + ['rating' => 5], + new Value(['rating' => 5]), + ), + ); + } + + protected function provideFieldTypeIdentifier() + { + return 'koreRating'; + } + + public function provideDataForGetName() + { + return array( + array($this->getEmptyValueExpectation(), '3'), + array(new Value(['rating' => 5]), '5'), + ); + } +} From 7e10d850b9752ef2dd2cb4c4abec613cc911c6b7 Mon Sep 17 00:00:00 2001 From: Kore Nordmann Date: Wed, 12 Jul 2017 09:21:59 +0200 Subject: [PATCH 9/9] Implemented external storage --- .../Storage/Legacy/Gateway.php | 94 +++++++++++++++ .../Storage/Legacy/Storage.php | 84 +++++++++++++ .../Tests/TypeIntegrationTest.php | 114 ++++++++++++++++++ 3 files changed, 292 insertions(+) create mode 100644 src/Kore/RatingFieldTypeBundle/Storage/Legacy/Gateway.php create mode 100644 src/Kore/RatingFieldTypeBundle/Storage/Legacy/Storage.php create mode 100644 src/Kore/RatingFieldTypeBundle/Tests/TypeIntegrationTest.php diff --git a/src/Kore/RatingFieldTypeBundle/Storage/Legacy/Gateway.php b/src/Kore/RatingFieldTypeBundle/Storage/Legacy/Gateway.php new file mode 100644 index 0000000000..a66abe207e --- /dev/null +++ b/src/Kore/RatingFieldTypeBundle/Storage/Legacy/Gateway.php @@ -0,0 +1,94 @@ + []]; + + /** + * @var \Doctrine\DBAL\Connection + */ + protected $connection; + + public function __construct(Connection $connection) + { + $this->connection = $connection; + } + + /** + * Stores the keyword list from $field->value->externalData. + * + * @param \eZ\Publish\SPI\Persistence\Content\Field + * @param int $contentTypeId + */ + public function storeFieldData(Field $field, $contentTypeId) + { + $this->virtualDatabase[self::RATING_TABLE][$field->id] = $field->value->externalData; + } + + /** + * Sets the list of assigned keywords into $field->value->externalData. + * + * @param \eZ\Publish\SPI\Persistence\Content\Field $field + */ + public function getFieldData(Field $field) + { + $field->value->externalData = ['rating' => 3]; + + if (isset($this->virtualDatabase[self::RATING_TABLE][$field->id])) { + $field->value->externalData = $this->virtualDatabase[self::RATING_TABLE][$field->id]; + } + } + + /** + * Retrieve the ContentType ID for the given $field. + * + * @param \eZ\Publish\SPI\Persistence\Content\Field $field + * + * @return int + */ + public function getContentTypeId(Field $field) + { + $query = $this->connection->createQueryBuilder(); + $query + ->select($this->connection->quoteIdentifier('contentclass_id')) + ->from($this->connection->quoteIdentifier('ezcontentclass_attribute')) + ->where( + $query->expr()->eq('id', ':fieldDefinitionId') + ) + ->setParameter(':fieldDefinitionId', $field->fieldDefinitionId); + + $statement = $query->execute(); + + $row = $statement->fetch(\PDO::FETCH_ASSOC); + + if ($row === false) { + throw new RuntimeException( + sprintf( + 'Content Type ID cannot be retrieved based on the field definition ID "%s"', + $field->fieldDefinitionId + ) + ); + } + + return intval($row['contentclass_id']); + } + + /** + * Deletes keyword data for the given $fieldId. + * + * @param int $fieldId + */ + public function deleteFieldData($fieldId) + { + unset($this->virtualDatabase[self::RATING_TABLE][$fieldId]); + } +} diff --git a/src/Kore/RatingFieldTypeBundle/Storage/Legacy/Storage.php b/src/Kore/RatingFieldTypeBundle/Storage/Legacy/Storage.php new file mode 100644 index 0000000000..d77b647279 --- /dev/null +++ b/src/Kore/RatingFieldTypeBundle/Storage/Legacy/Storage.php @@ -0,0 +1,84 @@ +gateway->getContentTypeId($field); + + return $this->gateway->storeFieldData($field, $contentTypeId); + } + + /** + * Populates $field value property based on the external data. + * $field->value is a {@link eZ\Publish\SPI\Persistence\Content\FieldValue} object. + * This value holds the data as a {@link eZ\Publish\Core\FieldType\Value} based object, + * according to the field type (e.g. for TextLine, it will be a {@link eZ\Publish\Core\FieldType\TextLine\Value} object). + * + * @param \eZ\Publish\SPI\Persistence\Content\Field $field + * @param array $context + */ + public function getFieldData(VersionInfo $versionInfo, Field $field, array $context) + { + // @todo: This should already retrieve the ContentType ID + return $this->gateway->getFieldData($field); + } + + /** + * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo + * @param array $fieldIds + * @param array $context + * + * @return bool + */ + public function deleteFieldData(VersionInfo $versionInfo, array $fieldIds, array $context) + { + // If current version being asked to be deleted is not published, then don't delete keywords + // if there is some other version which is published (as keyword table is not versioned) + if ($versionInfo->status !== VersionInfo::STATUS_PUBLISHED && + $versionInfo->contentInfo->isPublished + ) { + return false; + } + + foreach ($fieldIds as $fieldId) { + $this->gateway->deleteFieldData($fieldId); + } + + return true; + } + + /** + * Checks if field type has external data to deal with. + * + * @return bool + */ + public function hasFieldData() + { + return true; + } + + /** + * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo + * @param \eZ\Publish\SPI\Persistence\Content\Field $field + * @param array $context + * @return \eZ\Publish\SPI\Search\Field[]|null + */ + public function getIndexData(VersionInfo $versionInfo, Field $field, array $context) + { + return null; + } +} diff --git a/src/Kore/RatingFieldTypeBundle/Tests/TypeIntegrationTest.php b/src/Kore/RatingFieldTypeBundle/Tests/TypeIntegrationTest.php new file mode 100644 index 0000000000..3349cae90a --- /dev/null +++ b/src/Kore/RatingFieldTypeBundle/Tests/TypeIntegrationTest.php @@ -0,0 +1,114 @@ +getHandler( + 'koreRating', + $fieldType, + new Converter(), + new Storage( + new Gateway( + $this->getDatabaseHandler()->getConnection() + ) + ) + ); + } + + /** + * Returns the FieldTypeConstraints to be used to create a field definition + * of the FieldType under test. + * + * @return \eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints + */ + public function getTypeConstraints() + { + return new Content\FieldTypeConstraints(); + } + + /** + * Returns the field definition data expected after loading the newly + * created field definition with the FieldType under test. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function getFieldDefinitionData() + { + return array( + // The ezkeyword field type does not have any special field definition + // properties + array('fieldType', 'koreRating'), + array('fieldTypeConstraints', new Content\FieldTypeConstraints()), + ); + } + + /** + * Get initial field value. + * + * @return \eZ\Publish\SPI\Persistence\Content\FieldValue + */ + public function getInitialValue() + { + return new Content\FieldValue( + array( + 'data' => array(), + 'externalData' => array('rating' => 3), + 'sortKey' => '3', + ) + ); + } + + /** + * Get update field value. + * + * Use to update the field + * + * @return \eZ\Publish\SPI\Persistence\Content\FieldValue + */ + public function getUpdatedValue() + { + return new Content\FieldValue( + array( + 'data' => array(), + 'externalData' => array('rating' => 5), + 'sortKey' => '5', + ) + ); + } +}