Back

Database


Column Naming

  • Primary key should be always id
  • Foreign keys should be a singular with the suffix _id (user_id, product_id, category_id...)
  • When choosing a name you should be careful with reserved words

Recommended names for frequently used columns:

These names are recommendations on how to name certain columns to unify them across projects. These columns may be called differently, but it is good to keep them unified.

  • order_column - field for sorting entities (e.g. drag & drop)
  • company_number - field for CIN / Company Identification Number
  • vat_number - field for VATIN / VAT ID / VAT Identification Number
  • zip_code - field for ZIP Code / Postal Code / Post Code

Names by column type:

Recommended prefixes/suffixes for naming columns.

  • boolean - is_enabled, has_reviews
  • timestamp - published_at, valid_from, scheduled_for

Pro tip: Some of boolean columns could be converted to timestamps (e.g. from is_finished to finished_at). There is a benefit that you have in fact two pieces of information in one column.

Column Sorting

This sorting helps for better reading and orientation in the database.

  • id - should be always first
  • foreign keys
  • rest of columns - should be sorted by priorty and grouped by context
  • native timestamps - created_at, updated_at

UUIDs

Secure alternative to ids. They should be used for publicly accessible websites or for some tables with sensitive content (bank accounts, orders, invoices...). How to use UUIDs?

Indexes

Indexes are database structures that allow for faster retrieval of data. They work by creating a separate data structure that points to the location of the data in the database. When a query is run, the database can use the index to quickly find the relevant data instead of having to search the entire database.

Pro tip: Just use indexes on any columns that are frequently queried or used in joins and your database performance will be improved.

Migrations

Migrations are a way to version control your database schema. They allow you to make changes to the database schema in a structured way, and enable you to easily roll back changes if necessary. Read more

Best practices

  • During development, it's fine to modify existing migration files and run php artisan migrate:fresh to reset the database
  • Once the project is in production, you have to create new migrations for every change, even small ones
  • Don't break column sorting - when adding a new column use also after() and before() methods
  • Add short note with comment() method if column names are not clear (e.g. professional terms)

Pro tip: As you build your application, you may accumulate more and more migrations over time. This can lead to your database/migrations directory becoming bloated with potentially hundreds of migrations. If you would like, you may "squash" your migrations into a single SQL file. Read more

Seeders

Seeders are used to populate a database with initial/testing data. They are especially useful in scenarios where you need to populate your database with pre-existing data, such as test data or reference data.

By using a seeder, you can quickly populate your database with the necessary data without having to manually enter it every time. Read more

  • Create command: php artisan make:seeder UserSeeder
  • Running all seeders: php artisan db:seed
  • Running specific seeder: php artisan db:seed --class=UserSeeder

Pro tip: Sometimes you need to disable model events inside seeders. You can simply make it with a trait WithoutModelEvents. Read more

Naming

Singular with "Seeder" suffix (UserSeeder, ProductSeeder, CategorySeeder...)

Factories

Laravel factories are a convenient way to generate fake data for testing purposes. They allow you to quickly and easily create instances of your models with randomized or custom attributes.

By defining a factory for each of your models, you can easily generate the data you need to test your application's functionality.

They are mostly used in seeders and tests. Read more

Create command: php artisan make:factory UserFactory

1class ProductFactory extends Factory
2{
3 public function definition(): array
4 {
5 return [
6 'name' => fake()->name(),
7 'category_id' => Category::factory(),
8 ...
9 ];
10 }
11}

Naming

Singular model name with "Factory" suffix (UserFactory, ProductFactory, CategoryFactory...)

Usage Examples

Creating a single database record:

1User::factory()->create();

Creating a single model instance:

1User::factory()->make();

Creating multiple database records:

1User::factory()
2 ->count(10)
3 ->create();

Creating multiple model instances:

1User::factory()
2 ->count(10)
3 ->make();

Creating a factory with custom attributes:

1User::factory()->create(['email' => 'info@rockero.cz']);

Factory methods

Sequence

You can create models with more data variation. The sequence is looped until the desired number of models is reached. Read more

1// Creates 5 orders with state new and 5 orders with state pending:
2Order::factory()
3 ->count(10)
4 ->sequence(
5 ['state' => 'new'],
6 ['state' => 'pending']
7 )
8 ->create();

You can use sequence also with callback:

1Category::factory()
2 ->count(10)
3 ->sequence(fn (Sequence $sequence) => ['order_column' => $sequence->index])
4 ->create();

Recycle

Sometimes happens that nested factories have the same relationship as the base factory. For that, you may use the recycle() method. It will re-use the relationship instance. Read more

1$tenant = Tenant::factory()->create();
2 
3// Bad example:
4Product::factory()->create(['tenant_id' => $tenant]);
5ProductCategory::factory()->create(['tenant_id' => $tenant]);
6 
7// Good example:
8Product::factory()
9 ->recycle($tenant)
10 ->has(ProductCategory::factory())
11 ->create();

Relationships

1// Creates user with 3 posts:
2User::factory()
3 ->has(Post::factory()->count(3))
4 ->create();
5 
6// Or using magic methods:
7User::factory()
8 ->hasPosts(3)
9 ->create();
10 
11// Creates post that belongs to the user:
12Post::factory()
13 ->count(3)
14 ->for(User::factory(['name' => 'Rockero']))
15 ->create();
16 
17// Or using magic methods:
18Post::factory()
19 ->count(3)
20 ->forUser(['name' => 'Rockero'])
21 ->create();
22 
23// Creates user with 3 roles and custom pivot value:
24User::factory()
25 ->hasAttached(
26 Role::factory()->count(3),
27 ['active' => true]
28 )
29 ->create();
30 
31// Or using magic methods:
32User::factory()
33 ->hasRoles(1, ['name' => 'Editor'])
34 ->create();

Custom Methods

Instead of creating client with following setup in each test:

1Client::factory([
2 'platform' => 'github',
3 'email' => 'info@rockero.cz'
4])->create();

you can create a custom method in ClientFactory:

1public function github(array $attributes = []): self
2{
3 return $this->state([
4 'platform' => 'github',
5 'email' => $attributes['email'] ?? fake()->email()
6 'access_token' => $attributes['token'] ?? Str::random()
7 ]);
8}

and then use it like that:

1Client::factory()
2 ->github()
3 ->create();

Factory Callbacks

Factory callbacks can be used to perform an action after making or creating a model.

These methods are defined inside configure() method on factory class.

1public function configure(): static
2{
3 return $this->afterMaking(function (User $user) {
4 // ...
5 })->afterCreating(function (User $user) {
6 // ...
7 });
8}
Edit this page

We are hiring!