cycle / orm

PHP DataMapper, ORM
https://cycle-orm.dev
MIT License
1.22k stars 71 forks source link

PHP Schemas for SchemaBuilder and Bootloader #205

Open alexander-schranz opened 3 years ago

alexander-schranz commented 3 years ago

The Problem

As a library author I want my entity/objects be seperated from the orm schema configuration, so to isolating the metadata separately, since it is cycle-specific.

For Cycle/ORM i did stumble over the https://spiral.dev/docs/cycle-manual and https://cycle-orm.dev/docs/advanced-manual. Which I thought look the correct way. After I defined the Schemas I tried then using:

return $orm->withSchema(require 'schema.php');

Unexpectly it did errors with:

Caused by PDOException: SQLSTATE[HY000]: General error: 1 no such table: fcp_event

After some help from the Discord channel it seems like if I want that the schema is generated I need to use the Schema Builder and write so the Schema this way:

https://cycle-orm.dev/docs/advanced-schema-builder

The problem the schema builder my tests did work and the database correctly created. But the schema created over this syntax can not be used in example in a Bootloader class for my library when providing a spiral integrategration Bootloader class. So at current state I would need to duplicate the schema once writing the new Schema array and once writing in my test the schema using the SchemaBuilder.

How is this achieved in other ORMs

In doctrine I'm achieving this currently by using the .xml file for the schema mapping and configure then where this files can be found, in symfony integration of my library this will be configured over the doctrine bundle configuration using prepend configuration.

Expected Behaviour

My expected behaviour would be that every installable library can provide one or multiple schemas which are merged by the Generator together.

The schemas should then also be useable when using the SchemaBuilder to generate the Schema in the tests. Framework integration for Symfony, Spiral, ... should make it possible that a library (Symfony Bundle, Spiral Module, ...) can define one or multiple places from where this configuration files are loaded. For tests I can just initialize the ORM instance with the new Generator with the expected file paths or maybe provider services.

How this could be achieved

Prototype A (Generator based on SchemaProviderInterface)

Inspired by Doctrine where a service can be registered to listen to a loadClassMetadata event to add schema mapping data I implemented the following new Generator which get a list of providers, which will be given the Generator over the constructor like the Annotated Entities gets a ClassLocator. In framework a symfony cycle orm tagged services could be used to get all providers, in spiral maybe something simar exist (I'm not familiar what it).

https://gist.github.com/alexander-schranz/482e01273f901b0e709df4b0cb0e6b21

Prototype B (Generator based on php files / directories)

Maybe a provider interface is not needed and we could just directly return a array with the given array schema. The Generator will just need to know where to search for the files, this paths or directories can also be provided by symfony cycle orm bundle integration config or spiral cycle orm integration config.

https://gist.github.com/alexander-schranz/21793e604f3a99e453c9ed441df822b8

Prototype C (Generator based on array schema)

Create a generator which just gets a whole schema and so framework integration are responsible how config files are merged together or how the config array is configured.

https://gist.github.com/alexander-schranz/81907422add46ae0cd7f553e556339b7

Alternative / Workaround

When creating a library which provides schema data the library need to provide the Schema by creating a custom Generator. If this is the way it would be great to add an example of a Generator to the docs using the the schema builder to create the entities metadata.

roxblnfk commented 3 years ago

From a separation of concerns point of view, the generator should not read files. This is the responsibility of the external vendor (integration package or framework). Also, he should not read the schema arrays from the providers, since he will have to take on the tasks of resolving conflicts. The schema merging strategies are also the responsibility of the external service.

Just give the generator a ready-made schema array - and this will be enough for it to function.

alexander-schranz commented 3 years ago

@roxblnfk Thank you for your feedback I added your suggestion as Prototype C. Hopefully it does match what you had on your mind.