1<?php
2
3// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0.
4// See the LICENCE file in the repository root for full licence text.
5
6namespace App\Libraries\Transformers;
7
8use App\Transformers\TransformerAbstract;
9use InvalidArgumentException;
10use League\Fractal;
11use League\Fractal\Resource\Collection;
12use League\Fractal\Resource\Item;
13use League\Fractal\Resource\NullResource;
14use League\Fractal\Serializer\Serializer;
15
16class Scope extends Fractal\Scope
17{
18 /**
19 * {@inheritdoc}
20 *
21 * Exactly the same as overridden function but with a $data !== null check before injectAvailableIncludeData.
22 */
23 public function toArray(): ?array
24 {
25 [$rawData, $rawIncludedData] = $this->executeResourceTransformers();
26
27 $serializer = $this->manager->getSerializer();
28
29 $data = $this->serializeResource($serializer, $rawData);
30
31 // If the serializer wants the includes to be side-loaded then we'll
32 // serialize the included data and merge it with the data.
33 if ($serializer->sideloadIncludes()) {
34 //Filter out any relation that wasn't requested
35 $rawIncludedData = array_map([$this, 'filterFieldsets'], $rawIncludedData);
36
37 $includedData = $serializer->includedData($this->resource, $rawIncludedData);
38
39 // If the serializer wants to inject additional information
40 // about the included resources, it can do so now.
41 $data = $serializer->injectData($data, $rawIncludedData);
42
43 if ($this->isRootScope()) {
44 // If the serializer wants to have a final word about all
45 // the objects that are sideloaded, it can do so now.
46 $includedData = $serializer->filterIncludes(
47 $includedData,
48 $data
49 );
50 }
51
52 $data = $data + $includedData;
53 }
54
55 if ($data !== null && !empty($this->availableIncludes)) {
56 $data = $serializer->injectAvailableIncludeData($data, $this->availableIncludes);
57 }
58
59 if ($this->resource instanceof Collection) {
60 if ($this->resource->hasCursor()) {
61 $pagination = $serializer->cursor($this->resource->getCursor());
62 } elseif ($this->resource->hasPaginator()) {
63 $pagination = $serializer->paginator($this->resource->getPaginator());
64 }
65
66 if (!empty($pagination)) {
67 $this->resource->setMetaValue(key($pagination), current($pagination));
68 }
69 }
70
71 // Pull out all of OUR metadata and any custom meta data to merge with the main level data
72 $meta = $serializer->meta($this->resource->getMeta());
73
74 // in case of returning NullResource we should return null and not to go with array_merge
75 if (is_null($data)) {
76 if (!empty($meta)) {
77 return $meta;
78 }
79 return null;
80 }
81
82 return $data + $meta;
83 }
84
85 protected function executeResourceTransformers(): array
86 {
87 $transformer = $this->resource->getTransformer();
88 $data = $this->resource->getData();
89
90 $transformedData = $includedData = [];
91
92 if ($this->resource instanceof Item) {
93 [$transformedData, $includedData[]] = $this->fireTransformer($transformer, $data);
94 } elseif ($this->resource instanceof Collection) {
95 foreach ($data as $value) {
96 [$itemTransformedData, $itemIncludedData] = $this->fireTransformer($transformer, $value);
97 if ($itemTransformedData !== null) {
98 $transformedData[] = $itemTransformedData;
99 $includedData[] = $itemIncludedData;
100 }
101 }
102 } elseif ($this->resource instanceof NullResource) {
103 $transformedData = null;
104 $includedData = [];
105 } else {
106 throw new InvalidArgumentException(
107 'Argument $resource should be an instance of League\Fractal\Resource\Item'
108 .' or League\Fractal\Resource\Collection'
109 );
110 }
111
112 return [$transformedData, $includedData];
113 }
114
115 protected function fireTransformer($transformer, $data): array
116 {
117 if ($transformer instanceof TransformerAbstract) {
118 $permission = $transformer->getRequiredPermission();
119 if ($permission !== null && !priv_check($permission, $data)->can()) {
120 return [null, []];
121 }
122 }
123
124 [$transformedData, $includedData] = parent::fireTransformer($transformer, $data);
125
126 if (empty($transformedData) && empty($includedData)) {
127 return [null, []];
128 }
129
130 return [$transformedData, $includedData];
131 }
132
133 protected function serializeResource(Serializer $serializer, $data): ?array
134 {
135 if ($data === null) {
136 return null;
137 }
138
139 return parent::serializeResource($serializer, $data);
140 }
141}