This guide is a beginner-friendly walkthrough on installing and configuring Cycle ORM. We'll also touch upon the basics of setting up an entity. More detailed use-cases are available in other sections of the documentation.
Ensure your setup meets the following prerequisites:
Cycle ORM can be seamlessly integrated into your project with Composer. In your project's root directory, execute:
composer require cycle/orm
To define a schema for Cycle ORM using PHP attributes, you can use the package cycle/annotated. Its current version requires PHP 8.1. If you are using PHP 8.0, then version 3.x of the package will be installed. In this case, you will need to refer to the configuration for this version below.
To install the package - execute:
composer require cycle/annotated
Note:
This step is optional. If you prefer, you can define your mapping schema manually.
Before you get started, ensure your bootstrap includes the autoload vendor/autoload.php
generated by Composer:
<?php declare(strict_types=1);
include 'vendor/autoload.php';
First of all, you'll need to configure a database connection. The Cycle\Database\DatabaseManager
service, a part of
the cycle/database
package, serves as your go-to solution.
Here's how you can set up your first database connection:
<?php declare(strict_types=1);
include 'vendor/autoload.php';
use Cycle\Database;
use Cycle\Database\Config;
$dbal = new Database\DatabaseManager(
new Config\DatabaseConfig([
'default' => 'default',
'databases' => [
'default' => ['connection' => 'sqlite']
],
'connections' => [
'sqlite' => new Config\SQLiteDriverConfig(
connection: new Config\SQLite\MemoryConnectionConfig(),
queryCache: true,
),
]
])
);
Note:
To explore more on connecting to other databases, visit Connect to Database section.
You can check your database connection using the following code:
// ...
print_r($dbal->database('default')->getTables());
And run the script:
php index.php
The output should contain an array of tables in your database.
Mapping entities in Cycle ORM can be approached in multiple ways, ensuring flexibility based on your project's requirements. This section provides an overview of two predominant methods:
Start by defining your first entity within the src
directory of your project. Next, ensure Composer recognizes your
namespace:
"autoload": {
"psr-4": {
"Example\\": "src/"
}
}
Afterwards, refresh your autoload:
composer du
Now you can create your first entity. Let's call it User
:
<?php
declare(strict_types=1);
namespace Example;
class User
{
private int $id;
public function __construct(
private string $name,
) {
}
// getters and setters...
}
Attributes, introduced in PHP 8, provide a declarative way to specify metadata directly in the entity classes. They are a concise and expressive method for entity mapping. In most cases, they are the preferred way to define your entity schema.
Note:
Attributes won't degrade your application's performance. They are only used during the schema compilation process.
Let's add attributes to our User
entity:
<?php
declare(strict_types=1);
namespace Example;
use Cycle\Annotated\Annotation\Entity;
use Cycle\Annotated\Annotation\Column;
#[Entity]
class User
{
#[Column(type: "primary")]
private int $id;
public function __construct(
#[Column(type: "string")]
private string $name,
) {
}
// getters and setters...
}
In this example:
#[Entity]
attribute marks the class as an entity. It's required for all entities.
#[Column]
attribute specifies the column type for the respective properties.Note:
Read more about available attributes in the Annotated Entities section.
After defining your entities using attributes, you'll usually combine this with a schema generator to translate these declarations into a usable ORM schema.
In order to operate we need to generate an ORM Schema which will describe how our entities are configured. Though we can
do it manually, we will use the schema compiler and generators provided by cycle/schema-builder
package, and also
generators from cycle/annotated
package.
Initializing the Class Locator
First, we have to create instance of Spiral\Tokenizer\ClassLocator
which will automatically find the required
entities:
//...
$finder = (new \Symfony\Component\Finder\Finder())->files()->in([__DIR__]); // __DIR__ here is folder with entities
$classLocator = new \Spiral\Tokenizer\ClassLocator($finder);
To verify that your class is detectable (remember, ClassLocator
statically indexes your code):
print_r($classLocator->getClasses());
Compiling the Schema
With the class locator set, you can piece together your schema compilation pipeline. This is the sequence through which your entities pass, undergoing transformations, validations, and finally, integration into the ORM schema.
use Cycle\Schema;
use Cycle\Annotated;
use Cycle\Annotated\Locator\TokenizerEmbeddingLocator;
use Cycle\Annotated\Locator\TokenizerEntityLocator;
$embeddingLocator = new TokenizerEmbeddingLocator($classLocator);
$entityLocator = new TokenizerEntityLocator($classLocator);
$schema = (new Schema\Compiler())->compile(new Schema\Registry($dbal), [
new Schema\Generator\ResetTables(), // Reconfigure table schemas (deletes columns if necessary)
new Annotated\Embeddings($embeddingLocator), // Recognize embeddable entities
new Annotated\Entities($entityLocator), // Identify attributed entities
new Annotated\TableInheritance(), // Setup Single Table or Joined Table Inheritance
new Annotated\MergeColumns(), // Integrate table #[Column] attributes
new Schema\Generator\GenerateRelations(), // Define entity relationships
new Schema\Generator\GenerateModifiers(), // Apply schema modifications
new Schema\Generator\ValidateEntities(), // Ensure entity schemas adhere to conventions
new Schema\Generator\RenderTables(), // Create table schemas
new Schema\Generator\RenderRelations(), // Establish keys and indexes for relationships
new Schema\Generator\RenderModifiers(), // Implement schema modifications
new Schema\Generator\ForeignKeys(), // Define foreign key constraints
new Annotated\MergeIndexes(), // Merge table index attributes
new Schema\Generator\SyncTables(), // Align table changes with the database
new Schema\Generator\GenerateTypecast(), // Typecast non-string columns
]);
In our example, we use SyncTables
generator to automatically update the database schema. Read more about schema
synchronization in the Synchronizing Database Schema section.
Warning:
TheSyncTables
generator will modify your database structure in line with your schema. Do not execute this on a production database without understanding its implications!
With the schema compiled, it can be integrated into the ORM:
use Cycle\ORM;
$schema = /** ... */;
$orm = new ORM\ORM(new ORM\Factory($dbal), new ORM\Schema($schema));
It's best practice to cache the generated schema in production and regenerate it only when required, ensuring optimal performance.
For debugging or review, you can dump the $schema
variable, giving you a detailed look into your entity schema's
internal representation. For more human-friendly rendering, consider using the cycle/schema-renderer
package.
Updating Entity Schema
As your application grows, there may be times when you need to adjust your entity schema, such as adding new columns or changing existing ones. To do so, simply modify your entity and re-run the script.
#[Column(type: "int", nullable: true)]
protected ?int $age = null;
Upon your next script execution, the SyncTables
generator will automatically update the schema to reflect these
changes.
For projects that require a more dynamic or programmatic approach to entity mapping, Cycle ORM provides the ability to define entity schema programmatically.
Warning:
Remember, automatic database migrations aren't available in this mode.
The mapping for User
entity can be explicitly defined as:
use Cycle\ORM;
use Cycle\ORM\Mapper\Mapper;
$dbal = /** ... */;
$orm = new ORM\ORM(new ORM\Factory($dbal), new ORM\Schema([
'user' => [
ORM\Schema::MAPPER => Mapper::class, // default POPO mapper
ORM\Schema::ENTITY => User::class,
ORM\Schema::DATABASE => 'default',
ORM\Schema::TABLE => 'users',
ORM\Schema::PRIMARY_KEY => 'id',
// property => column
ORM\Schema::COLUMNS => [
'id' => 'id',
'name' => 'name',
],
ORM\Schema::TYPECAST => [
'id' => 'int',
],
ORM\Schema::RELATIONS => [],
]
]));
This method offers more control as it allows for dynamic and complex mappings. However, it can also be more verbose and might abstract away some of the clarity that attributes provide.
Note:
Read more about available schema options in the Manually defined Mapping Schema section.
For a deeper dive into various mapping techniques, consult the dedicated sections in the ORM documentation, such as dynamic mapping.
Cycle ORM offers an intuitive and seamless way to interact with your entities, handling persistence, retrieval,
updating, and deletion tasks with ease. Once you've properly configured your ORM, the Cycle\ORM\EntityManager
will
become the main tool for these operations. Below, we provide a walkthrough of common entity tasks.
Note:
Read more about Crud operations in the Create, Update and Delete section.
Before you delve into operations, ensure you've instantiated the EntityManager
:
use Cycle\ORM\EntityManager;
$orm = /** ... */;
$em = new EntityManager($orm);
To persist an entity, simply create a new instance and register it with the EntityManager
:
// ...
$user = new \Example\User("Antony");
$em->persist($user)->run();
You can immediately dump the object to see newly generated primary key:
// ...
print_r($user);
Entities can be easily fetched from the database using their primary key through the appropriate repository:
// ...
$user = $orm->getRepository(\Example\User::class)->findByPK(1);
print_r($user);
Note:
Read more about using repository in the Select Entity section.
Warning:
Remove the code from the section above to avoid fetching the entity from memory.
Updating an entity's data is straightforward: modify the desired properties, then persist the changes using
the EntityManager
:
// ...
$user = /** */;
$user->setName("John");
$em->persist($user)->run();
print_r($user);
Removing an entity from the database is executed through the delete
method of the EntityManager
:
// ...
$user = /** */;
$em->delete($user)->run();
Cycle ORM stands out as a robust and flexible tool for managing database operations in PHP applications. Its straightforward configuration process makes it approachable for both newcomers and experienced developers alike. Beyond its simplicity, its modular design and extensible architecture ensure that as your application grows and evolves, Cycle ORM can adapt and scale alongside it.