- Issue created by @dieterholvoet
- Status changed to Needs review
over 1 year ago 2:15pm 9 January 2024 - 🇧🇪Belgium dieterholvoet Brussels
The MR has a working version now. Still testing, not yet using in a production environment.
- 🇧🇪Belgium dieterholvoet Brussels
In Symfony 6
Symfony\Component\Serializer\Normalizer\NormalizerInterface
got more function argument types, including onemixed
type. In order to support both Symfony 5 and 6, we'll have to add those types toDrupal\api_toolkit\Normalizer\CachedNormalizer
, which means that we'll have to bump the minimum PHP version to 8.0 if we want to add themixed
type. I think that's reasonable. - 🇧🇪Belgium dieterholvoet Brussels
We should recommend using a Permanent Cache Bin → backend for the normalizer cache, to make it more efficient. This needs to be added to settings.php:
$settings['cache']['bins']['api_toolkit_normalizer'] = 'cache.backend.permanent_database';
- 🇧🇪Belgium dieterholvoet Brussels
I added something new, an experiment: a way to do placeholdering with lazy builders, similar to how it's already possible in other places in Drupal using
#lazy_builder
. It can be used to add highly dynamic data to normalization results while keeping their cacheability. Here's an example in action:namespace Drupal\example_module\Normalizer; use Drupal\api_toolkit\Normalizer\Placeholder; use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Security\TrustedCallbackInterface; use Drupal\example_module\Service\ActivityCounts; use Drupal\example_module\Entity\Node\UserGroup; use Drupal\serialization\Normalizer\NormalizerBase; class UserGroupNormalizer extends NormalizerBase implements TrustedCallbackInterface { protected $format = ['pro_api']; protected $supportedInterfaceOrClass = [UserGroup::class]; public function __construct( protected ActivityCounts $activityCounts, ) { } /** * @param UserGroup $object * @param array{cacheability: CacheableMetadata|null} $context */ public function normalize($object, $format = null, array $context = []): array { $context['cacheability'] ??= new CacheableMetadata(); $context['cacheability']->addCacheableDependency($object); return [ 'uuid' => $object->uuid(), 'created' => $object->getCreatedTime(), 'title' => $object->getTitle(), 'totalActivities' => new Placeholder([$this, 'getActivityCount']), 'totalDistance' => new Placeholder([$this, 'getTotalDistance']), ]; } public function getActivityCount(UserGroup $group): int { return $this->activityCounts->getActivityCount(group: $group); } public function getTotalDistance(UserGroup $group): int { return $this->activityCounts->getTotalDistance(group: $group); } public static function trustedCallbacks(): array { return ['getActivityCount', 'getTotalDistance']; } }
- 🇧🇪Belgium dieterholvoet Brussels
The previous approach didn't replace placeholders in case of nested normalizers, so I rewrote part of the implementation. The end result is a lot simpler:
namespace Drupal\example_module\Normalizer; use Drupal\api_toolkit\Normalizer\Placeholder\Placeholder; use Drupal\Core\Cache\CacheableMetadata; use Drupal\example_module\Service\ActivityCounts; use Drupal\example_module\Entity\Node\UserGroup; use Drupal\serialization\Normalizer\NormalizerBase; class UserGroupNormalizer extends NormalizerBase { protected $format = ['pro_api']; protected $supportedInterfaceOrClass = [UserGroup::class]; public function __construct( protected ActivityCounts $activityCounts, ) { } /** * @param UserGroup $object * @param array{cacheability: CacheableMetadata|null} $context */ public function normalize($object, $format = null, array $context = []): array { $context['cacheability'] ??= new CacheableMetadata(); $context['cacheability']->addCacheableDependency($object); return [ 'uuid' => $object->uuid(), 'created' => $object->getCreatedTime(), 'title' => $object->getTitle(), 'postalCodes' => new Placeholder([$this->serializer, 'normalize'], [$object->getPostalCodes(), $format, $context]), 'totalActivities' => new Placeholder([$this->activityCounts, 'getActivityCount'], ['group' => $object]), 'totalDistance' => new Placeholder([$this->activityCounts, 'getTotalDistance'], ['group' => $object]), ]; } }
Entity, field item list & field item arguments that are passed to placeholder callbacks are automatically normalized to a simple string format and the entity in question is reloaded from the database before placeholder replacement happens. A side result of the current implementation is that you can now also pass
[$this->serializer, 'normalize']
to a placeholder, which will cause the nested normalizations to not be part of the cached normalization anymore, which should in turn decrease cache sizes in case of a lot of nested normalizations. - Status changed to Fixed
11 months ago 2:57pm 5 June 2024 -
DieterHolvoet →
committed a68f0482 on 1.x
Issue #3370423 by DieterHolvoet: Add a cached normalizer service
-
DieterHolvoet →
committed a68f0482 on 1.x
Automatically closed - issue fixed for 2 weeks with no activity.