From 83e2a0a93329522588d5985b3aa560e4f1040bd7 Mon Sep 17 00:00:00 2001 From: gitbook-bot Date: Sun, 18 Aug 2024 23:48:55 +0000 Subject: [PATCH] GITBOOK-17: No subject --- docs/.gitbook/assets/Untitled-2.svg | 12 + docs/.gitbook/assets/Untitled-3.svg | 12 + docs/.gitbook/assets/Untitled-4.svg | 12 + docs/SUMMARY.md | 35 ++- docs/{guides => }/coreconcepts/README.md | 0 .../coreconcepts/altering-the-schema.md | 0 .../coreconcepts/crud-operations/README.md | 0 .../crud-operations/creating-records.md | 0 .../crud-operations/deleting-records.md | 0 .../crud-operations/reading-records.md | 0 .../crud-operations/updating-records.md | 0 .../coreconcepts/initializing-the-database.md | 2 +- docs/{guides => }/coreconcepts/migrations.md | 0 .../coreconcepts/patterns/README.md | 0 .../coreconcepts/patterns/active-record.md | 0 .../coreconcepts/patterns/entity-framework.md | 0 .../coreconcepts/patterns/repository.md | 0 docs/{guides => }/coreconcepts/schemas.md | 2 +- docs/guides/active-record-with-cql/README.md | 293 ++++++++++++++++++ .../active-record-with-cql/belongsto.md | 148 +++++++++ docs/guides/active-record-with-cql/hasone.md | 202 ++++++++++++ docs/{guides => }/installation.md | 0 docs/{guides => }/introduction.md | 0 23 files changed, 700 insertions(+), 18 deletions(-) create mode 100644 docs/.gitbook/assets/Untitled-2.svg create mode 100644 docs/.gitbook/assets/Untitled-3.svg create mode 100644 docs/.gitbook/assets/Untitled-4.svg rename docs/{guides => }/coreconcepts/README.md (100%) rename docs/{guides => }/coreconcepts/altering-the-schema.md (100%) rename docs/{guides => }/coreconcepts/crud-operations/README.md (100%) rename docs/{guides => }/coreconcepts/crud-operations/creating-records.md (100%) rename docs/{guides => }/coreconcepts/crud-operations/deleting-records.md (100%) rename docs/{guides => }/coreconcepts/crud-operations/reading-records.md (100%) rename docs/{guides => }/coreconcepts/crud-operations/updating-records.md (100%) rename docs/{guides => }/coreconcepts/initializing-the-database.md (97%) rename docs/{guides => }/coreconcepts/migrations.md (100%) rename docs/{guides => }/coreconcepts/patterns/README.md (100%) rename docs/{guides => }/coreconcepts/patterns/active-record.md (100%) rename docs/{guides => }/coreconcepts/patterns/entity-framework.md (100%) rename docs/{guides => }/coreconcepts/patterns/repository.md (100%) rename docs/{guides => }/coreconcepts/schemas.md (98%) create mode 100644 docs/guides/active-record-with-cql/README.md create mode 100644 docs/guides/active-record-with-cql/belongsto.md create mode 100644 docs/guides/active-record-with-cql/hasone.md rename docs/{guides => }/installation.md (100%) rename docs/{guides => }/introduction.md (100%) diff --git a/docs/.gitbook/assets/Untitled-2.svg b/docs/.gitbook/assets/Untitled-2.svg new file mode 100644 index 0000000..f357d70 --- /dev/null +++ b/docs/.gitbook/assets/Untitled-2.svg @@ -0,0 +1,12 @@ +*1postsidintegertitlevarcharbodytextstatusvarcharcreated_attimestampcommentsidintegerusernamevarcharcommentvarcharpost_idintegercreated_attimestamp \ No newline at end of file diff --git a/docs/.gitbook/assets/Untitled-3.svg b/docs/.gitbook/assets/Untitled-3.svg new file mode 100644 index 0000000..3769bdd --- /dev/null +++ b/docs/.gitbook/assets/Untitled-3.svg @@ -0,0 +1,12 @@ +1*usersidintegernamevarcharemailvarcharcreated_attimestampprofilesidintegeruser_idintegerbiovarcharavatar_urlvarchar \ No newline at end of file diff --git a/docs/.gitbook/assets/Untitled-4.svg b/docs/.gitbook/assets/Untitled-4.svg new file mode 100644 index 0000000..f412334 --- /dev/null +++ b/docs/.gitbook/assets/Untitled-4.svg @@ -0,0 +1,12 @@ +1*usersidintnamevarcharemailvarcharprofilesidintuser_idintbiotextavatar_urlvarchar \ No newline at end of file diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index ffc475d..0ea4a32 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -1,23 +1,26 @@ # Table of contents * [Cql](README.md) +* [Introduction](introduction.md) +* [Installation](installation.md) +* [Core Concepts](coreconcepts/README.md) + * [DB Schema](coreconcepts/schemas.md) + * [Initializing the Database](coreconcepts/initializing-the-database.md) + * [Altering the Schema](coreconcepts/altering-the-schema.md) + * [Migrations](coreconcepts/migrations.md) + * [CRUD Operations](coreconcepts/crud-operations/README.md) + * [Creating Records](coreconcepts/crud-operations/creating-records.md) + * [Reading Records](coreconcepts/crud-operations/reading-records.md) + * [Updating Records](coreconcepts/crud-operations/updating-records.md) + * [Deleting Records](coreconcepts/crud-operations/deleting-records.md) + * [Patterns](coreconcepts/patterns/README.md) + * [Entity Framework](coreconcepts/patterns/entity-framework.md) + * [Active Record](coreconcepts/patterns/active-record.md) + * [Repository](coreconcepts/patterns/repository.md) * [Guides](guides/README.md) - * [Introduction](guides/introduction.md) - * [Installation](guides/installation.md) - * [Core Concepts](guides/coreconcepts/README.md) - * [DB Schema](guides/coreconcepts/schemas.md) - * [Initializing the Database](guides/coreconcepts/initializing-the-database.md) - * [Altering the Schema](guides/coreconcepts/altering-the-schema.md) - * [Migrations](guides/coreconcepts/migrations.md) - * [CRUD Operations](guides/coreconcepts/crud-operations/README.md) - * [Creating Records](guides/coreconcepts/crud-operations/creating-records.md) - * [Reading Records](guides/coreconcepts/crud-operations/reading-records.md) - * [Updating Records](guides/coreconcepts/crud-operations/updating-records.md) - * [Deleting Records](guides/coreconcepts/crud-operations/deleting-records.md) - * [Patterns](guides/coreconcepts/patterns/README.md) - * [Entity Framework](guides/coreconcepts/patterns/entity-framework.md) - * [Active Record](guides/coreconcepts/patterns/active-record.md) - * [Repository](guides/coreconcepts/patterns/repository.md) + * [Active Record with Cql](guides/active-record-with-cql/README.md) + * [BelongsTo](guides/active-record-with-cql/belongsto.md) + * [HasOne](guides/active-record-with-cql/hasone.md) * [Cql API](cql-api/README.md) * [class Cql::Error](cql-api/class-cql-error.md) * [class Cql::Schema](cql-api/class-cql-schema/README.md) diff --git a/docs/guides/coreconcepts/README.md b/docs/coreconcepts/README.md similarity index 100% rename from docs/guides/coreconcepts/README.md rename to docs/coreconcepts/README.md diff --git a/docs/guides/coreconcepts/altering-the-schema.md b/docs/coreconcepts/altering-the-schema.md similarity index 100% rename from docs/guides/coreconcepts/altering-the-schema.md rename to docs/coreconcepts/altering-the-schema.md diff --git a/docs/guides/coreconcepts/crud-operations/README.md b/docs/coreconcepts/crud-operations/README.md similarity index 100% rename from docs/guides/coreconcepts/crud-operations/README.md rename to docs/coreconcepts/crud-operations/README.md diff --git a/docs/guides/coreconcepts/crud-operations/creating-records.md b/docs/coreconcepts/crud-operations/creating-records.md similarity index 100% rename from docs/guides/coreconcepts/crud-operations/creating-records.md rename to docs/coreconcepts/crud-operations/creating-records.md diff --git a/docs/guides/coreconcepts/crud-operations/deleting-records.md b/docs/coreconcepts/crud-operations/deleting-records.md similarity index 100% rename from docs/guides/coreconcepts/crud-operations/deleting-records.md rename to docs/coreconcepts/crud-operations/deleting-records.md diff --git a/docs/guides/coreconcepts/crud-operations/reading-records.md b/docs/coreconcepts/crud-operations/reading-records.md similarity index 100% rename from docs/guides/coreconcepts/crud-operations/reading-records.md rename to docs/coreconcepts/crud-operations/reading-records.md diff --git a/docs/guides/coreconcepts/crud-operations/updating-records.md b/docs/coreconcepts/crud-operations/updating-records.md similarity index 100% rename from docs/guides/coreconcepts/crud-operations/updating-records.md rename to docs/coreconcepts/crud-operations/updating-records.md diff --git a/docs/guides/coreconcepts/initializing-the-database.md b/docs/coreconcepts/initializing-the-database.md similarity index 97% rename from docs/guides/coreconcepts/initializing-the-database.md rename to docs/coreconcepts/initializing-the-database.md index b03a0ad..85fbb8c 100644 --- a/docs/guides/coreconcepts/initializing-the-database.md +++ b/docs/coreconcepts/initializing-the-database.md @@ -117,7 +117,7 @@ AcmeDB2.init ### Generated Database Tables -
+
#### What Happens During Initialization? diff --git a/docs/guides/coreconcepts/migrations.md b/docs/coreconcepts/migrations.md similarity index 100% rename from docs/guides/coreconcepts/migrations.md rename to docs/coreconcepts/migrations.md diff --git a/docs/guides/coreconcepts/patterns/README.md b/docs/coreconcepts/patterns/README.md similarity index 100% rename from docs/guides/coreconcepts/patterns/README.md rename to docs/coreconcepts/patterns/README.md diff --git a/docs/guides/coreconcepts/patterns/active-record.md b/docs/coreconcepts/patterns/active-record.md similarity index 100% rename from docs/guides/coreconcepts/patterns/active-record.md rename to docs/coreconcepts/patterns/active-record.md diff --git a/docs/guides/coreconcepts/patterns/entity-framework.md b/docs/coreconcepts/patterns/entity-framework.md similarity index 100% rename from docs/guides/coreconcepts/patterns/entity-framework.md rename to docs/coreconcepts/patterns/entity-framework.md diff --git a/docs/guides/coreconcepts/patterns/repository.md b/docs/coreconcepts/patterns/repository.md similarity index 100% rename from docs/guides/coreconcepts/patterns/repository.md rename to docs/coreconcepts/patterns/repository.md diff --git a/docs/guides/coreconcepts/schemas.md b/docs/coreconcepts/schemas.md similarity index 98% rename from docs/guides/coreconcepts/schemas.md rename to docs/coreconcepts/schemas.md index 631a732..a99b67f 100644 --- a/docs/guides/coreconcepts/schemas.md +++ b/docs/coreconcepts/schemas.md @@ -6,7 +6,7 @@ Defining the schema first is a fundamental approach in CQL, helping developers q
-
+
diff --git a/docs/guides/active-record-with-cql/README.md b/docs/guides/active-record-with-cql/README.md new file mode 100644 index 0000000..10358a8 --- /dev/null +++ b/docs/guides/active-record-with-cql/README.md @@ -0,0 +1,293 @@ +--- +description: >- + In this guide, we'll walk through using CQL with Crystal for setting up a + database schema, defining records (models), establishing relationships between + them, and handling migrations +--- + +# Active Record with Cql + +Getting Started with Active Record, Relations, and Migrations in CQL + +In this guide, we'll walk through using CQL with Crystal for setting up a database schema, defining records (models), establishing relationships between them, and handling migrations. This will be a foundational guide for developers who are familiar with Object-Relational Mapping (ORM) concepts from other frameworks like ActiveRecord (Rails), Ecto, or Hibernate, but are now learning CQL with Crystal. + +*** + +### Prerequisites + +Before getting started, ensure you have the following: + +* Crystal language installed (latest stable version). +* PostgreSQL or MySQL set up locally or in the cloud. +* CQL installed in your Crystal project. + +You can add CQL to your project by including it in your `shard.yml`: + +```yaml +dependencies: + cql: + github: azutoolkit/cql + version: "~> 0.1.0" +``` + +Run `shards install` to add the library to your project. + +*** + +### Setting up CQL + +First, you need to configure CQL in your application. Let's create a basic `config/database.cr` file for connecting to a PostgreSQL database: + +```crystal +require "cql" + +# Database configuration +CQL::DB.open("postgres://username:password@localhost:5432/myapp_development") do |db| + CQL::DB.setup(db) +end +``` + +Replace the database connection string with your actual PostgreSQL or MySQL credentials. + +*** + +### Creating the Database Schema + +CQL uses migrations to manage the database schema, similar to Rails' ActiveRecord or Ecto's migrations. + +1. **Generate a migration**: CQL provides generators to create migration files. + + ```bash + crystal lib/cql/bin/cql generate migration CreateUsers + ``` + + This will create a file in the `db/migrate` directory, for example, `20230817000000_create_users.cr`. +2. **Edit the migration**: Let's define a `users` table in this migration: + + ```crystal + # db/migrate/20230817000000_create_users.cr + + class CreateUsers < CQL::Migration + def up + schema.alter :users do + text :name, null: false + text :email, null: false, unique: true + timestamps + end + end + + def down + schema.alter :users do + drop_table :users + end + end + end + ``` + + Here, we create a `users` table with columns `name`, `email`, and `timestamps`. +3. **Run the migration**: After defining your migration, you can run it with: + + ```bash + crystal lib/cql/bin/cql migrate + ``` + +This command will execute all pending migrations and update your database schema. + +*** + +### Defining Records (Models) + +Now that the schema is ready, we can define the `User` record (model) that maps to the `users` table. + +1. **Create the Record**: + + ```crystal + # src/models/user.cr + struct User + include Cql::Record(User, Int64) + define AcmeDB, :users + + property id : Int64? + property name : String + property email : String + property created_at : Time + property updated_at : Time + end + ``` + + The `User` model is mapped to the `users` table. Here, we've defined the fields for `id`, `name`, `email`, and timestamps. We also added basic validations for `name` and `email`. +2. **Create Records**: You can now create user records using the `User` model. + + ```crystal + user = User.new(name: "John Doe", email: "john@example.com") + user.save + ``` + + This will insert a new record into the `users` table. +3. **Query Records**: You can query users using the `User` model. + + ```crystal + users = User.all + user = User.find(1) + ``` + + `User.all` fetches all users, and `User.find(1)` fetches the user with ID `1`. + +*** + +### Establishing Relations + +CQL supports associations similar to ActiveRecord, Ecto, and other ORMs. Let's define some common relationships such as `has_many` and `belongs_to`. + +**Example: Users and Posts** + +1. **Migration for Posts**: + + Create a new migration for the `posts` table. + + ```bash + crystal lib/cql/bin/cql generate migration CreatePosts + ``` + + Edit the migration to add the `posts` table, which has a foreign key to the `users` table: + + ```crystal + # db/migrate/20230817000001_create_posts.cr + + class CreatePosts < Cql::Migration + + schema.table :posts do + primary + text :title, null: false + text :body, null: false + bigint :user_id, null: false, index: true + timestamps + end + + def up + schema.alter :posts { AcmeDB.posts.create! } + end + + def down + schema.alter :posts { AcmeDB.posts.drop! } + end + end + ``` +2. **Define the Post Model**: + + Now, let's define the `Post` record and establish the relationships. + + ```crystal + # src/models/post.cr + struct Post + include Cql::Record(Post, Int64) + define AcmeDB, :posts + + property id : Int64? + property title : String + property body : String + property user_id : Int64 + property created_at : Time + property updated_at : Time + + belongs_to :user, User + end + ``` + + Here, the `Post` model includes a foreign key `user_id` and defines a `belongs_to` association to the `User` model. +3. **Define the `User` model's association**: + + Update the `User` model to reflect the relationship with `Post`. + + ```crystal + # src/models/user.cr + class User + include Cql::Record(User, Int64) + define AcmeDB, :users + + column id : Int64? + column name : String + column email : String + column created_at : Time + column updated_at : Time + + has_many :posts, Post + end + ``` + + This defines a `has_many` association on `User` so that each user can have multiple posts. +4. **Working with Relations**: + * Create a user and associate posts with them: + + ```crystal + user = User.create(name: "Jane Doe", email: "jane@example.com") + post = Post.new(title: "First Post", body: "This is the first post", user: user) + post.save + ``` + * Access posts through the user: + + ```crystal + user = User.find(1) + + user.posts.each do |post| + puts post.title + end + ``` + +*** + +### Handling Migrations + +CQL migrations allow you to create and alter your database schema easily. Here are some common migration tasks: + +1. **Adding Columns**: + + If you need to add a new column to an existing table, generate a migration: + + ```bash + crystal lib/cql/bin/cql generate migration AddAgeToUsers + ``` + + Update the migration to add the `age` column: + + ```crystal + class AddAgeToUsers < CQL::Migration + def up + schema.table :users do + add_column :age, Int32 + end + end + + def down + schema.table :users do + drop_column :age + end + end + end + ``` +2. **Rolling Back Migrations**: + + If something goes wrong with a migration, you can roll it back using: + + ```bash + AcmeDB.migrator.rollback + ``` + + This will undo the last migration that was applied. + +*** + +### Conclusion + +This guide has provided a basic overview of using CQL with Crystal to define records (models), create relationships, and handle migrations. You've learned how to: + +* Set up CQL and connect it to a database. +* Create and run migrations to define your schema. +* Define records and establish relationships using `has_many` and `belongs_to`. +* Manage your database schema with migrations. + +With this foundation, you can now expand your models, add validations, and explore more advanced querying and relationships in CQL.\ +\ +In the following guide, we'll take a closer look at the different relationships you can establish between models in CQL: `BelongsTo`, `HasOne`, `HasMany`, and `ManyToMany`. These relationships allow you to associate models with one another, making it easy to retrieve related data, enforce foreign key constraints, and maintain data integrity. + +We'll use simple examples with CQL's DSL to help you understand how to define and use these associations effectively. diff --git a/docs/guides/active-record-with-cql/belongsto.md b/docs/guides/active-record-with-cql/belongsto.md new file mode 100644 index 0000000..8f7215e --- /dev/null +++ b/docs/guides/active-record-with-cql/belongsto.md @@ -0,0 +1,148 @@ +# BelongsTo + +In this guide, we'll cover the `BelongsTo` relationship using CQL's Active Record syntax. We'll start with an Entity-Relationship Diagram (ERD) to illustrate how this relationship works and continuously build upon this diagram as we introduce new relationships in subsequent guides. + +## **What is a `BelongsTo` Relationship?** + +The `BelongsTo` association in a database indicates that one entity (a record) refers to another entity by holding a foreign key to that record. For example, a `Comment` belongs to a `Post`, and each comment references the `Post` it is associated with by storing the `post_id` as a foreign key. + +### Example Scenario: Posts and Comments + +Let's say you have a blog system where: + +* A **Post** can have many **Comments**. +* A **Comment** belongs to one **Post**. + +
+ +We'll start by implementing the `BelongsTo` relationship from the `Comment` to the `Post`. + +*** + +## Defining the Schema + +We'll first define the `posts` and `comments` tables using CQL’s schema DSL. + +```crystal +codeAcmeDB = Cql::Schema.define( + :acme_db, + adapter: Cql::Adapter::Postgres, + uri: ENV["DATABASE_URL"] +) do + + table :posts do + primary + text :title + text :body + timestamp :published_at + end + + table :comments do + primary + bigint :post_id + text :body + end +end +``` + +* **posts** table: Contains the blog post data (title, body, and published date). +* **comments** table: Contains the comment data and a foreign key `post_id` which references the `posts` table. + +*** + +## Defining the Models + +Next, we'll define the `Post` and `Comment` structs in CQL. + +**Post Model** + +```crystal +crystalCopy codestruct Post + include Cql::Record(Post, Int64) + + define AcmeDB, :posts + + getter id : Int64? + getter title : String + getter body : String + getter published_at : Time + + # Initializing a new post with title, body, and optional published_at + def initialize(@title : String, @body : String, @published_at : Time = Time.utc) + end +end +``` + +**Comment Model** + +```crystal +crystalCopy codestruct Comment + include Cql::Record(Comment, Int64) + define AcmeDB, :comments + + getter id : Int64? + getter post_id : Int64 + getter body : String + + # Initializing a comment with a post_id (foreign key) and body + def initialize(@post_id : Int64, @body : String) + end + + # Association: Each Comment belongs to one Post + belongs_to :post, Post +end +``` + +In the `Comment` model, we specify the `belongs_to :post` association, which links each comment to its parent post by using the `post_id` foreign key. + +*** + +## Creating and Querying Records + +Now that we have defined the `Post` and `Comment` models with a `belongs_to` relationship, let's see how to create and query records in CQL. + +**Creating a Post and Comment** + +```crystal +# Create a new Post +comment = Comment.new(post.id.not_nil!, "Great post!") +comment.create_post("My First Blog Post", "This is the body of the post.") +comment.savex + +``` + +* We instantiate a `Comment` and associate it with the post by creating a `post`. +* The post record is created and saved in the database. +* And the returned id is then associtated to the comment. + +**Querying the Associated Post from a Comment** + +Once we have a comment, we can retrieve the associated post using the `belongs_to` association. + +```crystal +crystalCopy code# Fetch the comment +comment = Comment.find(1) + +# Fetch the associated post +post = comment.post + +puts post.title # Outputs: "My First Blog Post" +``` + +In this example, `comment.post` will fetch the `Post` associated with that `Comment`. + +*** + +## Summary + +In this guide, we’ve covered the basics of the `belongs_to` relationship in CQL. We: + +* Defined the `Post` and `Comment` tables in the schema. +* Created the corresponding models, specifying the `belongs_to` relationship in the `Comment` model. +* Showed how to create and query records using the `belongs_to` association. + +#### Next Steps + +In the next guides, we'll build on this ERD and introduce other types of relationships like `has_one`, `has_many`, and `many_to_many`. Stay tuned for the next part where we'll cover the `has_many` relationship! + +Feel free to play around with this setup and extend the models or experiment with more queries to familiarize yourself with CQL's Active Record capabilities. diff --git a/docs/guides/active-record-with-cql/hasone.md b/docs/guides/active-record-with-cql/hasone.md new file mode 100644 index 0000000..9cae598 --- /dev/null +++ b/docs/guides/active-record-with-cql/hasone.md @@ -0,0 +1,202 @@ +# HasOne + +In this guide, we'll cover the `HasOne` relationship using CQL's Active Record syntax. Like in the previous `BelongsTo`guide, we’ll start with an Entity-Relationship Diagram (ERD) to visually represent how the `HasOne` relationship works and build on the structure we already introduced with the `BelongsTo` relationship. + +## **What is a `HasOne` Relationship?** + +The `HasOne` relationship indicates that one entity (a record) is related to exactly one other entity. For example, a **User** can have one **Profile** associated with it. This relationship is a one-to-one mapping between two entities. + +#### Example Scenario: Users and Profiles + +Let's say we have a system where: + +* A **User** can have one **Profile**. +* A **Profile** belongs to one **User**. + + + +
+ +We will represent this one-to-one relationship using CQL’s `HasOne` and `BelongsTo` associations. + +*** + +## Defining the Schema + +We'll define the `users` and `profiles` tables in the schema using CQL. + +```crystal +AcmeDB = Cql::Schema.define( + :acme_db, + adapter: Cql::Adapter::Postgres, + uri: ENV["DATABASE_URL"] +) do + table :users do + primary + text :name + text :email + end + + table :profiles do + primary + bigint :user_id, index: true + text :bio + text :avatar_url + end +end +``` + +* **users** table: Stores user details like `name` and `email`. +* **profiles** table: Stores profile details like `bio` and `avatar_url`. It has a `user_id` foreign key referencing the `users`table. + +*** + +## Defining the Models + +Let’s define the `User` and `Profile` models in CQL, establishing the `HasOne` and `BelongsTo` relationships. + +### **User Model** + +```crystal +struct User + include Cql::Record(User, Int64) + + define AcmeDB, :users + + getter id : Int64? + getter name : String + getter email : String + + # Initializing a new user with name and email + def initialize(@name : String, @email : String) + end + + # Association: A User has one Profile + has_one :profile, Profile +end +``` + +* The `has_one :profile` association in the `User` model indicates that each user has one profile. + +### **Profile Model** + +```crystal +struct Profile + include Cql::Record(Profile, Int64) + + define AcmeDB, :profiles + + getter id : Int64? + getter user_id : Int64 + getter bio : String + getter avatar_url : String + + # Initializing a profile with user_id, bio, and avatar_url + def initialize(@user_id : Int64, @bio : String, @avatar_url : String) + end + + # Association: A Profile belongs to one User + belongs_to :user, User +end +``` + +* The `belongs_to :user` association in the `Profile` model links each profile to a user by its `user_id`. + +## Creating and Querying Records + +Now that we have defined the `User` and `Profile` models with a `has_one` and `belongs_to` relationship, let's see how to create and query records in CQL. + +### **Creating a User and Profile** + +```crystal +# Create a new User +user = User.new("John Doe", "john@example.com") +user.save + +# Create a Profile for the User +profile = Profile.new(user.id.not_nil!, "Developer at Acme", "avatar_url.jpg") +profile.save +``` + +* First, we create a `User` and save it to the database. +* Then, we create a `Profile` and associate it with the user by passing `user.id` as the `user_id`. + +### **Accessing the Profile from the User** + +Once a user and their profile have been created, you can retrieve the profile using the `has_one` association. + +```crystal +# Fetch the user +user = User.find(1) + +# Fetch the associated profile +profile = user.profile + +puts profile.bio # Outputs: "Developer at Acme" +``` + +Here, `user.profile` fetches the profile associated with the user. + +### **Accessing the User from the Profile** + +Similarly, you can retrieve the associated user from the profile. + +```crystal +# Fetch the profile +profile = Profile.find(1) + +# Fetch the associated user +user = profile.user + +puts user.name # Outputs: "John Doe" +``` + +In this example, `profile.user` fetches the `User` associated with that `Profile`. + +## Updating and Deleting the Associations + +### **Updating the Profile for a User** + +You can update the profile associated with a user in the same way you would update any other record. + +```crystal +# Fetch the user +user = User.find(1) + +# Update the user's profile +profile = user.profile +profile.bio = "Senior Developer at Acme" +profile.save +``` + +Here, we retrieve the profile associated with the user, modify its `bio`, and save the changes. + +### **Deleting the Profile** + +You can also delete the associated profile, but note that this does not automatically delete the user. + +```crystal +crystalCopy code# Fetch the user's profile +profile = user.profile + +# Delete the profile +profile.delete +``` + +Similarly, deleting the user will not automatically delete the associated profile unless cascade rules are explicitly set in the database. + +*** + +## Summary + +In this guide, we explored the `has_one` relationship in CQL. We: + +* Defined the `User` and `Profile` tables in the schema. +* Created corresponding models, specifying the `has_one` relationship in the `User` model and the `belongs_to`relationship in the `Profile` model. +* Demonstrated how to create, query, update, and delete records using the `has_one` and `belongs_to` associations. + +### Next Steps + +In the next guide, we’ll extend the ERD and cover the `has_many` relationship, which is commonly used when one entity is associated with multiple records (e.g., a post having many comments). + +Feel free to experiment with the `has_one` relationship by adding more fields to your models, setting up validations, or extending your schema with more complex relationships. diff --git a/docs/guides/installation.md b/docs/installation.md similarity index 100% rename from docs/guides/installation.md rename to docs/installation.md diff --git a/docs/guides/introduction.md b/docs/introduction.md similarity index 100% rename from docs/guides/introduction.md rename to docs/introduction.md