22
33namespace OGSoft \ValueObjects ;
44
5+ use Carbon \Carbon ;
6+ use Exception ;
57use OGSoft \ValueObjects \Exceptions \ValidatorException ;
68use OGSoft \ValueObjects \Interfaces \GlobalValidatorInterface ;
79use OGSoft \ValueObjects \Interfaces \ValueObjectInterface ;
10+ use ReflectionClass ;
11+ use ReflectionException ;
12+ use ReflectionParameter ;
13+ use ReflectionType ;
814use Throwable ;
915
1016abstract class AbstractValueObject implements ValueObjectInterface
1117{
12- private $ touched = [];
13- private $ ignoredAttrs = ["ignoredAttrs " , "touched " ];
18+ private array $ touched = [];
19+ private array $ ignoredAttrs = ["ignoredAttrs " , "touched " ];
1420
1521 public function isIgnoredAttr (string $ attrName ): bool
1622 {
@@ -20,9 +26,10 @@ public function isIgnoredAttr(string $attrName): bool
2026 /**
2127 * @inheritDoc
2228 */
23- public function setTouched (string $ attrName ): void
29+ public function setTouched (string $ attrName ): self
2430 {
2531 $ this ->touched [$ attrName ] = true ;
32+ return $ this ;
2633 }
2734
2835 /**
@@ -44,7 +51,7 @@ public function getTouchedAll(): array
4451 /**
4552 * @inheritDoc
4653 */
47- public function init ($ data ): void
54+ public function init ($ data ): self
4855 {
4956 if (is_object ($ data )) {
5057 $ data = get_object_vars ($ data );
@@ -71,6 +78,8 @@ public function init($data): void
7178 if ($ this instanceof GlobalValidatorInterface) {
7279 $ this ->globalValidate ();
7380 }
81+
82+ return $ this ;
7483 }
7584
7685 /**
@@ -116,4 +125,144 @@ public function toArray(): array
116125 return $ out ;
117126 }
118127
128+ public function setValue (string $ attrName , string $ attrSetter , $ attrValue , object $ obj ): self
129+ {
130+ $ reflectionParameter = self ::getReflectionParameter ($ obj , $ attrSetter );
131+ // build-in types
132+ if ($ reflectionParameter ->getType ()->isBuiltin ()) {
133+ if (is_array ($ attrValue )) {
134+ $ attrValue = array_map (function ($ v ) use ($ reflectionParameter ) {
135+ return self ::transformBuildInType ($ reflectionParameter , $ v );
136+ }, $ attrValue );
137+ $ obj ->$ attrSetter (...$ attrValue );
138+ } else {
139+ $ attrValue = self ::transformBuildInType ($ reflectionParameter , $ attrValue );
140+ $ obj ->$ attrSetter ($ attrValue );
141+ }
142+ } // custom types
143+ else {
144+ self ::initAndValidateValueObject ($ obj , $ attrSetter , $ reflectionParameter , $ attrValue );
145+ }
146+
147+ return $ this ;
148+ }
149+
150+ /**
151+ * @param $obj
152+ * @param $setter
153+ * @return null|ReflectionParameter
154+ * @throws ReflectionException
155+ */
156+ private static function getReflectionParameter ($ obj , $ setter ): ?ReflectionParameter
157+ {
158+ $ reflectionClass = new ReflectionClass ($ obj );
159+ $ params = $ reflectionClass ->getMethod ($ setter )->getParameters ();
160+ // setter has only one param
161+ $ param = $ params [0 ];
162+ if ($ param ->getType () instanceof ReflectionType) {
163+ // $name = $param->getType()->getName();
164+ return $ param ;
165+ }
166+ return null ;
167+ }
168+
169+ /**
170+ * Normalize build-in type
171+ *
172+ * @param ReflectionParameter $reflectionParameter
173+ * @param $value
174+ * @return mixed
175+ */
176+ private static function transformBuildInType (ReflectionParameter $ reflectionParameter , $ value )
177+ {
178+ // bool transform
179+ if ($ reflectionParameter ->getType ()->getName () == "bool " ) {
180+ if (is_bool ($ value ) || is_null ($ value )) {
181+ return $ value ;
182+ } else {
183+ return filter_var ($ value , FILTER_VALIDATE_BOOLEAN , FILTER_NULL_ON_FAILURE );
184+ }
185+ }
186+
187+ // int transform
188+ if ($ reflectionParameter ->getType ()->getName () == "int " ) {
189+ if ($ value === '' ) {
190+ return null ; // set null on empty string (because of Mango API and types handling)
191+ }
192+ }
193+
194+ // general
195+ return $ value ;
196+ }
197+
198+ /**
199+ * @param object $obj
200+ * @param string $setter
201+ * @param ReflectionParameter $parameter
202+ * @param $data
203+ * @throws Exception
204+ */
205+ private static function initAndValidateValueObject (object $ obj , string $ setter , ReflectionParameter $ parameter , $ data )
206+ {
207+ $ type = $ parameter ->getType ()->getName ();
208+ if (is_subclass_of ($ type , AbstractValueObject::class)) {
209+ if (empty ($ data ) && !is_array ($ data )) {
210+ if ($ parameter ->allowsNull ()) {
211+ return ;
212+ }
213+ throw new ValidatorException (" Wrong data type of property. Data can not be null " , get_class ($ obj ), $ parameter ->name , $ data );
214+ }
215+ if ($ parameter ->isVariadic ()) {
216+ if (!is_array ($ data )) {
217+ $ obj ->$ setter (null );
218+ return ;
219+ }
220+ $ dataArr = [];
221+ foreach ($ data as $ objData ) {
222+ $ reqObj = self ::createValueObject ($ type , $ objData );
223+ if ($ reqObj ) {
224+ $ dataArr [] = $ reqObj ;
225+ }
226+ }
227+ $ obj ->$ setter (...$ dataArr );
228+ } else {
229+ $ reqObj = self ::createValueObject ($ type , $ data );
230+ $ obj ->$ setter ($ reqObj );
231+ }
232+ return ;
233+ } elseif ($ type == DateTime::class || $ type == Carbon::class) {
234+ if (!$ parameter ->allowsNull () && empty ($ data )) {
235+ throw new ValidatorException (" Wrong data type of property. Date can not be null " , get_class ($ obj ), $ parameter ->name , $ data );
236+ }
237+ /**
238+ * Parse date and time from data, if timezone is not part of the data string, user setting timezone is used (eg. messages from
239+ * machines come in UTC and without timezone information). If the timezone is part of the string, this one is used and has higher
240+ * priority, than user settings.
241+ * If the timezone is not specified within data string, nor in the user settings, the application default timezone is used.
242+ */
243+ // TODO add timezone support
244+ $ dateTime = empty ($ data ) ? null : Carbon::parse ($ data , /*getCustomUserTimezone()*/ );
245+ //$dateTime->setTimezone(new \DateTimeZone(config("app.timezone")));
246+
247+ $ obj ->$ setter ($ dateTime );
248+ return ;
249+ }
250+ throw new Exception ("Unknown array type. Type has to be build in or subclass of AbstractRequestBody - " . $ setter );
251+ }
252+
253+ /**
254+ * @param $className
255+ * @param $data
256+ * @return false|AbstractValueObject
257+ * @throws ValidatorException
258+ */
259+ private static function createValueObject ($ className , $ data )
260+ {
261+ $ valueObject = new $ className ();
262+ if ($ valueObject instanceof AbstractValueObject) {
263+ $ valueObject ->init ($ data );
264+ return $ valueObject ;
265+ }
266+ return false ;
267+ }
119268}
0 commit comments