symfony-doctrine-persistence — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited symfony-doctrine-persistence (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
You are an expert in Doctrine ORM within Symfony hexagonal architecture.
| Topic | Doctrine ORM 2.x (Symfony 6.4) | Doctrine ORM 3.x (Symfony 7.4 / 8.x) |
|---|---|---|
| Mapping drivers | XML, attributes, annotations, YAML | XML and attributes only (annotations + YAML removed) |
| XML mapping | XSD validation optional | XSD validation always on: invalid XML fails at boot |
| DBAL | 3.x | 4.x (Type::getName() removed, register types by name) |
| Lazy loading | proxy classes | PHP 8.4 + ORM 3.4+: native lazy objects (enable_native_lazy_objects: true, DoctrineBundle 2.16+; always on in DoctrineBundle 3.1) |
| Pagination | Tools\Pagination\Paginator | ORM 3.7+: OffsetPaginator + Window (Paginator deprecated) |
Readonly properties / private(set) / hooks | readonly since 2.13 | readonly; asymmetric visibility + property hooks since 3.4 |
XML entity mapping is unaffected by Symfony 8 removing XML service/route configuration.
#[ORM\Entity] or annotations on domain entitiesInfrastructure/{Module}/Persistence/Mapping/DispatchAfterCurrentBusStamp$connection->executeQuery(), $connection->executeStatement(), NativeQuery, $connection->prepare(), or raw SQL strings anywhere in application code. Always use Doctrine QueryBuilder (ORM or DBAL), DQL, finder methods, or Criteria API. The only exception is Doctrine Migrations ($this->addSql()) which requires raw SQL by design.namespace App\Infrastructure\{Module}\Persistence;
use App\Domain\{Module}\Entity\{Entity};
use App\Domain\{Module}\Port\{Entity}RepositoryInterface;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Messenger\Stamp\DispatchAfterCurrentBusStamp;
final readonly class Doctrine{Entity}Repository implements {Entity}RepositoryInterface
{
public function __construct(
private EntityManagerInterface $entityManager,
private MessageBusInterface $eventBus,
) {
}
public function save({Entity} $entity): void
{
$this->entityManager->persist($entity);
$this->entityManager->flush();
foreach ($entity->pullDomainEvents() as $event) {
// released after the current command (and its transaction) succeeds
$this->eventBus->dispatch($event, [new DispatchAfterCurrentBusStamp()]);
}
}
public function findById({Entity}Id $id): ?{Entity}
{
return $this->entityManager->find({Entity}::class, $id->value);
}
public function remove({Entity} $entity): void
{
$this->entityManager->remove($entity);
$this->entityManager->flush();
}
}File: src/Infrastructure/User/Persistence/Mapping/Entity.User.orm.xml (name = class relative to the mapping prefix, see references/mapping-patterns.md)
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="App\Domain\User\Entity\User" table="users">
<id name="id" type="user_id" column="id" />
<embedded name="email" class="App\Domain\User\ValueObject\Email" use-column-prefix="false" />
<field name="name" type="string" />
<field name="createdAt" type="datetime_immutable" column="created_at" />
</entity>
</doctrine-mapping>#[ORM\Entity] attributes must sit on the entity class itself, which lives in Domain. XML is the supported driver that keeps the mapping in Infrastructure. Legacy modules may keep attributes until they are refactored.
Always ask the user which strategy they prefer:
php bin/console doctrine:migrations:diff (generates from mapping)Native/raw SQL bypasses Doctrine's abstraction layers and creates several problems:
Flag these patterns as CRITICAL violations:
// FORBIDDEN - native SQL patterns
$connection->executeQuery('SELECT ...');
$connection->executeStatement('INSERT ...');
$connection->prepare('SELECT ...');
$connection->exec('DROP ...');
$entityManager->getConnection()->executeQuery(...);
$entityManager->createNativeQuery(...);
$rsm = new ResultSetMapping();
// ALLOWED - Doctrine abstractions
$queryBuilder->select(...)->from(...)->where(...); // DBAL QueryBuilder
$entityManager->createQueryBuilder()->select('u')...; // ORM QueryBuilder
$entityManager->createQuery('SELECT u FROM User u'); // DQL
$repository->findBy([...]); // Finder methods
$repository->matching($criteria); // Criteria APISee references/ for detailed guides:
repository-patterns.md - Repository patterns and transaction managementmapping-patterns.md - XML mapping, embeddables, relationsmigration-workflow.md - Migration strategies and best practicesno-native-sql.md - Why native SQL is forbidden and how to use QueryBuilder instead~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.