|
11 | 11 |
|
12 | 12 | namespace Tobyz\JsonApiServer;
|
13 | 13 |
|
14 |
| -use ReflectionClass; |
15 | 14 | use Tobyz\JsonApiServer\Adapter\AdapterInterface;
|
16 | 15 | use Tobyz\JsonApiServer\Exception\BadRequestException;
|
17 | 16 | use Tobyz\JsonApiServer\Schema\Attribute;
|
@@ -55,75 +54,129 @@ public function getSchema(): Type
|
55 | 54 | return $this->schema;
|
56 | 55 | }
|
57 | 56 |
|
58 |
| - public function scope($query, Context $context) |
| 57 | + /** |
| 58 | + * Apply the resource type's scopes to a query. |
| 59 | + */ |
| 60 | + public function applyScopes($query, Context $context): void |
59 | 61 | {
|
60 |
| - run_callbacks($this->getSchema()->getListeners('scope'), [$query, $context]); |
| 62 | + run_callbacks( |
| 63 | + $this->getSchema()->getListeners('scope'), |
| 64 | + [$query, $context] |
| 65 | + ); |
61 | 66 | }
|
62 | 67 |
|
63 |
| - public function filter($query, $filter, Context $context): void |
| 68 | + /** |
| 69 | + * Apply the resource type's filters to a query. |
| 70 | + */ |
| 71 | + public function applySort($query, string $sortString, Context $context): void |
64 | 72 | {
|
65 |
| - if (! is_array($filter)) { |
66 |
| - throw (new BadRequestException('filter must be an array'))->setSourceParameter('filter'); |
| 73 | + $schema = $this->getSchema(); |
| 74 | + $customSorts = $schema->getSorts(); |
| 75 | + $fields = $schema->getFields(); |
| 76 | + |
| 77 | + foreach ($this->parseSortString($sortString) as [$name, $direction]) { |
| 78 | + if ( |
| 79 | + isset($customSorts[$name]) |
| 80 | + && evaluate($customSorts[$name]->getVisible(), [$context]) |
| 81 | + ) { |
| 82 | + $customSorts[$name]->getCallback()($query, $direction, $context); |
| 83 | + continue; |
| 84 | + } |
| 85 | + |
| 86 | + $field = $fields[$name] ?? null; |
| 87 | + |
| 88 | + if ( |
| 89 | + $field instanceof Attribute |
| 90 | + && evaluate($field->getSortable(), [$context]) |
| 91 | + ) { |
| 92 | + $this->adapter->sortByAttribute($query, $field, $direction); |
| 93 | + continue; |
| 94 | + } |
| 95 | + |
| 96 | + throw (new BadRequestException("Invalid sort field: $name"))->setSourceParameter('sort'); |
67 | 97 | }
|
| 98 | + } |
| 99 | + |
| 100 | + private function parseSortString(string $string): array |
| 101 | + { |
| 102 | + return array_map(function ($field) { |
| 103 | + if ($field[0] === '-') { |
| 104 | + return [substr($field, 1), 'desc']; |
| 105 | + } else { |
| 106 | + return [$field, 'asc']; |
| 107 | + } |
| 108 | + }, explode(',', $string)); |
| 109 | + } |
68 | 110 |
|
| 111 | + /** |
| 112 | + * Apply the resource type's filters to a query. |
| 113 | + */ |
| 114 | + public function applyFilters($query, array $filters, Context $context): void |
| 115 | + { |
69 | 116 | $schema = $this->getSchema();
|
70 |
| - $adapter = $this->getAdapter(); |
71 |
| - $filters = $schema->getFilters(); |
| 117 | + $customFilters = $schema->getFilters(); |
72 | 118 | $fields = $schema->getFields();
|
73 | 119 |
|
74 |
| - foreach ($filter as $name => $value) { |
| 120 | + foreach ($filters as $name => $value) { |
75 | 121 | if ($name === 'id') {
|
76 |
| - $adapter->filterByIds($query, explode(',', $value)); |
| 122 | + $this->adapter->filterByIds($query, explode(',', $value)); |
77 | 123 | continue;
|
78 | 124 | }
|
79 | 125 |
|
80 |
| - if (isset($filters[$name]) && evaluate($filters[$name]->getVisible(), [$context])) { |
81 |
| - $filters[$name]->getCallback()($query, $value, $context); |
| 126 | + if ( |
| 127 | + isset($customFilters[$name]) |
| 128 | + && evaluate($customFilters[$name]->getVisible(), [$context]) |
| 129 | + ) { |
| 130 | + $customFilters[$name]->getCallback()($query, $value, $context); |
82 | 131 | continue;
|
83 | 132 | }
|
84 | 133 |
|
85 | 134 | [$name, $sub] = explode('.', $name, 2) + [null, null];
|
| 135 | + $field = $fields[$name] ?? null; |
86 | 136 |
|
87 |
| - if (isset($fields[$name]) && evaluate($fields[$name]->getFilterable(), [$context])) { |
88 |
| - if ($fields[$name] instanceof Attribute && $sub === null) { |
89 |
| - $this->filterByAttribute($adapter, $query, $fields[$name], $value); |
| 137 | + if ($field && evaluate($field->getFilterable(), [$context])) { |
| 138 | + if ($field instanceof Attribute && $sub === null) { |
| 139 | + $this->filterByAttribute($query, $field, $value); |
90 | 140 | continue;
|
91 |
| - } elseif ($fields[$name] instanceof Relationship) { |
92 |
| - if (is_string($relatedType = $fields[$name]->getType())) { |
| 141 | + } |
| 142 | + |
| 143 | + if ($field instanceof Relationship) { |
| 144 | + if (is_string($relatedType = $field->getType())) { |
93 | 145 | $relatedResource = $context->getApi()->getResourceType($relatedType);
|
94 |
| - $adapter->filterByRelationship($query, $fields[$name], function ($query) use ($relatedResource, $sub, $value, $context) { |
95 |
| - $relatedResource->filter($query, [($sub ?? 'id') => $value], $context); |
| 146 | + |
| 147 | + $this->adapter->filterByRelationship($query, $field, function ($query) use ($relatedResource, $sub, $value, $context) { |
| 148 | + $relatedResource->applyFilters($query, [($sub ?? 'id') => $value], $context); |
96 | 149 | });
|
97 | 150 | }
|
98 | 151 | continue;
|
99 | 152 | }
|
100 | 153 | }
|
101 | 154 |
|
102 |
| - throw (new BadRequestException("Invalid filter [$name]"))->setSourceParameter("filter[$name]"); |
| 155 | + throw (new BadRequestException("Invalid filter: $name"))->setSourceParameter("filter[$name]"); |
103 | 156 | }
|
104 | 157 | }
|
105 | 158 |
|
106 |
| - private function filterByAttribute(AdapterInterface $adapter, $query, Attribute $attribute, $value): void |
| 159 | + private function filterByAttribute($query, Attribute $attribute, $value): void |
107 | 160 | {
|
108 | 161 | if (preg_match('/(.+)\.\.(.+)/', $value, $matches)) {
|
109 | 162 | if ($matches[1] !== '*') {
|
110 |
| - $adapter->filterByAttribute($query, $attribute, $value, '>='); |
| 163 | + $this->adapter->filterByAttribute($query, $attribute, $value, '>='); |
111 | 164 | }
|
112 | 165 | if ($matches[2] !== '*') {
|
113 |
| - $adapter->filterByAttribute($query, $attribute, $value, '<='); |
| 166 | + $this->adapter->filterByAttribute($query, $attribute, $value, '<='); |
114 | 167 | }
|
115 | 168 |
|
116 | 169 | return;
|
117 | 170 | }
|
118 | 171 |
|
119 | 172 | foreach (['>=', '>', '<=', '<'] as $operator) {
|
120 | 173 | if (strpos($value, $operator) === 0) {
|
121 |
| - $adapter->filterByAttribute($query, $attribute, substr($value, strlen($operator)), $operator); |
| 174 | + $this->adapter->filterByAttribute($query, $attribute, substr($value, strlen($operator)), $operator); |
122 | 175 |
|
123 | 176 | return;
|
124 | 177 | }
|
125 | 178 | }
|
126 | 179 |
|
127 |
| - $adapter->filterByAttribute($query, $attribute, $value); |
| 180 | + $this->adapter->filterByAttribute($query, $attribute, $value); |
128 | 181 | }
|
129 | 182 | }
|
0 commit comments