Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
.gitignore export-ignore
/test export-ignore
/phpunit.xml.dist export-ignore
/phpcs.xml.dist export-ignore
4 changes: 2 additions & 2 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ jobs:
php-versions: ['8.3', '8.4']
name: PHP ${{ matrix.php-versions }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v7
- uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
- name: Get composer cache directory
id: composer-cache
run: |
echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- uses: actions/cache@v4
- uses: actions/cache@v6
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
Expand Down
103 changes: 44 additions & 59 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ This component lets you automatically store mutations based on two different str

Requirements
------------
The Entity Mutation Component requires a minimum of php 7.3 and runs on Doctrine2. For specific requirements, please check [composer.json](../master/composer.json).
The Entity Mutation Component requires a minimum of PHP 8.3 and Doctrine ORM 2.7. For specific requirements, please check [composer.json](../master/composer.json).

Installation
------------
Expand All @@ -40,13 +40,13 @@ Documentation
How does it work?
-----------------

It works by putting the `@Mutation` annotation on your Entity and registering the listener on the entityChanged event, assuming you have already configured the [Entity Tracker Component](https://github.com/hostnet/entity-tracker-component/#setup).
It works by putting the `#[Mutation]` attribute on your Entity and registering the listener on the entityChanged event, assuming you have already configured the [Entity Tracker Component](https://github.com/hostnet/entity-tracker-component/#setup).

For a usage example, follow the setup below.

Setup
-----
- You have to add `@Mutation` to your Entity
- You have to add `#[Mutation]` to your Entity
- You have to create your Mutation Entity
- Optionally you can add the `MutationAwareInterface` if your Entity knows about its own mutations

Expand All @@ -61,112 +61,100 @@ It might look a bit complicated to set up, but it's pretty much setting up the t

```php

use Hostnet\Component\EntityMutation\Listener\MutationListener;
use Hostnet\Component\EntityMutation\Resolver\MutationResolver;
use Hostnet\Component\EntityTracker\Listener\EntityChangedListener;
use Hostnet\Component\EntityTracker\Provider\EntityAnnotationMetadataProvider;
use Hostnet\Component\EntityTracker\Provider\EntityMetadataProvider;
use Hostnet\Component\EntityTracker\Provider\EntityMutationMetadataProvider;

/* @var $em \Doctrine\ORM\EntityManager */
$event_manager = $em->getEventManager();

// default doctrine annotation reader
$annotation_reader = new AnnotationReader();

// setup required providers
$annotation_metadata_provider = new EntityAnnotationMetadataProvider($annotation_reader);
$mutation_metadata_provider = new EntityMutationMetadataProvider($annotation_reader);
$entity_metadata_provider = new EntityMetadataProvider();
$mutation_metadata_provider = new EntityMutationMetadataProvider();

// pre flush event listener that uses the @Mutation annotation
// preFlush event listener that uses the #[Mutation] attribute
$entity_changed_listener = new EntityChangedListener(
$annotation_metadata_provider,
$entity_metadata_provider,
$mutation_metadata_provider
);

// the resolver is used to find the correct annotation, which
// the resolver is used to find the #[Mutation] attribute, which
// fields are considered to be tracked and stored as mutation
// and which entity represents your Mutation entity.
$mutation_resolver = new MutationResolver($annotation_metadata_provider);
$mutation_resolver = new MutationResolver($entity_metadata_provider);

// creating the mutation listener
$mutation_listener = new MutationListener($mutation_resolver);

// register the events
$event_manager->addEventListener('prePersist', $entity_changed_listener);
$event_manager->addEventListener('preFlush', $entity_changed_listener);
$event_manager->addEventListener('entityChanged', $mutation_listener);

```

#### Configuring the Entity
All we have to do now is put the `@Mutation` annotation on our Entity. The annotation has 2 options:
All we have to do now is put the `#[Mutation]` attribute on our Entity. The attribute has 1 option:
- strategy; This will determine if the current state or the previous state is stored in the Mutation
- class; the full namespace to your mutation class. By default it's the current class name suffixed with Mutation (i.e. `Acme\MyEntity` would be `Acme\MyEntityMutation` by default).

The Mutation class is always the entity's own class name suffixed with `Mutation` (i.e. `Acme\MyEntity`'s mutations are stored via `Acme\MyEntityMutation`) — this is no longer configurable.

Additionally you can configure your entity to be MutationAware, this is optional however.

```php

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\Mapping as ORM;
use Hostnet\Component\EntityMutation\Mutation;
use Hostnet\Component\EntityMutation\Attributes\Mutation;
use Hostnet\Component\EntityMutation\MutationAwareInterface;

/**
* @ORM\Entity
* @ORM\Table(name="users")
* @Mutation(
* class = "MyUserEntityMutation",
* strategy = "previous"
* )
* The above values are equal to the defaults. They are only
* here to show how you can use them outside of this example
*/
// The strategy below is the default, only shown for illustration.
#[ORM\Entity]
#[ORM\Table(name: 'users')]
#[Mutation(strategy: Mutation::STRATEGY_COPY_PREVIOUS)]
class MyUserEntity implements MutationAwareInterface
{
...
private $id;

/**
* @ORM\...
*/
private $city;
#[ORM\Column(...)]
private string $city;

/**
* @ORM\...
*/
private $name;
#[ORM\Column(...)]
private string $name;

/**
* @ORM\OneToMany...
* @var ArrayCollection
*/
private $mutations;
#[ORM\OneToMany(...)]
private Collection $mutations;

public function setName($name) { ... }
public function getName() { ... }
public function setName(string $name): void { ... }
public function getName(): string { ... }

public function addMutation($element)
#[\Override]
public function addMutation(object $mutation): void
{
$this->mutations->add($element);
$this->mutations->add($mutation);
}

public function getMutations()
#[\Override]
public function getMutations(): array
{
return $this->mutations;
return $this->mutations->toArray();
}

/**
* Used to get the last mutation stored, you might want to change
* it to return the one before that if your strategy is current.
*/
public function getPreviousMutation()
#[\Override]
public function getPreviousMutation(): ?object
{
$criteria = (new Criteria())
->orderBy(['id' => Criteria::DESC])
->setMaxResults(1);

return $this->mutations->matching($criteria)->current() ? : null;
return $this->mutations->matching($criteria)->current() ?: null;
}
}

Expand All @@ -185,22 +173,19 @@ This is done so you have full control over the what fields and how you want to s

use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
class MyUserEntityMutation
{
...

/**
* @ORM\ManyToOne(targetEntity="MyUserEntity", inversedBy="mutations")
*/
private $user;
#[ORM\ManyToOne(targetEntity: MyUserEntity::class, inversedBy: 'mutations')]
private MyUserEntity $user;

/**
* @ORM\...
*/
private $name;
#[ORM\Column(...)]
private string $name;

public function setName($name) { ... }
public function getName() { ... }
public function setName(string $name): void { ... }
public function getName(): string { ... }

public function __construct(MyUserEntity $user, MyUserEntity $original_data)
{
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"require": {
"php": "^8.3",
"doctrine/orm": "^2.7.4",
"hostnet/entity-tracker-component": "^2.3.0"
"hostnet/entity-tracker-component": "^3.0.0"
},
"require-dev": {
"hostnet/database-test-lib": "^2.0.2",
Expand Down
2 changes: 1 addition & 1 deletion phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd" colors="true" bootstrap="test/bootstrap.php">
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd" colors="true">
<coverage>
<include>
<directory>src</directory>
Expand Down
9 changes: 5 additions & 4 deletions src/Attributes/Mutation.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ public function __construct(
public string $strategy = self::STRATEGY_COPY_PREVIOUS,
) {
}
/**
* The Previous values will be stored in the mutation table. This is the
* default strategy.
*/

/**
* The Previous values will be stored in the mutation table. This is the
* default strategy.
*/
public const string STRATEGY_COPY_PREVIOUS = 'previous';

/**
Expand Down
28 changes: 7 additions & 21 deletions src/Listener/MutationListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

namespace Hostnet\Component\EntityMutation\Listener;

use Doctrine\ORM\EntityManagerInterface;
use Hostnet\Component\EntityMutation\Attributes\Mutation;
use Hostnet\Component\EntityMutation\MutationAwareInterface;
use Hostnet\Component\EntityMutation\Resolver\MutationResolverInterface;
Expand All @@ -16,18 +17,12 @@

class MutationListener
{
/**
* @param MutationResolverInterface $resolver
*/
public function __construct(
private MutationResolverInterface $resolver,
private CacheItemPoolInterface $is_mutation_cache = new ArrayAdapter()
) {
}

/**
* @param EntityChangedEvent $event
*/
public function entityChanged(EntityChangedEvent $event): void
{
$em = $event->getEntityManager();
Expand All @@ -47,16 +42,11 @@ public function entityChanged(EntityChangedEvent $event): void
return;
}

switch ($strategy) {
case Mutation::STRATEGY_COPY_CURRENT:
$mutation_source = $entity;
break;
case Mutation::STRATEGY_COPY_PREVIOUS:
$mutation_source = $event->getOriginalEntity();
break;
default:
throw new \RuntimeException(sprintf("Unknown strategy '%s'.", $strategy));
}
$mutation_source = match ($strategy) {
Mutation::STRATEGY_COPY_CURRENT => $entity,
Mutation::STRATEGY_COPY_PREVIOUS => $event->getOriginalEntity(),
default => throw new \RuntimeException(sprintf("Unknown strategy '%s'.", $strategy)),
};

$mutation = $em
->getClassMetadata($this->resolver->getMutationClassName($em, $entity))
Expand All @@ -70,7 +60,7 @@ public function entityChanged(EntityChangedEvent $event): void
}
}

private function getMutationStrategy($em, $entity): false|string
private function getMutationStrategy(EntityManagerInterface $em, object $entity): false|string
{
$cache_key = base64_encode('MUTATION-' . get_class($entity));
$cached_item = $this->is_mutation_cache->getItem($cache_key);
Expand All @@ -83,10 +73,6 @@ private function getMutationStrategy($em, $entity): false|string
return $this->save($cached_item, $attribute->getStrategy());
}

if (null !== $annotation = $this->resolver->getMutationAnnotation($em, $entity)) {
return $this->save($cached_item, $annotation->getStrategy());
}

return $this->save($cached_item, false);
}

Expand Down
58 changes: 0 additions & 58 deletions src/Mutation.php

This file was deleted.

Loading