44
55namespace Respect \Data ;
66
7+ use DomainException ;
78use ReflectionClass ;
8- use ReflectionException ;
99use ReflectionProperty ;
10- use stdClass ;
1110
1211use function class_exists ;
13- use function get_object_vars ;
12+ use function is_object ;
1413
1514/** Creates and manipulates entity objects using Style-based naming conventions */
1615class EntityFactory
@@ -21,9 +20,6 @@ class EntityFactory
2120 /** @var array<string, array<string, ReflectionProperty>> */
2221 private array $ propertyCache = [];
2322
24- /** @var array<string, array<string, ReflectionProperty>> */
25- private array $ persistableCache = [];
26-
2723 public function __construct (
2824 public readonly Styles \Stylable $ style = new Styles \Standard (),
2925 private readonly string $ entityNamespace = '\\' ,
@@ -35,7 +31,11 @@ public function createByName(string $name): object
3531 {
3632 $ entityName = $ this ->style ->styledName ($ name );
3733 $ entityClass = $ this ->entityNamespace . $ entityName ;
38- $ entityClass = class_exists ($ entityClass ) ? $ entityClass : stdClass::class;
34+
35+ if (!class_exists ($ entityClass )) {
36+ throw new DomainException ('Entity class ' . $ entityClass . ' not found for ' . $ name );
37+ }
38+
3939 $ ref = $ this ->reflectClass ($ entityClass );
4040
4141 if (!$ this ->disableConstructor ) {
@@ -47,42 +47,56 @@ public function createByName(string $name): object
4747
4848 public function set (object $ entity , string $ prop , mixed $ value ): void
4949 {
50- $ properties = $ this ->reflectProperties ($ entity ::class);
51-
52- if (isset ($ properties [$ prop ])) {
53- $ properties [$ prop ]->setValue ($ entity , $ value );
54-
55- return ;
56- }
50+ $ mirror = $ this ->reflectProperties ($ entity ::class)[$ prop ] ?? null ;
5751
58- $ entity ->{ $ prop } = $ value ;
52+ $ mirror ?->setValue( $ entity , $ value) ;
5953 }
6054
6155 public function get (object $ entity , string $ prop ): mixed
6256 {
6357 $ mirror = $ this ->reflectProperties ($ entity ::class)[$ prop ] ?? null ;
6458
65- if ($ mirror !== null ) {
66- return $ mirror -> isInitialized ( $ entity ) ? $ mirror -> getValue ( $ entity ) : null ;
59+ if ($ mirror === null || ! $ mirror -> isInitialized ( $ entity ) ) {
60+ return null ;
6761 }
6862
69- try {
70- return (new ReflectionProperty ($ entity , $ prop ))->getValue ($ entity );
71- } catch (ReflectionException ) {
72- return null ;
63+ return $ mirror ->getValue ($ entity );
64+ }
65+
66+ /**
67+ * Extract persistable columns, resolving entity objects to their FK representations.
68+ *
69+ * @return array<string, mixed>
70+ */
71+ public function extractColumns (object $ entity ): array
72+ {
73+ $ cols = $ this ->extractProperties ($ entity );
74+
75+ foreach ($ cols as $ key => $ value ) {
76+ if (!is_object ($ value )) {
77+ continue ;
78+ }
79+
80+ if ($ this ->style ->isRelationProperty ($ key )) {
81+ $ fk = $ this ->style ->remoteIdentifier ($ key );
82+ $ cols [$ fk ] = $ this ->get ($ value , $ this ->style ->identifier ($ key ));
83+ unset($ cols [$ key ]);
84+ } else {
85+ $ table = $ this ->style ->remoteFromIdentifier ($ key ) ?? $ key ;
86+ $ cols [$ key ] = $ this ->get ($ value , $ this ->style ->identifier ($ table ));
87+ }
7388 }
89+
90+ return $ cols ;
7491 }
7592
7693 /** @return array<string, mixed> */
7794 public function extractProperties (object $ entity ): array
7895 {
79- $ props = get_object_vars ($ entity );
80- $ persistable = $ this ->reflectPersistable ($ entity ::class);
96+ $ props = [];
8197
8298 foreach ($ this ->reflectProperties ($ entity ::class) as $ name => $ prop ) {
83- if (!isset ($ persistable [$ name ]) || !$ prop ->isInitialized ($ entity )) {
84- unset($ props [$ name ]);
85-
99+ if (!$ prop ->isInitialized ($ entity ) || $ prop ->getAttributes (NotPersistable::class)) {
86100 continue ;
87101 }
88102
@@ -96,8 +110,12 @@ public function hydrate(object $source, string $entityName): object
96110 {
97111 $ entity = $ this ->createByName ($ entityName );
98112
99- foreach (get_object_vars ($ source ) as $ prop => $ value ) {
100- $ this ->set ($ entity , $ prop , $ value );
113+ foreach ($ this ->reflectProperties ($ source ::class) as $ name => $ prop ) {
114+ if (!$ prop ->isInitialized ($ source )) {
115+ continue ;
116+ }
117+
118+ $ this ->set ($ entity , $ name , $ prop ->getValue ($ source ));
101119 }
102120
103121 return $ entity ;
@@ -126,22 +144,4 @@ private function reflectProperties(string $class): array
126144
127145 return $ this ->propertyCache [$ class ];
128146 }
129-
130- /** @return array<string, ReflectionProperty> */
131- private function reflectPersistable (string $ class ): array
132- {
133- if (!isset ($ this ->persistableCache [$ class ])) {
134- $ this ->persistableCache [$ class ] = [];
135-
136- foreach ($ this ->reflectProperties ($ class ) as $ name => $ prop ) {
137- if ($ prop ->getAttributes (NotPersistable::class)) {
138- continue ;
139- }
140-
141- $ this ->persistableCache [$ class ][$ name ] = $ prop ;
142- }
143- }
144-
145- return $ this ->persistableCache [$ class ];
146- }
147147}
0 commit comments