1414use Illuminate \Support \Collection ;
1515use Illuminate \Validation \ValidationException ;
1616use JsonSerializable ;
17+ use ReflectionAttribute ;
1718use ReflectionClass ;
1819use ReflectionProperty ;
1920use UnitEnum ;
2021use WendellAdriel \ValidatedDTO \Attributes \Cast ;
2122use WendellAdriel \ValidatedDTO \Attributes \DefaultValue ;
23+ use WendellAdriel \ValidatedDTO \Attributes \Lazy ;
2224use WendellAdriel \ValidatedDTO \Attributes \Map ;
25+ use WendellAdriel \ValidatedDTO \Attributes \Provide ;
26+ use WendellAdriel \ValidatedDTO \Attributes \Receive ;
2327use WendellAdriel \ValidatedDTO \Attributes \Rules ;
2428use WendellAdriel \ValidatedDTO \Casting \ArrayCast ;
2529use WendellAdriel \ValidatedDTO \Casting \Castable ;
@@ -67,6 +71,9 @@ abstract class SimpleDTO implements BaseDTO, CastsAttributes, JsonSerializable
6771 /** @internal */
6872 protected array $ dtoMapTransform = [];
6973
74+ /** @internal */
75+ private static array $ classReflections = [];
76+
7077 /**
7178 * @throws ValidationException|MissingCastTypeException|CastTargetException
7279 */
@@ -381,6 +388,49 @@ private function buildAttributesData(): void
381388 $ this ->dtoCasts [$ property ] = $ attributeInstance ;
382389 }
383390
391+ $ classReflection = $ this ->classReflection ($ this ::class);
392+ $ classAttributes = collect ($ classReflection ->getAttributes ());
393+ $ lazyAttribute = $ classAttributes ->first (
394+ fn (ReflectionAttribute $ attribute ) => $ attribute ->getName () === Lazy::class
395+ );
396+ /** @var ReflectionAttribute $receiveAttribute */
397+ $ receiveAttribute = $ classAttributes ->first (
398+ fn (ReflectionAttribute $ attribute ) => $ attribute ->getName () === Receive::class
399+ );
400+ /** @var ReflectionAttribute $provideAttribute */
401+ $ provideAttribute = $ classAttributes ->first (
402+ fn (ReflectionAttribute $ attribute ) => $ attribute ->getName () === Provide::class
403+ );
404+
405+ if (! is_null ($ lazyAttribute )) {
406+ $ this ->lazyValidation = true ;
407+ }
408+
409+ $ receiveCase = null ;
410+ $ provideCase = null ;
411+ if (! is_null ($ receiveAttribute )) {
412+ /** @var Receive $receive */
413+ $ receive = $ receiveAttribute ->newInstance ();
414+ $ receiveCase = $ receive ->propertyCase ;
415+ }
416+
417+ if (! is_null ($ provideAttribute )) {
418+ /** @var Provide $provide */
419+ $ provide = $ provideAttribute ->newInstance ();
420+ $ provideCase = $ provide ->propertyCase ;
421+ }
422+
423+ if (! is_null ($ receiveCase ) || ! is_null ($ provideCase )) {
424+ foreach (array_keys ($ publicProperties ) as $ property ) {
425+ if (! is_null ($ receiveCase )) {
426+ $ this ->dtoMapData [$ receiveCase ->format ($ property )] = $ property ;
427+ }
428+ if (! is_null ($ provideCase )) {
429+ $ this ->dtoMapTransform [$ property ] = $ provideCase ->format ($ property );
430+ }
431+ }
432+ }
433+
384434 $ mapDataProperties = $ this ->getPropertiesForAttribute ($ publicProperties , Map::class);
385435 foreach ($ mapDataProperties as $ property => $ attribute ) {
386436 $ attributeInstance = $ attribute ->newInstance ();
@@ -397,7 +447,7 @@ private function buildAttributesData(): void
397447
398448 private function getPublicProperties (): array
399449 {
400- $ reflectionClass = new ReflectionClass ($ this );
450+ $ reflectionClass = $ this -> classReflection ($ this ::class );
401451 $ dtoProperties = [];
402452
403453 foreach ($ reflectionClass ->getProperties (ReflectionProperty::IS_PUBLIC ) as $ property ) {
@@ -427,6 +477,15 @@ private function getPropertiesForAttribute(array $properties, string $attribute)
427477 return $ result ;
428478 }
429479
480+ private function classReflection (string $ class ): ReflectionClass
481+ {
482+ if (! isset (self ::$ classReflections [$ class ])) {
483+ self ::$ classReflections [$ class ] = new ReflectionClass ($ class );
484+ }
485+
486+ return self ::$ classReflections [$ class ];
487+ }
488+
430489 private function mapDTOData (array $ mapping , array $ data ): array
431490 {
432491 $ mappedData = [];
0 commit comments