Skip to content

Commit

Permalink
wip: design draft - relationships, migrations
Browse files Browse the repository at this point in the history
  • Loading branch information
roydejong committed Jan 19, 2024
1 parent 11dfd91 commit ef59533
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 0 deletions.
44 changes: 44 additions & 0 deletions docs/Migrations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Migrations
When you define models in Instarecord, you can also use them to automatically generate and run database migrations.

That way, you can keep your database schema in sync with your models without having to write any SQL.

## Defining your models
You define your models as usual - they must inherit from `Model` and contain some fields that translate to database columns.

### Default assumptions
Instarecord makes the following assumptions by default, and you cannot currently change them:

- 🔑 `id` is the primary key, and is an auto-incrementing unsigned integer
- 🔤 Column names are `snake_case` versions of the field names (e.g. `firstName` becomes `first_name`)

### Defining foreign keys
You can define foreign keys by adding a field with an `Relation` attribute:

```php
<?php

use SoftwarePunt\Instarecord\Model;
use SoftwarePunt\Instarecord\Attributes\Relation;

class User extends Model
{
public int $id;
public string $name;

#[Relation(Organization::class)]
public Organisation $organization;

/**
* @var Post[]
*/
#[Relation(Post::class)]
public array $posts;
}
```

The example above will automatically infer the type of relationship from the type of the field:

👉 `Organization` is a single object, which makes it a "belongs to" relationship. It will be backed by an `organization_id` column, with a foreign key pointing to the `Organization` model's primary key (`id`).

👉 `Post[]` is an array of objects, which makes it a "has many" relationship. The `Post` class should have a corresponding `user_id` column or `User` relationship, with a foreign key pointing to the `User` model's primary key (`id`).
71 changes: 71 additions & 0 deletions docs/Relationships.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Relationships
You can define relationships between models using the `Relation` attribute pointing to another model.

## Defining relationships
Relationships can generally be categorized into two types:

- **One-to-one**: A single object of type `A` is related to a single object of type `B`. For example, a `User` has a single `Profile`.
- **One-to-many**: A single object of type `A` is related to multiple objects of type `B`. For example, a `User` has many `Posts`.

### One-to-one
You can define a one-to-one relationship by adding a field with an `Relation` attribute:

```php
<?php

use SoftwarePunt\Instarecord\Model;
use SoftwarePunt\Instarecord\Attributes\Relation;

class User extends Model
{
public int $id;
public string $name;

#[Relation(Profile::class)]
public Profile $profile;
}
```

Because this is **a single object**, it is recognized as a one-way relationship. That means the following:
- A backing column named `profile_id` will be expected in the users table (if you use migrations, this will be created automatically).
- When you query this model, Instarecord can automatically load and populate the `profile` field.

### One-to-many
You can define a one-to-many relationship by adding a field with an `Relation` attribute:

```php
<?php

use SoftwarePunt\Instarecord\Model;
use SoftwarePunt\Instarecord\Attributes\Relation;

class User extends Model
{
public int $id;
public string $name;

/**
* @var Post[]
*/
#[Relation(Post::class)]
public array $posts;
}
```

Because this is **an array of objects**, it is recognized as a one-to-many relationship. That means the following:
- A backing column named `user_id` will be expected in the posts table.
- You will usually define a corresponding `User` relationship in the `Post` model.
- When you query this model, Instarecord can automatically load and populate the `posts` array.

Note: The `@var` annotation is not required by Instarecord, but it is recommended to help your IDE understand the type of the field.

## Loading relationships
When you query a model, you can optionally specify which relationships to load. If a relationship is not loaded, the corresponding field will remain uninitialized (meaning accessing it would cause a PHP error).

There are two loading strategies:

- **Lazy loading**: Relationships are loaded on-demand, when you access the corresponding field. This means no unnecessary queries are executed, but you may end up with a lot of queries if you access many relationships.
- **Auto loading**: After you query a model, or collection of models, Instarecord will automatically load all relationships that were not loaded yet in a combined query. This may result in fewer queries, but they may be more complex and slower.

### Lazy loading
This is the default and fallback behavior, and requires no additional code. Simply access the field, and if it is uninitialized, Instarecord will automatically try to load it for you.

0 comments on commit ef59533

Please sign in to comment.