MimisK's avatar
Level 12

Relationship: User can block countries & users

I'm facing a situation where the user should be able to block users from an entire country but should also be able to block individual users.

How should the tables and relationships be set up for something like this?

thnx!

0 likes
2 replies
LaryAI's avatar
Level 58

To address the requirement where a user can block both entire countries and individual users, you can set up your database tables and relationships as follows:

Database Tables

  1. users

    • id
    • name
    • email
    • etc.
  2. countries

    • id
    • name
    • code
  3. country_blocks

    • user_id
    • country_id
  4. user_blocks

    • user_id
    • blocked_user_id

Relationships

  • Users can have many Country Blocks and many User Blocks.
  • Countries can be associated with many Country Blocks.

Eloquent Models

User Model

class User extends Model
{
    public function blockedCountries()
    {
        return $this->belongsToMany(Country::class, 'country_blocks');
    }

    public function blockedUsers()
    {
        return $this->belongsToMany(User::class, 'user_blocks', 'user_id', 'blocked_user_id');
    }
}

Country Model

class Country extends Model
{
    public function blockingUsers()
    {
        return $this->belongsToMany(User::class, 'country_blocks');
    }
}

Migration Example

Here's how you might define the migrations for the country_blocks and user_blocks tables:

Migration for country_blocks

Schema::create('country_blocks', function (Blueprint $table) {
    $table->unsignedBigInteger('user_id');
    $table->unsignedBigInteger('country_id');
    $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
    $table->foreign('country_id')->references('id')->on('countries')->onDelete('cascade');
    $table->primary(['user_id', 'country_id']);
});

Migration for user_blocks

Schema::create('user_blocks', function (Blueprint $table) {
    $table->unsignedBigInteger('user_id');
    $table->unsignedBigInteger('blocked_user_id');
    $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
    $table->foreign('blocked_user_id')->references('id')->on('users')->onDelete('cascade');
    $table->primary(['user_id', 'blocked_user_id']);
});

Usage Example

To block a country:

$user->blockedCountries()->attach($countryId);

To block a user:

$user->blockedUsers()->attach($blockedUserId);

This setup allows for flexibility in blocking mechanisms and can be easily extended or modified based on further requirements.

1 like
kevinbui's avatar

@laryai solution looks legit. I just don't really like one thing about it. at any given time, countries and users only need to be blocked by a single user. So those many-to-many relationships sound overkilled.

There are indeed a number of approaches to solve this, @laryai solution is one of those.

FIRST, the simplest solution that I can think of is adding a blocked and blocked_by columns to both the countries and users table:

users:
- id
- name
- email
- country_id
- blocked (type: boolean, default: false)
- blocked_by

countries:
- id
- name
- two_letter_code
- three_letter_code
- blocked (type: boolean, default: false)
- blocked_by

We then can simply update the blocked and blocked_by fields.

SECOND, I am thinking about making @laryai solution polymorphic. We will have a single blocks table for both users and countries:

users:
- id
- name
- email
- country_id

countries:
- id
- name
- two_letter_code
- three_letter_code

blocks:
- id
- blockable_type
- blockable_id
- blocked_by

Please or to participate in this conversation.