Skip to content

Commit

Permalink
Merge branch 'creme332:main' into updatereviewtest
Browse files Browse the repository at this point in the history
  • Loading branch information
Divyeshhhh authored Jun 1, 2024
2 parents 2c52658 + d17eeef commit 48c0ef5
Show file tree
Hide file tree
Showing 15 changed files with 626 additions and 295 deletions.
22 changes: 19 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,24 @@ includes:

For more details, see the [software requirements specification](docs/SOFTWARE_SPECS.md).

The code for the admin website is found in a separate repository.
This repository contains the code for the client website and the API. The code for the admin website is found in a
separate repository.

## Main features

- MVC pattern
- Semantic URL routing
- Email-based password recovery
- Email notification on order
- Testing with phpUnit
- Email notification on checkout
- Integration testing with phpUnit
- Mobile-responsive website
- Utilizes Webpack for efficient code bundling and compatibility with older browsers.
- Product review system with nested comments
- Fuzzy searching on shop page
- Pagination
- SEO optimized
- REST API
- API testing with Guzzler and phpUnit

## Documentation

Expand All @@ -58,3 +60,17 @@ Attribution-ShareAlike
- https://github.com/kevinisaac/php-mvc
4. The filesystem was inspired by https://github.com/php-pds/sklseleton
5. Additional references are included within the code itself.

# Todo

Add `X-TEST-ENV` to the header of your request and set its value to `testing` if you want to use the testing database.
This is required when running tests for API. Without this key-value pair, the production database will be used.

USE DOCKER PHP

- read guzzle documentation. read base_uri
- test database is being used
- add variable to .env

line 18 in Reviews API is redundant
use correct namespace
48 changes: 0 additions & 48 deletions public/styles/views/Shop.css
Original file line number Diff line number Diff line change
Expand Up @@ -61,52 +61,4 @@ article header {
#item-grid {
grid-template-columns: repeat(1, 1fr);
}
}

.pagination {
display: flex;
list-style: none;
border-radius: 0.25rem;
gap: 0.45rem;
margin-top: 2cm;
}


.page-item {
--bs-padding-x: 0.5rem;
--bs-padding-y: 0.25rem;
}

.page-link {
position: relative;
display: block;
padding: var(--bs-padding-y) var(--bs-padding-x);
text-decoration: none;
transition: color .25s ease-in-out, background-color .25s ease-in-out;
outline: 1px solid #dee2e6;
}

.page-link:hover {
z-index: 2;
background-color: var(--contrast-hover);
color: var(--contrast-inverse);
}

.page-link:focus {
z-index: 3;
outline: 0;
box-shadow: 0 0 0.25rem rgba(0, 0, 0, 0.25);
}

.page-item.active .page-link {
z-index: 3;
background-color: var(--contrast);
color: var(--contrast-inverse);
}

.page-item.disabled .page-link {
color: var(--form-element-disabled-opacity);
outline-color: var(--form-element-disabled-border-color);
pointer-events: none;
background-color: var(--form-element-disabled-background-color);
}
78 changes: 78 additions & 0 deletions src/controllers/Orders.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php

declare(strict_types=1);

namespace Steamy\Controller;

use Steamy\Core\Controller;
use Steamy\Core\Utility;
use Steamy\Model\Order;
use Steamy\Model\OrderProduct;

class Orders
{
use Controller;

private array $view_data = [];

private function validateURL(): bool
{
$url = Utility::getURL();
// Check if the URL matches the expected pattern
return preg_match('/^orders\/\d+$/', $url) === 1;
}

private function getOrderIDFromURL(): ?int
{
if ($this->validateURL()) {
$url = Utility::getURL();
$parts = explode('/', $url);
// Check if the last part of the URL is a valid integer
$lastPart = end($parts);
if (is_numeric($lastPart)) {
return (int)$lastPart;
} else {
return null;
}
}
return null;
}


private function handleInvalidURL(): void
{
if (!$this->validateURL()) {
(new Error())->handlePageNotFoundError();
die();
}
}

public function index(): void
{
$this->handleInvalidURL();

$order_id = $this->getOrderIDFromURL();
if ($order_id === null) {
(new Error())->handlePageNotFoundError();
return;
}

$order = Order::getByID($order_id);
if (!$order) {
(new Error())->handlePageNotFoundError();
return;
}

$order_products = Order::getOrderProducts($order->getOrderID());

$this->view_data['order'] = $order;
$this->view_data['line_items'] = $order_products;

$this->view(
'orders',
$this->view_data,
'Order #' . $order_id,
enableIndexing: false
);
}
}
76 changes: 76 additions & 0 deletions src/controllers/Pagination.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

declare(strict_types=1);

namespace Steamy\Controller;

/**
* Controller for managing the pagination component
*/
class Pagination
{
private int $items_per_page;
private int $total_items;
private int $current_page_number;

public function __construct(int $items_per_page = 1, int $total_items = 1, int $current_page = 1)
{
$this->items_per_page = $items_per_page;
$this->total_items = $total_items;
$this->current_page_number = $current_page;
}

/**
* Returns a query string that maintains all current query string parameters, except page number.
* @return string Query string
*/
private function getCurrentQueryString(): string
{
// create a string with all past query parameters except page and url
unset($_GET['page']);
unset($_GET['url']);

return '?' . http_build_query($_GET);
}

/**
* @param array $array
* @return array New array containing only elements to be displayed on current page
*/
public function getCurrentItems(array $array): array
{
return array_slice(
$array,
($this->current_page_number - 1) * $this->items_per_page,
$this->items_per_page
);
}

/**
* Returns HTML code need to display pagination items
* @return string
*/
public function getHTML(): string
{
$current_page_number = $this->current_page_number;
$total_pages = (int)ceil((float)$this->total_items / $this->items_per_page);
$query_string = $this->getCurrentQueryString();

$view_file_path = __DIR__ . '/../views/Pagination.php';
$html = '';

// get content from view file
ob_start();
include $view_file_path;
$html = ob_get_contents();
ob_end_clean();

return $html;
}

public function index(): void
{
// we don't want the page /pagination to be accessible
(new Error())->handlePageNotFoundError();
}
}
25 changes: 23 additions & 2 deletions src/controllers/Product.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

use Steamy\Core\Controller;
use Steamy\Core\Utility;
use Steamy\Model\Client;
use Steamy\Model\Comment;
use Steamy\Model\Review;
use Steamy\Model\User;
Expand All @@ -19,6 +18,8 @@ class Product
{
use Controller;

private static int $MAX_REVIEWS_PER_PAGE = 2;

private ?ProductModel $product = null; // product to be displayed
private array $view_data;
private ?User $signed_user; // currently logged-in user
Expand Down Expand Up @@ -232,6 +233,14 @@ private function showCommentForm(): void
}
}

/**
* @return int Page number on shop page. Defaults to 1.
*/
public function getPageNumber(): int
{
return (int)($_GET['page'] ?? 1);
}

private function validateURL(): bool
{
return preg_match("/^shop\/products\/[0-9]+$/", Utility::getURL()) === 1;
Expand Down Expand Up @@ -270,11 +279,23 @@ public function index(): void
$this->handleCommentSubmission();
}

$this->view_data['product_reviews'] = array_filter(
// get all reviews that match criteria
$all_matching_reviews = array_filter(
$this->view_data['product_reviews'],
array($this, "filterReviews")
);

$pagination_controller = new Pagination(
Product::$MAX_REVIEWS_PER_PAGE,
count($all_matching_reviews),
$this->getPageNumber()
);

// get html code for displaying pagination
$this->view_data['review_pagination'] = $pagination_controller->getHTML();

$this->view_data['product_reviews'] = $pagination_controller->getCurrentItems($all_matching_reviews);

$this->view_data['rating_distribution'] = $this->formatRatingDistribution();

$this->view(
Expand Down
2 changes: 1 addition & 1 deletion src/controllers/Profile.php
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ public function cancelOrder(): void
}

// Cancel the order
$order->deleteOrder();
$order->cancelOrder();
}

private function handleProfileEditSubmission(): void
Expand Down
43 changes: 17 additions & 26 deletions src/controllers/Shop.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class Shop
{
use Controller;

private array $data;
private array $view_data;
private static int $MAX_PRODUCTS_PER_PAGE = 4;

/**
Expand Down Expand Up @@ -154,20 +154,6 @@ public function getPageNumber(): int
return (int)($_GET['page'] ?? 1);
}

/**
* @param $products
* @return array Products which should be displayed on current page
*/
public function applyPagination($products): array
{
// Slice the products based on pagination
return array_slice(
$products,
($this->getPageNumber() - 1) * Shop::$MAX_PRODUCTS_PER_PAGE,
Shop::$MAX_PRODUCTS_PER_PAGE
);
}

public function index(): void
{
// check if URL follows format /shop/products/<number>
Expand All @@ -187,22 +173,27 @@ public function index(): void
// get all products that match user criteria
$filtered_products = $this->getMatchingProducts();

// Slice the products based on pagination
$paginated_products = $this->applyPagination($filtered_products);
// get html for pagination
$pagination_controller = new Pagination(
Shop::$MAX_PRODUCTS_PER_PAGE,
count($filtered_products),
$this->getPageNumber()
);

$this->view_data['pagination'] = $pagination_controller->getHTML();
$this->view_data['products'] = $pagination_controller->getCurrentItems($filtered_products);

// Initialize view variables (existing functionality)
$this->data['products'] = $paginated_products;
$this->data['search_keyword'] = $_GET['keyword'] ?? "";
$this->data['categories'] = Product::getCategories();
$this->data['sort_option'] = $_GET['sort'] ?? "";
$this->data['selected_categories'] = $_GET['categories'] ?? [];
$this->data['current_page_number'] = $this->getPageNumber();
$this->data['total_pages'] = (int)ceil(count($filtered_products) / Shop::$MAX_PRODUCTS_PER_PAGE);
// Initialize view variables
$this->view_data['search_keyword'] = $_GET['keyword'] ?? "";
$this->view_data['categories'] = Product::getCategories();
$this->view_data['sort_option'] = $_GET['sort'] ?? "";
$this->view_data['selected_categories'] = $_GET['categories'] ?? [];
$this->view_data['current_page_number'] = $this->getPageNumber();

// Render the view with pagination information
$this->view(
'Shop',
$this->data,
$this->view_data,
'Shop',
template_tags: $this->getLibrariesTags(['aos']),
template_meta_description: "Explore a delightful selection of aromatic coffees, teas, and delectable
Expand Down
Loading

0 comments on commit 48c0ef5

Please sign in to comment.