Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

phuocbt's avatar

I want to concatenate two user names in a BelongsToMany relationship

I have 3 tables as follows channels [id, created_at, updated_at], users [id, name], channel_user [channel_id, user_id], The two models have relationships with each other (users, channels), I have a list of channels displayed with the name channel (which is the name of two users concatenated together), I am searching for the name of that channel but I am having problems concatenating two user names. in the users relationship when I go from model channel

 $query = $this
                ->whereHas('users', function ($q) use ($keyword) {
                    $q->when($keyword, function ($q) use ($keyword) {
                        $q->where(DB::raw("CONCAT(users.name, '_', users.name)"), "LIKE", "{$keyword}");
                    });
                })
                ->whereDoesntHave('users', function ($q) {
                    $q->where('users.type', 'admin');
                })
                ->with('users')
                ->orderBy('created_at', 'DESC');

Thanks for your help!

0 likes
11 replies
DhPandya's avatar

If you're trying to search the user name in the database with concatenation, then it will work.

$builder->where(DB::raw('CONCAT_WS(" ",firstname,lastname)'), 'LIKE', '%Firstname Lastname%');

Noted that firstname and lastname both are different columns.

phuocbt's avatar

@DhPandya No, here I'm trying to match the names of two users belonging to the same channel

DhPandya's avatar

@phuocbt If matching is only for users.name then why to use CONCAT ? You can also go without using it with a Like statement. isn't it?

 $q->where('users.name ', "LIKE", "%{$keyword}%");
phuocbt's avatar

@DhPandya No, the problem I'm having is that my channel doesn't have a name, so I'm going to combine the names of the users that belong to that channel, and do a search on that new name.

tangtang's avatar

searching for the name of that channel but I am having problems concatenating two user names

well, unless you have another pivot table, the query in the DB::raw only result john_john not john_maria.

it's just repeated same name.

but would you like to explain more what really this query do ?

why you concat 2 user name in one channel ?

what the condition of this channel ?

phuocbt's avatar

@tangtang Yes, the way you are explaining is correct for the problem I am facing? My channel currently has no name, but its name is based on combining the names of the users belonging to that channel, so when I search for the channel name, I need to combine the names of that user and search on that new name.

tangtang's avatar

@phuocbt

without another pivot table, there is no way the query will run as you wish. (and assume it run, it will be lack of parameters and will give more headache if lot of data exists)

it's more wise if this pivot table have the column channel_name, or if you dont want the pivot table, just add the channel_name in the channel table.

Bogey's avatar

I think your pivot table needs another column to hold the channel name it pivots the user to.

For instance:

channels [id, created_at, updated_at]

users [id, name]

channel_user [channel_id, user_id, channel_name]

And that 'channel_name' column would hold the concatenated name and would be inserted with the sync method (or attach) as follows...

The model relationships would need the 'withPivot()' method to point to the additional column added to the pivot table.

// Channel.php
class Channel extends Model
{
    public function users()
    {
        return $this->belongsToMany(User::class, 'channel_user')->withPivot('channel_name');
    }
}

// User.php
class User extends Model
{
    public function channels()
    {
        return $this->belongsToMany(Channel::class, 'channel_user')->withPivot('channel_name');
    }
}

And then to actually sync the channel and user together alongside the channel_name...

// Syncing
$channel = Channel::find($channelId);
$channel->users()->sync([
    $userId => ['channel_name' => $user1->name . '_' . $user2->name],
    // more if you doing multiple
]);

// Updating
$channel->users()->updateExistingPivot($userId, ['channel_name' => 'new_value']);

// Removing
$channel = Channel::find($channelId);
$channel->users()->detach($userId);
Bogey's avatar
SELECT CONCAT(user1.first_name, '_', user2.first_name) AS channel_name
FROM users AS user1, users AS user2
WHERE user1.user_id = 'user1_id' AND user2.user_id = 'user2_id';

Or is that closer to what you are looking for?

kokoshneta's avatar

I have a list of channels displayed with the name channel (which is the name of two users concatenated together)

What two users are concatenated as the channel name? If the channel has ten users, which two are supposed to be concatenated? And what if a channel only has one user? And how do you determine whether your channel is called john_maria or maria_john? Are your user-to-channel associations inherently ordered? What if several channels have the same two users and end up with the same name?

So far, everything you’ve written indicates that the channel name is not stored in the database. So why are you trying to search for it in the database if it’s not stored there, especially when it’s not even clear how the concatenated value is actually generated?

As others have said, the right way to do what you’re trying to do is to store the channel name in the database. That way, you also don’t have to generate the name on the fly each time you show or fetch the channel – only when you attach or detach users.

I disagree with part of @bogey’s answer, though: the correct place to store the channel name is in the channels table, directly on the channel model – not in the pivot table. Otherwise, each channel will have a (potentially different) name for each of its associated users.

Unfortunately, there doesn’t seem to be an easy, built-in way to automatically update the name whenever a row in the pivot table is added or deleted from either a channel or user model – the easiest way would probably be to use a custom pivot model, and then in that model call an update method on the parent channel when the created or deleted model is fired:

// app/Models/Channel.php
public function users() {
	return $this->belongsToMany(User::class)->using(ChannelUser::class);
}

public function updateName() {
	$this->name = $this->users->pluck('name')->sort()->join('_');
}
// app/Models/User.php
public function channels() {
	return $this->belongsToMany(Channel::class)->using(ChannelUser::class);
}
// app/Models/ChannelUser.php
namespace App\Models;

use Illuminate\Database\Eloquent\Relations\Pivot;

class ChannelUser extends Pivot {
	public $timestamps = FALSE;

	protected static function booted() : void {
		static::created(fn (self $pivot) => $pivot->updateChannelName());
		static::deleted(fn (self $pivot) => $pivot->updateChannelName());
	}

	public function updateChannelName() {
		if ($this->pivotParent instanceof Channel) {
			// The user was added from a channel model with `$channel->users()->attach()`
			$channel = $this->pivotParent;
		} else {
			// The channel was added from a user model with `$user->channels()->attach()`
			$channel = Channel::find($this->channel_id);
		}

		$channel->updateName();
	}
}

Note: The questions I asked above about how to determine the channel name from the users are still pertinent. I’ve simply assumed here that it should be generated based on the same principles Laravel expects pivot tables to adhere to: the name property of all associated users, alphabetised and joined with an underscore. You should of course use whatever logic you already have for this.

phuocbt's avatar

@kokoshneta oh! Thank you for your detailed instructions! it helped me understand a lot better

Please or to participate in this conversation.