## Getting Started
### Minimal Setup
1. **Installation**:
```bash
composer require alms/cycle-bundle
Ensure your config/packages/ directory includes cycle.yaml (auto-generated by the bundle).
Configuration:
Update config/packages/cycle.yaml with your database connection details (e.g., pdo_mysql or pdo_pgsql):
cycle:
dbal:
default:
connection: default
driver: pdo_mysql
dsn: 'mysql:host=localhost;dbname=your_db;charset=utf8mb4'
username: 'your_user'
password: 'your_password'
First Use Case:
Define an entity (e.g., src/Entity/User.php) with Cycle ORM annotations:
use Cycle\Annotated\Annotation\Column;
use Cycle\Annotated\Annotation\Entity;
use Cycle\Annotated\Annotation\Table;
#[Entity]
#[Table(name: 'users')]
class User
{
#[Column(type: 'primary')]
public int $id;
#[Column(type: 'string')]
public string $name;
}
Run migrations:
php bin/console cycle:migrate
Dependency Injection:
Inject the Cycle\ORM\ORM service into your controllers/services:
use Cycle\ORM\ORM;
public function __construct(private ORM $orm) {}
CRUD Operations:
Use the ORM service to interact with entities:
// Create
$user = new User();
$user->name = 'John Doe';
$this->orm->run(function (ORM $orm) use ($user) {
$orm->getRepository(User::class)->save($user);
});
// Read
$user = $this->orm->run(function (ORM $orm) {
return $orm->getRepository(User::class)->find(1);
});
// Update/Delete
$this->orm->run(function (ORM $orm) use ($user) {
$user->name = 'Updated Name';
$orm->getRepository(User::class)->save($user);
// $orm->getRepository(User::class)->delete($user);
});
Transactions: Wrap operations in transactions for atomicity:
$this->orm->run(function (ORM $orm) {
$orm->getRepository(User::class)->save($user1);
$orm->getRepository(User::class)->save($user2);
// Both save or none
});
Repository Methods: Leverage built-in repository methods for common queries:
// Find by ID
$user = $this->orm->getRepository(User::class)->find(1);
// Find all
$users = $this->orm->getRepository(User::class)->findAll();
// Custom queries
$users = $this->orm->getRepository(User::class)
->select(['name'])
->where('age > ?', 18)
->fetchAll();
Selective Loading:
Use select() to fetch only required columns:
$users = $this->orm->getRepository(User::class)
->select(['id', 'name'])
->fetchAll();
Generating Migrations:
Use the cycle:migrations:generate command to create migration files:
php bin/console cycle:migrations:generate
Review and customize the generated migration in migrations/.
Running Migrations: Apply migrations with:
php bin/console cycle:migrate
Rollbacks: Rollback migrations using:
php bin/console cycle:migrate:rollback
Event Listeners:
Integrate Cycle events (e.g., Cycle\ORM\Event\BeforeSave) into Symfony's event system:
# config/services.yaml
services:
App\EventListener\CycleEventListener:
tags:
- { name: kernel.event_listener, event: cycle.orm.before_save, method: onBeforeSave }
Doctrine Bridge (Optional):
If using Doctrine alongside Cycle, configure the cycle.doctrine section in cycle.yaml:
cycle:
doctrine:
enabled: true
entity_manager: default
Entity Not Found:
#[Entity] and #[Table].cycle.yaml configuration matches your database schema.Migration Issues:
Transaction Scope:
ORM::run() will not be transactional. Always wrap in run() for atomicity:
// ❌ Non-transactional
$this->orm->getRepository(User::class)->save($user);
// ✅ Transactional
$this->orm->run(fn(ORM $orm) => $orm->getRepository(User::class)->save($user));
Lazy Loading:
$user = $this->orm->getRepository(User::class)
->with('posts') // Eager-load posts
->find(1);
Enable Query Logging:
Configure cycle.yaml to log SQL queries:
cycle:
orm:
debug: true
Check logs in var/log/dev.log.
Schema Validation: Validate your schema before migrations:
php bin/console cycle:schema:validate
Dependency Injection:
Ensure the Cycle\ORM\ORM service is properly autowired. If issues arise, manually bind it in services.yaml:
services:
Cycle\ORM\ORM:
alias: cycle.orm.default
Custom Behaviors: Extend Cycle's behavior system by creating custom behaviors (e.g., soft deletes, timestamps):
use Cycle\Annotated\Annotation\Behavior;
#[Behavior(SoftDelete::class)]
class User {}
Event Subscribers: Subscribe to Cycle events for custom logic:
use Cycle\ORM\Event\BeforeSave;
public function onBeforeSave(BeforeSave $event): void
{
$entity = $event->getEntity();
if ($entity instanceof User) {
$entity->updatedAt = new \DateTime();
}
}
Custom Drivers:
Integrate with non-PDO databases by implementing a custom Cycle\DBAL\Driver:
use Cycle\DBAL\Driver;
class CustomDriver implements Driver {}
Register it in cycle.yaml:
cycle:
dbal:
default:
driver: App\Cycle\CustomDriver
Batch Operations:
Use fetchAll() for bulk reads and saveMany() for bulk writes:
$users = $this->orm->getRepository(User::class)->fetchAll();
$this->orm->run(fn(ORM $orm) => $orm->getRepository(User::class)->saveMany($users));
Indexing: Add indexes to frequently queried columns in your schema:
#[Column(type: 'string', index: true)]
public string $email;
Caching: Enable Symfony's cache for ORM metadata (reduces overhead):
cycle:
orm:
cache:
enabled: true
pool: cache.app
Multiple Connections:
Configure multiple DBAL connections in cycle.yaml:
cycle:
dbal:
default:
connection: default
driver: pdo_mysql
dsn: 'mysql:host=localhost;dbname=app_db'
read_replica:
connection: read_replica
driver: pdo_mysql
dsn: 'mysql:host=replica;dbname=app_db'
Environment-Specific Config: Use Symfony's parameter bag for environment variables:
cycle:
dbal:
default:
dsn: '%env(DATABASE_URL)%'
Schema Rendering: Generate SQL for your schema without executing it:
php bin/console cycle:schema:render
---
How can I help you explore Laravel packages today?