Skip to content

Commit

Permalink
✨ Implement Follow management in Vue (#702)
Browse files Browse the repository at this point in the history
* add manage follower button + correspondung sub page

* Add api routes for follow listings

* Added pluck to sort out followings/followers

* no message

* no message

* fix userFollowX relations

* Basic follower-view/api implementation in vue

* fetchMore

* reformatted follow-table

* fix user_id

* add "no followers/followings/requests"

* Update app/Http/Controllers/Backend/User/FollowController.php

Co-authored-by: Kris <[email protected]>

Co-authored-by: Levin Herr <[email protected]>
  • Loading branch information
MrKrisKrisu and HerrLevin authored Feb 6, 2022
1 parent 7019417 commit abbd61e
Show file tree
Hide file tree
Showing 16 changed files with 669 additions and 73 deletions.
127 changes: 127 additions & 0 deletions app/Http/Controllers/API/v1/FollowController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
<?php

namespace App\Http\Controllers\API\v1;

use App\Exceptions\AlreadyFollowingException;
use App\Exceptions\IdenticalModelException;
use App\Exceptions\PermissionException;
use App\Http\Controllers\API\ResponseController;
use App\Http\Controllers\Backend\User\FollowController as FollowBackend;
use App\Http\Controllers\Backend\User\FollowController as SettingsBackend;
use App\Http\Controllers\UserController as UserBackend;
use App\Http\Resources\UserResource;
use App\Models\Follow;
use App\Models\User;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\Rule;

class FollowController extends ResponseController
{

public static function createFollow(Request $request, FollowController $instance): JsonResponse {
$validated = $request->validate(['userId' => ['required', 'exists:users,id']]);
$userToFollow = User::find($validated['userId']);

try {
$createFollowResponse = UserBackend::createOrRequestFollow(Auth::user(), $userToFollow);
} catch (AlreadyFollowingException) {
return $instance->sendv1Error(['message' => __('controller.user.follow-error')], 409);
} catch (IdenticalModelException) {
abort(409);
}

return $instance->sendv1Response(new UserResource($createFollowResponse), 204);
}

public static function destroyFollow(Request $request, FollowController $instance): JsonResponse {
$validated = $request->validate(['userId' => ['required', 'exists:users,id']]);
$userToUnfollow = User::find($validated['userId']);

$destroyFollowResponse = UserBackend::destroyFollow(Auth::user(), $userToUnfollow);
if ($destroyFollowResponse === false) {
return $instance->sendv1Error(['message' => __('controller.user.follow-404')], 409);
}

$userToUnfollow->fresh();
return $instance->sendv1Response(new UserResource($userToUnfollow));

}

public function getFollowers(): AnonymousResourceCollection {
$followersResponse = FollowBackend::getFollowers(user: auth()->user());
return UserResource::collection($followersResponse);
}

public function getFollowRequests(): AnonymousResourceCollection {
$followRequestResponse = FollowBackend::getFollowRequests(user: auth()->user());
return UserResource::collection($followRequestResponse);
}

public function getFollowings(): AnonymousResourceCollection {
$followingResponse = FollowBackend::getFollowings(user: auth()->user());
return UserResource::collection($followingResponse);
}

public function removeFollower(Request $request): void {
$validated = $request->validate([
'userId' => [
'required',
Rule::in(auth()->user()->followers->pluck('user_id')),
]
]);

$follow = Follow::where('user_id', $validated['userId'])
->where('follow_id', auth()->user()->id)
->firstOrFail();

try {
$removeResponse = FollowBackend::removeFollower(follow: $follow, user: auth()->user());
} catch (PermissionException) {
abort(403);
}

if ($removeResponse === true) {
abort(204);
}
abort(500);
}

public function approveFollowRequest(Request $request) {
$validated = $request->validate([
'userId' => [
'required',
Rule::in(auth()->user()->followRequests->pluck('user_id'))
]
]);

try {
FollowBackend::approveFollower(auth()->user()->id, $validated['userId']);
abort(204);
} catch (ModelNotFoundException) {
abort(404);
} catch (AlreadyFollowingException $exception) {
report($exception);
}
abort(500);
}

public function rejectFollowRequest(Request $request) {
$validated = $request->validate([
'userId' => [
'required',
Rule::in(auth()->user()->followRequests->pluck('user_id'))
]
]);
try {
FollowBackend::rejectFollower(auth()->user()->id, $validated['userId']);
abort(204);
} catch (ModelNotFoundException) {
abort(404);
}
abort(500);
}
}
31 changes: 0 additions & 31 deletions app/Http/Controllers/API/v1/UserController.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
namespace App\Http\Controllers\API\v1;


use App\Exceptions\AlreadyFollowingException;
use App\Exceptions\IdenticalModelException;
use App\Exceptions\PermissionException;
use App\Exceptions\UserAlreadyMutedException;
use App\Exceptions\UserNotMutedException;
Expand Down Expand Up @@ -68,35 +66,6 @@ public function show(string $username): UserResource {
return new UserResource(User::where('username', 'like', $username)->firstOrFail());
}

public function createFollow(Request $request): JsonResponse {
$validated = $request->validate(['userId' => ['required', 'exists:users,id']]);
$userToFollow = User::find($validated['userId']);

try {
$createFollowResponse = UserBackend::createOrRequestFollow(Auth::user(), $userToFollow);
} catch (AlreadyFollowingException) {
return $this->sendv1Error(['message' => __('controller.user.follow-error')], 409);
} catch (IdenticalModelException) {
abort(409);
}

return $this->sendv1Response(new UserResource($createFollowResponse), 201);
}

public function destroyFollow(Request $request): JsonResponse {
$validated = $request->validate(['userId' => ['required', 'exists:users,id']]);
$userToUnfollow = User::find($validated['userId']);

$destroyFollowResponse = UserBackend::destroyFollow(Auth::user(), $userToUnfollow);
if ($destroyFollowResponse === false) {
return $this->sendv1Error(['message' => __('controller.user.follow-404')], 409);
}

$userToUnfollow->fresh();
return $this->sendv1Response(new UserResource($userToUnfollow));

}

public function createMute(Request $request): JsonResponse {
$validated = $request->validate([
'userId' => [
Expand Down
74 changes: 74 additions & 0 deletions app/Http/Controllers/Backend/User/FollowController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

namespace App\Http\Controllers\Backend\User;

use App\Exceptions\AlreadyFollowingException;
use App\Exceptions\PermissionException;
use App\Http\Controllers\Controller;
use App\Http\Controllers\UserController;
use App\Models\Follow;
use App\Models\FollowRequest;
use App\Models\User;
use Illuminate\Contracts\Pagination\Paginator;
use Illuminate\Database\Eloquent\ModelNotFoundException;

abstract class FollowController extends Controller
{
public static function getFollowers(User $user): Paginator {
return $user->userFollowers()->simplePaginate(perPage: 15);
}

public static function getFollowRequests(User $user): Paginator {
return $user->userFollowRequests()->simplePaginate(perPage: 15);
}

public static function getFollowings(User $user): Paginator {
return $user->userFollowings()->simplePaginate(perPage: 15);
}

/**
* @param Follow $follow
* @param User $user - The acting user
*
* @return bool|null
* @throws PermissionException
*/
public static function removeFollower(Follow $follow, User $user): bool|null {
if ($user->cannot('delete', $follow)) {
throw new PermissionException();
}
return $follow->delete();
}

/**
* @param int $userId
* @param int $followerID
*
* @return FollowRequest|null
*/
public static function rejectFollower(int $userId, int $followerID): ?FollowRequest {
$request = FollowRequest::where('user_id', $followerID)->where('follow_id', $userId)->firstOrFail();

$request->delete();
return $request;
}

/**
*
* @param int $userId The id of the user who is approving a follower
* @param int $approverId The id of a to-be-approved follower
*
* @throws ModelNotFoundException|AlreadyFollowingException
*/
public static function approveFollower(int $userId, int $approverId): bool {
$request = FollowRequest::where('user_id', $approverId)->where('follow_id', $userId)->firstOrFail();

$follow = UserController::createFollow($request->user, $request->requestedFollow, true);

if ($follow) {
$request->delete();
}
return $follow;
}

}
5 changes: 3 additions & 2 deletions app/Http/Controllers/Frontend/SettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@

use App\Enum\StatusVisibility;
use App\Exceptions\AlreadyFollowingException;
use App\Http\Controllers\Backend\User\FollowController;
use App\Http\Controllers\Backend\User\FollowController as SettingsBackend;
use App\Http\Controllers\Backend\User\SessionController;
use App\Http\Controllers\Backend\User\TokenController;
use App\Http\Controllers\Controller;
use App\Http\Controllers\SettingsController as SettingsBackend;
use Illuminate\Contracts\Support\Renderable;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\RedirectResponse;
Expand Down Expand Up @@ -138,7 +139,7 @@ public function rejectFollower(Request $request): RedirectResponse {
]
]);
try {
$approval = SettingsBackend::rejectFollower(auth()->user()->id, $validated['user_id']);
$approval = FollowController::rejectFollower(auth()->user()->id, $validated['user_id']);
} catch (ModelNotFoundException) {
abort(404);
}
Expand Down
41 changes: 6 additions & 35 deletions app/Http/Controllers/SettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@

namespace App\Http\Controllers;

use App\Exceptions\AlreadyFollowingException;
use App\Models\Follow;
use App\Models\FollowRequest;
use Illuminate\Contracts\Support\Renderable;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
Expand All @@ -16,13 +13,19 @@
*/
class SettingsController extends Controller
{
/**
* @deprecated
*/
public function renderFollowerSettings(): Renderable {
return view('settings.follower', [
'requests' => auth()->user()->followRequests()->with('user')->paginate(15),
'followers' => auth()->user()->followers()->with('user')->paginate(15)
]);
}

/**
* @deprecated
*/
public function removeFollower(Request $request): RedirectResponse {
$validated = $request->validate([
'user_id' => [
Expand All @@ -40,36 +43,4 @@ public function removeFollower(Request $request): RedirectResponse {

return back()->with('success', __('settings.follower.delete-success'));
}

/**
*
* @param int $userId The id of the user who is approving a follower
* @param int $approverId The id of a to-be-approved follower
*
* @throws ModelNotFoundException|AlreadyFollowingException
*/
public static function approveFollower(int $userId, int $approverId): bool {
$request = FollowRequest::where('user_id', $approverId)->where('follow_id', $userId)->firstOrFail();

$follow = UserController::createFollow($request->user, $request->requestedFollow, true);

if ($follow) {
$request->delete();
}
return $follow;
}

/**
* @param int $userId
* @param int $followerID
*
* @return FollowRequest|null
*/
public static function rejectFollower(int $userId, int $followerID): ?FollowRequest {
$request = FollowRequest::where('user_id', $followerID)->where('follow_id', $userId)->firstOrFail();

$request->delete();
return $request;

}
}
6 changes: 5 additions & 1 deletion app/Models/Follow.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ class Follow extends Model
];

public function user(): BelongsTo {
return $this->belongsTo(User::class);
return $this->belongsTo(User::class, 'user_id', 'id');
}

public function following() {
return $this->belongsTo(User::class, 'follow_id', 'id');
}
}
Loading

0 comments on commit abbd61e

Please sign in to comment.