Skip to content

Commit 6cda2b6

Browse files
authored
Merge pull request #138 from michaviehauser/michaviehauser/follow-requests
Add follow requests
2 parents 34de296 + a805baa commit 6cda2b6

File tree

6 files changed

+193
-24
lines changed

6 files changed

+193
-24
lines changed

README.md

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
</p>
1515

1616
> 💡 The new version has been split into several packages:
17+
>
1718
> - Follow: [overtrue/laravel-follow](https://github.com/overtrue/laravel-follow)
1819
> - Like: [overtrue/laravel-like](https://github.com/overtrue/laravel-like)
1920
> - Favorite: [overtrue/laravel-favorite](https://github.com/overtrue/laravel-favorite)
@@ -65,33 +66,48 @@ $user2 = User::find(2);
6566
$user1->follow($user2);
6667
$user1->unfollow($user2);
6768
$user1->toggleFollow($user2);
69+
$user1->acceptFollowRequestFrom($user2);
70+
$user1->rejectFollowRequestFrom($user2);
6871

69-
$user1->isFollowing($user2);
70-
$user2->isFollowdBy($user1);
72+
$user1->isFollowing($user2);
73+
$user2->isFollowdBy($user1);
74+
$user2->hasRequestedToFollow($user1);
7175

7276
$user1->areFollowingEachOther($user2);
7377
```
7478

7579
#### Get followings:
7680

7781
```php
78-
$user->followings;
82+
$user->followings;
7983
```
8084

8185
#### Get followers:
8286

8387
```php
84-
$user->followers;
88+
$user->followers;
89+
```
90+
91+
### Follow Requests
92+
93+
If you would like to have some follow requests to need to be accepted by the user being followed, simply override the **needsToApproveFollowRequests()** method in the model that uses the **Followable** trait with your custom logic:
94+
95+
```php
96+
public function needsToApproveFollowRequests()
97+
{
98+
// Your custom logic here
99+
return (bool) $this->private;
100+
}
85101
```
86102

87103
### Aggregations
88104

89105
```php
90106
// followings count
91-
$user->followings()->count();
107+
$user->followings()->count();
92108

93109
// with query where
94-
$user->followings()->where('gender', 'female')->count();
110+
$user->followings()->where('gender', 'female')->count();
95111

96112
// followers count
97113
$post->followers()->count();
@@ -126,13 +142,12 @@ foreach($users as $user) {
126142
}
127143
```
128144

129-
130145
### Events
131146

132-
| **Event** | **Description** |
133-
| --- | --- |
134-
| `Overtrue\LaravelFollow\Events\Followd` | Triggered when the relationship is created. |
135-
| `Overtrue\LaravelFollow\Events\Unfollowd` | Triggered when the relationship is deleted. |
147+
| **Event** | **Description** |
148+
| ----------------------------------------- | ------------------------------------------- |
149+
| `Overtrue\LaravelFollow\Events\Followd` | Triggered when the relationship is created. |
150+
| `Overtrue\LaravelFollow\Events\Unfollowd` | Triggered when the relationship is deleted. |
136151

137152
## Contributing
138153

migrations/2020_04_04_000000_create_user_follower_table.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public function up()
2424
$table->increments('id');
2525
$table->unsignedBigInteger('following_id')->index();
2626
$table->unsignedBigInteger('follower_id')->index();
27+
$table->timestamp('accepted_at')->nullable()->index();
2728
$table->timestamps();
2829
});
2930
}

src/Followable.php

Lines changed: 73 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,32 +21,83 @@
2121
*/
2222
trait Followable
2323
{
24+
/**
25+
* @return bool
26+
*/
27+
public function needsToApproveFollowRequests()
28+
{
29+
return false;
30+
}
31+
2432
/**
2533
* @param \Illuminate\Database\Eloquent\Model|int $user
34+
*
35+
* @return array
2636
*/
2737
public function follow($user)
2838
{
29-
$this->followings()->attach($user);
39+
$isPending = $user->needsToApproveFollowRequests() ?: false;
40+
41+
$this->followings()->attach($user, [
42+
'accepted_at' => $isPending ? null : now()
43+
]);
44+
45+
return ['pending' => $isPending];
3046
}
3147

3248
/**
3349
* @param \Illuminate\Database\Eloquent\Model|int $user
34-
*
35-
* @return int
3650
*/
3751
public function unfollow($user)
3852
{
39-
return $this->followings()->detach($user);
53+
$this->followings()->detach($user);
4054
}
4155

4256
/**
4357
* @param \Illuminate\Database\Eloquent\Model|int $user
4458
*
45-
* @return array|array[]
4659
*/
4760
public function toggleFollow($user)
4861
{
49-
return $this->followings()->toggle($user);
62+
$this->isFollowing($user) ? $this->unfollow($user) : $this->follow($user);
63+
}
64+
65+
/**
66+
* @param \Illuminate\Database\Eloquent\Model|int $user
67+
*/
68+
public function rejectFollowRequestFrom($user)
69+
{
70+
$this->followers()->detach($user);
71+
}
72+
73+
/**
74+
* @param \Illuminate\Database\Eloquent\Model|int $user
75+
*/
76+
public function acceptFollowRequestFrom($user)
77+
{
78+
$this->followers()->updateExistingPivot($user, ['accepted_at' => now()]);
79+
}
80+
81+
/**
82+
* @param \Illuminate\Database\Eloquent\Model|int $user
83+
*/
84+
public function hasRequestedToFollow(Model $user): bool
85+
{
86+
if ($user instanceof Model) {
87+
$user = $user->getKey();
88+
}
89+
90+
/* @var \Illuminate\Database\Eloquent\Model $this */
91+
if ($this->relationLoaded('followings')) {
92+
return $this->followings
93+
->whereNull('pivot.accepted_at')
94+
->contains($user);
95+
}
96+
97+
return $this->followings()
98+
->wherePivot('accepted_at', null)
99+
->where($this->getQualifiedKeyName(), $user)
100+
->exists();
50101
}
51102

52103
/**
@@ -62,10 +113,15 @@ public function isFollowing($user)
62113

63114
/* @var \Illuminate\Database\Eloquent\Model $this */
64115
if ($this->relationLoaded('followings')) {
65-
return $this->followings->contains($user);
116+
return $this->followings
117+
->whereNotNull('pivot.accepted_at')
118+
->contains($user);
66119
}
67120

68-
return $this->followings()->where($this->getQualifiedKeyName(), $user)->exists();
121+
return $this->followings()
122+
->wherePivot('accepted_at', '!=', null)
123+
->where($this->getQualifiedKeyName(), $user)
124+
->exists();
69125
}
70126

71127
/**
@@ -81,10 +137,15 @@ public function isFollowedBy($user)
81137

82138
/* @var \Illuminate\Database\Eloquent\Model $this */
83139
if ($this->relationLoaded('followers')) {
84-
return $this->followers->contains($user);
140+
return $this->followers
141+
->whereNotNull('pivot.accepted_at')
142+
->contains($user);
85143
}
86144

87-
return $this->followers()->where($this->getQualifiedKeyName(), $user)->exists();
145+
return $this->followers()
146+
->wherePivot('accepted_at', '!=', null)
147+
->where($this->getQualifiedKeyName(), $user)
148+
->exists();
88149
}
89150

90151
/**
@@ -109,7 +170,7 @@ public function followers()
109170
\config('follow.relation_table', 'user_follower'),
110171
'following_id',
111172
'follower_id'
112-
)->withTimestamps()->using(UserFollower::class);
173+
)->withPivot('accepted_at')->withTimestamps()->using(UserFollower::class);
113174
}
114175

115176
/**
@@ -123,6 +184,6 @@ public function followings()
123184
\config('follow.relation_table', 'user_follower'),
124185
'follower_id',
125186
'following_id'
126-
)->withTimestamps()->using(UserFollower::class);
187+
)->withPivot('accepted_at')->withTimestamps()->using(UserFollower::class);
127188
}
128189
}

tests/PrivacyTest.php

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the overtrue/laravel-follow.
5+
*
6+
* (c) overtrue <[email protected]>
7+
*
8+
* This source file is subject to the MIT license that is bundled
9+
* with this source code in the file LICENSE.
10+
*/
11+
12+
namespace Tests;
13+
14+
use Illuminate\Support\Facades\Event;
15+
use Overtrue\LaravelFollow\Events\Followed;
16+
use Overtrue\LaravelFollow\Events\Unfollowed;
17+
18+
/**
19+
* Class PrivacyTest.
20+
*/
21+
class PrivacyTest extends TestCase
22+
{
23+
public function setUp(): void
24+
{
25+
parent::setUp();
26+
27+
config(['auth.providers.users.model' => User::class]);
28+
}
29+
30+
public function test_following_private_user_sets_request_pending()
31+
{
32+
$user1 = User::create(['name' => 'user1']);
33+
$user2 = User::create(['name' => 'user2', 'private' => true]);
34+
35+
$user1->follow($user2);
36+
37+
$this->assertTrue($user1->hasRequestedToFollow($user2));
38+
$this->assertFalse($user2->isFollowedBy($user1));
39+
}
40+
41+
public function test_pending_request_can_be_accepted()
42+
{
43+
$user1 = User::create(['name' => 'user1']);
44+
$user2 = User::create(['name' => 'user2', 'private' => true]);
45+
46+
$user1->follow($user2);
47+
48+
$user2->acceptFollowRequestFrom($user1);
49+
50+
$this->assertFalse($user1->hasRequestedToFollow($user2));
51+
$this->assertTrue($user2->isFollowedBy($user1));
52+
}
53+
54+
public function test_pending_request_can_be_rejected()
55+
{
56+
$user1 = User::create(['name' => 'user1']);
57+
$user2 = User::create(['name' => 'user2', 'private' => true]);
58+
59+
$user1->follow($user2);
60+
61+
$user2->rejectFollowRequestFrom($user1);
62+
63+
$this->assertFalse($user1->hasRequestedToFollow($user2));
64+
$this->assertFalse($user2->isFollowedBy($user1));
65+
}
66+
67+
public function test_following_private_user_sets_request_pending_with_eager_loading()
68+
{
69+
$user1 = User::create(['name' => 'user1']);
70+
$user2 = User::create(['name' => 'user2', 'private' => true]);
71+
72+
$user1->follow($user2);
73+
74+
$user1->load('followings');
75+
76+
$this->assertTrue($user1->hasRequestedToFollow($user2));
77+
$this->assertFalse($user2->isFollowedBy($user1));
78+
}
79+
}

tests/User.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,17 @@ class User extends Model
2121
{
2222
use Followable;
2323

24-
protected $fillable = ['name'];
24+
protected $fillable = ['name', 'private'];
25+
26+
protected $casts = [
27+
'private' => 'boolean',
28+
];
29+
30+
/**
31+
* @return bool
32+
*/
33+
public function needsToApproveFollowRequests()
34+
{
35+
return $this->private ?? false;
36+
}
2537
}

tests/migrations/2020_04_04_000000_create_users_table.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public function up()
2222
Schema::create('users', function (Blueprint $table) {
2323
$table->increments('id');
2424
$table->string('name');
25+
$table->boolean('private')->default(false);
2526
$table->timestamps();
2627
});
2728
}

0 commit comments

Comments
 (0)