Until now, Small Entity Schema was mainly designed around entities using Small Swoole Entity Manager.
That was useful for my own ecosystem, but it also meant that the schema editor was tightly coupled to one ORM representation.
The latest evolution changes that.
Small Entity Schema can now understand Doctrine 3 entities used in Symfony applications.
And more importantly, a project can contain Doctrine entities and Small Swoole Entity Manager entities at the same time.
What is Small Entity Schema?
Small Entity Schema is a desktop application for visually exploring and editing PHP entity models.
Instead of navigating through dozens of PHP classes to understand a data model, the application builds a graphical representation of the entities and their relations.
It can be used to:
- visualize entities;
- inspect their properties;
- understand relationships;
- reorganize the schema visually;
- edit entity definitions;
- regenerate the corresponding PHP source code.
The goal is not to replace an ORM.
It is to provide a visual layer above the ORM.
Doctrine 3 entity detection
The importer can now detect Doctrine entities such as:
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
#[ORM\Table(name: 'product')]
class Product
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private string $name;
}
Both the alias syntax:
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
and direct imports are interpreted.
The importer currently understands the main Doctrine mapping attributes used to describe an entity model:
Entity
Table
Id
Column
ManyToOne
OneToOne
OneToMany
ManyToMany
JoinColumn
Doctrine DBAL type constants are also supported.
For example:
use Doctrine\DBAL\Types\Types;
#[ORM\Column(type: Types::STRING)]
private string $title;
is interpreted as a string property by the schema editor.
Symfony Validator attributes
Doctrine support alone would not be enough for a Symfony application.
Entity properties very often contain Symfony Validator constraints too.
For example:
use Symfony\Component\Validator\Constraints as Assert;
#[Assert\NotBlank]
#[Assert\Length(min: 2, max: 100)]
#[ORM\Column(length: 100)]
private string $name;
Small Entity Schema now imports these attributes as well.
Several common constraints are interpreted, including:
NotBlank
NotNull
Email
Url
Uuid
Ulid
Positive
PositiveOrZero
Negative
NegativeOrZero
GreaterThan
GreaterThanOrEqual
LessThan
LessThanOrEqual
Regex
Choice
Range
Length
The original Symfony constraint is retained by the schema representation.
This means that when the entity is exported again as Doctrine, the Symfony validation metadata can also be regenerated.
Mixing Doctrine and Small Swoole entities
This was one of the most important architectural changes.
The schema model now stores the origin of each entity.
An entity can currently be identified as:
doctrine
or:
swoole
Consider a project containing:
src/Entity/User.php -> Doctrine
src/Entity/Post.php -> Doctrine
src/Entity/Product.php -> Small Swoole Entity Manager
Small Entity Schema can import all three into the same diagram.
The visual representation is therefore no longer tied to a specific ORM.
This is an important step toward treating the application as a more generic PHP entity schema editor.
Exporting back to the original ORM
By default, Small Entity Schema remembers the entity environment detected during import.
So:
Doctrine entity
↓
Small Entity Schema
↓
Doctrine entity
and:
Small Swoole entity
↓
Small Entity Schema
↓
Small Swoole entity
This behavior is called the automatic export mode.
It makes it possible to edit mixed projects without converting every entity to the same ORM.
Forcing the export format
Sometimes conversion is exactly what you want.
A new option in the project menu allows the export strategy to be changed.
Three modes are available:
Automatic
Force Small Swoole
Force Doctrine 3
With:
Automatic
each entity keeps its original environment.
With:
Force Doctrine 3
the schema is generated using Doctrine attributes.
And with:
Force Small Swoole
entities are generated using Small Swoole Entity Manager attributes.
This also opens an interesting possibility:
using Small Entity Schema as an ORM migration assistant.
It is not yet a full automatic ORM migration tool, but the underlying architecture is now there.
Tested against the Symfony Demo application
Synthetic unit tests are useful, but ORM compatibility needs to be tested against real code.
For that reason, I also tested the importer using the official Symfony Demo application.
The project contains real-world Doctrine patterns such as:
#[ORM\Entity]
#[ORM\Table(...)]
Doctrine DBAL types:
Types::INTEGER
Types::STRING
relations:
#[ORM\ManyToOne]
#[ORM\OneToMany]
#[ORM\ManyToMany]
Symfony Validator constraints:
#[Assert\NotBlank]
#[Assert\Length(...)]
#[Assert\Email]
and modern PHP patterns such as:
private readonly string $name;
Small Entity Schema successfully imports the Symfony Demo entities into its visual model.
This test was particularly useful because it revealed several edge cases that simple test entities did not expose.
For example:
#[ORM\JoinColumn(nullable: false)]
must obviously not be interpreted as if false were the column name.
The same testing also exposed cases involving:
\DateTimeImmutable
and readonly properties during source regeneration.
Desktop support matters too
One particularly interesting bug appeared during the Symfony Demo test.
The Doctrine importer itself worked correctly.
But the desktop application displayed:
0 entities
The reason was architectural.
The Electron bridge was still calling the old entity importer directly instead of the new compatibility layer.
So the backend could understand Doctrine, while the desktop application could not actually access that functionality.
The desktop bridge now uses the same compatibility layer for both import and export.
This is exactly the kind of issue that only appears when testing the complete application instead of isolated services.
Current test coverage
The compatibility work is covered by backend and frontend regression tests.
The current test suite includes checks for:
- mixed Doctrine/Swoole projects;
- Doctrine entity detection;
- Symfony Validator attributes;
- Doctrine column type inference;
- Doctrine relations;
-
DateTimeImmutable; - readonly PHP properties;
- desktop bridge import/export;
- automatic export mode;
- forced Doctrine export;
- forced Small Swoole export.
The implementation is also checked with PHPStan.
A note about Doctrine round-tripping
There is an important distinction between:
supporting a Doctrine entity model
and:
reproducing every Doctrine attribute byte-for-byte.
The first level is now implemented.
The second is still evolving.
Doctrine supports a very large amount of mapping metadata:
GeneratedValue
indexes
unique constraints
precision / scale
custom column options
inheritance mapping
discriminator maps
association options
cascade configuration
orphanRemoval
ordering
fetch strategies
...
Some of these attributes are not yet represented explicitly by Small Entity Schema.
The next step is therefore to retain more Doctrine-specific metadata while keeping the internal schema independent from Doctrine itself.
The objective is not to turn the internal model into a copy of Doctrine metadata.
The challenge is to find the right abstraction.
Toward an ORM-independent schema model
Doctrine compatibility required a useful change in the architecture.
Previously, the conceptual model was roughly:
Small Swoole Entity
↓
Schema
↓
Small Swoole Entity
It is now closer to:
┌─ Doctrine
│
PHP Entity ───┼─ Small Swoole
│
└─ future adapters
↓
Schema Model
↓
ORM Writer
That separation is much more interesting.
Doctrine is the first major external ORM supported by this architecture, but it does not have to be the last one.
In the future, adapters could potentially target other persistence models without changing the visual editor itself.
What's next?
The next Doctrine-related improvements will focus on richer metadata preservation, especially:
- exact relation types such as
OneToOneversusManyToOne; -
ManyToManymetadata; -
GeneratedValue; - column length, precision and scale;
- unique constraints and indexes;
- nullability;
- cascade configuration;
- additional Symfony attributes.
The long-term objective is straightforward:
Import an existing PHP domain model, visually edit it, and safely regenerate it without caring which supported ORM originally produced it.
Doctrine 3 compatibility is a significant step toward that goal.
Small Entity Schema is open source:
Top comments (2)
It's impressive to see how Small Entity Schema can now handle both Doctrine and Small Swoole entities seamlessly, allowing developers to visualize and manage mixed environments more effectively. The incorporation of Symfony Validator attributes adds significant value, as it ensures that validation logic is preserved during export, which is crucial for maintaining data integrity. One area for potential improvement could be providing more customization options for the visual representation, enabling teams to tailor the editor to their specific workflows. If you're looking for additional development support in enhancing the schema editor or its integration features, I'd be glad to discuss a paid collaboration. What challenges have you encountered while ensuring compatibility between the different ORM systems?
Thanks for your feedback.
One of the main goals behind Small Entity Schema is precisely to improve interoperability between different ecosystems rather than enforcing a single model.
Doctrine / Small Swoole Entity Manager compatibility follows that approach: the schema acts as an intermediate representation between multiple ORMs while preserving as much of their specific metadata as possible.
A significant part of this work also relies on Small Class Manipulator, which makes it relatively easy to interact with PHP classes both as structured representations and directly at code level. This greatly simplifies entity analysis, transformation, and generation without having to build a completely separate implementation for each framework or ORM.
The main challenge is therefore not simply mapping one ORM property to another, but defining a sufficiently neutral representation that allows conversions without losing ORM-specific information. Symfony Validator attributes are a good example: they are not strictly part of Doctrine mapping, but they are part of the functional definition of an entity, so they need to survive an import/export cycle.
Regarding paid collaboration, my open source projects are intentionally non-profit. I mainly build them to experiment, share reusable components across my own projects, and make those components available to the community. As a result, they do not generate a budget that I can redistribute through paid contributions.
Technical contributions, architectural feedback, real-world use cases, and pull requests are of course very welcome.