Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
e2ce84a
feat: convert AbstractLogEntry::$data from array to json type for DBA…
Copilot May 7, 2026
337ef49
feat: add migration script and update documentation for json data field
Copilot May 7, 2026
77364bd
fix: address code review issues in migration script (SQL injection, p…
Copilot May 7, 2026
69b076e
fix: further improve migration script based on code review (regex, PR…
Copilot May 7, 2026
d63f682
chore: remove bin from .gitignore and move migration script to bin/
Copilot May 7, 2026
24df0e0
docs(loggable): document --drop-legacy option and all CLI options in …
Copilot May 7, 2026
6bd19f8
feat(loggable): convert JSON migration script to Symfony command with…
Copilot May 8, 2026
f6196ab
chore(loggable): clarify DBAL fallback comment and extract SQLite reg…
Copilot May 8, 2026
e95c19b
fix(loggable): harden migration unserialize handling for malformed data
Copilot May 8, 2026
bbb2443
fix: register loggable command via #[AsCommand] and make DSN option o…
Copilot May 8, 2026
01ba89e
feat: remove DSN and auto-resolve log entry tables for migration command
Copilot May 8, 2026
0abcd35
fix: harden loggable table resolution
Copilot May 8, 2026
06c9959
fix: compare loggable connection params strictly
Copilot May 8, 2026
da26e58
refactor: normalize loggable connection identity matching
Copilot May 8, 2026
44803c0
refactor: avoid duplicate loggable connection lookups
Copilot May 8, 2026
3f6e517
fix: require injected connection for loggable migration command
Copilot May 8, 2026
6f4961b
refactor: tidy loggable connection identity handling
Copilot May 8, 2026
c39e75d
refactor: cache loggable target connection identity
Copilot May 8, 2026
a6d07ba
refactor: extract loggable connection match helper
Copilot May 8, 2026
95bc7e1
docs: clarify AsCommand vendor registration note
Copilot May 8, 2026
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: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
.php-cs-fixer.cache
bin
vendor
composer.lock
coverage.xml
Expand Down
77 changes: 76 additions & 1 deletion doc/loggable.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
The **Loggable** behavior adds support for logging changes to and restoring prior versions of your Doctrine objects.

> [!NOTE]
> The Loggable extension is NOT compatible with `doctrine/dbal` 4.0 or later
> The `data` column in the log entry table was changed from PHP's `serialize()` format to JSON in order to be compatible with `doctrine/dbal` 4.0 or later. If you are upgrading from an older version, see [Migrating existing serialized data to JSON](#migrating-existing-serialized-data-to-json) below.

## Index

Expand All @@ -13,6 +13,7 @@ The **Loggable** behavior adds support for logging changes to and restoring prio
- [Object Repositories](#object-repositories)
- [Fetching a Model's Log Entries](#fetching-a-models-log-entries)
- [Revert a Model to a Previous Version](#revert-a-model-to-a-previous-version)
- [Migrating Existing Serialized Data to JSON](#migrating-existing-serialized-data-to-json)

## Getting Started

Expand Down Expand Up @@ -247,3 +248,77 @@ $repo = $em->getRepository(LogEntry::class);
// We are now able to revert to an older version
$repo->revert($article, 2);
```

## Migrating Existing Serialized Data to JSON

If you have an existing `ext_log_entries` table (or custom ORM log entry tables) with data stored in the old
PHP `serialize()` format, you need to migrate it to JSON before using `doctrine/dbal` 4.0 or later.

The project provides a Symfony console command for this migration. The command:

1. Renames the existing `data` column to `data_serialized` (so no existing data is lost).
2. Adds a new `data` column with a JSON-compatible type.
3. Reads each row, calls `unserialize()` on the old value and `json_encode()` on the result, and writes it into the new column.
4. Optionally drops the `data_serialized` backup column automatically when `--drop-legacy` is passed. Without this flag the column is kept so you can verify the migration manually before dropping it.

### Usage (Symfony command)

```bash
php bin/console gedmo:loggable:migrate-data-to-json \
[--batch-size=500] \
[--drop-legacy]
```

The command uses the injected Doctrine DBAL connection and resolves the mapped ORM log entry table(s)
from Doctrine metadata, so you do not need to pass credentials or table names on the command line.

> [!NOTE]
> Symfony does not automatically discover command services from vendor packages by the `#[AsCommand]` attribute alone.
> If you integrate this library directly, register the command as a service yourself or use a Symfony bundle/recipe that imports vendor services for you. For example:
>
> ```yaml
> services:
> Gedmo\Loggable\Command\MigrateDataToJsonCommand: ~
> ```

| Option | Description |
|---|---|
| `--batch-size` | Number of rows to process per database round-trip (default: `500`). |
| `--drop-legacy` | Drop the `data_serialized` backup column automatically after a successful migration. Omit this flag if you want to keep the backup column for manual verification first. |

> [!NOTE]
> Run this script **before** updating the entity mapping to use the `json` type and before upgrading to DBAL 4.
> Make a database backup before running the migration.

### Manual migration approach

If you prefer to handle the migration yourself, the steps are:

1. **Rename** the `data` column to `data_serialized`:
```sql
ALTER TABLE ext_log_entries RENAME COLUMN data TO data_serialized;
```

2. **Add** a new `data` column (use `LONGTEXT` for MySQL/MariaDB or `TEXT` for SQLite/PostgreSQL):
```sql
ALTER TABLE ext_log_entries ADD COLUMN data LONGTEXT DEFAULT NULL;
```

3. **Convert** each row using PHP (the `unserialize()` → `json_encode()` round-trip):

```php
$rows = $connection->fetchAllAssociative('SELECT id, data_serialized FROM ext_log_entries WHERE data_serialized IS NOT NULL');
foreach ($rows as $row) {
$deserialized = unserialize($row['data_serialized'], ['allowed_classes' => false]);
$json = json_encode($deserialized, JSON_THROW_ON_ERROR);
$connection->executeStatement(
'UPDATE ext_log_entries SET data = ? WHERE id = ?',
[$json, $row['id']]
);
}
```

4. **Drop** the legacy column once you have verified the migration:
```sql
ALTER TABLE ext_log_entries DROP COLUMN data_serialized;
```
Loading