From cf3a43f1a7499a5f99a673c0593ffe3e386ec488 Mon Sep 17 00:00:00 2001 From: arsaizdihar <arsadihar@gmail.com> Date: Tue, 3 Oct 2023 16:43:44 +0700 Subject: [PATCH] feat: list kost --- src/Controllers/AuthController.php | 4 --- src/Controllers/DormController.php | 10 ++++++ src/Core/Application.php | 4 +-- src/Core/Request.php | 10 ++++++ src/Models/BaseModel.php | 23 ++++++------- src/Models/Dorm.php | 53 ++++++++++++++++++++++++++++++ src/Views/index.php | 27 +++++++++++++-- src/public/static/styles/home.css | 49 +++++++++++++++++++++++++++ src/public/static/styles/main.css | 11 +++++-- 9 files changed, 169 insertions(+), 22 deletions(-) diff --git a/src/Controllers/AuthController.php b/src/Controllers/AuthController.php index 235a327..97a5bb1 100644 --- a/src/Controllers/AuthController.php +++ b/src/Controllers/AuthController.php @@ -17,10 +17,6 @@ use app\Utils\Toast; class AuthController extends Controller { - public function index() - { - $this->render('index'); - } public function login() { diff --git a/src/Controllers/DormController.php b/src/Controllers/DormController.php index 6c1f169..d450921 100644 --- a/src/Controllers/DormController.php +++ b/src/Controllers/DormController.php @@ -15,6 +15,16 @@ use app\Utils\FileManager; class DormController extends Controller { + + public function list() + { + $page = Request::getPage(); + $data = Dorm::getAllFiltered($page); + $data["page"] = $page; + $this->render('index', $data); + } + + public function create() { $form = new Validation([ diff --git a/src/Core/Application.php b/src/Core/Application.php index 563a075..c24a4eb 100644 --- a/src/Core/Application.php +++ b/src/Core/Application.php @@ -32,7 +32,7 @@ class Application public function configureRoutes() { $this->router = new Router(); - $this->router->get('/', [], AuthController::class, 'index'); + $this->router->get('/', [], DormController::class, 'list'); $this->router->methods(["GET", "POST"], '/login', [], AuthController::class, 'login'); $this->router->methods(["GET", "POST"], '/register', [], AuthController::class, 'register'); $this->router->get('/logout', [], AuthController::class, 'logout'); @@ -46,4 +46,4 @@ class Application $this->router->methods(["GET", "POST"], "/dorms/create", [AdminOnly::class], DormController::class, 'create'); $this->router->methods(["GET", "POST"], "/dorms/{dormId}/media", [AdminOnly::class], DormController::class, 'media'); } -} \ No newline at end of file +} diff --git a/src/Core/Request.php b/src/Core/Request.php index d579818..35f1662 100644 --- a/src/Core/Request.php +++ b/src/Core/Request.php @@ -59,4 +59,14 @@ class Request { return $_FILES[$fieldName] ?? null; } + + public static function getPage() + { + $page = $_GET['page'] ?? "1"; + try { + return intval($page); + } catch (\Throwable $th) { + return 1; + } + } } diff --git a/src/Models/BaseModel.php b/src/Models/BaseModel.php index 0e341c7..886477f 100644 --- a/src/Models/BaseModel.php +++ b/src/Models/BaseModel.php @@ -4,17 +4,18 @@ namespace app\Models; use app\Core\Application; use app\Core\Database; +use PDO; #[\AllowDynamicProperties] class BaseModel { - protected static Database $db; + protected static PDO $db; protected static string $table = ""; protected static string $primaryKey = ""; public static function connect() { - self::$db = Application::$db; + self::$db = Application::$db->pdo; } public static function toModel($data): self @@ -28,7 +29,7 @@ class BaseModel return $model; } - protected static function toModelArray($data) + public static function toModelArray($data) { $models = []; foreach ($data as $row) { @@ -39,14 +40,14 @@ class BaseModel public static function all($orderBy = null) { - $stmt = self::$db->pdo->prepare("SELECT * FROM " . static::$table . ($orderBy ? " ORDER BY " . $orderBy : "")); + $stmt = self::$db->prepare("SELECT * FROM " . static::$table . ($orderBy ? " ORDER BY " . $orderBy : "")); $stmt->execute(); return self::toModelArray($stmt->fetchAll()); } public static function findById(int $id): static { - $stmt = self::$db->pdo->prepare("SELECT * FROM " . static::$table . " WHERE " . static::$primaryKey . " = :id"); + $stmt = self::$db->prepare("SELECT * FROM " . static::$table . " WHERE " . static::$primaryKey . " = :id"); $stmt->bindValue(":id", $id); $stmt->execute(); $data = $stmt->fetch(); @@ -58,7 +59,7 @@ class BaseModel public static function deleteById(int $id) { - $stmt = self::$db->pdo->prepare("DELETE FROM " . static::$table . " WHERE " . static::$primaryKey . " = :id"); + $stmt = self::$db->prepare("DELETE FROM " . static::$table . " WHERE " . static::$primaryKey . " = :id"); $stmt->bindValue(":id", $id); $stmt->execute(); return $stmt->rowCount(); @@ -73,7 +74,7 @@ class BaseModel } $where = substr($where, 0, -5); - $stmt = self::$db->pdo->prepare("SELECT * FROM " . static::$table . " WHERE " . $where); + $stmt = self::$db->prepare("SELECT * FROM " . static::$table . " WHERE " . $where); foreach ($arrFilter as $key => $value) { $stmt->bindValue(":" . $key, $value); } @@ -90,7 +91,7 @@ class BaseModel } $where = substr($where, 0, -5); - $stmt = self::$db->pdo->prepare("SELECT * FROM " . static::$table . " WHERE " . $where . " LIMIT 1"); + $stmt = self::$db->prepare("SELECT * FROM " . static::$table . " WHERE " . $where . " LIMIT 1"); foreach ($arrFilter as $key => $value) { $stmt->bindValue(":" . $key, $value); } @@ -122,7 +123,7 @@ class BaseModel $sql = "INSERT INTO " . static::$table . " (" . implode(",", $keys) . ") VALUES (:" . implode(",:", $keys) . ") RETURNING " . static::$primaryKey . ";"; - $stmt = self::$db->pdo->prepare($sql); + $stmt = self::$db->prepare($sql); foreach ($fields as $key => $value) { $stmt->bindValue(":" . $key, $value); @@ -149,7 +150,7 @@ class BaseModel $sql = substr($sql, 0, -2); $sql .= " WHERE " . static::$primaryKey . " = :" . static::$primaryKey . ";"; - $stmt = self::$db->pdo->prepare($sql); + $stmt = self::$db->prepare($sql); foreach ($fields as $key => $value) { $stmt->bindValue(":" . $key, $value); @@ -163,7 +164,7 @@ class BaseModel { $sql = "DELETE FROM " . static::$table . " WHERE " . static::$primaryKey . " = :" . static::$primaryKey . ";"; - $stmt = self::$db->pdo->prepare($sql); + $stmt = self::$db->prepare($sql); $stmt->bindValue(":" . static::$primaryKey, $this->{static::$primaryKey}); diff --git a/src/Models/Dorm.php b/src/Models/Dorm.php index 3157abf..89edd8d 100644 --- a/src/Models/Dorm.php +++ b/src/Models/Dorm.php @@ -14,4 +14,57 @@ class Dorm extends BaseModel public int $owner_id; protected static string $table = "dorms"; protected static string $primaryKey = "dorm_id"; + + public static function getAllFiltered($page = 1, $orderBy = "dorm_id DESC", $search = "") + { + $limit = 4; + $offset = ($page - 1) * $limit; + + $stmt = self::$db->prepare( + $search ? + "SELECT * FROM dorms WHERE name ILIKE :1 ORDER BY :2 LIMIT :3 OFFSET :4" : + "SELECT * FROM dorms ORDER BY :2 LIMIT :3 OFFSET :4" + ); + + if ($search) { + $stmt->bindValue(":1", "%{$search}%"); + } + $stmt->bindValue(":2", $orderBy); + $stmt->bindValue(":3", $limit); + $stmt->bindValue(":4", $offset); + + $stmt->execute(); + $dorms = self::toModelArray($stmt->fetchAll()); + + $stmt = self::$db->prepare( + $search ? + "SELECT COUNT(dorm_id) FROM dorms WHERE name ILIKE :1" : + "SELECT COUNT(dorm_id) FROM dorms" + ); + + if ($search) { + $stmt->bindValue(":1", "%{$search}%"); + } + + $stmt->execute(); + + $count = $stmt->fetchColumn(); + $totalPage = ceil($count / $limit); + + $dormIds = array_map(fn ($dorm) => $dorm->dorm_id, $dorms); + + $stmt = self::$db->prepare("SELECT * FROM medias + NATURAL JOIN ( + SELECT MIN(media_id) AS media_id, dorm_id FROM medias WHERE type = 'photo' AND dorm_id IN (" . implode(",", $dormIds) . ") GROUP BY dorm_id + ) AS m"); + $stmt->execute(); + + $medias = Media::toModelArray($stmt->fetchAll()); + $medias = array_reduce($medias, function ($acc, $media) { + $acc[$media->dorm_id] = $media; + return $acc; + }, []); + + return ["dorms" => $dorms, "medias" => $medias, "totalPage" => $totalPage]; + } } diff --git a/src/Views/index.php b/src/Views/index.php index 4cf6a43..077aa69 100644 --- a/src/Views/index.php +++ b/src/Views/index.php @@ -7,8 +7,29 @@ <p class="hero-desc">Dapatkan infonya dan langsung sewa di MyKos. </p> <? if ($user->is_admin ?? false) : ?> - <div class="cta-admin"> - <a href="/dorms/create" class="btn btn-primary">Tambah Kos</a> - </div> + <div class="cta-admin"> + <a href="/dorms/create" class="btn btn-primary">Tambah Kos</a> + </div> <? endif; ?> + <ul class="dorm_list"> + <? foreach ($dorms as $dorm) : ?> + <li class="dorm_item"> + <a href="/dorms/<?= $dorm->dorm_id ?>"> + <? $media = array_key_exists($dorm->dorm_id, $medias) ? $medias[$dorm->dorm_id] : null ?> + <? if ($media) : ?> + <img class="dorm_image" src="<?= $media->endpoint ?>" alt="<?= $media->alt_text ?>" /> + <? else : ?> + <div class="dorm_image"></div> + <? endif; ?> + <div class="dorm_item__content"> + <h3 class="dorm_item__title line-clamp-1"><?= $dorm->name ?></h3> + <p class="dorm_item__city"><?= $dorm->city ?></p> + <p class="dorm_item__price">Rp<?= number_format($dorm->price, 0, ',', '.') ?> + <span> / bulan</span> + </p> + </div> + </a> + </li> + <? endforeach; ?> + </ul> </section> \ No newline at end of file diff --git a/src/public/static/styles/home.css b/src/public/static/styles/home.css index 6d0bf26..d9305c5 100644 --- a/src/public/static/styles/home.css +++ b/src/public/static/styles/home.css @@ -21,3 +21,52 @@ min-width: 10rem; text-align: center; } + +.dorm_list { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 2rem; + margin-top: 4rem; +} + +.dorm_item__title { + color: #303030; + font-size: var(--text-sm); + margin-top: 0.5rem; +} + +.dorm_item__city { + color: #404040; + font-size: var(--text-sm); + font-weight: 700; + margin-top: 0.5rem; +} + +.dorm_item__price { + color: #303030; + font-size: var(--text-base); + font-weight: 700; + margin-top: 0.5rem; +} + +.dorm_item__price span { + font-weight: 400; +} + +.dorm_image { + width: 100%; + aspect-ratio: 4/3; + object-fit: cover; + background-color: white; + border-radius: 1rem; + border: 1px solid var(--color-gray-darker); + transition: all 0.1s ease-in-out; +} + +.dorm_item:hover .dorm_image { + transform: scale(1.05); +} + +div.dorm_image { + background-color: var(--color-gray-darker); +} diff --git a/src/public/static/styles/main.css b/src/public/static/styles/main.css index 7995ecd..1d5b233 100644 --- a/src/public/static/styles/main.css +++ b/src/public/static/styles/main.css @@ -17,6 +17,13 @@ html { --text-sm: 0.875rem; } +.line-clamp-1 { + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 1; +} + body { min-height: 100%; display: flex; @@ -385,7 +392,7 @@ td { .slides-container { max-width: 100%; width: 100%; - aspect-ratio: 16 / 9; + aspect-ratio: 4 / 3; display: flex; list-style: none; margin: 0; @@ -403,7 +410,7 @@ td { video { width: 100%; height: 100%; - object-fit: contain; + object-fit: cover; } .slides-container { -- GitLab