😘Dự án xây dựng MVC PHP (ok)
Last updated
Last updated
1. https://github.com/phendan/custom-cms 2. https://github.com/rizkhal/php-crud-mvc 3. https://github.com/khalidhagane/school_manager (Dự án này chỉnh sửa lại code để chạy trên lva1.com lva1-school_manager.rar)
https://github.com/AbdAllAH-ElRhmany/future_link/tree/main (Đã có chỉnh sửa sang tiếng anh)
app\Controllers\DashboardController.php
<?php
namespace App\Controllers;
use App\BaseController;
use App\Request;
class DashboardController extends BaseController {
public function index(Request $request): void
{
$this->view->render("dashboard",
[
"user" => $this->user
]
);
}
}
app\Controllers\HomeController.php
<?php
namespace App\Controllers;
use App\BaseController;
use App\Request;
class HomeController extends BaseController {
public function index(Request $request): void
{
$this->view->render("home");
}
}
app\Controllers\LoginController.php
<?php
namespace App\Controllers;
use App\BaseController;
use App\Request;
class LoginController extends BaseController
{
public function index(Request $request)
{
if($this->user->isLoggedIn()) {
$this->redirectTo(path: "dashboard");
}
if ($request->getMethod() != "POST") {
$this->view->render("login");
return;
}
try {
$inputForm = $request->getInput();
$this->user->login($inputForm);
$this->redirectTo(path: "dashboard");
} catch (\Throwable $th) {
throw $th;
}
}
}
app\Helpers\Str.php
<?php
namespace App\Helpers;
class Str
{
public static function toPascalCase(string $subject): string
{
return str_replace('_', '', ucwords($subject, '_'));
}
public static function toCamelCase(string $subject): string
{
return lcfirst(self::toPascalCase($subject));
}
}
app\Models\Database.php
<?php
namespace App\Models;
use PDO;
use PDOStatement;
class Database
{
private $host = "localhost";
private $dbname = "forum";
private $username = "root";
private $password = "";
private PDO $db;
private PDOStatement $statement;
public function __construct()
{
$this->db = new PDO(
dsn: "mysql:host={$this->host};dbname={$this->dbname}",
username: $this->username,
password: $this->password
);
}
public function query($sql,$values=[]): static {
$this->statement = $this->db->prepare(query: $sql);
$this->statement->execute(params: $values);
return $this;
}
public function results():array {
return $this->statement->fetchAll(PDO::FETCH_ASSOC);
}
public function first():array {
return $this->results()[0];
}
public function count():int {
return $this->statement->rowCount();
}
}
app\Models\User.php
<?php
namespace App\Models;
use App\Helpers\Str;
class User
{
private Database $db;
private $id;
private $email;
private $firstName;
private $lastName;
private $password;
public function __construct(Database $db)
{
$this->db = $db;
}
public function find(string|int $identifier): bool
{
$column = is_int(value: $identifier) ? "id" : "email";
$sql = "SELECT * FROM `users` WHERE `{$column}` = :identifier";
$userQuery = $this->db->query($sql, ["identifier" => $identifier]);
if (!$userQuery->count()) return false;
$userData = $userQuery->first();
foreach ($userData as $column => $value) {
$columnCamel = Str::toCamelCase($column);
$this->{$columnCamel} = $value;
}
return true;
}
public function isLoggedIn(): bool {
return isset($_SESSION['userId']);
}
public function login(array $userData): void
{
if (!$this->find($userData["email"])) {
throw new \Exception(message: "Email Not Corrent 😌");
}
$_SESSION['userId'] = $this->getId();
}
public function getId(): int
{
return (int)($this->id ?? $_SESSION['userId']);
}
public function getFirstName():string {
return $this->firstName;
}
}
app\views\partials\header.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Custom CMS</title>
<link rel="stylesheet" href="https://lva3.com/styles/app.css">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
app\views\partials\footer.php
</body>
</html>
app\views\dashboard.php
Hello: <?= $user->getFirstName(); ?>
app\views\home.php
home.php
app\views\login.php
<div class="container">
<h1>Login</h1>
<form method="post" novalidate>
<div>
<label for="email" class="form-label">Email</label>
<input type="email" id="email" name="email" class="form-control">
</div>
<div>
<label for="password" class="form-label">Password</label>
<input type="password" id="password" name="password" class="form-control">
</div>
<input type="submit" value="Sign In" class="mt-3 btn btn-success">
</form>
</div>
app\App.php
<?php
namespace App;
class App
{
public function __construct()
{
$router = new Router();
$requestedControlller = $router->getRequestedController();
$requestedMethod = $router->getRequestedMethod();
$controller = new $requestedControlller();
$params = $router->getParams();
$request = new Request(pageParams: $params);
$controller->{$requestedMethod}($request);
}
}
app\BaseController.php
<?php
namespace App;
use App\Models\Database;
use App\Models\User;
abstract class BaseController {
protected View $view;
protected User $user;
protected Database $db;
public function __construct() {
$this->db = new Database();
$this->user = new User(db: $this->db);
$this->view = new View(user: $this->user);
if ($this->user->isLoggedIn()) {
$this->user->find($this->user->getId());
}
}
abstract public function index(Request $request);
public function redirectTo(string $path): never {
header(header: "Location:".$path);
exit();
}
}
app\Request.php
<?php
namespace App;
class Request {
private $pageParams;
private $getParams;
private $postParams;
private $fileParams;
public function __construct($pageParams) {
$this->pageParams = $pageParams;
$this->getParams = $_GET;
$this->postParams = $_POST;
$this->fileParams = $_FILES;
}
public function getMethod(): string {
return $_SERVER['REQUEST_METHOD'];
}
public function getInput(string $kind="post") {
$input = match($kind) {
"page" => $this->pageParams,
"get" => $this->getParams,
"post" => $this->postParams,
"file" => $this->fileParams
};
return $input;
}
}
app\Router.php
<?php
namespace App;
use App\Controllers\HomeController;
class Router {
private $controller = HomeController::class;
private $method = "index";
private $params = [];
public function __construct() {
$url = $this->parseUrl();
if(!isset($url[0])) return;
$requestedController = "App\\Controllers\\" . ucfirst(string: $url[0]) . "Controller";
$this->controller = $requestedController;
}
public function getRequestedController(): string {
return $this->controller;
}
public function getRequestedMethod(): string {
return $this->method;
}
public function getParams(): array {
return $this->params;
}
public function parseUrl(): array {
if(!isset($_GET['url'])) return [];
$url = rtrim(string: $_GET['url'], characters: "/");
$url = explode(separator: "/",string: $url);
return $url;
}
}
app\View.php
<?php
namespace App;
use App\Models\User;
class View {
private User $user;
public function __construct(User $user) {
$this->user = $user;
}
public function render(string $view,$data=[]): void {
extract($data);
require_once("../app/views/partials/header.php");
require_once("../app/views/{$view}.php");
require_once("../app/views/partials/footer.php");
}
}
public\.htaccess
Options -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.+)$ index.php?url=$1 [QSA,L]
public\index.php
<?php
use App\App;
session_start();
require_once '../vendor/autoload.php';
$app = new App;
composer.json
{
"autoload": {
"psr-4": {
"App\\": "app/"
}
}
}
app\Router.php
<?php
namespace App;
use App\Controllers\HomeController;
class Router {
private $controller = HomeController::class;
private $method = "index";
private $params = [];
public function __construct() {
$url = $this->parseUrl();
if(!isset($url[0])) return;
$requestedController = "App\\Controllers\\" . ucfirst(string: $url[0]) . "Controller";
$this->controller = $requestedController;
unset($url[0]);
$this->params = array_values(array: $url);
}
public function getRequestedController(): string {
return $this->controller;
}
public function getRequestedMethod(): string {
return $this->method;
}
public function getParams(): array {
return $this->params;
}
public function parseUrl(): array {
if(!isset($_GET['url'])) return [];
$url = rtrim(string: $_GET['url'], characters: "/");
$url = explode(separator: "/",string: $url);
return $url;
}
}
app\Models\Post.php
<?php
namespace App\Models;
use App\Helpers\Str;
use DateTime;
class Post
{
private Database $db;
private $id;
private $userId;
private string $title;
private string $slug;
private string $body;
private $createdAt;
public function __construct(Database $db, $data = [])
{
$this->db = $db;
$this->fill($data);
}
public function fill($data)
{
foreach ($data as $column => $value) {
$columnCamel = Str::toCamelCase($column);
$this->{$columnCamel} = $value;
}
}
public function find(int $identifier): bool
{
$sql = "SELECT * FROM `posts` WHERE `id` = :id";
$postQuery = $this->db->query($sql, ['id' => $identifier]);
if(!$postQuery->count()) {
return false;
}
$post = $postQuery->first();
$this->fill($post);
return true;
}
public function getTitle(): string
{
return $this->title;
}
}
app\Controllers\PostController.php
<?php
namespace App\Controllers;
use App\BaseController;
use App\Models\Post;
use App\Request;
class PostController extends BaseController {
public function index(Request $request): void
{
$identifier = $request->getInput('page')[0];
$post = new Post(db: $this->db);
$post->find($identifier);
$this->view->render("posts/index",[
"post" => $post
]);
}
}
app\Models\User.php
<?php
namespace App\Models;
use App\Helpers\Str;
class User
{
private Database $db;
private $id;
private $email;
private $firstName;
private $lastName;
private $password;
public function __construct(Database $db)
{
$this->db = $db;
}
public function find(string|int $identifier): bool
{
$column = is_int(value: $identifier) ? "id" : "email";
$sql = "SELECT * FROM `users` WHERE `{$column}` = :identifier";
$userQuery = $this->db->query($sql, ["identifier" => $identifier]);
if (!$userQuery->count()) return false;
$userData = $userQuery->first();
foreach ($userData as $column => $value) {
$columnCamel = Str::toCamelCase($column);
$this->{$columnCamel} = $value;
}
return true;
}
public function isLoggedIn(): bool {
return isset($_SESSION['userId']);
}
public function login(array $userData): void
{
if (!$this->find($userData["email"])) {
throw new \Exception(message: "Email Not Corrent 😌");
}
$_SESSION['userId'] = $this->getId();
}
public function getId(): int
{
return (int)($this->id ?? $_SESSION['userId']);
}
public function getFirstName():string {
return $this->firstName;
}
public function getPosts():array {
$sql = "SELECT * FROM `posts` WHERE `user_id` = :user_id";
$userQuery = $this->db->query($sql,['user_id'=>$this->getId()]);
if(!$userQuery->count()) {
return [];
}
$posts = [];
foreach ($userQuery->results() as $value) {
$posts[] = new Post($this->db, $value);
}
return $posts;
}
}
app\Models\Post.php
<?php
namespace App\Models;
use App\Helpers\Str;
use DateTime;
class Post
{
private Database $db;
private $id;
private $userId;
private string $title;
private string $slug;
private string $body;
private $createdAt;
public function __construct(Database $db, $data = [])
{
$this->db = $db;
$this->fill($data);
}
public function fill($data)
{
foreach ($data as $column => $value) {
$columnCamel = Str::toCamelCase($column);
$this->{$columnCamel} = $value;
}
}
public function find(int $identifier): bool
{
$sql = "SELECT * FROM `posts` WHERE `id` = :id";
$postQuery = $this->db->query($sql, ['id' => $identifier]);
if(!$postQuery->count()) {
return false;
}
$post = $postQuery->first();
$this->fill($post);
return true;
}
public function getTitle(): string
{
return $this->title;
}
public function getId():int {
return $this->id;
}
public function getSlug():string {
return $this->slug;
}
}
app\Views\dashboard.php
Hello: <?= $user->getFirstName(); ?>
<h2>Your Posts</h2>
<?php foreach ($user->getPosts() as $post): ?>
<div class="list-group mb-3">
<a class="list-group-item list-group-item-action w-50" href="/post/<?php echo $post->getId(); ?>/<?php echo $post->getSlug(); ?>"><?php echo $post->getTitle(); ?></a>
<div class="d-flex">
<a class="btn btn-danger w-25 list-group-flush" href="/post/delete/<?php echo $post->getId(); ?>">Delete</a>
<a class="btn btn-warning w-25 list-group-flush" href="/post/edit/<?php echo $post->getId(); ?>">Edit</a>
</div>
</div>
<?php endforeach; ?>
app\Controllers\PostController.php
<?php
namespace App\Controllers;
use App\BaseController;
use App\Models\Post;
use App\Request;
class PostController extends BaseController {
public function index(Request $request): void
{
$idifitier = $request->getInput("page")[0];
$post = new Post(db: $this->db);
$post->find($idifitier);
$this->view->render("posts/index", [
"post" => $post
]);
}
public function edit(Request $request) {
$idifitier = $request->getInput("page")[1];
$post = new Post(db: $this->db);
$post->find($idifitier);
$this->view->render("posts/edit",[
"post" => $post
]);
}
}
app\Models\Post.php
<?php
namespace App\Models;
use App\Helpers\Str;
class Post {
private Database $db;
private $id;
private $userId;
private string $title;
private string $slug;
private string $body;
private $createdAt;
public function __construct(Database $db,$data=[]) {
$this->db = $db;
$this->fill($data);
}
public function fill($data): void {
foreach ($data as $column => $value) {
$columnCamel = Str::toCamelCase($column);
$this->{$columnCamel} = $value;
}
}
public function find(int $idifitier): bool {
$sql = "SELECT * FROM `posts` WHERE `id` = :id";
$postQuery = $this->db->query($sql,['id'=>$idifitier]);
if(!$postQuery->count()) {
return false;
}
$postData = $postQuery->first();
$this->fill($postData);
return true;
}
public function getTitle(): string {
return $this->title;
}
public function getId():int {
return $this->id;
}
public function getSlug():string {
return $this->slug;
}
public function getBody(): string {
return $this->body;
}
}
app\Controllers\PostController.php
<?php
namespace App\Controllers;
use App\BaseController;
use App\Models\Post;
use App\Request;
class PostController extends BaseController {
public function index(Request $request): void
{
$identifier = $request->getInput('page')[0];
$post = new Post(db: $this->db);
$post->find($identifier);
$this->view->render("posts/index",[
"post" => $post
]);
}
public function edit(Request $request) {
$identifier = $request->getInput('page')[1];
$post = new Post(db: $this->db);
$post->find($identifier);
if ($request->getMethod() !== 'POST') {
$this->view->render('/posts/edit', [
'post' => $post
]);
return;
}
$formInput = $request->getInput();
if (!$post->update(postData: $formInput)) {
die("Chưa cập nhật bài viết");
}
$this->redirectTo("/post/{$post->getId()}/{$post->getSlug()}");
}
}
app\Router.php
<?php
namespace App;
use App\Controllers\HomeController;
class Router {
private $controller = HomeController::class;
private $method = "index";
private $params = [];
public function __construct() {
$url = $this->parseUrl();
if(!isset($url[0])) return;
$requestedController = "App\\Controllers\\" . ucfirst(string: $url[0]) . "Controller";
$this->controller = $requestedController;
unset($url[0]);
if(isset($url[1]) && method_exists(object_or_class: $this->controller,method: $url[1])) {
$this->method = $url[1];
}
$this->params = array_values(array: $url);
unset($url[1]);
}
public function getRequestedController(): string {
return $this->controller;
}
public function getRequestedMethod(): string {
return $this->method;
}
public function getParams(): array {
return $this->params;
}
public function parseUrl(): array {
if(!isset($_GET['url'])) return [];
$url = rtrim(string: $_GET['url'], characters: "/");
$url = explode(separator: "/",string: $url);
return $url;
}
}
app\Models\Post.php
<?php
namespace App\Models;
use App\Helpers\Str;
use DateTime;
class Post
{
private Database $db;
private $id;
private $userId;
private string $title;
private string $slug;
private string $body;
private $createdAt;
public function __construct(Database $db, $data = [])
{
$this->db = $db;
$this->fill($data);
}
public function fill($data)
{
foreach ($data as $column => $value) {
$columnCamel = Str::toCamelCase($column);
$this->{$columnCamel} = $value;
}
}
public function find(int $identifier): bool
{
$sql = "SELECT * FROM `posts` WHERE `id` = :id";
$postQuery = $this->db->query($sql, ['id' => $identifier]);
if (!$postQuery->count()) {
return false;
}
$post = $postQuery->first();
$this->fill($post);
return true;
}
public function getTitle(): string
{
return $this->title;
}
public function getId(): int
{
return $this->id;
}
public function getSlug(): string
{
return $this->slug;
}
public function getBody(): string
{
return $this->body;
}
public function update(array $postData)
{
$sql = "UPDATE `posts` SET `title` = :title, `slug` = :slug, `body` =:body WHERE `id` = :id";
$slug = Str::slug($postData['title']);
$postData = [
'id' => $this->getId(),
'title' => $postData['title'],
'slug' => $slug,
'body' => $postData['body']
];
$editQuery = $this->db->query($sql, $postData);
$this->fill($postData);
return (bool) $editQuery->count();
}
}
app\Helpers\Str.php
<?php
namespace App\Helpers;
class Str
{
public static function toPascalCase(string $subject): string
{
return str_replace('_', '', ucwords($subject, '_'));
}
public static function toCamelCase(string $subject): string
{
return lcfirst(self::toPascalCase($subject));
}
public static function slug(string $string)
{
$disallowedCharacters = '/[^\-\s\pN\pL]+/';
$spacesDuplicateHyphens = '/[\-\s]+/';
$slug = mb_strtolower($string, 'UTF-8');
$slug = preg_replace($disallowedCharacters, '', $slug);
$slug = preg_replace($spacesDuplicateHyphens, '-', $slug);
$slug = trim($slug, '-');
return $slug;
}
}
app\Controllers\PostController.php
<?php
namespace App\Controllers;
use App\BaseController;
use App\Models\Post;
use App\Request;
use Exception;
class PostController extends BaseController {
public function index(Request $request): void
{
$idifitier = $request->getInput("page")[0];
$post = new Post(db: $this->db);
$post->find($idifitier);
$this->view->render("posts/index", [
"post" => $post
]);
}
public function edit(Request $request) {
$identifier = $request->getInput("page")[1];
$post = new Post(db: $this->db);
$post->find($identifier);
if($request->getMethod() != "POST") {
$this->view->render("posts/edit", [
"post"=> $post
]);
return;
}
$inputForm = $request->getInput();
if (!$post->update(postData: $inputForm)) {
die("Not Update");
}
$this->redirectTo("/post/{$post->getId()}/{$post->getSlug()}");
}
public function create(Request $request) {
if($request->getMethod() != "POST") {
$this->view->render("posts/create");
return;
}
$formInput = $request->getInput();
$fileInput = $request->getInput('file');
$post = new Post($this->db);
try {
$post->add(userId: $this->user->getId(), postData: $formInput, image: $fileInput['image']);
$this->redirectTo('/dashboard');
} catch (Exception $e) {
$this->view->render('posts/create', [
'errors' => [
'root' => $e->getMessage()
]
]);
}
}
}
app\views\posts\create.php
<div class="container">
<h1>Write Your Post</h1>
<form method="post" enctype="multipart/form-data">
<div>
<label for="title" class="form-label">Title</label>
<input type="text" id="title" name="title" class="form-control">
</div>
<div>
<label for="body" class="form-label">Post Body</label>
<textarea name="body" id="body" class="form-control"></textarea>
</div>
<div>
<label for="image" class="form-label">Image</label>
<input type="file" id="image" name="image" class="form-control">
</div>
<input type="submit" value="Create Post" class="mt-3 btn btn-success">
</form>
</div>
app\Models\Post.php
<?php
namespace App\Models;
use App\Helpers\Str;
class Post
{
private Database $db;
private $id;
private $userId;
private string $title;
private $slug;
private $body;
private $createdAt;
public function __construct(Database $db, $data = [])
{
$this->db = $db;
$this->fill($data);
}
public function fill($data): void
{
foreach ($data as $column => $value) {
$columnCamel = Str::toCamelCase($column);
$this->{$columnCamel} = $value;
}
}
public function find(int $idifitier): bool
{
$sql = "SELECT * FROM `posts` WHERE `id` = :id";
$postQuery = $this->db->query($sql, ['id' => $idifitier]);
if (!$postQuery->count()) {
return false;
}
$postData = $postQuery->first();
$this->fill($postData);
return true;
}
public function getTitle(): string
{
return $this->title;
}
public function getId(): int
{
return $this->id;
}
public function getSlug(): string
{
return $this->slug;
}
public function getBody(): string {
return $this->body;
}
public function update(array $postData) {
$sql = "UPDATE `posts` SET `title`=:title, `slug`=:slug,`body` =:body WHERE `id` = :id";
$slug = Str::slug($postData['title']);
$postData = [
'id' => $this->getId(),
'title'=> $postData['title'],
'slug'=> $slug,
'body'=> $postData['body'],
];
$updateQuery = $this->db->query($sql, $postData);
$this->fill($postData);
return (bool) ($updateQuery->count());
}
public function add(int $userId, array $postData, array $image) {
$sql = "INSERT INTO `posts` (`user_id`, `title`, `slug`, `body`, `created_at`) VALUES (:userId, :title, :slug, :body, :createdAt)";
$slug = Str::slug($postData['title']);
$this->db->query($sql, [
'userId' => $userId,
'title' => $postData['title'],
'slug' => $slug,
'body' => $postData['body'],
'createdAt' => time()
]);
}
}
app\Controllers\PostController.php
<?php
namespace App\Controllers;
use App\BaseController;
use App\Models\Post;
use App\Request;
use Exception;
class PostController extends BaseController {
public function index(Request $request): void
{
$idifitier = $request->getInput("page")[0];
$post = new Post(db: $this->db);
$post->find($idifitier);
$this->view->render("posts/index", [
"post" => $post
]);
}
public function edit(Request $request) {
$identifier = $request->getInput("page")[1];
$post = new Post(db: $this->db);
$post->find($identifier);
if($request->getMethod() != "POST") {
$this->view->render("posts/edit", [
"post"=> $post
]);
return;
}
$inputForm = $request->getInput();
if (!$post->update(postData: $inputForm)) {
die("Not Update");
}
$this->redirectTo("/post/{$post->getId()}/{$post->getSlug()}");
}
public function create(Request $request) {
if($request->getMethod() != "POST") {
$this->view->render("posts/create");
return;
}
$formInput = $request->getInput();
$fileInput = $request->getInput('file');
$post = new Post($this->db);
try {
$post->add(userId: $this->user->getId(), postData: $formInput, image: $fileInput['image']);
$this->redirectTo('/dashboard');
} catch (Exception $e) {
$this->view->render('posts/create', [
'errors' => [
'root' => $e->getMessage()
]
]);
}
}
}
app\views\posts\create.php
<div class="container">
<h1>Write Your Post</h1>
<form method="post" enctype="multipart/form-data">
<div>
<label for="title" class="form-label">Title</label>
<input type="text" id="title" name="title" class="form-control">
</div>
<div>
<label for="body" class="form-label">Post Body</label>
<textarea name="body" id="body" class="form-control"></textarea>
</div>
<div>
<label for="image" class="form-label">Image</label>
<input type="file" id="image" name="image" class="form-control">
</div>
<input type="submit" value="Create Post" class="mt-3 btn btn-success">
</form>
</div>
app\Models\Post.php
<?php
namespace App\Models;
use App\Helpers\Str;
use App\Config;
class Post
{
private Database $db;
private $id;
private $userId;
private string $title;
private $slug;
private $body;
private $createdAt;
public function __construct(Database $db, $data = [])
{
$this->db = $db;
$this->fill($data);
}
public function fill($data): void
{
foreach ($data as $column => $value) {
$columnCamel = Str::toCamelCase($column);
$this->{$columnCamel} = $value;
}
}
public function find(int $idifitier): bool
{
$sql = "SELECT * FROM `posts` WHERE `id` = :id";
$postQuery = $this->db->query($sql, ['id' => $idifitier]);
if (!$postQuery->count()) {
return false;
}
$postData = $postQuery->first();
$this->fill($postData);
return true;
}
public function getTitle(): string
{
return $this->title;
}
public function getId(): int
{
return $this->id;
}
public function getSlug(): string
{
return $this->slug;
}
public function getBody(): string {
return $this->body;
}
public function update(array $postData) {
$sql = "UPDATE `posts` SET `title`=:title, `slug`=:slug,`body` =:body WHERE `id` = :id";
$slug = Str::slug($postData['title']);
$postData = [
'id' => $this->getId(),
'title'=> $postData['title'],
'slug'=> $slug,
'body'=> $postData['body'],
];
$updateQuery = $this->db->query($sql, $postData);
$this->fill($postData);
return (bool) ($updateQuery->count());
}
public function add(int $userId, array $postData, array $image) {
$sql = "INSERT INTO `posts` (`user_id`, `title`, `slug`, `body`, `created_at`) VALUES (:userId, :title, :slug, :body, :createdAt)";
$slug = Str::slug($postData['title']);
$this->db->query($sql, [
'userId' => $userId,
'title' => $postData['title'],
'slug' => $slug,
'body' => $postData['body'],
'createdAt' => time()
]);
$sql = "SELECT MAX(`id`) AS 'id' FROM `posts` WHERE `user_id` = :user_id";
$postQuery = $this->db->query($sql, ['user_id' => $userId]);
$postId = $postQuery->first()['id'];
$fileStorage = new FileStorage(file: $image);
$fileStorage->saveIn(Config::get('app.uploadFolder'));
$imageName = $fileStorage->getGeneratedName();
$sql = "INSERT INTO `post_images` (`post_id`, `filename`, `created_at`) VALUES (:post_id, :filename, :created_at)";
$this->db->query($sql, [
'post_id' => $postId,
'filename' => $imageName,
'created_at' => time()
]);
}
}
app\Models\Post.php
<?php
namespace App\Models;
use App\Models\Database;
use App\Helpers\Str;
use App\Models\User;
use App\Config;
use App\Models\FileStorage;
class Post
{
private Database $db;
private string $id;
private string $userId;
private string $title;
private string $slug;
private string $body;
private string $createdAt;
public function __construct(Database $db, ?array $data = [])
{
$this->db = $db;
$this->fill($data);
}
public function find(int $identifier): bool
{
$sql = "SELECT * FROM `posts` WHERE `id` = :identifier";
$postQuery = $this->db->query($sql, ['identifier' => $identifier]);
if (!$postQuery->count()) {
return false;
}
$this->fill($postQuery->first());
return true;
}
public function fill(array $data)
{
foreach ($data as $field => $value) {
$this->{Str::toCamelCase($field)} = $value;
}
}
public function create(int $userId, array $postData, array $image)
{
$sql = "
INSERT INTO `posts`
(`user_id`, `title`, `slug`, `body`, `created_at`)
VALUES (:userId, :title, :slug, :body, :createdAt)
";
$slug = Str::slug($postData['title']);
$this->db->query($sql, [
'userId' => $userId,
'title' => $postData['title'],
'slug' => $slug,
'body' => $postData['body'],
'createdAt' => time()
]);
$sql = "SELECT MAX(`id`) AS 'id' FROM `posts` WHERE `user_id` = :user_id";
$postQuery = $this->db->query($sql, ['user_id' => $userId]);
$postId = $postQuery->first()['id'];
$fileStorage = new FileStorage($image);
$fileStorage->saveIn(Config::get('app.uploadFolder'));
$imageName = $fileStorage->getGeneratedName();
$sql = "
INSERT INTO `post_images`
(`post_id`, `filename`, `created_at`)
VALUES (:post_id, :filename, :created_at)
";
$this->db->query($sql, [
'post_id' => $postId,
'filename' => $imageName,
'created_at' => time()
]);
}
public function edit(array $postData): bool
{
$sql = "
UPDATE `posts`
SET `title` = :title, `slug` = :slug, `body` = :body
WHERE `id` = :id
";
$slug = Str::slug($postData['title']);
$postData = [
'id' => $this->getId(),
'title' => $postData['title'],
'slug' => $slug,
'body' => $postData['body']
];
$editQuery = $this->db->query($sql, $postData);
$this->fill($postData);
return (bool) $editQuery->count();
}
public function delete(): bool
{
$images = $this->getImages();
foreach ($images as $image) {
FileStorage::delete($image);
}
$sql = "DELETE FROM `posts` WHERE `id` = :id";
$deleteQuery = $this->db->query($sql, ['id' => $this->getId()]);
return (bool) $deleteQuery->count();
}
public function like(int $userId): bool
{
$sql = "
INSERT INTO `post_likes`
(`user_id`, `post_id`, `created_at`)
VALUES (:user_id, :post_id, :created_at)
";
$likeQuery = $this->db->query($sql, [
'user_id' => $userId,
'post_id' => $this->getId(),
'created_at' => time()
]);
return (bool) $likeQuery->count();
}
public function dislike(int $userId): bool
{
$sql = "DELETE FROM `post_likes` WHERE `post_id` = :post_id AND `user_id` = :user_id";
$deleteQuery = $this->db->query($sql, [
'post_id' => $this->getId(),
'user_id' => $userId
]);
return (bool) $deleteQuery->count();
}
public function getTotalLikes(): int
{
$sql = "SELECT COUNT(`id`) as 'like_count' FROM `post_likes` WHERE `post_id` = :post_id";
$likesQuery = $this->db->query($sql, [
'post_id' => $this->getId()
]);
return (int) $likesQuery->first()['like_count'];
}
public function isLikedBy(int $userId): bool
{
$sql = "SELECT 1 FROM `post_likes` WHERE `post_id` = :post_id AND `user_id` = :user_id";
$likeQuery = $this->db->query($sql, [
'post_id' => $this->getId(),
'user_id' => $userId
]);
return (bool) $likeQuery->count();
}
public function getId(): int
{
return (int) $this->id;
}
public function getUserId(): int
{
return (int) $this->userId;
}
public function getTitle(): string
{
return $this->title;
}
public function getSlug(): string
{
return $this->slug;
}
public function getBody(): string
{
return $this->body;
}
public function getCreatedAt(): string
{
return date('D, d.m.Y H:i:s', $this->createdAt);
}
public function getUser(): User
{
$user = new User($this->db);
$user->find($this->getUserId());
return $user;
}
public function getImages(): array
{
$sql = "SELECT `filename` FROM `post_images` WHERE `post_id` = :post_id";
$query = $this->db->query($sql, ['post_id' => $this->getId()]);
$images = array_map(function ($image) {
return DIRECTORY_SEPARATOR . Config::get('app.uploadFolder') . DIRECTORY_SEPARATOR . $image['filename'];
}, $query->results());
return $images;
}
}
app\Config.php
<?php
namespace App;
class Config
{
private static array $options = [
'app' => [
'uploadFolder' => 'images'
],
'database' => [
'host' => 'localhost',
'name' => 'forum',
'username' => 'root',
'password' => '',
'charset' => 'utf8mb4'
]
];
public static function get(string $selector)
{
$elements = explode(separator: '.', string: $selector);
$dataset = self::$options;
foreach ($elements as $element) {
$dataset = $dataset[$element];
}
return $dataset;
}
}
$fileInput = $request->getInput('file');
echo '<pre>';
var_export($fileInput);
echo '</pre>';
die("gg");
array (
'image' =>
array (
'name' => '5.jpg',
'full_path' => '5.jpg',
'type' => 'image/jpeg',
'tmp_name' => 'C:\\xampp82\\tmp\\php49D1.tmp',
'error' => 0,
'size' => 312563,
),
)
app\Models\FileStorage.php
<?php
namespace App\Models;
use App\Helpers\Str;
use Exception;
class FileStorage
{
private array $file;
private string $extension;
private string $currentLocation;
private string $generatedName;
public function __construct(array $file)
{
$this->file = $file;
$this->extension = strtolower(string: pathinfo(path: $this->file['name'], flags: PATHINFO_EXTENSION));
$this->currentLocation = $this->file['tmp_name'];
$this->generatedName = Str::token() . '.' . $this->extension;
}
public function getGeneratedName(): string
{
return $this->generatedName;
}
public function saveIn(string $folder): void
{
$destination = "{$folder}/{$this->generatedName}";
if (!move_uploaded_file(from: $this->currentLocation, to: $destination)) {
throw new Exception(message: 'We encountered an error uploading the file.');
}
}
public static function delete(string $path): bool
{
return unlink(filename: ltrim(string: $path, characters: DIRECTORY_SEPARATOR));
}
}
app\Helpers\Str.php
<?php
namespace App\Helpers;
class Str
{
public static function toPascalCase(string $subject): string
{
return str_replace('_', '', ucwords($subject, '_'));
}
public static function toCamelCase(string $subject): string
{
return lcfirst(self::toPascalCase($subject));
}
public static function slug(string $string)
{
$disallowedCharacters = '/[^\-\s\pN\pL]+/';
$spacesDuplicateHyphens = '/[\-\s]+/';
$slug = mb_strtolower(string: $string, encoding: 'UTF-8');
$slug = preg_replace(pattern: $disallowedCharacters, replacement: '', subject: $slug);
$slug = preg_replace(pattern: $spacesDuplicateHyphens, replacement: '-', subject: $slug);
$slug = trim(string: $slug, characters: '-');
return $slug;
}
public static function token(): string
{
return bin2hex(string: random_bytes(length: 16));
}
}
public\index.php
<?php
require '../app/init.php';
$app = new App();
$app->run();
app\init.php
<?php
require_once 'config/config.php';
require_once 'core/App.php';
require_once 'core/Controller.php';
app\config\config.php
<?php
define('BASEURL', 'https://lva2.com');
define('DB_HOST', 'localhost');
define('DB_USER', 'root');
define('DB_PASS', '');
define('DB_NAME', 'lva1');
app\core\App.php
<?php
class App
{
protected $controller = 'homeController';
protected $method = 'index';
protected $params = [];
public function __construct()
{
$url = $this->parseURL();
if (file_exists(filename: '../app/controllers/' . $url[0] . '.php')) {
$this->controller = $url[0];
unset($url[0]);
} else {
die("{$url[0]} not found");
}
require_once '../app/controllers/' . $this->controller . '.php';
$this->controller = new $this->controller;
if (isset($url[1])) {
if (method_exists(object_or_class: $this->controller, method: $url[1])) {
$this->method = $url[1];
unset($url[1]);
}
}
if (!empty($url)) {
$this->params = array_values(array: $url);
}
}
public function parseURL()
{
if (isset($_GET['url'])) {
$url = explode(separator: '/', string: filter_var(value: trim(string: $_GET['url']), filter: FILTER_SANITIZE_URL));
$url[0] = $url[0] . 'Controller';
} else {
$url[0] = 'homeController';
}
return $url;
}
public function run()
{
return call_user_func_array(callback: [$this->controller, $this->method], args: $this->params);
}
}
app\core\Controller.php
<?php
class Controller
{
public function view($view, $data = [])
{
require_once '../app/views/' . $view . '.php';
}
public function model($model)
{
require_once '../app/models/' . $model . '.php';
return new $model;
}
public function redirect($url)
{
header('Location: ' . BASEURL . '/' . $url);
exit;
}
}
app\controllers\homeController.php
<?php
class homeController extends Controller
{
public function index(): void
{
$data['title'] = 'Lionel Home';
echo $data['title'];
}
}
app\controllers\mahasiswaController.php
<?php
class mahasiswaController extends Controller {
public function index() {
$data['title'] = 'Halaman mahasiswa';
$data['judul'] = 'Data mahasiswa';
$data['mahasiswa'] = $this->model('Mahasiswa')->getAll();
$this->view('templates/header', $data);
$this->view('mahasiswa/index', $data);
$this->view('templates/footer');
}
}
app\core\App.php
<?php
class App
{
protected $controller = 'homeController';
protected $method = 'index';
protected $params = [];
public function __construct()
{
$url = $this->parseURL();
if (file_exists(filename: '../app/controllers/' . $url[0] . '.php')) {
$this->controller = $url[0];
unset($url[0]);
} else {
die("{$url[0]} not found");
}
require_once '../app/controllers/' . $this->controller . '.php';
$this->controller = new $this->controller;
if (isset($url[1])) {
if (method_exists(object_or_class: $this->controller, method: $url[1])) {
$this->method = $url[1];
unset($url[1]);
}
}
if (!empty($url)) {
$this->params = array_values(array: $url);
}
}
public function parseURL()
{
if (isset($_GET['url'])) {
$url = explode(separator: '/', string: filter_var(value: trim(string: $_GET['url']), filter: FILTER_SANITIZE_URL));
$url[0] = $url[0] . 'Controller';
} else {
$url[0] = 'homeController';
}
return $url;
}
public function run()
{
return call_user_func_array(callback: [$this->controller, $this->method], args: $this->params);
}
}
app\core\Controller.php
<?php
class Controller
{
public function view($view, $data = [])
{
require_once '../app/views/' . $view . '.php';
}
public function model($model)
{
require_once '../app/models/' . $model . '.php';
return new $model;
}
public function redirect($url)
{
header('Location: ' . BASEURL . '/' . $url);
exit;
}
}
app\core\Database.php
<?php
class Database {
private $host = DB_HOST;
private $dbname = DB_NAME;
private $username = DB_USER;
private $password = DB_PASS;
private $db;
private $st;
public function __construct()
{
try {
$this->db = new PDO(dsn: "mysql:host={$this->host};dbname={$this->dbname}", username: $this->username, password: $this->password);
} catch (PDOException $e) {
print_r(value: $e->getMessage());
die;
}
}
public function query($query): void
{
$this->st = $this->db->prepare($query);
}
public function execute(): void
{
$this->st->execute();
}
public function resultSet(): array
{
$this->execute();
return $this->st->fetchAll(PDO::FETCH_ASSOC);
}
public function single(): array
{
$this->execute();
return $this->st->fetch(PDO::FETCH_ASSOC);
}
}
app\models\Mahasiswa.php
<?php
class Mahasiswa {
private $db;
private $table = 'mahasiswa';
public function __construct()
{
$this->db = new Database;
}
public function getAll()
{
$this->db->query("SELECT * FROM {$this->table}");
return $this->db->resultSet();
}
}
app\views\mahasiswa\index.php
<div class="container">
<div class="jumbotron mt-4">
<h1 class="display-4"><?= $data['judul']; ?></h1>
<a href="<?= BASEURL ?>/mahasiswa/create" class="btn btn-sm btn-primary">
Add
</a>
<div class="table-responsive mt-5">
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Name</th>
<th scope="col">Email</th>
<th scope="col">Action</th>
</tr>
</thead>
<tbody>
<?php foreach ($data['mahasiswa'] as $key => $value): ?>
<tr>
<th scope="row"><?= ++$key; ?></th>
<td><?= $value['name']; ?></td>
<td><?= $value['email']; ?></td>
<td style=" display: flex; gap: 10px; ">
<a href="<?= BASEURL; ?>/mahasiswa/edit/<?= $value['id']; ?>" class="btn btn-xs btn-info">Edit</a>
<form action="<?= BASEURL; ?>/mahasiswa/destroy/<?= $value['id'] ?>" method="post">
<button class="btn btn-xs btn-danger">Delete</button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
app\views\templates\header.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title><?= $data['title'] ?></title>
<link rel="stylesheet" href="<?= BASEURL; ?>/assets/css/bootstrap.min.css">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container">
<a class="navbar-brand" href="#">LOGO</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item active">
<a class="nav-link" href="<?= BASEURL; ?>">Home <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item">
<a class="nav-link" href="<?= BASEURL; ?>/mahasiswa">Mahasiswa</a>
</li>
<li class="nav-item">
<a class="nav-link" href="<?= BASEURL; ?>/about">About</a>
</li>
</ul>
</div>
</div>
</nav>
app\views\templates\footer.php
<script src="https://lva1.com/assets/js/jquery.slim.min.js"></script>
<script src="https://lva1.com/assets/js/popper.min.js"></script>
<script src="https://lva1.com/assets/js/bootstrap.min.js"></script>
</body>
</html>
app\init.php
<?php
require_once 'config/config.php';
require_once 'core/Database.php';
require_once 'core/App.php';
require_once 'core/Controller.php';