-
Notifications
You must be signed in to change notification settings - Fork 6
Implement post fetching and pagination for Test 1. #7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| <?php | ||
|
|
||
| namespace app\models; | ||
|
|
||
| use yii\base\Model; | ||
| use yii\data\Pagination; | ||
|
|
||
| class Post extends Model | ||
| { | ||
| public $id; | ||
| public $userId; | ||
| public $title; | ||
| public $body; | ||
|
|
||
| /** | ||
| * Fetch posts from JSONPlaceholder API | ||
| * @return array|false | ||
| */ | ||
| public static function fetchPosts() | ||
| { | ||
| $url = 'https://jsonplaceholder.typicode.com/posts'; | ||
|
|
||
| $context = stream_context_create([ | ||
| 'http' => [ | ||
| 'timeout' => 10, | ||
| 'method' => 'GET', | ||
| 'header' => 'Content-Type: application/json' | ||
| ] | ||
| ]); | ||
|
|
||
| $response = @file_get_contents($url, false, $context); | ||
|
|
||
| if ($response === false) { | ||
| return false; | ||
| } | ||
|
|
||
| $data = json_decode($response, true); | ||
|
|
||
| if (json_last_error() !== JSON_ERROR_NONE) { | ||
| return false; | ||
| } | ||
|
|
||
| $posts = []; | ||
| foreach ($data as $item) { | ||
| $post = new self(); | ||
| $post->id = $item['id']; | ||
| $post->userId = $item['userId']; | ||
| $post->title = $item['title']; | ||
| $post->body = $item['body']; | ||
| $posts[] = $post; | ||
| } | ||
|
|
||
| return $posts; | ||
| } | ||
|
|
||
| /** | ||
| * Get paginated posts | ||
| * @param int $pageSize | ||
| * @return array|false | ||
| */ | ||
| public static function getPaginatedPosts($pageSize = 6) | ||
| { | ||
| $allPosts = self::fetchPosts(); | ||
|
|
||
| if ($allPosts === false) { | ||
| return false; | ||
| } | ||
|
|
||
| $pagination = new Pagination([ | ||
| 'totalCount' => count($allPosts), | ||
| 'pageSize' => $pageSize, | ||
| 'pageSizeParam' => false, | ||
| 'forcePageParam' => false, | ||
| ]); | ||
|
|
||
| $offset = $pagination->offset; | ||
| $limit = $pagination->limit; | ||
| $posts = array_slice($allPosts, $offset, $limit); | ||
|
|
||
| return [ | ||
| 'posts' => $posts, | ||
| 'pagination' => $pagination | ||
| ]; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -27,7 +27,67 @@ | |||||
|
|
||||||
| <div class="posts-list"> | ||||||
| <div class="row mt-4"> | ||||||
| <h2>Posts</h2> | ||||||
| <div class="col-md-12"> | ||||||
| <h2>Posts</h2> | ||||||
|
|
||||||
| <?php if (isset($error) && $error): ?> | ||||||
| <div class="alert alert-danger" role="alert"> | ||||||
| <?= $error ?> | ||||||
| </div> | ||||||
| <?php endif; ?> | ||||||
|
|
||||||
| <?php if (!empty($posts)): ?> | ||||||
| <div class="row"> | ||||||
| <?php foreach ($posts as $post): ?> | ||||||
| <div class="col-md-6 col-lg-4 mb-4"> | ||||||
| <div class="card h-100"> | ||||||
| <div class="card-body"> | ||||||
| <h5 class="card-title"><?= $post->title ?></h5> | ||||||
| <p class="card-text"><?= $post->body ?></p> | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The post body is displayed without HTML escaping. Since this content comes from an external API, it should be escaped to prevent potential XSS attacks.
Suggested change
|
||||||
| </div> | ||||||
| <div class="card-footer"> | ||||||
| <small class="text-muted"> | ||||||
| Post ID: <?= $post->id ?> | User ID: <?= $post->userId ?> | ||||||
| </small> | ||||||
| </div> | ||||||
| </div> | ||||||
| </div> | ||||||
| <?php endforeach; ?> | ||||||
| </div> | ||||||
|
|
||||||
| <?php if ($pagination && $pagination->pageCount > 1): ?> | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The pagination check should first verify if $pagination is set before checking its pageCount property to avoid potential PHP errors.
Suggested change
|
||||||
| <div class="row mt-4"> | ||||||
| <div class="col-md-12"> | ||||||
| <nav aria-label="Posts pagination"> | ||||||
| <?= \yii\widgets\LinkPager::widget([ | ||||||
| 'pagination' => $pagination, | ||||||
| 'options' => ['class' => 'pagination justify-content-center'], | ||||||
| 'linkOptions' => ['class' => 'page-link'], | ||||||
| 'pageCssClass' => 'page-item', | ||||||
| 'activePageCssClass' => 'active', | ||||||
| 'disabledPageCssClass' => 'disabled', | ||||||
| 'prevPageLabel' => '« Previous', | ||||||
| 'nextPageLabel' => 'Next »', | ||||||
| 'firstPageLabel' => '«« First', | ||||||
| 'lastPageLabel' => 'Last »»', | ||||||
| ]) ?> | ||||||
| </nav> | ||||||
|
|
||||||
| <div class="pagination-info text-center mt-3"> | ||||||
| <small class="text-muted"> | ||||||
| Showing <?= $pagination->offset + 1 ?> - <?= min($pagination->offset + $pagination->limit, $pagination->totalCount) ?> | ||||||
| of <?= $pagination->totalCount ?> posts | ||||||
| </small> | ||||||
| </div> | ||||||
| </div> | ||||||
| </div> | ||||||
| <?php endif; ?> | ||||||
| <?php else: ?> | ||||||
| <div class="alert alert-info" role="alert"> | ||||||
| No posts available. | ||||||
| </div> | ||||||
| <?php endif; ?> | ||||||
| </div> | ||||||
| </div> | ||||||
| </div> | ||||||
| </div> | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The post title is displayed without HTML escaping. Since this content comes from an external API, it should be escaped to prevent potential XSS attacks.