Skip to content
Draft
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
59 changes: 59 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,64 @@
# Upgrade to 2.8

## ⚠ Doctrine ORM 3.x Upgrade

Roadiz has been upgraded from **Doctrine ORM 2.20** to **Doctrine ORM 3.6**, bringing performance improvements and better maintainability. This is a major internal upgrade with the following changes:

### What Changed

- **Doctrine ORM**: Upgraded from `~2.20.0` to `^3.6`
- **Doctrine DBAL**: Automatically upgraded to `^3.0` (ORM 3 dependency)
- **DoctrineBundle**: Remains at `^2.12` (supports both ORM 2.x and 3.x, PHP 8.3 compatible)

### Breaking Changes

⚠️ **Internal changes only** - The following deprecated APIs were removed from Roadiz codebase:

- ✅ Removed `HINT_FORCE_PARTIAL_LOAD` query hint (removed in ORM 3.0)
- Affected files: `LogRepository`, `TagRepository`, `OptimizedNodesSourcesGraphPathAggregator`
- This hint is no longer needed as ORM 3.0 properly handles scalar and array results
- ✅ No `EntityManager::merge()` usage
- ✅ No cascade merge definitions
- ✅ All entities use PHP 8 attributes (not annotations)
- ✅ No YAML mappings
- ✅ No partial flush usage
- ✅ Lazy ghost objects already enabled

### Configuration Changes

The Doctrine configuration in `config/packages/doctrine.yaml` now includes `metadata_cache_driver` for the production environment to optimize metadata caching with ORM 3.

### For Custom Code

If you have custom code in your project that extends Roadiz:

1. **Check for `merge()` usage**: ORM 3 removed `EntityManager::merge()`. Use explicit load and update patterns instead.
2. **Check cascade definitions**: `cascade={"merge"}` is no longer supported.
3. **Review custom DQL queries**: Verify they still work correctly.
4. **Test lazy loading**: Verify proxy generation and lazy loading work as expected.

### Migration Steps

No migration steps required. The upgrade is transparent for standard Roadiz installations.

### Testing

After upgrading, verify:

```bash
# Validate schema
php bin/console doctrine:schema:validate

# Sync migrations metadata storage (required for ORM 3)
php bin/console doctrine:migrations:sync-metadata-storage

# Run migrations
php bin/console doctrine:migrations:migrate

# Clear caches
php bin/console cache:clear
```

## ⚠ Twig breaking changes

- Removed bulk confirmation templates (`bulk_base.html.twig`, `bulk_delete.html.twig`), use `admin/confirm_action.html.twig` instead
Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@
"composer/package-versions-deprecated": "1.11.99.3",
"deeplcom/deepl-php": "^1.12",
"doctrine/collections": ">=1.6",
"doctrine/doctrine-bundle": "^2.8.1",
"doctrine/doctrine-bundle": "^2.12",
"doctrine/doctrine-migrations-bundle": "^3.1",
"doctrine/migrations": "^3.1.1",
"doctrine/orm": "~2.20.0",
"doctrine/orm": "^3.6",
"dragonmantank/cron-expression": "^3.4",
"endroid/qr-code": "^4.0",
"enshrined/svg-sanitize": "^0.22",
Expand Down
5 changes: 5 additions & 0 deletions config/packages/doctrine.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ doctrine:
url: '%env(resolve:DATABASE_URL)%'
profiling_collect_backtrace: '%kernel.debug%'
use_savepoints: true
types:
array: Doctrine\DBAL\Types\JsonType
orm:
auto_generate_proxy_classes: true
default_entity_manager: default
Expand Down Expand Up @@ -101,6 +103,9 @@ when@prod:
orm:
auto_generate_proxy_classes: false
proxy_dir: '%kernel.build_dir%/doctrine/orm/Proxies'
metadata_cache_driver:
type: pool
pool: doctrine.system_cache_pool
query_cache_driver:
type: pool
pool: doctrine.system_cache_pool
Expand Down
4 changes: 2 additions & 2 deletions lib/Documents/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"ext-zip": "*",
"ext-simplexml": "*",
"ext-fileinfo": "*",
"doctrine/orm": "~2.20.0",
"doctrine/orm": "^3.6",
"enshrined/svg-sanitize": "^0.22",
"intervention/image": "^3.11",
"league/flysystem": "^3.0",
Expand All @@ -43,7 +43,7 @@
},
"require-dev": {
"api-platform/metadata": "^4.1.18",
"doctrine/doctrine-bundle": "^2.8.1",
"doctrine/doctrine-bundle": "^2.12",
"php-coveralls/php-coveralls": "^2.4",
"phpstan/phpstan": "^2.1.36",
"phpstan/phpdoc-parser": "<2",
Expand Down
4 changes: 2 additions & 2 deletions lib/Models/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
],
"require": {
"php": ">=8.3",
"doctrine/orm": "~2.20.0",
"doctrine/orm": "^3.6",
"api-platform/doctrine-orm": "^4.1.18",
"api-platform/metadata": "^4.1.18",
"roadiz/nodetype-contracts": "^3.1.1",
Expand All @@ -31,7 +31,7 @@
"symfony/validator": "7.4.*"
},
"require-dev": {
"doctrine/doctrine-bundle": "^2.8.1",
"doctrine/doctrine-bundle": "^2.12",
"php-coveralls/php-coveralls": "^2.4",
"phpstan/phpstan": "^2.1.36",
"phpstan/phpdoc-parser": "<2",
Expand Down
4 changes: 2 additions & 2 deletions lib/RoadizCoreBundle/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@
"ext-mbstring": "*",
"api-platform/doctrine-orm": "^4.1.18",
"api-platform/symfony": "^4.1.18",
"doctrine/doctrine-bundle": "^2.8.1",
"doctrine/doctrine-bundle": "^2.12",
"doctrine/doctrine-migrations-bundle": "^3.1",
"doctrine/orm": "~2.20.0",
"doctrine/orm": "^3.6",
"dragonmantank/cron-expression": "^3.4",
"gedmo/doctrine-extensions": "^3.19.0",
"inlinestyle/inlinestyle": "~1.2.7",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
use Doctrine\ORM\Event\PostLoadEventArgs;
use Doctrine\ORM\Events;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\ORM\Mapping\ClassMetadata;
use Psr\Log\LoggerInterface;
use RZ\Roadiz\Contracts\NodeType\NodeTypeClassLocatorInterface;
use RZ\Roadiz\Contracts\NodeType\NodeTypeFieldInterface;
Expand Down Expand Up @@ -90,9 +90,9 @@ public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs): void
];

if (Configuration::INHERITANCE_TYPE_JOINED === $this->inheritanceType) {
$metadata->setInheritanceType(ClassMetadataInfo::INHERITANCE_TYPE_JOINED);
$metadata->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_JOINED);
} elseif (Configuration::INHERITANCE_TYPE_SINGLE_TABLE === $this->inheritanceType) {
$metadata->setInheritanceType(ClassMetadataInfo::INHERITANCE_TYPE_SINGLE_TABLE);
$metadata->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE);
/*
* If inheritance type is single table, we need to set indexes on parent class: NodesSources
*/
Expand Down
2 changes: 1 addition & 1 deletion lib/RoadizCoreBundle/src/Entity/Attribute.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class Attribute implements AttributeInterface
#[ORM\OneToMany(
mappedBy: 'attribute',
targetEntity: AttributeDocuments::class,
cascade: ['persist', 'merge'],
cascade: ['persist'],
orphanRemoval: true
),
ORM\OrderBy(['position' => 'ASC']),
Expand Down
4 changes: 2 additions & 2 deletions lib/RoadizCoreBundle/src/Entity/AttributeDocuments.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class AttributeDocuments implements PositionedInterface, PersistableInterface
public function __construct(
#[ORM\ManyToOne(
targetEntity: Attribute::class,
cascade: ['persist', 'merge'],
cascade: ['persist'],
fetch: 'EAGER',
inversedBy: 'attributeDocuments'
),
Expand All @@ -43,7 +43,7 @@ public function __construct(
protected Attribute $attribute,
#[ORM\ManyToOne(
targetEntity: Document::class,
cascade: ['persist', 'merge'],
cascade: ['persist'],
fetch: 'EAGER',
inversedBy: 'attributeDocuments'
),
Expand Down
1 change: 0 additions & 1 deletion lib/RoadizCoreBundle/src/Entity/Document.php
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,6 @@ class Document implements \Stringable, AdvancedDocumentInterface, HasThumbnailIn
/**
* @var Collection<int, FolderInterface>
*/
#[ORM\JoinTable(name: 'documents_folders')]
#[ORM\ManyToMany(targetEntity: Folder::class, mappedBy: 'documents')]
#[SymfonySerializer\Ignore]
protected Collection $folders;
Expand Down
2 changes: 1 addition & 1 deletion lib/RoadizCoreBundle/src/Entity/Setting.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class Setting implements PersistableInterface

#[ORM\ManyToOne(
targetEntity: SettingGroup::class,
cascade: ['persist', 'merge'],
cascade: ['persist'],
inversedBy: 'settings'
)]
#[ORM\JoinColumn(name: 'setting_group_id', referencedColumnName: 'id', onDelete: 'SET NULL')]
Expand Down
2 changes: 1 addition & 1 deletion lib/RoadizCoreBundle/src/Entity/Tag.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ class Tag implements DateTimedInterface, LeafInterface, PersistableInterface, \S
#[ORM\OneToMany(
mappedBy: 'parent',
targetEntity: Tag::class,
cascade: ['persist', 'merge'],
cascade: ['persist'],
orphanRemoval: true
)]
#[ORM\OrderBy(['position' => 'ASC'])]
Expand Down
2 changes: 1 addition & 1 deletion lib/RoadizCoreBundle/src/Entity/TagTranslation.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class TagTranslation implements PersistableInterface
#[ORM\OneToMany(
mappedBy: 'tagTranslation',
targetEntity: TagTranslationDocuments::class,
cascade: ['persist', 'merge'],
cascade: ['persist'],
orphanRemoval: true
)]
#[ORM\OrderBy(['position' => 'ASC'])]
Expand Down
4 changes: 2 additions & 2 deletions lib/RoadizCoreBundle/src/Entity/TagTranslationDocuments.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class TagTranslationDocuments implements PositionedInterface, PersistableInterfa
public function __construct(
#[ORM\ManyToOne(
targetEntity: TagTranslation::class,
cascade: ['persist', 'merge'],
cascade: ['persist'],
fetch: 'EAGER',
inversedBy: 'tagTranslationDocuments'
)]
Expand All @@ -41,7 +41,7 @@ public function __construct(
protected TagTranslation $tagTranslation,
#[ORM\ManyToOne(
targetEntity: Document::class,
cascade: ['persist', 'merge'],
cascade: ['persist'],
fetch: 'EAGER',
inversedBy: 'tagTranslations'
)]
Expand Down
10 changes: 1 addition & 9 deletions lib/RoadizCoreBundle/src/EntityHandler/TranslationHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@

namespace RZ\Roadiz\CoreBundle\EntityHandler;

use Doctrine\Common\Cache\FlushableCache;
use Doctrine\ORM\EntityManagerInterface;
use RZ\Roadiz\Core\AbstractEntities\TranslationInterface;
use RZ\Roadiz\Core\Handlers\AbstractHandler;
use RZ\Roadiz\CoreBundle\Entity\Translation;
use Symfony\Component\Cache\ResettableInterface;

/**
* Handle operations with translations entities.
Expand Down Expand Up @@ -48,13 +46,7 @@ public function makeDefault(): static
$this->objectManager->flush();

if ($this->objectManager instanceof EntityManagerInterface) {
$cache = $this->objectManager->getConfiguration()->getResultCacheImpl();
if ($cache instanceof FlushableCache) {
$cache->flushAll();
}
if ($cache instanceof ResettableInterface) {
$cache->reset();
}
$this->objectManager->getConfiguration()->getResultCache()?->clear();
}

return $this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

namespace RZ\Roadiz\CoreBundle\EventSubscriber;

use Doctrine\Common\Cache\CacheProvider;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\ManagerRegistry;
use RZ\Roadiz\CoreBundle\Event\Cache\CachePurgeRequestEvent;
Expand Down Expand Up @@ -41,11 +40,8 @@ public function purgeCache(Event $event, string $eventName, EventDispatcherInter
{
$manager = $this->managerRegistry->getManager();
// Clear result cache
if (
$manager instanceof EntityManagerInterface
&& $manager->getConfiguration()->getResultCacheImpl() instanceof CacheProvider
) {
$manager->getConfiguration()->getResultCacheImpl()->deleteAll();
if ($manager instanceof EntityManagerInterface) {
$manager->getConfiguration()->getResultCache()?->clear();
}
$dispatcher->dispatch(new CachePurgeRequestEvent());
}
Expand Down
6 changes: 1 addition & 5 deletions lib/RoadizCoreBundle/src/Importer/SettingsImporter.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

namespace RZ\Roadiz\CoreBundle\Importer;

use Doctrine\Common\Cache\CacheProvider;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\ManagerRegistry;
use RZ\Roadiz\CoreBundle\Entity\Setting;
Expand Down Expand Up @@ -42,10 +41,7 @@ public function import(string $serializedData): bool

if ($manager instanceof EntityManagerInterface) {
// Clear result cache
$cacheDriver = $manager->getConfiguration()->getResultCacheImpl();
if ($cacheDriver instanceof CacheProvider) {
$cacheDriver->deleteAll();
}
$manager->getConfiguration()->getResultCache()?->clear();
}

return true;
Expand Down
2 changes: 1 addition & 1 deletion lib/RoadizCoreBundle/src/Model/AttributeTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ trait AttributeTrait

#[ORM\ManyToOne(
targetEntity: AttributeGroupInterface::class,
cascade: ['persist', 'merge'],
cascade: ['persist'],
fetch: 'EAGER',
inversedBy: 'attributes'
),
Expand Down
1 change: 0 additions & 1 deletion lib/RoadizCoreBundle/src/Repository/LogRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ public function findLatestByNodesSources(int $maxResult = 5): Paginator
->setMaxResults($maxResult)
;
$ids = $qb->getQuery()
->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true)
->getScalarResult();

$qb2 = $this->createQueryBuilder('log');
Expand Down
1 change: 0 additions & 1 deletion lib/RoadizCoreBundle/src/Repository/TagRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,6 @@ public function findAllLinkedToNodeChildren(Node $parentNode, ?TranslationInterf
}

return $qb->getQuery()
->setHint(\Doctrine\ORM\Query::HINT_FORCE_PARTIAL_LOAD, true)
->getResult()
;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ private function getIdentifiers(NodesSources $source): array
'translation' => $source->getTranslation(),
])
->getQuery()
->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true)
->setCacheable(true)
->getArrayResult()
;
Expand Down
8 changes: 3 additions & 5 deletions lib/RoadizRozierBundle/src/Controller/SettingController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

namespace RZ\Roadiz\RozierBundle\Controller;

use Doctrine\Common\Cache\CacheProvider;
use Doctrine\Persistence\ManagerRegistry;
use RZ\Roadiz\CoreBundle\Bag\Settings;
use RZ\Roadiz\CoreBundle\Entity\Setting;
Expand Down Expand Up @@ -241,12 +240,11 @@ public function editAction(Request $request, int $settingId): Response
protected function resetSettingsCache(): void
{
$this->settingsBag->reset();
/** @var CacheProvider|null $cacheDriver */
$cacheDriver = $this->managerRegistry
$this->managerRegistry
->getManagerForClass(Setting::class)
?->getConfiguration()
->getResultCacheImpl();
$cacheDriver?->deleteAll();
->getResultCache()
?->clear();
$this->eventDispatcher->dispatch(new CachePurgeRequestEvent());
}

Expand Down
2 changes: 1 addition & 1 deletion lib/RoadizSolrBundle/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"ext-zip": "*",
"ext-json": "*",
"ext-mbstring": "*",
"doctrine/orm": "~2.20.0",
"doctrine/orm": "^3.6",
"nelmio/solarium-bundle": "^5.1",
"roadiz/core-bundle": "2.8.x-dev",
"roadiz/documents": "2.8.x-dev",
Expand Down
2 changes: 1 addition & 1 deletion lib/RoadizTwoFactorBundle/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"prefer-stable": true,
"require": {
"php": ">=8.3",
"doctrine/orm": "~2.20.0",
"doctrine/orm": "^3.6",
"endroid/qr-code": "^4.0",
"roadiz/core-bundle": "2.8.x-dev",
"roadiz/rozier-bundle": "2.8.x-dev",
Expand Down
2 changes: 1 addition & 1 deletion lib/RoadizUserBundle/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"require": {
"php": ">=8.3",
"api-platform/symfony": "^4.1.18",
"doctrine/orm": "~2.20.0",
"doctrine/orm": "^3.6",
"roadiz/core-bundle": "2.8.x-dev",
"roadiz/models": "2.8.x-dev",
"symfony/framework-bundle": "7.4.*",
Expand Down
Loading