Laravel One to Many relationships is used to define situations where one entity in our database is related to many entities of the same type.
In a voting system example, we can see that the political parties and candidates have one to many
relationships.
A political party can have many candidates — at least two candidates for each category of elections, for the running mates; and it could have several candidates if we extend to presidential, governorship and other levels of elections.
However, a candidate can only belong to one political party (at a time, at least).
By the way, if you’re STARTING with Laravel, I have created a Complete Laravel Guide just for you.
Another good example is categories and products — a simple system could exist where a category can have many products, but each product should belong to only one category.
Before we dive into the article, subscribe to get access to our free Laravel tips that will improve your productivity.
If we were to define this relationship in plain English, we’d say:
“A political party has many candidates”.
Eloquent makes it easy to define such relationships:
Go ahead and guess how Eloquent defines it?
Well, it’s done by using the hasMany
method (I hope you got it right).
We use it like this:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class PoliticalParty extends Model {
/**
* Get the candidates this party has
*/
public function candidates() {
return $this->hasMany(App\Model\Candidate::class);
}
}
To retrieve the entities that this relationship defines, we can use the dynamic property like this:
<?php
namespace App\Http\Controllers;
use App\Models\PoliticalParty;
class RandomController extends Controller {
/**
* Gets candidates
*/
public function getCandidates() {
$party = PoliticalParty::find(1);
$candidates = $party->candidates; // Returns a Laravel Collection
return response(['candidates' => $candidates], 200); // Returns JSON object containing an array of candidates
}
}
Eloquent uses the “id” prefix to locate foreign and local keys.
This means all candidates are expected to have a column called political_party_id
that would be used to locate all candidates of a political party dynamically.
This means we have the same table structure for one to many relationships as one to one relationships:
These column names can be replaced using the same convention as in the one to one relationships:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class PoliticalParty extends Model {
/**
* Get the candidates this party has
*/
public function candidates() {
return $this->hasMany(App\Model\Candidate::class, 'foreign_key', 'local_key');
}
}
Defining the Inverse of One to Many Relationships
In our hypothetical voting app, there are many scenarios where we’d want to get the political party each candidate belongs to.
We’ll do that by defining the inverse of the relationship.
To get the inverse of a one to many relationships, we use the belongsTo
method in the same way we define the inverse of one to one relationships.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Candidate extends Model {
/**
* Gets the political party a candidate belongs to
*/
public function politicalParty() {
return $this->belongsTo(App\Models\PoliticalParty::class);
}
}
As usual, Eloquent uses the “id” naming convention to locate local and foreign keys.
And just like we’ve done everywhere above, we can change the names of these columns by using the second and third arguments of the belongsTo
method.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Candidate extends Model {
/**
* Gets the political party a candidate belongs to
*/
public function politicalParty() {
return $this->belongsTo(App\Models\PoliticalParty::class, 'foreign_key', 'local_key');
}
}
Laravel Collections
One thing to keep in mind is :
When using one to many relationships (or any Laravel relationship where we expect to get multiple values), our dynamic property would return a Laravel Collection containing all the candidates this party has.
Laravel Collection is a wrapper around PHP arrays that provide some helpful functions for manipulating arrays. Assuming we wanted to act as each candidate, we could use a foreach
loop like this:
<?php
namespace App\Http\Controllers;
use App\Models\PoliticalParty;
class RandomController extends Controller {
/**
* Gets candidates
*/
public function getCandidates() {
$party = PoliticalParty::find(1);
$candidates = $party->candidates; // Returns a Laravel Collection
foreach($candidates as $candidate) {
// Do what you want with $candidate
}
}
}
Using Dynamic Methods
For Laravel relationships that retrieve multiple values, it is possible to use the dynamic method to create different relationships.
An illustration would explain this better:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class PoliticalParty extends Model {
/**
* Get the candidates this party has
*/
public function candidates() {
return $this->hasMany(App\Model\Candidate::class, 'foreign_key', 'local_key');
}
/**
* Get presidential candidates
*/
public function presidentialCandidates() {
return $this->candidates()->where(['type', 'president']);
}
}
The defined method can then be used as a dynamic property, just like any other defined relationship:
<?php
namespace App\Http\Controllers;
use App\Models\PoliticalParty;
class RandomController extends Controller {
/**
* Gets candidates
*/
public function getCandidates() {
$party = PoliticalParty::find(1);
$candidates = $party->presidentialCandidates; // Returns a Laravel Collection
foreach($candidates as $candidate) {
// Do what you want with $candidate
}
}
}
Take a break and subscribe to get access to our free Laravel tips that will improve your productivity.
CRUD Example
To better illustrate one to many relationships, we’ve prepared a repository you can clone that shows one-to-many relationships in action.
In this repository, we build a simple app that lists our products based on the category you select.
It also allows you to create new categories and new products under a category.
It builds on the relationship “One Category has Many Products”.
Feel free to clone the repository to get started:
To run the application after cloning, you’d have install composer dependencies with:
composer install
Then you’d have to run the migrations (after creating a database and setting credentials in your .env
file of course:
php artisan migrate
Then, we’d have to seed the database:
php artisan db:seed
Finally, we’d have to serve our app:
php artisan serve
The application uses two entities: Category
and Product
and they have one to many relationships.
Every Product belongs to one Category
but each Category
can have many products.
We first create this entity and its migrations and controllers.
We can run the commands:
php artisan make:model Category -m -c
php artisan make:model Product -m -c
Next, we define our migrations; they’ll have a simple structure for brevity:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
use HasFactory;
public function products() {
return $this->hasMany(Product::class);
}
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
use HasFactory;
public function category()
{
return $this->belongsTo(Category::class);
}
}
Finally, we update the controllers to correctly display the products and categories and accept new categories and products.
Open the App\Http\Controllers\CategoryController
and paste in the following code.
<?php
namespace App\Http\Controllers;
use App\Models\Category;
use Illuminate\Http\Request;
class CategoryController extends Controller
{
public function products($id) {
$categories = Category::all();
$current_category = Category::findOrFail($id);
$products = $current_category->products;
return view('index', compact('current_category', 'categories', 'products'));
}
public function new() {
return view('new.category');
}
public function add(Request $request) {
$data = $request->validate(
[
'name' => ['string', 'required', 'unique:categories']
]
);
$category = new Category();
$category->name = $data['name'];
$category->save();
session()->flash("status", "success");
session()->flash("title", "Success!");
session()->flash('message', "The category was created successfully!");
return redirect()->route('index');
}
}
Open the App\Http\Controllers\ProductController
and paste in the following code.
<?php
namespace App\Http\Controllers;
use App\Models\Category;
use App\Models\Product;
use Illuminate\Http\Request;
class ProductController extends Controller
{
public function index(Request $request) {
$categories = Category::all();
$products = Product::all();
return view('index', compact('categories', 'products',));
}
public function new() {
$categories = Category::all();
return view('new.product', compact('categories'));
}
public function add(Request $request) {
$data = $request->validate([
'name' => ['string', 'required'],
'category' => ['required', 'numeric'],
'picture' => ['file', 'required', 'image']
]);
$category = Category::findOrFail($data['category']);
$product = new Product();
$product->name = $data['name'];
$file = $request->file('picture')->store('uploads', 'public');
$product->image = explode("/", $file)[1];
$category->products()->save($product);
session()->flash("status", "success");
session()->flash("title", "Success!");
session()->flash('message', "The product was created successfully!");
return redirect()->route('index');
}
}
The rest of the repository codes are front-end code and the code to render the words and matches on the frontend.
What’s Next?
You can get an in-depth article on “Laravel many to many relationships with example” that explain everything from creating the relationship, the inverse method of creating it, and how to retrieve the data.
Conclusion
In this article, we had an in-depth look at Laravel One to Many relationships and an application implementing the relationship to solve a problem.
One to Many relationships are used extensively in real-world applications, and understanding them provides a lot of flexibility and power for handling lots of scenarios in the apps we build.
Feel free to check out our other articles, where we similarly go in-depth to explain other Laravel relationships (there are 6 of them).
Feel free to let us know if you have any questions and we’d love to help.