44
55namespace EduardoMarques \DynamoPHP \Serializer ;
66
7+ use EduardoMarques \DynamoPHP \Attribute \AbstractIndex ;
8+ use EduardoMarques \DynamoPHP \Attribute \AbstractKey ;
9+ use EduardoMarques \DynamoPHP \Attribute \GlobalIndex ;
10+ use EduardoMarques \DynamoPHP \Attribute \LocalIndex ;
711use EduardoMarques \DynamoPHP \Metadata \MetadataException ;
812use EduardoMarques \DynamoPHP \Metadata \MetadataLoader ;
913use ReflectionException ;
@@ -32,11 +36,23 @@ public function __construct(
3236 public function normalize (object $ entity , bool $ includePrimaryKey = true ): array
3337 {
3438 $ primaryKey = $ includePrimaryKey ? $ this ->normalizePrimaryKey ($ entity ) : [];
39+ $ attributes = $ this ->normalizeAttributes ($ entity );
40+ $ indexes = $ this ->normalizeIndexesFromEntity ($ entity );
3541
36- return [
37- ...$ primaryKey ,
38- ...$ this ->normalizeAttributes ($ entity ),
39- ];
42+ $ item = [...$ primaryKey , ...$ attributes ];
43+
44+ $ overlappingIndexFields = array_intersect_key ($ indexes , $ item );
45+
46+ if (false === empty ($ overlappingIndexFields )) {
47+ throw new InvalidEntityException (
48+ sprintf (
49+ 'Index attributes cannot have overlap other item attributes: %s ' ,
50+ json_encode (array_keys ($ overlappingIndexFields ))
51+ )
52+ );
53+ }
54+
55+ return [...$ item , ...$ indexes ];
4056 }
4157
4258 /**
@@ -159,34 +175,8 @@ protected function normalizePartitionKeyValueFromEntity(object $entity): string
159175 {
160176 $ entityMetadata = $ this ->metadataLoader ->getEntityMetadata ($ entity ::class);
161177 $ key = $ entityMetadata ->getPartitionKey ();
162- $ definedFields = $ key ->getFields ();
163- $ delimiter = $ key ->getDelimiter ();
164- $ prefix = $ key ->getPrefix ();
165-
166- $ classMetadata = $ this ->metadataLoader ->getClassMetadata ($ entity ::class);
167- $ finalValue = $ prefix ?? '' ;
168-
169- foreach ($ definedFields as $ field ) {
170- if (false === $ classMetadata ->has ($ field )) {
171- throw new InvalidFieldException (
172- sprintf (
173- 'Field "%s" defined in Partition Key is invalid. Are you sure it exists in the entity class? ' ,
174- $ field
175- )
176- );
177- }
178-
179- /** @var ReflectionProperty $reflectionProperty */
180- $ reflectionProperty = $ classMetadata ->get ($ field );
181- $ propertyValue = $ reflectionProperty ->getValue ($ entity );
182-
183- /** @var scalar $currentFieldValue */
184- $ currentFieldValue = $ this ->normalizer ->normalize ($ propertyValue );
185178
186- $ finalValue .= empty ($ finalValue ) ? $ currentFieldValue : $ delimiter . $ currentFieldValue ;
187- }
188-
189- return $ finalValue ;
179+ return $ this ->normalizeKeyValueFromEntity ($ entity , $ key );
190180 }
191181
192182 /**
@@ -205,6 +195,17 @@ protected function normalizeSortKeyValueFromEntity(object $entity): ?string
205195 return null ;
206196 }
207197
198+ return $ this ->normalizeKeyValueFromEntity ($ entity , $ key );
199+ }
200+
201+ /**
202+ * @template T of object
203+ * @param T $entity
204+ * @throws ReflectionException
205+ * @throws ExceptionInterface
206+ */
207+ protected function normalizeKeyValueFromEntity (object $ entity , AbstractKey $ key ): string
208+ {
208209 $ definedFields = $ key ->getFields ();
209210 $ delimiter = $ key ->getDelimiter ();
210211 $ prefix = $ key ->getPrefix ();
@@ -216,8 +217,9 @@ protected function normalizeSortKeyValueFromEntity(object $entity): ?string
216217 if (false === $ classMetadata ->has ($ field )) {
217218 throw new InvalidFieldException (
218219 sprintf (
219- 'Field "%s" defined in Sort Key is invalid. Are you sure it exists in the entity class? ' ,
220- $ field
220+ 'Field "%s" defined in %s is invalid. Are you sure it exists in the entity class? ' ,
221+ $ field ,
222+ $ key ::class
221223 )
222224 );
223225 }
@@ -247,49 +249,8 @@ protected function normalizePartitionKeyValueFromArray(string $class, array $val
247249 {
248250 $ entityMetadata = $ this ->metadataLoader ->getEntityMetadata ($ class );
249251 $ key = $ entityMetadata ->getPartitionKey ();
250- $ definedFields = $ key ->getFields ();
251- $ delimiter = $ key ->getDelimiter ();
252- $ prefix = $ key ->getPrefix ();
253-
254- $ valuesByFieldSorted = [];
255-
256- foreach ($ definedFields as $ field ) {
257- if (isset ($ valuesByField [$ field ])) {
258- $ valuesByFieldSorted [$ field ] = $ valuesByField [$ field ];
259- }
260- }
261-
262- $ allFieldsProvided = empty (array_diff_key (array_flip ($ definedFields ), $ valuesByFieldSorted ));
263252
264- if (false === $ allFieldsProvided ) {
265- throw new InvalidFieldException (
266- 'Provided Partition Key fields do not match the ones defined in the entity '
267- );
268- }
269-
270- $ classMetadata = $ this ->metadataLoader ->getClassMetadata ($ class );
271- $ finalValue = $ prefix ?? '' ;
272-
273- foreach ($ valuesByFieldSorted as $ field => $ value ) {
274- if (false === $ classMetadata ->has ($ field )) {
275- throw new InvalidFieldException (
276- sprintf ('Field "%s" is invalid. Are you sure it exists in the entity class? ' , $ field )
277- );
278- }
279-
280- if (empty ($ value )) {
281- throw new InvalidFieldException (
282- sprintf ('Field "%s" is invalid. Are you sure its value is provided? ' , $ field )
283- );
284- }
285-
286- /** @var scalar $currentFieldValue */
287- $ currentFieldValue = $ this ->normalizer ->normalize ($ value );
288-
289- $ finalValue .= empty ($ finalValue ) ? $ currentFieldValue : $ delimiter . $ currentFieldValue ;
290- }
291-
292- return $ finalValue ;
253+ return $ this ->normalizeKeyValueFromArray ($ class , $ valuesByField , $ key );
293254 }
294255
295256 /**
@@ -309,6 +270,21 @@ protected function normalizeSortKeyValueFromArray(string $class, array $valuesBy
309270 return null ;
310271 }
311272
273+ return $ this ->normalizeKeyValueFromArray ($ class , $ valuesByField , $ key );
274+ }
275+
276+ /**
277+ * @template T of object
278+ * @param class-string<T> $class
279+ * @param array<string, mixed> $valuesByField
280+ * @throws ReflectionException
281+ * @throws ExceptionInterface
282+ */
283+ protected function normalizeKeyValueFromArray (
284+ string $ class ,
285+ array $ valuesByField ,
286+ AbstractKey $ key ,
287+ ): string {
312288 $ definedFields = $ key ->getFields ();
313289 $ delimiter = $ key ->getDelimiter ();
314290 $ prefix = $ key ->getPrefix ();
@@ -325,7 +301,7 @@ protected function normalizeSortKeyValueFromArray(string $class, array $valuesBy
325301
326302 if (false === $ allFieldsProvided ) {
327303 throw new InvalidFieldException (
328- 'Provided Sort Key fields do not match the ones defined in the entity '
304+ 'Provided Partition Key fields do not match the ones defined in the entity '
329305 );
330306 }
331307
@@ -339,6 +315,12 @@ protected function normalizeSortKeyValueFromArray(string $class, array $valuesBy
339315 );
340316 }
341317
318+ if (empty ($ value )) {
319+ throw new InvalidFieldException (
320+ sprintf ('Field "%s" is invalid. Are you sure its value is provided? ' , $ field )
321+ );
322+ }
323+
342324 /** @var scalar $currentFieldValue */
343325 $ currentFieldValue = $ this ->normalizer ->normalize ($ value );
344326
@@ -372,4 +354,74 @@ protected function normalizeAttributes(object $entity): array
372354
373355 return $ attributes ;
374356 }
357+
358+ /**
359+ * @template T of object
360+ * @param T $entity
361+ * @return array<string, string>
362+ * @throws ReflectionException
363+ * @throws ExceptionInterface
364+ * @throws MetadataException
365+ */
366+ protected function normalizeIndexesFromEntity (object $ entity ): array
367+ {
368+ $ entityMetadata = $ this ->metadataLoader ->getEntityMetadata ($ entity ::class);
369+ $ indexes = $ entityMetadata ->getIndexes ();
370+
371+ $ normalized = [];
372+
373+ foreach ($ indexes as $ index ) {
374+ $ currentNormalized = $ this ->normalizeIndexFromEntity ($ entity , $ index );
375+ $ overlappingKeys = array_intersect_key ($ currentNormalized , $ normalized );
376+
377+ if (false === empty ($ overlappingKeys )) {
378+ throw new InvalidEntityException (
379+ sprintf (
380+ 'Index attributes cannot overlap other index attributes: %s ' ,
381+ json_encode (array_keys ($ overlappingKeys ))
382+ )
383+ );
384+ }
385+
386+ $ normalized = [...$ normalized , ...$ currentNormalized ];
387+ }
388+
389+ return $ normalized ;
390+ }
391+
392+ /**
393+ * @template T of object
394+ * @param T $entity
395+ * @return array<string, string>
396+ * @throws ReflectionException
397+ * @throws ExceptionInterface
398+ */
399+ protected function normalizeIndexFromEntity (object $ entity , AbstractIndex $ index ): array
400+ {
401+ /** @var LocalIndex|GlobalIndex $index */
402+ $ sortKey = $ index ->sortKey ;
403+
404+ $ sortKeyNormalized = null !== $ sortKey
405+ ? [$ sortKey ->getName () => $ this ->normalizeKeyValueFromEntity ($ entity , $ sortKey )]
406+ : [];
407+
408+ if ($ index instanceof LocalIndex) {
409+ return $ sortKeyNormalized ;
410+ }
411+
412+ /** @var GlobalIndex $index */
413+ $ partitionKey = $ index ->partitionKey ;
414+ $ partitionKeyName = $ partitionKey ->getName ();
415+
416+ if (isset ($ sortKeyNormalized [$ partitionKeyName ])) {
417+ throw new InvalidEntityException (
418+ sprintf ('Keys within index "%s" cannot overlap each other: %s ' , $ index ->name , $ partitionKeyName )
419+ );
420+ }
421+
422+ return [
423+ ...$ sortKeyNormalized ,
424+ $ partitionKeyName => $ this ->normalizeKeyValueFromEntity ($ entity , $ partitionKey ),
425+ ];
426+ }
375427}
0 commit comments