diff --git a/README.md b/README.md index 2651ef6..c9c9ce9 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,8 @@ entities and generates URLs that need purging based on configured routes. ## Features -- **Doctrine Event Integration**: Listens to **Doctrine** lifecycle events (`postUpdate`, `postRemove`, `postPersist`) - to automatically detect when entities are modified, created, or deleted. +- **Doctrine Event Integration**: Listens to **Doctrine** lifecycle events (`postPersist`, `postUpdate`, `preRemove`) + to automatically detect when entities are created, modified, or deleted. - **Automatic URL Generation**: Automatically generates purge requests for relevant URLs based on the affected entities and their associated routes. diff --git a/docs/README.md b/docs/README.md index f69f602..28ff6b8 100644 --- a/docs/README.md +++ b/docs/README.md @@ -25,8 +25,8 @@ It also provides a `void` purger, which can be used during development when cach purger simply ignores all purge requests, making it ideal for non-production environments. Additionally, an `in-memory` purger is included, specifically designed for testing purposes. -For advanced use cases, you can create [custom purgers](custom-purgers.md) to integrate with any custom or -third-party HTTP cache backend that fits your project requirements. +For advanced use cases, you can create [custom purgers](custom-purgers.md) to integrate with any custom or third-party +HTTP cache backend that fits your project requirements. ### Configuring Symfony's HTTP Cache @@ -41,6 +41,8 @@ purgatory: purger: symfony ``` +This is a shorthand for `purger: { name: symfony }`. + ### Configuring Varnish Cache To enable Varnish to support `PURGE` requests, add the following example configuration to your VCL file. You may need to @@ -123,10 +125,16 @@ bin/console messenger:consume async ## How It Works -The bundle listens to **Doctrine** lifecycle events (`postUpdate`, `postRemove`, `postPersist`) to automatically detect -when entities are modified, created, or deleted. When these changes are flushed to the database, the bundle steps in to +The bundle listens to **Doctrine** lifecycle events (`postPersist`, `postUpdate`, `preRemove`) to automatically detect +when entities are created, modified, or deleted. When these changes are flushed to the database, the bundle steps in to process them. +By default, the bundle registers a Doctrine DBAL middleware that triggers the processing of collected purge requests +right after the database transaction is committed. This ensures purges only happen for changes that were actually +persisted. If the middleware is disabled (`doctrine_middleware: { enabled: false }`), the bundle falls back to +Doctrine's `postFlush` event instead, which runs after changes are flushed but before an eventual wrapping transaction +is committed. + The bundle uses **purge subscriptions**, which are predefined rules that associate specific entities and their properties with corresponding routes and route parameters. These subscriptions help identify which content should be purged based on changes to the entities. @@ -147,8 +155,8 @@ entities and properties, giving you greater control over purging behavior in mor Purge subscriptions can be configured using the [`#[PurgeOn]`][1] attribute. Controllers using this attribute **MUST** be registered as services. -You can also configure purge subscriptions [using YAML](purge-subscriptions-using-yaml.md). This is particularly -useful if you have routes without an associated controller or action. +You can also configure purge subscriptions [using YAML](purge-subscriptions-using-yaml.md). This is particularly useful +if you have routes without an associated controller or action. ### Basic Example @@ -455,7 +463,18 @@ when@test: ``` To write tests, use the `InteractsWithPurgatory` trait in your test class, which provides helper methods to verify -purged URLs and clear the in-memory purger: +purged URLs and clear the in-memory purger. + +The following example assumes a `post_details` route with a `{slug}` parameter, where the slug is generated from the +post's title: + +```php +#[Route('/post/{slug}', name: 'post_details', methods: 'GET')] +#[PurgeOn(Post::class, routeParams: ['slug' => 'slug'])] +public function detailsAction(Post $post) +{ +} +``` ```php use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; @@ -465,17 +484,17 @@ class PurgeTest extends KernelTestCase { use InteractsWithPurgatory; - // ... - public function testPurgePost() { + $entityManager = self::getContainer()->get('doctrine.orm.entity_manager'); + // Create and persist a new Post entity $post = new Post(); $post->title = 'Title'; $post->text = 'Text'; - $this->entityManager->persist($post); - $this->entityManager->flush(); + $entityManager->persist($post); + $entityManager->flush(); // Assert that the URL for the post has been purged self::assertUrlIsPurged('/post/title'); @@ -486,7 +505,7 @@ class PurgeTest extends KernelTestCase // Update the Post entity and flush the changes $post->title = 'Title New'; - $this->entityManager->flush(); + $entityManager->flush(); // Assert that both the old and new URLs have been purged self::assertUrlIsPurged('/post/title'); @@ -518,11 +537,7 @@ This command provides insights into which routes and parameters are associated w - [Custom Expression Language Functions](custom-expression-language-functions.md) [0]: https://github.com/sofascore/purgatory-bundle/blob/1.x/src/Purger/PurgerInterface.php - [1]: https://github.com/sofascore/purgatory-bundle/blob/1.x/src/Attribute/PurgeOn.php - [2]: https://github.com/sofascore/purgatory-bundle/blob/1.x/src/Attribute/TargetedProperties.php - [3]: https://github.com/sofascore/purgatory-bundle/blob/1.x/src/Listener/Enum/Action.php - [4]: https://github.com/sofascore/purgatory-bundle/blob/1.x/src/Test/InteractsWithPurgatory.php diff --git a/docs/complex-route-params.md b/docs/complex-route-params.md index 3092075..ede179b 100644 --- a/docs/complex-route-params.md +++ b/docs/complex-route-params.md @@ -51,7 +51,7 @@ elements within the collections are purged: ```php #[Route('/posts/{tag}/{commentId<\d+>}', name: 'posts_list', methods: 'GET')] -#[PurgeOn(Post::class, routeParams: ['tag' => 'tags[*].id', 'commentId' => 'comments[*].id'])] +#[PurgeOn(Post::class, routeParams: ['tag' => 'tags[*].name', 'commentId' => 'comments[*].id'])] public function listAction(string $tag, Comment $comment) { } @@ -134,9 +134,8 @@ public function listAction() } ``` -By default, the entire entity being purged is passed to the route parameter service. -If your service only needs a specific part of the entity, you can limit what is passed by providing a second argument to -`DynamicValues`. +By default, the entire entity being purged is passed to the route parameter service. If your service only needs a +specific part of the entity, you can limit what is passed by providing a second argument to `DynamicValues`. This argument is a **Symfony PropertyAccess property path** and will be resolved against the entity before being passed to the service: diff --git a/docs/custom-expression-language-functions.md b/docs/custom-expression-language-functions.md index 7e89418..be270e4 100644 --- a/docs/custom-expression-language-functions.md +++ b/docs/custom-expression-language-functions.md @@ -10,6 +10,20 @@ public function detailsAction(Post $post) } ``` +When used in an `if` expression, the function's return value determines whether the purge occurs, so it should return a +boolean. When used in an `ExpressionValues` expression, it should return the value (or an array of values) to use for +the route parameter: + +```php +use Sofascore\PurgatoryBundle\Attribute\RouteParamValue\ExpressionValues; + +#[Route('/posts-by-author/{full_name}', name: 'posts_list_by_author', methods: 'GET')] +#[PurgeOn(Author::class, routeParams: ['full_name' => new ExpressionValues('author_full_name(obj)')])] +public function listAction(Author $author) +{ +} +``` + To enable this functionality, make sure your service is tagged correctly in the service configuration: ```yaml diff --git a/docs/custom-purgers.md b/docs/custom-purgers.md index dd96464..0ad19d6 100644 --- a/docs/custom-purgers.md +++ b/docs/custom-purgers.md @@ -20,7 +20,7 @@ class CloudflarePurger implements PurgerInterface } ``` -### Enabling Your Custom Purger +## Enabling Your Custom Purger To enable your custom purger, update your configuration file with the alias you specified: diff --git a/docs/custom-route-providers.md b/docs/custom-route-providers.md index bf6e71a..6dd61a2 100644 --- a/docs/custom-route-providers.md +++ b/docs/custom-route-providers.md @@ -9,7 +9,7 @@ To create a custom route provider, implement the [`RouteProviderInterface`][0]. [`PurgeRoute`][1] to define route names and their parameters. You can implement custom logic to determine the appropriate routes based on the action, the entity, and any changes detected in the entity's properties. -### Example +## Example Here's an example of a custom route provider for handling `Post` entities: @@ -20,6 +20,12 @@ use Sofascore\PurgatoryBundle\RouteProvider\RouteProviderInterface; class MyPostRouteProvider implements RouteProviderInterface { + public function supports(Action $action, object $entity): bool + { + // Define the conditions under which this provider should be used + return $entity instanceof Post; + } + public function provideRoutesFor(Action $action, object $entity, array $entityChangeSet): iterable { // Custom logic to determine routes based on the action, entity, and changes @@ -31,16 +37,10 @@ class MyPostRouteProvider implements RouteProviderInterface ] ); } - - public function supports(Action $action, object $entity): bool - { - // Define the conditions under which this provider should be used - return $entity instanceof Post; - } } ``` -### Registering Your Custom Route Provider +## Registering Your Custom Route Provider If you're not using Symfony's [autoconfigure](https://symfony.com/doc/current/service_container.html#the-autoconfigure-option) feature, you @@ -57,5 +57,4 @@ By tagging it with `purgatory.route_provider`, the bundle will automatically rec provider when processing purge requests. [0]: https://github.com/sofascore/purgatory-bundle/blob/1.x/src/RouteProvider/RouteProviderInterface.php - [1]: https://github.com/sofascore/purgatory-bundle/blob/1.x/src/RouteProvider/PurgeRoute.php diff --git a/docs/purge-subscriptions-using-yaml.md b/docs/purge-subscriptions-using-yaml.md index 5ca6974..b7c728b 100644 --- a/docs/purge-subscriptions-using-yaml.md +++ b/docs/purge-subscriptions-using-yaml.md @@ -3,6 +3,10 @@ Purge subscriptions can also be configured using YAML. This is particularly useful if you have routes without an associated controller or action. +Each top-level key is the **route name** the subscription applies to. YAML files placed in `config/purgatory/` are +loaded automatically. To load definitions from other files or directories, list them under the `mapping_paths` +configuration option. + To get started, create a YAML file in `config/purgatory/`, for example: ```yaml