- Notifications
You must be signed in to change notification settings - Fork0
Cycle ORM bridge for integrating with any framework
License
wakebit/cycle-bridge
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
This package provides a set of Symfony commands, classes for integrationCycle ORM with any framework. We are supporting integration both versions of the ORM.If you want to integrate this to Laravel we already have aseparated package which uses this bridge.
- PHP >= 8.0
- Cycle ORM 1 or 2 (branches
v1.x
andv2.x
accordingly)
- Install the package via composer:
composer require wakebit/cycle-bridge
- Declare config
config/config.php
and fill database credentials:
<?phpdeclare(strict_types=1);usefunctionDI\env;return [// Environment variables'debug' =>env('APP_DEBUG','true'),// Database'db.connection' =>env('DB_CONNECTION','mysql'),'db.host' =>env('DB_HOST','localhost'),'db.database' =>env('DB_DATABASE',''),'db.username' =>env('DB_USERNAME',''),'db.password' =>env('DB_PASSWORD',''),];
- Declare ORM config
config/cycle.php
. Dont forget to set up correct paths to entities path, migrations path:
<?phpuseCycle\Database\Config\DatabaseConfig;useCycle\Migrations\Config\MigrationConfig;usePsr\Container\ContainerInterface;useSpiral\Tokenizer\Config\TokenizerConfig;useWakebit\CycleBridge\Schema\Config\SchemaConfig;usefunctionDI\create;/** * @psalm-suppress UndefinedClass * @psalm-suppress MixedArgument */return ['database' =>staticfunction (ContainerInterface$container):DatabaseConfig {returnnewDatabaseConfig(['default' =>'default','databases' => ['default' => ['connection' =>$container->get('config')['db.connection'], ], ],'connections' => ['sqlite' =>new \Cycle\Database\Config\SQLiteDriverConfig( connection:new \Cycle\Database\Config\SQLite\MemoryConnectionConfig(), queryCache:true, ),'mysql' =>new \Cycle\Database\Config\MySQLDriverConfig( connection:new \Cycle\Database\Config\MySQL\TcpConnectionConfig( database:$container->get('config')['db.database'], host:$container->get('config')['db.host'], port:3306, user:$container->get('config')['db.username'], password:$container->get('config')['db.password'], ), queryCache:true, ),'postgres' =>new \Cycle\Database\Config\PostgresDriverConfig( connection:new \Cycle\Database\Config\Postgres\TcpConnectionConfig( database:$container->get('config')['db.database'], host:$container->get('config')['db.host'], port:5432, user:$container->get('config')['db.username'], password:$container->get('config')['db.password'], ), schema:'public', queryCache:true, ),'sqlServer' =>new \Cycle\Database\Config\SQLServerDriverConfig( connection:new \Cycle\Database\Config\SQLServer\TcpConnectionConfig( database:$container->get('config')['db.database'], host:$container->get('config')['db.host'], port:1433, user:$container->get('config')['db.username'], password:$container->get('config')['db.password'], ), queryCache:true, ), ], ]); },'orm' => ['schema' =>staticfunction ():SchemaConfig {returnnewSchemaConfig(); },'tokenizer' =>staticfunction ():TokenizerConfig {returnnewTokenizerConfig(['directories' => [__DIR__ .'/../src/Entity', ],'exclude' => [ ], ]); }, ],'migrations' =>staticfunction (ContainerInterface$container):MigrationConfig {returnnewMigrationConfig(['directory' =>__DIR__ .'/../resources/migrations','table' =>'migrations','safe' =>filter_var($container->get('config')['debug'],FILTER_VALIDATE_BOOLEAN), ]); },];
- Declare dependencies for PHP-DI container
config/container.php
:
<?phpdeclare(strict_types=1);useCycle\Database\Config\DatabaseConfig;useCycle\Database\DatabaseInterface;useCycle\Database\DatabaseManager;useCycle\Database\DatabaseProviderInterface;useCycle\Migrations\Config\MigrationConfig;useCycle\Migrations\FileRepository;useCycle\Migrations\RepositoryInterface;useCycle\ORM\EntityManager;useCycle\ORM\EntityManagerInterface;useCycle\ORM\Factory;useCycle\ORM\FactoryInterface;useCycle\ORM\ORM;useCycle\ORM\ORMInterface;useCycle\ORM\SchemaInterface;usePsr\Container\ContainerInterface;useSpiral\Tokenizer\ClassesInterface;useSpiral\Tokenizer\ClassLocator;useSpiral\Tokenizer\Config\TokenizerConfig;useSpiral\Tokenizer\Tokenizer;useWakebit\CycleBridge\Contracts\Schema\CacheManagerInterface;useWakebit\CycleBridge\Contracts\Schema\CompilerInterface;useWakebit\CycleBridge\Contracts\Schema\GeneratorQueueInterface;useWakebit\CycleBridge\Schema\CacheManager;useWakebit\CycleBridge\Schema\Compiler;useWakebit\CycleBridge\Schema\Config\SchemaConfig;useWakebit\CycleBridge\Schema\GeneratorQueue;useWakebit\CycleBridge\Schema\SchemaFactory;usefunctionDI\autowire;usefunctionDI\factory;usefunctionDI\get;return ['config' =>require'config.php','cycle' =>require'cycle.php', DatabaseConfig::class =>staticfunction (ContainerInterface$container):DatabaseConfig {return$container->get('cycle')['database']; }, SchemaConfig::class =>staticfunction (ContainerInterface$container):SchemaConfig {return$container->get('cycle')['orm']['schema']; }, TokenizerConfig::class =>staticfunction (ContainerInterface$container):TokenizerConfig {return$container->get('cycle')['orm']['tokenizer']; }, MigrationConfig::class =>staticfunction (ContainerInterface$container):MigrationConfig {return$container->get('cycle')['migrations']; }, DatabaseProviderInterface::class =>autowire(DatabaseManager::class), DatabaseInterface::class =>staticfunction (ContainerInterface$container):DatabaseInterface {return$container->get(DatabaseProviderInterface::class)->database(); }, DatabaseManager::class =>get(DatabaseProviderInterface::class), ClassLocator::class =>get(ClassesInterface::class), ClassesInterface::class =>staticfunction (ContainerInterface$container):ClassesInterface {return$container->get(Tokenizer::class)->classLocator(); }, FactoryInterface::class =>autowire(Factory::class), CacheManagerInterface::class =>staticfunction ():CacheManagerInterface {// Here you need to pass PSR-16 compatible cache pool. See example with cache file below.// Packages: league/flysystem, cache/filesystem-adapter$filesystemAdapter =new \League\Flysystem\Adapter\Local(__DIR__ .'/../var/cache');$filesystem =new \League\Flysystem\Filesystem($filesystemAdapter);$pool =new \Cache\Adapter\Filesystem\FilesystemCachePool($filesystem);returnnewCacheManager($pool); }, GeneratorQueueInterface::class =>autowire(GeneratorQueue::class), CompilerInterface::class =>autowire(Compiler::class), SchemaInterface::class =>factory([SchemaFactory::class,'create']), ORMInterface::class =>staticfunction (ContainerInterface$container):ORMInterface {returnnewORM($container->get(FactoryInterface::class),$container->get(SchemaInterface::class)); }, EntityManagerInterface::class =>autowire(EntityManager::class), RepositoryInterface::class =>autowire(FileRepository::class),];
- Now, you need to load a dependencies array created in the step above to PHP-DI. After you are free to use dependencies, write your code.
Let's look at quick example. Define entity:
<?phpdeclare(strict_types=1);namespaceApp\Entity;useCycle\Annotated\Annotation\Entity;useCycle\Annotated\Annotation\Column;#[Entity]class User{ #[Column(type:'primary')]privateint$id; #[Column(type:'string')]privatestring$name;publicfunctiongetId():int {return$this->id; }publicfunctiongetName():string {return$this->name; }publicfunctionsetName(string$name):void {$this->name =$name; }}
You can take DBAL, ORM and EntityManager from the container. Quick example of usage:
<?phpdeclare(strict_types=1);namespaceApp;useCycle\Database\DatabaseProviderInterface;useCycle\ORM\EntityManagerInterface;useCycle\ORM\ORMInterface;finalclass SomeClass{publicfunction__construct(privateDatabaseProviderInterface$dbal,privateEntityManagerInterface$em,privateORMInterface$orm, ) { }publicfunction__invoke() {// DBAL$tables =$this->dbal->database()->getTables();$tableNames =array_map(fn (\Cycle\Database\TableInterface$table):string =>$table->getName(),$tables);dump($tableNames);// Create, modify, delete entities using Transaction$user =new \App\Entity\User();$user->setName("Hello World");$this->em->persist($user);$this->em->run();dump($user);// ORM$repository =$this->orm->getRepository(\App\Entity\User::class);$users =$repository->findAll();dump($users);$user =$repository->findByPK(1);dump($user); }}
See more onthe official Cycle ORM documentation.
Command | Description | Options | Symfony command class FQN |
---|---|---|---|
cycle:schema:migrate | Generate ORM schema migrations | ---run : Automatically run generated migration.- -v : Verbose output. | \Wakebit\CycleBridge\Console\Command\Schema\MigrateCommand::class |
cycle:schema:cache | Compile and cache ORM schema | \Wakebit\CycleBridge\Console\Command\Schema\CacheCommand::class | |
cycle:schema:clear | Clear cached schema (schema will be generated every request now) | \Wakebit\CycleBridge\Console\Command\Schema\ClearCommand::class | |
cycle:schema:sync | Sync ORM schema with database without intermediate migration (risk operation!) | \Wakebit\CycleBridge\Console\Command\Schema\SyncCommand::class | |
cycle:schema:render | Render available CycleORM schemas. | ---no-color : Display output without colors. | \Wakebit\CycleBridge\Console\Command\Schema\RenderCommand::class |
Command | Description | Options | Symfony command class FQN |
---|---|---|---|
cycle:migrate:init | Initialize migrator: create a table for migrations | \Wakebit\CycleBridge\Console\Command\Migrate\InitCommand::class | |
cycle:migrate | Run all outstanding migrations | ---one : Execute only one (first) migration.- --force : Force the operation to run when in production. | \Wakebit\CycleBridge\Console\Command\Migrate\MigrateCommand::class |
cycle:migrate:rollback | Rollback the last migration | ---all : Rollback all executed migrations.- --force : Force the operation to run when in production. | \Wakebit\CycleBridge\Console\Command\Migrate\RollbackCommand::class |
cycle:migrate:replay | Replay (down, up) one or multiple migrations. | ---all : Replay all migrations. | \Wakebit\CycleBridge\Console\Command\Migrate\ReplayCommand::class |
cycle:migrate:status | Get a list of available migrations | \Wakebit\CycleBridge\Console\Command\Migrate\StatusCommand::class |
Command | Description | Options | Symfony command class FQN |
---|---|---|---|
cycle:db:list | Get list of available databases, their tables and records count | ---database : Database name | \Wakebit\CycleBridge\Console\Command\Database\ListCommand::class |
cycle:db:table <table> | Describe table schema of specific database | ---database : Database name | \Wakebit\CycleBridge\Console\Command\Database\TableCommand::class |
If you are using memory database (SQLite) you can just run migrations in thesetUp
method of the your test calling the console commandcycle:migrate
.For another databases followthis instruction and drop all tables in thetearDown
method.
If you want to use a manually defined ORM schema you can define it in thecycle.php
orm.schema.map
config key:
useWakebit\CycleBridge\Schema\Config\SchemaConfig;return [// ...'orm' => ['schema' =>staticfunction ():SchemaConfig {returnnewSchemaConfig(['map' =>require__DIR__ .'/../orm_schema.php', ]); }, ]// ...]
Manually defined schema should be presented as array. It will be passed to\Cycle\ORM\Schema
constructor. See morehere.
You can redefine the ORM schema compilation generators in thecycle.php
orm.schema.generators
config key:
useWakebit\CycleBridge\Schema\Config\SchemaConfig;return [// ...'orm' => ['schema' =>staticfunction ():SchemaConfig {returnnewSchemaConfig(['generators' => ['index' => [],'render' => [ \Cycle\Schema\Generator\ResetTables::class,// re-declared table schemas (remove columns) \Cycle\Schema\Generator\GenerateRelations::class,// generate entity relations \Cycle\Schema\Generator\ValidateEntities::class,// make sure all entity schemas are correct \Cycle\Schema\Generator\RenderTables::class,// declare table schemas \Cycle\Schema\Generator\RenderRelations::class,// declare relation keys and indexes ],'postprocess' => [ \Cycle\Schema\Generator\GenerateTypecast::class,// typecast non string columns ], ] ]); }, ]// ...]
Classes will be resolved by DI container. Default pipeline you can seehere.
You can specify predefined or your own collection factory in container definition:
FactoryInterface::class =>staticfunction (ContainerInterface$container):FactoryInterface {returnnew \Cycle\ORM\Factory( dbal:$container->get(DatabaseProviderInterface::class), defaultCollectionFactory:new \Cycle\ORM\Collection\DoctrineCollectionFactory(), );},
- Cycle ORM, PHP DataMapper ORM and Data Modelling Engine by SpiralScout.
- Spiral Scout, author of the Cycle ORM.
- Spiral Framework Cycle Bridge for code samples, example of usage.
About
Cycle ORM bridge for integrating with any framework