77use Illuminate \Pagination \LengthAwarePaginator ;
88use Illuminate \Pagination \Paginator ;
99use Illuminate \Support \Collection ;
10+ use Illuminate \Support \Str ;
1011
12+ /**
13+ * Class ApiTransformer
14+ * @package Napp\Core\Api\Transformers
15+ */
1116class ApiTransformer implements TransformerInterface
1217{
1318 /**
1419 * @var array
1520 */
16- protected $ apiMapping = [];
21+ public $ apiMapping = [];
1722
1823 /**
1924 * Strict mode removes keys that are
@@ -67,16 +72,65 @@ public function transformOutput($data): array
6772 }
6873
6974 if (true === $ data instanceof Collection) {
70- foreach ($ data as $ item ) {
71- $ output [] = $ this ->transformOutput ($ item );
72- }
75+ $ output = $ this ->transformCollection ($ output , $ data );
76+ } else if (true === $ data instanceof Model) {
77+ $ output = $ this ->transformAttributes ($ output , $ data ->getAttributes ());
78+ $ output = $ this ->transformRelationships ($ output , $ data );
7379 } else {
7480 $ data = (true === \is_array ($ data )) ? $ data : $ data ->toArray ();
75- foreach ($ data as $ key => $ value ) {
76- if (true === $ this ->strict && false === array_key_exists ($ key , $ this ->apiMapping )) {
81+ $ output = $ this ->transformAttributes ($ output , $ data );
82+ }
83+
84+ return $ output ;
85+ }
86+
87+
88+ /**
89+ * @param array $output
90+ * @param array $data
91+ * @return array
92+ */
93+ protected function transformAttributes (array $ output , array $ data ): array
94+ {
95+ foreach ($ data as $ key => $ value ) {
96+ if (true === $ this ->strict && false === array_key_exists ($ key , $ this ->apiMapping )) {
97+ continue ;
98+ }
99+
100+ $ output [$ this ->findNewKey ($ key )] = $ this ->convertValueType ($ key , $ value );
101+ }
102+
103+ return $ output ;
104+ }
105+
106+ /**
107+ * @param array $output
108+ * @param Model $data
109+ * @return array
110+ */
111+ protected function transformRelationships (array $ output , Model $ data ): array
112+ {
113+ /** @var Model $data */
114+ $ relationships = $ data ->getRelations ();
115+ foreach ($ relationships as $ relationshipName => $ relationship ) {
116+ if (true === $ relationship instanceof Collection) {
117+ // do not transform empty relationships
118+ if ($ relationship ->isEmpty ()) {
77119 continue ;
78120 }
79- $ output [$ this ->findNewKey ($ key )] = $ this ->convertValueType ($ key , $ value );
121+
122+ if ($ this ->isTransformAware ($ relationship ->first ())) {
123+ $ output [$ relationshipName ] = $ relationship ->first ()->getTransformer ()->transformOutput ($ relationship );
124+ } else {
125+ $ output [$ relationshipName ] = $ relationship ->toArray ();
126+ }
127+ } else {
128+ // model
129+ if ($ this ->isTransformAware ($ relationship )) {
130+ $ output [$ relationshipName ] = $ relationship ->getTransformer ()->transformOutput ($ relationship );
131+ } else {
132+ $ output [$ relationshipName ] = $ relationship ->getAttributes ();
133+ }
80134 }
81135 }
82136
@@ -92,6 +146,20 @@ protected function transformPaginatedOutput($data): array
92146 return $ result ;
93147 }
94148
149+ /**
150+ * @param array $output
151+ * @param Collection $data
152+ * @return array
153+ */
154+ protected function transformCollection (array $ output , Collection $ data ): array
155+ {
156+ foreach ($ data as $ item ) {
157+ $ output [] = $ this ->transformOutput ($ item );
158+ }
159+
160+ return $ output ;
161+ }
162+
95163 /**
96164 * @param string $newKey
97165 * @return string
@@ -123,6 +191,7 @@ protected function findNewKey(string $originalKey): string
123191 /**
124192 * @param string $key
125193 * @param mixed $value
194+ * @param string $newKey
126195 * @return mixed
127196 */
128197 protected function convertValueType (string $ key , $ value )
@@ -131,21 +200,140 @@ protected function convertValueType(string $key, $value)
131200 ? $ this ->apiMapping [$ key ]['dataType ' ]
132201 : 'string ' ;
133202
203+ foreach (static ::normalizeType ($ type ) as list ($ method , $ parameters )) {
204+ if (true === empty ($ method )) {
205+ return $ value ;
206+ }
207+
208+ if ('Nullable ' === $ method ) {
209+ if (true === empty ($ value ) && false === \is_numeric ($ value )) {
210+ return null ;
211+ }
212+
213+ continue ;
214+ }
215+
216+ $ method = "convert {$ method }" ;
217+
218+ if (false === method_exists (TransformerMethods::class, $ method )) {
219+ return $ value ;
220+ }
221+
222+ return TransformerMethods::$ method ($ value , $ parameters );
223+ }
224+ }
225+
226+ /**
227+ * @param $type
228+ * @return array
229+ */
230+ protected static function parseStringDataType ($ type ): array
231+ {
232+ $ parameters = [];
233+
234+ // The format for transforming data-types and parameters follows an
235+ // easy {data-type}:{parameters} formatting convention. For instance the
236+ // data-type "float:3" states that the value will be converted to a float with 3 decimals.
237+ if (mb_strpos ($ type , ': ' ) !== false ) {
238+ list ($ dataType , $ parameter ) = explode (': ' , $ type , 2 );
239+
240+ $ parameters = static ::parseParameters ($ parameter );
241+ }
242+
243+ $ dataType = static ::normalizeDataType (trim ($ dataType ?? $ type ));
244+
245+ return [Str::studly ($ dataType ), $ parameters ?? []];
246+ }
247+
248+ /**
249+ * Parse a parameter list.
250+ *
251+ * @param string $parameter
252+ * @return array
253+ */
254+ protected static function parseParameters ($ parameter ): array
255+ {
256+ return str_getcsv ($ parameter );
257+ }
258+
259+ /**
260+ * @param $type
261+ * @return array
262+ */
263+ protected static function parseManyDataTypes ($ type ): array
264+ {
265+ $ parsed = [];
266+
267+ $ dataTypes = explode ('| ' , $ type );
268+
269+ foreach ($ dataTypes as $ dataType ) {
270+ $ parsed [] = static ::parseStringDataType (trim ($ dataType ));
271+ }
272+
273+ return $ parsed ;
274+ }
275+
276+ /**
277+ * @param $type
278+ * @return array
279+ */
280+ protected static function normalizeType ($ type ): array
281+ {
282+ if (false !== mb_strpos ($ type , '| ' )) {
283+ return self ::normalizeNullable (
284+ static ::parseManyDataTypes ($ type )
285+ );
286+ }
287+
288+ return [static ::parseStringDataType (trim ($ type ))];
289+ }
290+
291+ /**
292+ * @param $type
293+ * @return bool
294+ */
295+ protected static function hasParameters ($ type ): bool
296+ {
297+ return false !== mb_strpos ($ type , ': ' );
298+ }
299+
300+ /**
301+ * @param $dataTypes
302+ * @return array
303+ */
304+ protected static function normalizeNullable ($ dataTypes ): array
305+ {
306+ if (isset ($ dataTypes [1 ][0 ]) && $ dataTypes [1 ][0 ] === 'Nullable ' ) {
307+ return array_reverse ($ dataTypes );
308+ }
309+
310+ return $ dataTypes ;
311+ }
312+
313+ /**
314+ * @param $type
315+ * @return string
316+ */
317+ protected static function normalizeDataType ($ type ): string
318+ {
134319 switch ($ type ) {
135- case 'datetime ' :
136- return strtotime ($ value ) > 0 ? date ("c " , strtotime ($ value )) : '' ;
137320 case 'int ' :
138- return ( int ) $ value ;
321+ return ' integer ' ;
139322 case 'bool ' :
140- return (bool ) $ value ;
141- case 'array ' :
142- return (array ) $ value ;
143- case 'json ' :
144- return json_decode ($ value );
145- case 'float ' :
146- return (float ) $ value ;
323+ return 'boolean ' ;
324+ case 'date ' :
325+ return 'datetime ' ;
147326 default :
148- return $ value ;
327+ return $ type ;
149328 }
150329 }
330+
331+ /**
332+ * @param $model
333+ * @return bool
334+ */
335+ protected function isTransformAware ($ model ): bool
336+ {
337+ return array_key_exists (TransformerAware::class, class_uses ($ model ));
338+ }
151339}
0 commit comments