Building robust web applications requires the careful design of database relationships. Laravel offers a sophisticated ORM (Object-Relational Mapping) system that makes handling database relationships a breeze. As your application evolves, you might need to refactor relationships for greater simplicity and efficiency. Laravel relationships make it easy to access database records. There are different types of relationships provided by Laravel. Today, we are going to discuss one exciting feature that you can convert a hasMany relation to hasOne. It sounds confusing to you. But, yes, we can convert that without changing the hasMany relation to hasOne. We will cover this in today’s article. It streamlines your data structure and optimizes your codebase.
Let me clarify more this before implementing this with an example.
Initial Scenario of hasMany Relationship in Laravel
In a hasMany relationship, one record in the parent table can be associated with multiple records in the child table. For instance, let’s consider a scenario where a User
model has multiple Todo
models, indicating that each user can have many todos.
Now, let’s see the scenario in which we will be converting a hasMany to a hasOne relationship.
Desired Scenario of hasOne Relationship in Laravel
On the other hand, in a hasOne relationship, each record in the parent table is associated with just one record in the child table. In the same example, converting the relationship to hasOne would mean that each user has only one post.
So, let’s see the steps to convert hasMany to hasOne in Laravel 10.
Prerequisites
In this post, we will be using the latest version (Laravel 10 as of now). Hence, to have this application setup, you will require the below configurations.
- PHP >=8.1
- Composer
- Apache/Nginx Server
- VS Code Editor (Optional)
- MySQL (version > 5)
After having the above configurations, you will be good to go with the Laravel 10 application.
Step 1 – Create a Project to Convert hasMany to hasOne Relation
To create a Laravel 10 project, you will have to open the terminal and hit the below command.
composer create-project --prefer-dist laravel/laravel relation-app
After creating the project, you will have to configure the database.
Step 2 – Configure a Database in Laravel 10
Firstly, create a database and then navigate to the .env file of the project. Lastly, come under the DB section and add the DB credentials.
Hidden Power of oldestOfMany Relationship in Laravel 10
Step 3 – Model and Migration Setup for hasMany to hasOne Relation
You will require two database tables to create a relation between them. Hence, let’s create them one by one. However, initially, we can go with the User model and migration for the first. But, you will need to create a second model and migration.
Currently, I am going to create a second model and migration for the Todo.
php artisan make:model Todo -m
The command will create a model and migration for the Todo.
Step 4 – Add Schema for the User and Todo Table
We required a few columns in both migrations in order to create a relation between them. Hence, add the schema as shown below.
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('users');
}
};
After that, you will need to add the schema in the todos table as well.
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('todos', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('user_id');
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->string('title')->nullable();
$table->text('description')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('todos');
}
};
Thereafter, you will have to migrate the schemas using the migrate command.
php artisan migrate
Also, you will have to add the fillable properties in both models. Hence, let’s add that one as well.
Firstly, start with the User model as shown below.
<?php
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
Similarly, you will have to add in the Todo model.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Todo extends Model
{
use HasFactory;
protected $fillable = [
'user_id',
'title',
'description',
'reviews_count'
];
}
After having the fillable data in the respective models, we will create a relation to convert hasMany to hasOne in Laravel.
Therefore let’s proceed with that functionality.
Simplify Data Retrieval Using latestOfMany Relation in Laravel 10
Step 5 – Create a Relation to Convert hasMany to hasOne Relation
In order to convert hasMany to hasOne relation in Laravel 10, you must have one hasMany relation. This example is for converting existing hasMany relation to hasOne. Hence, let’s consider we have a hasMany relation as shown in the below User model.
<?php
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
/**
* Function : Relation for hasMany todo
* @relationType: hasMany
* @return todo
*/
public function todos() {
return $this->hasMany(Todo::class, 'user_id', 'id');
}
}
In the above model, we have a hasMany relationships to fetch many todos against the user. Now, let’s suppose, you have already used this relation function in some places in your project. Therefore, it is not recommended to modify this relation to hasOne because it can hamper your running codes.
That’s why we will create another relation function by using this. In that function, we will convert this hasMany to hasOne relation.
/**
* Function : Relation for converting hasMany to hasOne
* @relationType: oneOfMany
*/
public function todo() {
return $this->todos()->one()->ofMany();
}
Take a look at the above function. I have created a new relation function and inside that I have called the hasMany relation function. Then through that, I have fetched one todo from many.
I am assuming, you already have data in both tables (users and todos). So, based on that, you can fetch in your controller.
I have already created some fake data using factory and seeder as shown below.
Similarly, I have created 3 todos for every user along with the user id.
Thereafter, let’s create a controller for writing the functionality.
How to Create and Use belongsTo Relationship in Laravel 10
Step 6 – Create a Controller For Rendering Data with Relation
Next, create a controller using the below command.
php artisan make:controller UserController
After creating the controller, let’s write a function to fetch the data.
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Http\Request;
class UserController extends Controller
{
/**
* Function : Fetch Users with Todo
* @param NA
* @return response
*/
public function index() {
return User::with('todo')->get();
}
}
In the above function, I have fetched all the users with todo for lazy loading.
How to Create and Use hasMany Relationship in Laravel 10
Step 7 – Add Route in Laravel 10
To add the route, you will have to navigate to the web.php file. After that, you will have to add a route as shown below.
<?php
use App\Http\Controllers\UserController;
use Illuminate\Support\Facades\Route;
Route::get('users', [UserController::class, 'index']);
After adding the route, you can run the application and check the result.
In the above result, you can see the user list with the latest todo list. Here, you can put a condition as well in the relation function for fetching data conditionally.
Conclusion
As a result, we implemented the functionality to convert hasMany to hasOne relation in Laravel 10. This has not ended yet. You can extend this to a more advanced level for data retrieval. I hope this post will provide you with a quick idea on this implementation.
Leave a Reply