<?php
namespace AppServicesFeed;
use AppRepositoriesPollPollRepositoryInterface;
use AppRepositoriesPostPostRepositoryInterface;
use IlluminateHttpResourcesJsonAnonymousResourceCollection;
use IlluminatePaginationLengthAwarePaginator;
use IlluminateSupportFacadesLog;
class FeedService implements FeedServiceInterface
{
public function __construct(
private PostRepositoryInterface $postRepository,
private PollRepositoryInterface $pollRepository
) {}
/** Send all feeds
*
* @param array $data The filtered data
* @return array The feed array
*/
public function getFeeds(array $data): array
{
$page = $data['page'] ?? config('data.pagination.default_page', 1);
$limit = $data['limit'] ?? config('data.pagination.default_limit', 10);
// Get paginated posts and polls (fetching only required records)
$posts = $this->postRepository->getPosts($data);
$polls = $this->pollRepository->getPolls($data);
// Ensure collections exist and default to empty collections if null
$postsCollection = isset($posts->collection) ? collect($posts->collection) : collect();
$pollsCollection = isset($polls->collection) ? collect($polls->collection) : collect();
// Merge and sort by combined_created_at
$mergedFeed = $postsCollection->merge($pollsCollection)
->sortByDesc('combined_created_at')
->values();
// Paginate the merged feed
$total = $mergedFeed->count();
$paginatedItems = $mergedFeed->slice(($page - 1) * $limit, $limit)->values();
// Apply transformations **only on the paginated items**
$startIndex = ($page - 1) * $limit; // Start index based on pagination
$transformedFeed = $paginatedItems->map(function ($item) use (&$startIndex, $data) {
// Ensure JSON is properly decoded as an **object**
$item->show_user_details = json_decode($item->show_user_details, false);
$item->is_shared = (bool) $item->is_shared;
// Generate unique ID using type, ID, index, page, and limit
$uniqueKey = "{$item->type}-{$item->id}-{$startIndex}-{$data['page']}-{$data['limit']}";
$item->unique_id = hash('sha256', $uniqueKey);
$startIndex++; // Increment index
return $item; // Ensure transformation is applied
});
// Create the paginator
$paginatedFeed = new LengthAwarePaginator(
$transformedFeed, // Only transformed paginated items
$total, // Total items count
$limit, // Items per page
$page, // Current page
['path' => request()->url(), 'query' => request()->query()] // Maintain query parameters
);
return [
'post' => $paginatedFeed->items(), // Extract items directly
'pagination' => [
'total' => $paginatedFeed->total(),
'per_page' => $paginatedFeed->perPage(),
'current_page' => $paginatedFeed->currentPage(),
'last_page' => $paginatedFeed->lastPage(),
'from' => $paginatedFeed->firstItem(),
'to' => $paginatedFeed->lastItem(),
],
];
}
}
/**
* Send all posts
*
* @param array $data The filtered data
* @return AnonymousResourceCollection The post collection
*/
public function getPosts(array $data): AnonymousResourceCollection
{
$this->setPaginationAndOrderBy($data);
$postableId = $data["postable_id"] ?? null;
$postableType = $data["postable_type"] ?? null;
$username = $data['username'] ?? null;
// Fetch user ID from username
$userId = null;
if ($username) {
$userId = $this->user->where('username', $username)->value('id');
}
// Original Posts with Creator's Name
$originalPosts = $this->post
->select(
'posts.*',
DB::raw('posts.created_at as combined_created_at'),
DB::raw("
CASE
WHEN posts.postable_type LIKE '%User' THEN users.username
WHEN posts.postable_type LIKE '%Club' THEN clubs.name
ELSE NULL
END as show_user
"),
DB::raw("
CASE
WHEN posts.postable_type LIKE '%User' THEN CAST(
JSON_OBJECT(
'id', users.id,
'profile_image', users.profile_image,
'username', users.username,
'email', users.email,
'first_name', users.first_name,
'last_name', users.last_name,
'gender',
CASE
WHEN users.gender = 0 THEN 'Male'
WHEN users.gender = 1 THEN 'Female'
WHEN users.gender = 2 THEN 'Others'
ELSE NULL
END
) AS JSON
)
WHEN posts.postable_type LIKE '%Club' THEN CAST(
JSON_OBJECT(
'id', clubs.id,
'name', clubs.name,
'description', clubs.description
) AS JSON
)
ELSE NULL
END as show_user_details
"),
DB::raw("FALSE as is_shared") // Adding is_shared flag
)
->leftJoin('users', function ($join) {
$join->on('posts.postable_id', '=', 'users.id')
->where('posts.postable_type', 'App\Models\User');
})
->leftJoin('clubs', function ($join) {
$join->on('posts.postable_id', '=', 'clubs.id')
->where('posts.postable_type', 'App\Models\Club');
});
// Apply filters for `postable_id` and `postable_type`
if ($postableId && $postableType) {
$originalPosts->where('posts.postable_id', $postableId)
->where('posts.postable_type', $postableType);
}
// Apply filter for user posts if username exists
if ($userId) {
$originalPosts->where('posts.postable_id', $userId)
->where('posts.postable_type', 'App\Models\User');
}
// Shared Posts with Sharer's Username
$sharedPosts = $this->post
->select(
'posts.*',
DB::raw('post_shares.created_at as combined_created_at'),
'shared_users.username as show_user',
DB::raw("
CAST(
JSON_OBJECT(
'id', shared_users.id,
'profile_image', shared_users.profile_image,
'username', shared_users.username,
'email', shared_users.email,
'first_name', shared_users.first_name,
'last_name', shared_users.last_name,
'gender',
CASE
WHEN shared_users.gender = 0 THEN 'Male'
WHEN shared_users.gender = 1 THEN 'Female'
WHEN shared_users.gender = 2 THEN 'Others'
ELSE NULL
END
) AS JSON
) as show_user_details
"),
DB::raw("TRUE as is_shared") // Adding is_shared flag
)
->join('post_shares', 'posts.id', '=', 'post_shares.post_id')
->join('users as shared_users', 'post_shares.user_id', '=', 'shared_users.id');
// Apply filter for shared posts if username exists
if ($userId) {
$sharedPosts->where('post_shares.user_id', $userId);
}
// Combine Original Posts + Shared Posts using UNION
$posts = $originalPosts
->unionAll($sharedPosts)
->orderByDesc('combined_created_at')
// ->paginate($this->limit, ['*'], 'page', $this->page);
->get();
// $startIndex = ($data['page'] - 1) * $data['limit']; // Start index based on pagination
// // Handles JSON decoding
// $posts->transform(function ($post) use ($startIndex, $data) {
// $post->show_user_details = json_decode($post->show_user_details, true);
// $post->is_shared = (bool) $post->is_shared; // Ensure boolean type
// // Generate unique ID using post ID, index, page, and limit
// $uniqueKey = "{$post->type}-{$post->id}-{$startIndex}-{$data['page']}-{$data['limit']}";
// // Generate unique ID using hash
// $post->unique_id = hash('sha256', $uniqueKey);
// $startIndex++; // Increment the index for next post
// return $post;
// });
// Load default relations
$posts->load([
'postMedias',
'postable',
'comments.commentable',
'club'
]);
return PostResource::collection($posts);
}
/**
* Send all polls
*
* @param array $data The filtered data
* @return AnonymousResourceCollection The poll collection
*/
public function getPolls(array $data): AnonymousResourceCollection
{
$this->setPaginationAndOrderBy($data);
$pollableId = $data['pollable_id'] ?? null;
$pollableType = $data['pollable_type'] ?? null;
$username = $data['username'] ?? null;
// Fetch user ID from username
$userId = null;
if ($username) {
$userId = $this->user->where('username', $username)->value('id');
}
// Original Polls with Creator's Name
$originalPolls = $this->poll
->select(
'polls.*',
DB::raw('polls.created_at as combined_created_at'),
DB::raw("
CASE
WHEN polls.pollable_type LIKE '%User' THEN users.username
WHEN polls.pollable_type LIKE '%Admin' THEN admins.name
WHEN polls.pollable_type LIKE '%Club' THEN clubs.name
ELSE NULL
END as show_user
"),
DB::raw("
CASE
WHEN polls.pollable_type LIKE '%User' THEN CAST(
JSON_OBJECT(
'id', users.id,
'profile_image', users.profile_image,
'username', users.username,
'email', users.email,
'first_name', users.first_name,
'last_name', users.last_name,
'gender',
CASE
WHEN users.gender = 0 THEN 'Male'
WHEN users.gender = 1 THEN 'Female'
WHEN users.gender = 2 THEN 'Others'
ELSE NULL
END
) AS JSON
)
WHEN polls.pollable_type LIKE '%Admin' THEN CAST(
JSON_OBJECT(
'id', admins.id,
'name', admins.name,
'email', admins.email
) AS JSON
)
WHEN polls.pollable_type LIKE '%Club' THEN CAST(
JSON_OBJECT(
'id', clubs.id,
'name', clubs.name,
'description', clubs.description
) AS JSON
)
ELSE NULL
END as show_user_details
"),
DB::raw("FALSE as is_shared") // Adding is_shared flag
)
->leftJoin('users', function ($join) {
$join->on('polls.pollable_id', '=', 'users.id')
->where('polls.pollable_type', 'App\Models\User');
})
->leftJoin('admins', function ($join) {
$join->on('polls.pollable_id', '=', 'admins.id')
->where('polls.pollable_type', 'App\Models\Admin');
})
->leftJoin('clubs', function ($join) {
$join->on('polls.pollable_id', '=', 'clubs.id')
->where('polls.pollable_type', 'App\Models\Club');
});
// Apply filters for `postable_id` and `postable_type`
if ($pollableId && $pollableType) {
$originalPolls->where('polls.pollable_id', $pollableId)
->where('polls.pollable_type', $pollableType);
}
// Apply filter for user polls if username exists
if ($userId) {
$originalPolls->where('polls.pollable_id', $userId)
->where('polls.pollable_type', 'App\Models\User');
}
// Shared Polls with Sharer's Username
$sharedPolls = $this->poll
->select(
'polls.*',
DB::raw('poll_shares.created_at as combined_created_at'),
'shared_users.username as show_user',
DB::raw("
CAST(
JSON_OBJECT(
'id', shared_users.id,
'profile_image', shared_users.profile_image,
'username', shared_users.username,
'email', shared_users.email,
'first_name', shared_users.first_name,
'last_name', shared_users.last_name,
'gender',
CASE
WHEN shared_users.gender = 0 THEN 'Male'
WHEN shared_users.gender = 1 THEN 'Female'
WHEN shared_users.gender = 2 THEN 'Others'
ELSE NULL
END
) AS JSON
) as show_user_details
"),
DB::raw("TRUE as is_shared") // Adding is_shared flag
)
->join('poll_shares', 'polls.id', '=', 'poll_shares.poll_id')
->join('users as shared_users', 'poll_shares.user_id', '=', 'shared_users.id');
// Apply filter for shared polls if username exists
if ($userId) {
$sharedPolls->where('poll_shares.user_id', $userId);
}
// Combine Original Polls + Shared Polls using UNION
$polls = $originalPolls
->unionAll($sharedPolls)
->orderByDesc('combined_created_at')
//->paginate($this->limit, ['*'], 'page', $this->page);
->get();
// $startIndex = ($data['page'] - 1) * $data['limit']; // Start index based on pagination
// // Handles JSON decoding
// $polls->transform(function ($poll) use ($startIndex, $data) {
// $poll->show_user_details = json_decode($poll->show_user_details, true);
// $poll->is_shared = (bool) $poll->is_shared; // Ensure boolean type
// // Generate unique ID using post ID, index, page, and limit
// $uniqueKey = "{$poll->type}-{$poll->id}-{$startIndex}-{$data['page']}-{$data['limit']}";
// // Generate unique ID using hash
// $poll->unique_id = hash('sha256', $uniqueKey);
// $startIndex++; // Increment the index for next post
// return $poll;
// });
// Load default relations
$polls->load([
'pollOptions',
'pollable',
'comments.commentable',
'club',
]);
return PollResource::collection($polls);
}
Here we’ve an API /feeds?page=1&limit=10 and API response will be sent from the getFeeds function and we’re merging the post and poll data and after merging we apply the pagination.
In response of the API show_user_details not decoded properly.Instead of is_shared 0 or 1 in response false or true should come. And unique_id is not present in the response