In the ever-evolving ecosystem of modern web applications, efficient data retrieval is very major factor. This stands as a cornerstone of user satisfaction and optimal performance. Laravel introduces developers to a wide collection of eloquent relationships. These are streamlining interactions with databases. Among these relationships, the hasOneThrough relationship emerges as a powerful tool for optimizing data retrieval in intricate scenarios. In this exclusive exploration, we embark on a journey through the nuances of efficient data retrieval using the hasOneThrough relation in Laravel 10. Prepare to uncover how this dynamic mechanism can revolutionize your application’s performance while maintaining code integrity and elegance.
Before diving deep into the example, let’s understand what actually is a hasOneThrough relationship in Laravel.
Simplify Relation By Converting hasMany to hasOne in Laravel 10
Understand hasOneThrough Relation in Laravel
The hasOneThrough relationship in Laravel is a powerful feature that allows you to define a relationship between three database tables. In this process, there will be an intermediary model. That means the first model can access the data of the third model through the second model. It enables you to retrieve data from a distant relationship in an elegant and efficient manner. It saves us from writing such complex SQL queries to join three tables.
In simpler terms, when you have three related models where one model is related to another through an intermediate model, you can use the hasOneThrough relationship to access data from the third model through the intermediary model.
Let’s understand this by a simple and practical example of a Book Publisher platform. To harness the full potential of the hasOneThrough relationship in Laravel 10. Consider a book publisher application where you have authors, books, and publishers tables. Each author can have at least one book and the book can have at least one publisher. Hence, by using the hasOneThrough relationship, you can seamlessly fetch publisher details through the book.
Let’s take a recap of the result that we are going to achieve in this post.
Now, let’s implement this with an example in this post.
Hidden Power of oldestOfMany Relationship in Laravel 10
Prerequisites
In order to implement the hasOneThrough relation in Laravel 10, you will require a Laravel 10 application. So for proceeding with a Laravel 10 application, your system must meet the below configurations.
- PHP >=8.1
- Composer
- Apache/Nginx Server
- VS Code Editor (Optional)
- MySQL (version > 5)
However, it is not mandatory that the hasOneThrough relation will be implemented in Laravel 10 only. You can implement it in the older version of Laravel as well.
Let’s proceed with the application once you are done.
Step 1 – Create Project For hasOneThrough Relation in Laravel 10
In order to create a Laravel 10 project, you will have to enter the below command in your terminal.
composer create-project --prefer-dist laravel/laravel laravel-relation
Once the project setup is done, you will have to configure a database for this.
Simplify Data Retrieval Using latestOfMany Relation in Laravel 10
Step 2 – Configure a Database in Laravel 10
For configuring the database, firstly, create a database in MySQL. After that navigate to the project folder and look at the .env file. Here, come under the DB section and add the DB credentials as shown below.
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_blog
DB_USERNAME={{ DATABASE_USERNAME }}
DB_PASSWORD={{ DATABASE_PASSWORD }}
Don’t forget to replace your actual credentials with the above.
Step 3 – Create Models and Migrations in Laravel 10
We will be picking up an example of e-commerce for implementing the hasOneThrough relationship. Hence, you will require three models and migrations for having a hasOneThrough relation.
Therefore, let’s create them quickly one by one.
php artisan make:model Author -m
php artisan make:model Book -m
php artisan make:model Publisher -m
The above commands will create the model and its respective migration as shown below.
After having the models and migrations, you will have to add the schema in these models. Hence, let’s add one by one in every migration.
How to Create and Use belongsTo Relationship in Laravel 10
Step 4 – Add Schema in the Migrations
To achieve the hasOneThrough relation in Laravel, we have created three migrations. These are the following.
- authors table
- books table and
- publishers table
Now, let’s start adding the schema in these migrations one by one.
<?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('authors', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('authors');
}
};
Next, you will have to add the schema for the books table.
The book table will contain a foreign key of the author table. So, just relate like every author will have at least one book.
<?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('books', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->double('price');
$table->unsignedBigInteger('author_id');
$table->foreign('author_id')->references('id')->on('authors')->onDelete('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('books');
}
};
After that, let’s add the schema in the publisher table.
<?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('publishers', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->unsignedBigInteger('book_id');
$table->foreign('book_id')->references('id')->on('books')->onDelete('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('publishers');
}
};
So, after adding the schemas in the migrations, let’s migrate these all tables.
php artisan migrate
After that, you will have to define the mass assignment properties in these three models as per the migration.
How to Create and Use hasMany Relationship in Laravel 10
Step 5 – Add Mass Assignment in the Models
You will have to define the mass assignment in the models. Actually, we will be filling up the data in the tables using the model eloquent. Hence, you will have to specify the fillable data in it.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Author extends Model
{
use HasFactory;
protected $fillable = [
'name',
'email'
];
}
Similarly, navigate to the Book and Publisher model as well. After that add the fillable property in both.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Book extends Model
{
use HasFactory;
protected $fillable = [
'title',
'price',
'author_id'
];
}
Lastly, add in the Publisher model as well.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Publisher extends Model
{
use HasFactory;
protected $fillable = [
'name',
'book_id'
];
}
After that, we can create a hasOneThrough relation for retrieving the data.
Step 6 – Create hasOneThrough Relation in Laravel 10
In order to create hasOneThrough relation according to this project, you will have to navigate to the Author model. After that, you will have to add the below relation function.
/**
* Function : bookPublisher
* @relationType : hasOneThrough
* @return bookPublisher
*/
public function bookPublisher() {
return $this->hasOneThrough(Publisher::class, Book::class);
}
Let me quickly explain the above snippet. The hasOneThrough function works between two models. The first model will be the model from which we want to fetch the information. However, the second model will be a mediator through which we can fetch data from the first model.
So, overall the concept is like from the primary model we can fetch data from the third model via the second model.
The Author model will contain other relations like hasOne as well. Similarly, you can create relations in the Book model as well. So, let’s see the model snippets as shown below after adding the relations.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Author extends Model
{
use HasFactory;
protected $fillable = [
'name',
'email'
];
/**
* Function : book
* @relationType : hasOne
* @return book
*/
public function book() {
return $this->hasOne(Book::class);
}
/**
* Function : bookPublisher
* @relationType : hasOneThrough
* @return bookPublisher
*/
public function bookPublisher() {
return $this->hasOneThrough(Publisher::class, Book::class);
}
}
So, I have created the relations in the above snippet. Now, let’s take a detailed look at the parameters that this function is supposed to access.
/**
* Function : bookPublisher
* @relationType : hasOneThrough
* @return bookPublisher
*/
public function bookPublisher() {
return $this->hasOneThrough(
Publisher::class,
Book::class,
'author_id', // Foreign key on books table
'book_id', // Foreign key on publishers table
'id', // Local key on authors table
'id' // Local key on books table
);
}
Similarly, I have created an inverse relation of the author in the book model. Also, there is a hasOne relation for the publisher from the book model.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Book extends Model
{
use HasFactory;
protected $fillable = [
'title',
'price',
'author_id'
];
/**
* Function : author
* @relationType : belongsTo
* @return author
*/
public function author() {
return $this->belongsTo(Author::class);
}
/**
* Function : publisher
* @relationType : hasOne
* @return publisher
*/
public function publisher() {
return $this->hasOne(Publisher::class);
}
}
In this example, we are focusing on the hasOneThrough relation mostly. Other relations are optional.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Publisher extends Model
{
use HasFactory;
protected $fillable = [
'name',
'book_id'
];
public function book() {
return $this->belongsTo(Book::class);
}
}
Next, you will require some data for testing purposes.
How to Create and Use hasOne Relationship in Laravel 10
Step 7 – Create Factory Class to Save Dummy Data For hasOneThrough Relation
This step is again optional if you have actual data in your case. But, if not then you can feed some data using the factory and seeder. So, I have created three factory classes using the below command.
php artisan make:factory AuthorFactory --model=Author
php artisan make:factory BookFactory --model=Book
php artisan make:factory PublisherFactory --model=Publisher
After having the factory classes, you will have to define the key attributes for which you will be inserting dummy records.
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Author>
*/
class AuthorFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'name' => fake()->name(),
'email' => fake()->email()
];
}
}
Secondly, do the same in the BookFactory class.
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Book>
*/
class BookFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'title' => fake()->sentence(2),
'price' => fake()->numberBetween($min = 500, $max = 1000)
];
}
}
Lastly, you will have to add in the Publisher factory.
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Publisher>
*/
class PublisherFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'name' => fake()->name(),
];
}
}
Yeah, you are done with the factory properties. Now, you will have to call these factories in the seeder to seed the data in the database tables.
Step 8 – Add Seeder to Seed Test Data
You will have to run the factory classes using seeder. So, for this, either you can create a seeder file or go with the default seeder. In Laravel, you will have the DatabaseSeeder.php file. So, simply open this and add the snippet as shown below.
<?php
namespace Database\Seeders;
// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use App\Models\Author;
use App\Models\Book;
use App\Models\Publisher;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*/
public function run(): void
{
Author::factory(4)->has(
Book::factory(1)->has(
Publisher::factory(1)->count(1)
)
)->create();
}
}
In the above snippet, I have added factory count to seed some count of data. You can add more based on your needs. In this case, there will be four authors, and every author will have one book. Also, every book will have one publisher.
After that, you will have to execute the seeder using the below command.
php artisan db:seed
You are done with the data now in the database. Therefore, it’s time to fetch the data based on hasOneThrough relation.
How to Create a Pagination Using Bootstrap in Laravel 10
Step 9 – Create a Controller to Fetch Data
Simply create a controller using the below command.
php artisan make:controller AuthorController
After having the controller, let’s put the functionality over there.
<?php
namespace App\Http\Controllers;
use App\Models\Author;
use Illuminate\Http\Request;
class AuthorController extends Controller
{
public function index() {
$authors = Author::with('book', 'bookPublisher')->get();
dd($authors);
}
}
Lastly, you required a route in order to run this application. I haven’t displayed data to the view as of now.
Step 10 – Add Route
You need to add a route in the web.php file. Hence, add it quickly.
<?php
use App\Http\Controllers\AuthorController;
use Illuminate\Support\Facades\Route;
Route::get('authors', [AuthorController::class, 'index']);
Now, you are ready to go with the test results by running the application.
Conclusion
The hasOneThrough relationship in Laravel is a powerful tool that simplifies the management of complex relationships. In our example, it enabled us to link authors, books, and publishers information through an intermediate model. Understanding and utilizing this relationship can significantly enhance your Laravel application. It makes it more efficient and maintainable. Whether you’re building a small online store or a large-scale e-commerce platform, the hasOneThrough relationship can be a valuable addition to your toolkit.
Leave a Reply