Skip to content

Commit

Permalink
refactor: fix last deprecations
Browse files Browse the repository at this point in the history
  • Loading branch information
nikophil committed Dec 29, 2023
1 parent 81d6bc7 commit 892ac53
Show file tree
Hide file tree
Showing 30 changed files with 738 additions and 418 deletions.
180 changes: 180 additions & 0 deletions UPGRADE-2.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
# Migration guide from Foundry 1.x to 2.0

Foundry 2 has changed some of its API.
The global philosophy is still the same.
The main change is that we've introduced a separation between "object" factories,
"persistence" factories and "persistence with proxy" factories.

When Foundry 1.x was "persistence first", Foundry 2 is "object first".
This would allow more decoupling from the persistence layer.

## How to

Every modification needed for a 1.x to 2.0 migration is covered by a deprecation.
All you have to do is to upgrade to the latest 1.x version, activate the deprecation helper, make the tests run, and
fix all the deprecations reported.

Here is an example of how the deprecation helper can be activated.
You should set the `SYMFONY_DEPRECATIONS_HELPER` variable in `phpunit.xml` or `.env.local` file:
```shell
SYMFONY_DEPRECATIONS_HELPER="max[self]=0&max[direct]=0&quiet[]=indirect&quiet[]=other"
```

Here is the full list of modifications needed:

## Factory

- `withAttributes()` and `addState()` are both deprecated in favor of `with()`
- `sequence()` and `createSequence()` do not accept `callable` as a parameter anymore

### Change factories' base class

`Zenstruck\Foundry\ModelFactory` is now deprecated.
You should choose between:
- `\Zenstruck\Foundry\Object\ObjectFactory`: creates not-persistent plain objects,
- `\Zenstruck\Foundry\Persistence\PersistentObjectFactory`: creates and stores persisted objects, and directly return them,
- `\Zenstruck\Foundry\Persistence\PersistentProxyObjectFactory`: same as above, but returns a "proxy" version of the object.
This last class basically acts the same way as the old `ModelFactory`.

As a rule of thumb to help you to choose between these two new factory parent classes:
- using `ObjectFactory` is straightforward: if the object cannot be persisted, you must use this one
- only entities (ORM) or documents (ODM) should use `PersistentObjectFactory` or `PersistentProxyObjectFactory`
- you should only use `PersistentProxyObjectFactory` if you want to leverage "auto refresh" behavior

> [!WARNING]
> nor `PersistentObjectFactory` or `PersistentProxyObjectFactory` should be chosen to create not persistent objects.
> This will throw a deprecation in 1.x and will create an error in 2.0
> [!IMPORTANT]
> Since `PersistentObjectFactory` does not return a `Proxy` anymore, you'll have to remove all calls to `->object()`
> or any other proxy method on object created by this type of factory.
> [!NOTE]
> You will have to change some methods prototypes in your classes:
```php
// before
protected function getDefaults(): array
{
// ...
}

// after
protected function defaults(): array|callable
{
// ...
}
```

```php
// before
protected static function getClass(): string
{
// ...
}

// after
public static function class(): string
{
// ...
}
```

```php
// before
protected function initialize()
{
// ...
}

// after
protected function initialize(); static
{
// ...
}
```

## Proxy

Foundry 2.0 will completely change how `Proxy` system works, by leveraging Symfony's lazy proxy mechanism.
`Proxy` won't be anymore a wrapper class, but a "real" proxy, meaning your objects will be of the desired class AND `Proxy` object.
This implies that calling `->object()` (or, now, `_real()`) everywhere to satisfy the type system won't be needed anymore!

`Proxy` class comes with deprecations as well:
- replace everywhere you're type-hinting `Zenstruck\Foundry\Proxy` to the interface `Zenstruck\Foundry\Persistence\Proxy`
- most of `Proxy` methods are deprecated:
- `object()` -> `_real()`
- `save()` -> `_save()`
- `remove()` -> `_delete()`
- `refresh()` -> `_refresh()`
- `forceSet()` -> `_set()`
- `forceGet()` -> `_get()`
- `repository()` -> `_repository()`
- `enableAutoRefresh()` -> `_enableAutoRefresh()`
- `disableAutoRefresh()` -> `_disableAutoRefresh()`
- `withoutAutoRefresh()` -> `_withoutAutoRefresh()`
- `isPersisted()` is removed without any replacement
- `forceSetAll()` is removed without any replacement
- `assertPersisted()` is removed without any replacement
- `assertNotPersisted()` is removed without any replacement
- Everywhere you've type-hinted `Zenstruck\Foundry\FactoryCollection<T>` which was coming from a `PersistentProxyObjectFactory`, replace to `Zenstruck\Foundry\FactoryCollection<Proxy<T>>`

## Instantiator

- `Zenstruck\Foundry\Instantiator` class is deprecated in favor of `\Zenstruck\Foundry\Object\Instantiator`. You should change them everywhere.
- `new Instantiator()` is deprecated: use `Instantiator::withConstructor()` or `Instantiator::withoutConstructor()` depending on your needs.
- `Instantiator::alwaysForceProperties()` is deprecated in favor of `Instantiator::alwaysForce()`. Be careful of the modification of the parameter which is now a variadic.
- `Instantiator::allowExtraAttributes()` is deprecated in favor of `Instantiator::allowExtra()`. Be careful of the modification of the parameter which is now a variadic.
- Configuration `zenstruck_foundry.without_constructor` is deprecated in favor of `zenstruck_foundry.use_constructor`

## Standalone functions

- `Zenstruck\Foundry\create()` -> `Zenstruck\Foundry\Persistence\persist()`
- `Zenstruck\Foundry\instantiate()` -> `Zenstruck\Foundry\object()`
- `Zenstruck\Foundry\repository()` -> `Zenstruck\Foundry\Persistence\repository()`
- `Zenstruck\Foundry\Factory::delayFlush()` -> `Zenstruck\Foundry\Persistence\flush_after()`
- Usage of any method in `Zenstruck\Foundry\Test\TestState` should be replaced by `Zenstruck\Foundry\Test\UnitTestConfig::configure()`
- `Zenstruck\Foundry\instantiate_many()` is removed without any replacement
- `Zenstruck\Foundry\create_many()` is removed without any replacement

## Trait `Factories`
- `Factories::disablePersist()` -> `Zenstruck\Foundry\Persistence\disable_persisting()`
- `Factories::enablePersist()` -> `Zenstruck\Foundry\Persistence\enable_persisting()`
- both `disablePersist()` and `enable_persisting()` should not be called when Foundry is booted without Doctrine (ie: in a unit test)

## Bundle configuration

Here is a diff of the bundle's configuration, all configs in red should be migrated to the green ones:

```diff
zenstruck_foundry:
- auto_refresh_proxies: null
instantiator:
- without_constructor: false
+ use_constructor: true
+ orm:
+ auto_persist: true
+ reset:
+ connections: [default]
+ entity_managers: [default]
+ mode: schema
+ mongo:
+ auto_persist: true
+ reset:
+ document_managers: [default]
- database_resetter:
- enabled: true
- orm:
- connections: []
- object_managers: []
- reset_mode: schema
- odm:
- object_managers: []
```

## Misc.
- type-hinting to `Zenstruck\Foundry\RepositoryProxy` should be replaced by `Zenstruck\Foundry\Persistence\RepositoryDecorator`
- type-hinting to `Zenstruck\Foundry\RepositoryAssertions` should be replaced by `Zenstruck\Foundry\Persistence\RepositoryAssertions`



1 change: 0 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@
"psr-4": {"Zenstruck\\Foundry\\": "src/"},
"files": [
"src/functions.php",
"src/deprecations.php",
"src/Persistence/functions.php"
]
},
Expand Down
25 changes: 25 additions & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,21 @@ parameters:
count: 1
path: src/FactoryCollection.php

-
message: "#^Class Zenstruck\\\\Foundry\\\\Instantiator extends @final class Zenstruck\\\\Foundry\\\\Object\\\\Instantiator\\.$#"
count: 1
path: src/Instantiator.php

-
message: "#^If condition is always false\\.$#"
count: 1
path: src/Instantiator.php

-
message: "#^Class Zenstruck\\\\Foundry\\\\Object\\\\ObjectFactory not found\\.$#"
count: 2
path: src/ModelFactory.php

-
message: "#^Method Zenstruck\\\\Foundry\\\\Persistence\\\\PersistentObjectFactory\\:\\:__callStatic\\(\\) should return array\\<int, TModel of object\\> but returns array\\<int, \\(TModel of object\\)\\|Zenstruck\\\\Foundry\\\\Persistence\\\\Proxy\\<TModel of object\\>\\>\\.$#"
count: 1
Expand Down Expand Up @@ -80,11 +95,21 @@ parameters:
count: 1
path: src/Proxy.php

-
message: "#^Class Zenstruck\\\\Foundry\\\\RepositoryAssertions extends @final class Zenstruck\\\\Foundry\\\\Persistence\\\\RepositoryAssertions\\.$#"
count: 1
path: src/RepositoryAssertions.php

-
message: "#^If condition is always false\\.$#"
count: 1
path: src/RepositoryAssertions.php

-
message: "#^Class Zenstruck\\\\Foundry\\\\RepositoryProxy extends @final class Zenstruck\\\\Foundry\\\\Persistence\\\\RepositoryDecorator\\.$#"
count: 1
path: src/RepositoryProxy.php

-
message: "#^If condition is always false\\.$#"
count: 1
Expand Down
4 changes: 2 additions & 2 deletions src/Bundle/DependencyInjection/ZenstruckFoundryExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
use Symfony\Component\HttpKernel\DependencyInjection\ConfigurableExtension;
use Zenstruck\Foundry\Bundle\Command\StubMakeFactory;
use Zenstruck\Foundry\Bundle\Command\StubMakeStory;
use Zenstruck\Foundry\Instantiator;
use \Zenstruck\Foundry\Object\Instantiator;
use Zenstruck\Foundry\Persistence\PersistentObjectFactory;
use Zenstruck\Foundry\Persistence\PersistentProxyObjectFactory;
use Zenstruck\Foundry\Story;
Expand Down Expand Up @@ -127,7 +127,7 @@ private function configureDatabaseResetter(array $config, ContainerBuilder $cont
$legacyConfig = $config['database_resetter'];

if (false === $legacyConfig['enabled']) {
trigger_deprecation('zenstruck\foundry', '1.37.0', \sprintf('Disabling database reset via bundle configuration is deprecated and will be removed in 2.0. Instead you should not use "%s" trait in your test.', ResetDatabase::class));
trigger_deprecation('zenstruck\foundry', '1.37.0', 'Disabling database reset via bundle configuration is deprecated and will be removed in 2.0. Instead you should not use "%s" trait in your test.', ResetDatabase::class);

$configurationDefinition->addMethodCall('disableDatabaseReset');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public function getAutocompleteValues(): array

$class = $this->toPSR4($rootPath, $phpFile, $namespacePrefix);

if (\in_array($class, ['Zenstruck\Foundry\Proxy', 'Zenstruck\Foundry\RepositoryProxy'])) {
if (\in_array($class, ['Zenstruck\Foundry\Proxy', 'Zenstruck\Foundry\RepositoryProxy', 'Zenstruck\Foundry\RepositoryAssertions'])) {
// do not load legacy Proxy: prevents deprecations in tests.
continue;
}
Expand Down
4 changes: 2 additions & 2 deletions src/Bundle/Resources/config/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
https://symfony.com/schema/dic/services/services-1.0.xsd">

<services>
<service id=".zenstruck_foundry.default_instantiator" class="Zenstruck\Foundry\Instantiator">
<factory class="Zenstruck\Foundry\Instantiator" method="withConstructor" />
<service id=".zenstruck_foundry.default_instantiator" class="Zenstruck\Foundry\Object\Instantiator">
<factory class="Zenstruck\Foundry\Object\Instantiator" method="withConstructor" />
</service>
<service id=".zenstruck_foundry.faker" class="Faker\Generator">
<factory class="Faker\Factory" method="create" />
Expand Down
8 changes: 4 additions & 4 deletions src/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public function create(
bool $noProxy = false
): object {
if (2 === \count(\func_get_args()) && !\str_starts_with(\debug_backtrace(options: \DEBUG_BACKTRACE_IGNORE_ARGS, limit: 1)[0]['class'] ?? '', 'Zenstruck\Foundry')) {
trigger_deprecation('zenstruck\foundry', '1.37.0', \sprintf('Parameter "$noProxy" of method "%s()" is deprecated and will be removed in Foundry 2.0.', __METHOD__));
trigger_deprecation('zenstruck\foundry', '1.37.0', 'Parameter "$noProxy" of method "%s()" is deprecated and will be removed in Foundry 2.0.', __METHOD__);
}

// merge the factory attribute set with the passed attributes
Expand Down Expand Up @@ -203,7 +203,7 @@ final public function many(int $min, ?int $max = null): FactoryCollection
final public function sequence(iterable|callable $sequence): FactoryCollection
{
if (\is_callable($sequence)) {
trigger_deprecation('zenstruck\foundry', '1.37.0', \sprintf('Passing a callable to method "%s()" is deprecated and will be removed in 2.0.', __METHOD__));
trigger_deprecation('zenstruck\foundry', '1.37.0', 'Passing a callable to method "%s()" is deprecated and will be removed in 2.0.', __METHOD__);

$sequence = $sequence();
}
Expand Down Expand Up @@ -231,7 +231,7 @@ public function withoutPersisting(): self
*/
final public function withAttributes($attributes = []): self
{
trigger_deprecation('zenstruck\foundry', '1.37.0', \sprintf('Method "%s()" is deprecated and will be removed in 2.0. Use "%s::with()" instead.', __METHOD__, self::class));
trigger_deprecation('zenstruck\foundry', '1.37.0', 'Method "%s()" is deprecated and will be removed in 2.0. Use "%s::with()" instead.', __METHOD__, self::class);

return $this->with($attributes);
}
Expand Down Expand Up @@ -352,7 +352,7 @@ final public static function faker(): Faker\Generator
*/
final public static function delayFlush(callable $callback): mixed
{
trigger_deprecation('zenstruck\foundry', '1.37.0', \sprintf('Method "%s()" is deprecated and will be removed in Foundry 2.0. Use "Zenstruck\Foundry\Persistence\flush_after()" instead.', __METHOD__));
trigger_deprecation('zenstruck\foundry', '1.37.0', 'Method "%s()" is deprecated and will be removed in Foundry 2.0. Use "Zenstruck\Foundry\Persistence\flush_after()" instead.', __METHOD__);

return self::configuration()->delayFlush($callback);
}
Expand Down
2 changes: 1 addition & 1 deletion src/FactoryCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public function create(
bool $noProxy = false
): array {
if (2 === \count(\func_get_args()) && !\str_starts_with(\debug_backtrace(options: \DEBUG_BACKTRACE_IGNORE_ARGS, limit: 1)[0]['class'] ?? '', 'Zenstruck\Foundry')) {
trigger_deprecation('zenstruck\foundry', '1.37.0', \sprintf('Parameter "$noProxy" of method "%s()" is deprecated and will be removed in Foundry 2.0.', __METHOD__));
trigger_deprecation('zenstruck\foundry', '1.37.0', 'Parameter "$noProxy" of method "%s()" is deprecated and will be removed in Foundry 2.0.', __METHOD__);
}

$objects = [];
Expand Down
Loading

0 comments on commit 892ac53

Please sign in to comment.