Skip to content

Commit c6d5d7c

Browse files
committed
Merge 4.0
2 parents da37ca9 + 155c01f commit c6d5d7c

File tree

7 files changed

+86
-36
lines changed

7 files changed

+86
-36
lines changed

.github/workflows/guides.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ on:
44
push:
55
pull_request:
66

7+
concurrency:
8+
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
9+
cancel-in-progress: true
10+
711
env:
812
COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }}
913
COVERAGE: '0'

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,23 @@ On write operations, we added the [expectsHeader](https://www.hydra-cg.com/spec/
4747
* [d0a442786](https://github.com/api-platform/core/commit/d0a44278630d201b91cbba0774a09f4eeaac88f7) feat(doctrine): enhance getLinksHandler with method validation and typo suggestions (#6874)
4848
* [f67f6f1ac](https://github.com/api-platform/core/commit/f67f6f1acb6476182c18a3503f2a8bc80ae89a0b) feat(doctrine): doctrine filters like laravel eloquent filters (#6775)
4949

50+
=======
51+
## v4.0.17
52+
53+
### Bug fixes
54+
55+
* [5124d8c57](https://github.com/api-platform/core/commit/5124d8c571bc4324aa060e4ff808d48f0ffa8d73) fix(laravel): Prevent overwriting existing routes on the router (#6941)
56+
* [5c7e0d2c0](https://github.com/api-platform/core/commit/5c7e0d2c015b5264b2f857abc7e6cb944582de21) fix(symfony): error wrongly inherit normalization context (#6939)
57+
* [af35d34d0](https://github.com/api-platform/core/commit/af35d34d01e62e96dd81dadde6a056ce67d47703) fix(laravel): mitigate property metadata read for Error (#6951)
58+
* [d5b48b1cd](https://github.com/api-platform/core/commit/d5b48b1cd6163ee755211476fdd3d4dd0bf6f7ae) fix(laravel): SwaggerUI custom CSS (#6937)
59+
* [da796b979](https://github.com/api-platform/core/commit/da796b979384663c3eaf4e4fe4d215b447800844) fix(metadata): allow serializer attribute object in ApiProperty::$serialize (#6946)
60+
* [de2d298e3](https://github.com/api-platform/core/commit/de2d298e306b196bce834af290aec242807ea39b) fix: ensure template files have a tpl file extension (#6826) (#6829)
61+
* [b6a67a197](https://github.com/api-platform/core/commit/b6a67a197a668ce15f216cffbcddc637f19c69c2) perf: various optimizations for Laravel/Symfony (#6954)
62+
63+
To save some time during cache warmup we recommend to define uri variables such as: `uriVariables: ['id']`. More details at #6954.
64+
65+
### Features
66+
5067
## v4.0.16
5168

5269
### Bug fixes

src/Laravel/ApiPlatformProvider.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ public function register(): void
250250
);
251251
});
252252

253+
$this->app->singleton(ModelMetadata::class);
253254
$this->app->bind(LoaderInterface::class, AttributeLoader::class);
254255
$this->app->bind(ClassMetadataFactoryInterface::class, ClassMetadataFactory::class);
255256
$this->app->singleton(ClassMetadataFactory::class, function (Application $app) {

src/Laravel/Eloquent/Metadata/ModelMetadata.php

Lines changed: 21 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
use Illuminate\Database\Eloquent\Model;
1717
use Illuminate\Database\Eloquent\Relations\Relation;
1818
use Illuminate\Support\Collection;
19-
use Illuminate\Support\Facades\Gate;
2019
use Illuminate\Support\Str;
2120

2221
/**
@@ -26,6 +25,16 @@
2625
*/
2726
final class ModelMetadata
2827
{
28+
/**
29+
* @var array<class-string, Collection<string, mixed>>
30+
*/
31+
private $attributesLocalCache = [];
32+
33+
/**
34+
* @var array<class-string, Collection<int, mixed>>
35+
*/
36+
private $relationsLocalCache = [];
37+
2938
/**
3039
* The methods that can be called in a model to indicate a relation.
3140
*
@@ -45,31 +54,25 @@ final class ModelMetadata
4554
'morphedByMany',
4655
];
4756

48-
/**
49-
* Gets the first policy associated with this model.
50-
*/
51-
public function getPolicy(Model $model): ?string
52-
{
53-
$policy = Gate::getPolicyFor($model::class);
54-
55-
return $policy ? $policy::class : null;
56-
}
57-
5857
/**
5958
* Gets the column attributes for the given model.
6059
*
6160
* @return Collection<string, mixed>
6261
*/
6362
public function getAttributes(Model $model): Collection
6463
{
64+
if (isset($this->attributesLocalCache[$model::class])) {
65+
return $this->attributesLocalCache[$model::class];
66+
}
67+
6568
$connection = $model->getConnection();
6669
$schema = $connection->getSchemaBuilder();
6770
$table = $model->getTable();
6871
$columns = $schema->getColumns($table);
6972
$indexes = $schema->getIndexes($table);
7073
$relations = $this->getRelations($model);
7174

72-
return collect($columns)
75+
return $this->attributesLocalCache[$model::class] = collect($columns)
7376
->reject(
7477
fn ($column) => $relations->contains(
7578
fn ($relation) => $relation['foreign_key'] === $column['name']
@@ -112,7 +115,7 @@ private function isColumnPrimaryKey(array $indexes, string $column): bool
112115
*
113116
* @return Collection<int, mixed>
114117
*/
115-
public function getVirtualAttributes(Model $model, array $columns): Collection
118+
private function getVirtualAttributes(Model $model, array $columns): Collection
116119
{
117120
$class = new \ReflectionClass($model);
118121

@@ -155,7 +158,11 @@ public function getVirtualAttributes(Model $model, array $columns): Collection
155158
*/
156159
public function getRelations(Model $model): Collection
157160
{
158-
return collect(get_class_methods($model))
161+
if (isset($this->relationsLocalCache[$model::class])) {
162+
return $this->relationsLocalCache[$model::class];
163+
}
164+
165+
return $this->relationsLocalCache[$model::class] = collect(get_class_methods($model))
159166
->map(fn ($method) => new \ReflectionMethod($model, $method))
160167
->reject(
161168
fn (\ReflectionMethod $method) => $method->isStatic()
@@ -207,20 +214,6 @@ public function getRelations(Model $model): Collection
207214
->values();
208215
}
209216

210-
/**
211-
* Gets the Events that the model dispatches.
212-
*
213-
* @return Collection<int, mixed>
214-
*/
215-
public function getEvents(Model $model): Collection
216-
{
217-
return collect($model->dispatchesEvents())
218-
->map(fn (string $class, string $event) => [
219-
'event' => $event,
220-
'class' => $class,
221-
])->values();
222-
}
223-
224217
/**
225218
* Gets the cast type for the given column.
226219
*/

src/Metadata/Resource/Factory/ConcernsResourceNameCollectionFactory.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,14 @@ public function create(): ResourceNameCollection
4747
}
4848

4949
foreach (ReflectionClassRecursiveIterator::getReflectionClassesFromDirectories($this->paths) as $className => $reflectionClass) {
50+
if (!$reflectionClass->hasMethod('apiResource')) {
51+
continue;
52+
}
53+
54+
$m = $reflectionClass->getMethod('apiResource');
55+
5056
if (
51-
$reflectionClass->hasMethod('apiResource')
52-
&& ($m = $reflectionClass->getMethod('apiResource'))
53-
&& $m->isPublic()
57+
$m->isPublic()
5458
&& $m->isStatic()
5559
) {
5660
$classes[$className] = true;

src/Metadata/Resource/Factory/LinkFactory.php

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@
2626
*/
2727
final class LinkFactory implements LinkFactoryInterface, PropertyLinkFactoryInterface
2828
{
29+
/**
30+
* @var array<class-string, string[]>
31+
*/
32+
private $localIdentifiersPerResourceClassCache = [];
33+
2934
public function __construct(private readonly PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, private readonly PropertyMetadataFactoryInterface $propertyMetadataFactory, private readonly ResourceClassResolverInterface $resourceClassResolver)
3035
{
3136
}
@@ -138,8 +143,17 @@ public function completeLink(Link $link): Link
138143
return $link;
139144
}
140145

146+
/**
147+
* @param class-string $resourceClass
148+
*
149+
* @return string[]
150+
*/
141151
private function getIdentifiersFromResourceClass(string $resourceClass): array
142152
{
153+
if (isset($this->localIdentifiersPerResourceClassCache[$resourceClass])) {
154+
return $this->localIdentifiersPerResourceClassCache[$resourceClass];
155+
}
156+
143157
$hasIdProperty = false;
144158
$identifiers = [];
145159
foreach ($this->propertyNameCollectionFactory->create($resourceClass) as $property) {
@@ -155,14 +169,14 @@ private function getIdentifiersFromResourceClass(string $resourceClass): array
155169
}
156170

157171
if ($hasIdProperty && !$identifiers) {
158-
return ['id'];
172+
return $this->localIdentifiersPerResourceClassCache[$resourceClass] = ['id'];
159173
}
160174

161175
if (!$hasIdProperty && !$identifiers && enum_exists($resourceClass)) {
162-
return ['value'];
176+
return $this->localIdentifiersPerResourceClassCache[$resourceClass] = ['value'];
163177
}
164178

165-
return $identifiers;
179+
return $this->localIdentifiersPerResourceClassCache[$resourceClass] = $identifiers;
166180
}
167181

168182
/**

src/Metadata/Util/ReflectionClassRecursiveIterator.php

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,26 @@
2222
*/
2323
final class ReflectionClassRecursiveIterator
2424
{
25+
/**
26+
* @var array<string, array<class-string, \ReflectionClass>>
27+
*/
28+
private static array $localCache;
29+
2530
private function __construct()
2631
{
2732
}
2833

29-
public static function getReflectionClassesFromDirectories(array $directories): \Iterator
34+
/**
35+
* @return array<class-string, \ReflectionClass>
36+
*/
37+
public static function getReflectionClassesFromDirectories(array $directories): array
3038
{
39+
$id = hash('xxh3', implode('', $directories));
40+
if (isset(self::$localCache[$id])) {
41+
return self::$localCache[$id];
42+
}
43+
44+
$includedFiles = [];
3145
foreach ($directories as $path) {
3246
$iterator = new \RegexIterator(
3347
new \RecursiveIteratorIterator(
@@ -61,12 +75,15 @@ public static function getReflectionClassesFromDirectories(array $directories):
6175
$sortedInterfaces = get_declared_interfaces();
6276
sort($sortedInterfaces);
6377
$declared = [...$sortedClasses, ...$sortedInterfaces];
78+
$ret = [];
6479
foreach ($declared as $className) {
6580
$reflectionClass = new \ReflectionClass($className);
6681
$sourceFile = $reflectionClass->getFileName();
6782
if (isset($includedFiles[$sourceFile])) {
68-
yield $className => $reflectionClass;
83+
$ret[$className] = $reflectionClass;
6984
}
7085
}
86+
87+
return self::$localCache[$id] = $ret;
7188
}
7289
}

0 commit comments

Comments
 (0)