cycle / schema-builder Goto Github PK
View Code? Open in Web Editor NEWDeclarative schema generation for Cycle ORM
License: MIT License
Declarative schema generation for Cycle ORM
License: MIT License
Copy from Spiral Cycle Bridge
Есть 2 entity с одинаковым именем класса User, лежат по разным неймспейсам и смотрят в разные БД
1ая Entity user
<?php
declare(strict_types=1);
namespace App\Admin\Database;
use Cycle\Annotated\Annotation as Cycle;
/**
* @Cycle\Entity(database="admintool", table="users")
* @Cycle\Table()
*/
class User
{
/**
* @Cycle\Column(type="bigPrimary")
*/
public $id;
/**
* @Cycle\Column(type="string")
*/
public $email;
}
2ая Entity user
<?php
declare(strict_types=1);
namespace App\Ugg\Database;
use Cycle\Annotated\Annotation as Cycle;
/**
* @Cycle\Entity(database="ugg", role="ugg_users", table="users")
* @Cycle\Table()
*/
class User
{
/** @Cycle\Column(type="bigPrimary") */
public $id;
/** @Cycle\Column(type="enum(active,inactive,deleted,unknown)", default="active") */
public $status;
}
выполняем
php app.php cycle:sync
результат
Detecting schema changes:
• ugg.users: 5 change(s) detected
• admintool.users: 2 change(s) detected
отрабатывает без ошибок, но появится только одна таблица users в БД admintool
Если запустить через миграцию, появится таблица users и в admintool и в ugg БД
php app.php cycle:migrate
php app.php migrate
2 unique indexes gets generated in pivot table
unique(product_category_id, product_id)
unique(product_id, product_category_id)
$this->table('product_product_category')
->addColumn('id', 'bigPrimary', [
'nullable' => false,
'default' => null
])
->addColumn('product_category_id', 'bigInteger', [
'nullable' => false,
'default' => null
])
->addColumn('product_id', 'bigInteger', [
'nullable' => false,
'default' => null
])
->addIndex(["product_category_id", "product_id"], [
'name' => '0e92fceae82e7e0e91d346a72bf831ae',
'unique' => true
])
->addIndex(["product_category_id"], [
'name' => 'product_product_category_index_product_category_id_6035f3284ea68',
'unique' => false
])
->addIndex(["product_id"], [
'name' => 'product_product_category_index_product_id_6035f3284ea79',
'unique' => false
])
->addIndex(["product_id", "product_category_id"], [
'name' => '642dad4db053452e89cb383adbe9089f',
'unique' => true
])
->addForeignKey(["product_category_id"], 'product_categories', ["id"], [
'name' => '4c20a9d910d4f5d5ffa685a41db81ccc',
'delete' => 'CASCADE',
'update' => 'CASCADE'
])
->addForeignKey(["product_id"], 'products', ["id"], [
'name' => 'product_product_category_foreign_product_id_6035f3284ea75',
'delete' => 'CASCADE',
'update' => 'CASCADE'
])
->setPrimaryKeys(["id"])
->create();
refers to spiral/cycle-bridge#50
Entities
/**
* @Cycle\Entity()
*/
class Post
{
/**
* @Cycle\Column(type="primary")
*/
public int $id;
/**
* @Cycle\Relation\Embedded(target="Seo")
*/
public Seo $seo;
/**
* @Cycle\Column(type="primary")
*/
public DateTimeImmutable $createdAt;
}
/**
* @Embeddable(columnPrefix="seo_")
*/
class Seo
{
/**
* @Cycle\Column(type="string", name="title", nullable=true)
*/
public ?string $title;
/**
* @Cycle\Column(type="string", name="description", nullable=true)
*/
public ?string $description;
/**
* @Cycle\Column(type="string", name="keywords", nullable=true)
*/
public ?string $keywords;
}
Generated migration
$this->table('posts')
->addColumn('id', 'primary', [
'nullable' => false,
'default' => null
])
->addColumn('created_at', 'primary', [
'nullable' => false,
'default' => null
])
->addColumn('seo_title', 'string', [
'nullable' => true,
'default' => null,
'size' => 255
])
->addColumn('seo_description', 'string', [
'nullable' => true,
'default' => null,
'size' => 255
])
->addColumn('seo_keywords', 'string', [
'nullable' => true,
'default' => null,
'size' => 255
])
->setPrimaryKeys(["id", "created_at"])
->create();
What i want is that created_at
should be generated after Seo
columns, i.e. in this case after seo_keywords
As part of the cycle v2's composite keys support, relations could also be linked via composite inner or outer keys.
Currently, schema builder restricts ManyToMany through keys to a single field. This should be changed once V2 is out.
Is it possible generate schema only use info form entity, not to load actual schema from DB?
Problem:
I use "cycle/orm and co" among with symfony. Schema is compiled while cache:clear, cache:clear is run while docker build.
As there is no db connection, this error occur.
[PDOException (7)]
SQLSTATE[08006] [7] could not translate host name "db" to address: Name does not resolve
Exception trace:
at /srv/app/vendor/cycle/database/src/Driver/Driver.php:637
PDO->__construct() at /srv/app/vendor/cycle/database/src/Driver/Driver.php:637
Cycle\Database\Driver\Driver->createPDO() at /srv/app/vendor/cycle/database/src/Driver/Postgres/PostgresDriver.php:213
Cycle\Database\Driver\Postgres\PostgresDriver->createPDO() at /srv/app/vendor/cycle/database/src/Driver/Driver.php:200
Cycle\Database\Driver\Driver->connect() at /srv/app/vendor/cycle/database/src/Driver/Driver.php:653
Cycle\Database\Driver\Driver->getPDO() at /srv/app/vendor/cycle/database/src/Driver/Driver.php:487
Cycle\Database\Driver\Driver->prepare() at /srv/app/vendor/cycle/database/src/Driver/Driver.php:443
Cycle\Database\Driver\Driver->statement() at /srv/app/vendor/cycle/database/src/Driver/Driver.php:457
Cycle\Database\Driver\Driver->statement() at /srv/app/vendor/cycle/database/src/Driver/Driver.php:253
Cycle\Database\Driver\Driver->query() at /srv/app/vendor/cycle/database/src/Driver/Postgres/PostgresHandler.php:71
Cycle\Database\Driver\Postgres\PostgresHandler->hasTable() at /srv/app/vendor/cycle/database/src/Schema/AbstractTable.php:99
Cycle\Database\Schema\AbstractTable->__construct() at /srv/app/vendor/cycle/database/src/Driver/Postgres/PostgresHandler.php:31
Cycle\Database\Driver\Postgres\PostgresHandler->getSchema() at /srv/app/vendor/cycle/database/src/Table.php:88
Cycle\Database\Table->getSchema() at /srv/app/vendor/cycle/schema-builder/src/Registry.php:161
Cycle\Schema\Registry->linkTable() at /srv/app/vendor/cycle/annotated/src/Entities.php:80
Cycle\Annotated\Entities->run() at /srv/app/vendor/cycle/schema-builder/src/Compiler.php:57
Cycle\Schema\Compiler->compile() at /srv/app/package/CycleORMBundle/src/Service/SchemaCompiler.php:26
As a workaround, I do cache:clear at startup time.
But it loads DB, every kubernetes pod get schema from DB at the same time.
Auto-generated columns don't have the same attributes like in the related source column
see https://discord.com/channels/538114875570913290/988308945045180436/1074709906017501325
#[Cycle\Entity(table: 'country')]
class Country
{
#[Cycle\Column(type: 'primary' /*, unsigned: true*/)]
private ?int $id = null;
}
#[Cycle\Entity(table: 'city')]
class City
{
#[Cycle\Column(type: 'primary')]
private ?int $id = null;
#[Cycle\Relation\BelongsTo(target: Country::class, nullable: true)]
private ?Country $country = null;
}
When compiling a schema, the default database value as configured within the database manager will be used inside of the schema instead of null.
Example with annotated entity:
use Cycle\Annotated\Annotation\Entity;
#[Entity(role: AccountType::class, repository: AccountTypeRepository::class, table: 'account_type')]
class AccountType
{
#[Column(type: 'tinyInteger', name: 'account_type_id', primary: true, nullable: true)]
protected ?int $accountTypeId = null;
// ...
}
In the above example the "database" entity option is not set, so I expect the schema to have a null
value for SchemaInterface::DATABASE
instead it uses the default value as configured on the database manager (in my example that would be test).
Schema snippit:
namespace Cycle\ORM\SchemaInterface;
class CompiledSchema
{
public const LIST = [
AccountType::class => [
// ...
SchemaInterface::MAPPER => 'Cycle\ORM\Mapper\Mapper',
SchemaInterface::SOURCE => 'Cycle\ORM\Select\Source',
SchemaInterface::REPOSITORY => AccountTypeRepository::class,
SchemaInterface::DATABASE => 'test', // <================ should be null not test
// ...
],
];
}
If I compile the schema during my build step which uses a different database name compared to production I end up with the following error, on production.
Cycle\Database\Exception\DBALException: Unable to create Database, no presets for 'build_2024_abfertgg_db' found
I can resolve this by setting the database to null after the schema has been compiled. But I feel like this is not the expected behavior.
Explicitly setting the entity option "database" to null
does not change the outcome.
"cycle/annotated": "^3.4",
"cycle/database": "^2.7",
"cycle/orm": "^2.6",
Let's say we have a parent entity:
<?php
declare(strict_types=1);
namespace App\Database;
use Cycle\Annotated\Annotation;
/** @Annotation\Entity() */
class Parent
{
/** @Annotation\Column(type="primary") */
public $id;
/** @Annotation\Relation\Embedded(target="Embed") */
public Embed $embed;
}
and an embeddable one:
<?php
declare(strict_types=1);
namespace App\Database;
use Cycle\Annotated\Annotation;
/** @Annotation\Embeddable(columnPrefix="embedded_") */
class Embed
{
/** @Annotation\Column(type="int") */
public $id;
/** @Annotation\Column(type="text") */
public $data;
}
Cycle fails on making scheme of such relations:
php app.php cycle
Updating ORM schema... [Cycle\Schema\Exception\FieldException]
Field `id` already exists
I would agree on that if we had no columnPrefix
attribute. But the real column name will be equal to embedded_id
and this is not a duplicate. There's a tricky hack (which allows you to remain the same DB scheme, but with changes in the class) - rename the property and add the name attribute with the previous value:
<?php
declare(strict_types=1);
namespace App\Database;
use Cycle\Annotated\Annotation;
/** @Annotation\Embeddable(columnPrefix="embedded_") */
class Embed
{
/** @Annotation\Column(type="int", name="id") */
public $myRandomIDField;
/** @Annotation\Column(type="text") */
public $data;
}
Entity:
<?php declare(strict_types=1);
namespace App\Core\Entity\Wall;
use Cycle\Annotated\Annotation\Entity;
use Cycle\Annotated\Annotation\Relation\BelongsTo;
use Cycle\Annotated\Annotation\Table;
// #[PrimaryKey(['group_id', 'block_id'])] does not work too
#[Table(primary: new Table\PrimaryKey(['group_id', 'block_id']))]
#[Entity(table: 'wall_block')]
class WallBlock
{
public function __construct(
#[BelongsTo(target: Group::class, innerKey: 'group_id')]
private Group $group,
#[BelongsTo(target: Block::class, innerKey: 'block_id')]
private Block $block,
) {
}
}
Error:
Entity `wallBlock` must have defined primary key
Exception trace:
at /srv/app/vendor/cycle/schema-builder/src/Relation/RelationSchema.php:127
Cycle\Schema\Relation\RelationSchema->getPrimaryColumns() at /srv/app/vendor/cycle/schema-builder/src/Relation/RelationSchema.php:89
Cycle\Schema\Relation\RelationSchema->compute() at /srv/app/vendor/cycle/schema-builder/src/Relation/BelongsTo.php:52
Cycle\Schema\Relation\BelongsTo->compute() at /srv/app/vendor/cycle/schema-builder/src/Generator/GenerateRelations.php:117
Cycle\Schema\Generator\GenerateRelations->register() at /srv/app/vendor/cycle/schema-builder/src/Generator/GenerateRelations.php:91
Cycle\Schema\Generator\GenerateRelations->run() at /srv/app/vendor/cycle/schema-builder/src/Compiler.php:57
ORM 2.2.1
PHP 8.2
Now if entity has relation to another entity with naming like ProductBrand
, orm generates column name like productBrand_id
.
It would be better if output name was product_brand_id
, coz entity properties gets generated with snake case.
Было бы здорово добавить поддержку постгресовских схем. Сейчас по умолчанию захардкожена дефолтная public.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.