In relational databases, the biggest source of “silent data corruption” is deleting a parent record that still has dependent child records. Laravel’s restrictOnDelete() is a database-level safety mechanism that prevents that scenario.
In your migration:
$table->foreignId('continent_id')
->constrained()
->restrictOnDelete();
this means: a continent cannot be deleted if any country still references it.
What restrictOnDelete() Actually Does
restrictOnDelete() sets the foreign key constraint’s ON DELETE rule to RESTRICT.
At the database level, RESTRICT means:
- If a
continents.idis referenced by any row incountries.continent_id - and someone tries to delete that continent
- the database will reject the delete operation with a foreign key constraint error
This protection applies regardless of whether the delete is triggered from:
- Filament UI
- Laravel code (
Model::delete()) - raw SQL
- a queue job
- an admin script
- another service hitting the same DB
When You Should Use restrictOnDelete()
1) When Child Records Must Never Become Orphans
If your domain rules say a Country must always belong to a valid Continent, then deleting the continent while countries exist is invalid.
This is exactly your case:
countries.continent_idmust always reference a real continent- therefore the continent must not be deletable while countries exist
2) When You Want the Database to Enforce Integrity (Not Only Your App)
UI-level protection (like hiding delete buttons in Filament) is helpful, but it is not enough on its own.
Without DB constraints, a delete can still happen from:
- tinker
- seeders
- a buggy job
- another admin panel
- direct SQL access
restrictOnDelete() makes your DB a “final gatekeeper”.
3) When You Don’t Want Cascading Deletes
If you used cascadeOnDelete(), deleting a continent would also delete all related countries.
That’s dangerous when:
- countries are important historical records
- other tables reference
countries - you want explicit control over what gets deleted
Using RESTRICT forces a conscious workflow:
- move countries to another continent, or
- delete countries intentionally, or
- block deletion altogether
4) When You Combine It With Soft Deletes
Your countries table uses softDeletes().
That’s important: soft deleting a country keeps the row in the database, which means the foreign key still exists.
So with restrictOnDelete():
- even if countries are “soft deleted”, they still reference the continent
- the DB will still block deleting that continent
This is usually a good thing if you want:
- recoverability
- audit history
- prevention of accidental hard deletes
If your business rule is “a continent can be deleted only when there are no countries at all (even soft-deleted ones)”, RESTRICT matches that perfectly.
Advantages of restrictOnDelete() (Practical Benefits)
Database-level safety
The database enforces the rule even if application logic fails.
Prevents accidental mass data loss
It blocks a delete rather than deleting related data automatically.
Makes domain rules explicit
Your schema documents your business rule: “countries depend on continents”.
Protects you from future code changes
Even if someone removes policy logic or Filament rules later, the DB still protects integrity.
Works across all entry points
Anything that touches the DB must respect it.
restrictOnDelete() vs Other Options
cascadeOnDelete()
Best when:
- child records are meaningless without parent
- deleting parent should automatically remove all children
Not recommended for high-value child records.
nullOnDelete()
Best when:
- child can exist without parent
- foreign key is nullable
Example: optional relationships, archive references.
restrictOnDelete()
Best when:
- child must always have parent
- you want to block parent deletion if children exist
This is the strictest and safest option for relational integrity.
How This Relates to Filament Policies
If you already hide delete buttons in Filament using policies, restrictOnDelete() complements that approach:
- Policy = user-facing safety + good UX
- DB constraint (RESTRICT) = last-line safety
Even if a bug or bypass occurs, the database will still stop invalid deletes.
Summary
Use restrictOnDelete() when you want to ensure:
- the parent record cannot be deleted while it has dependent children
- your application is protected from accidental or unauthorized deletions
- data integrity is enforced at the database level, not only in the UI
In your schema (Country -> Continent), restrictOnDelete() is a strong, correct choice because a country should never be left without a valid continent.