No mundo do desenvolvimento web, frameworks desempenham um papel crucial na simplificação do processo de desenvolvimento e na organização do código. É difícil encontrar times de desenvolvimento que começam um novo projeto do zero sem pelo menos um "esqueleto", uma base - que justamente os frameworks provém (com algumas outras facilidades).
No mundo PHP (no qual eu tenho maior experiência) temos vários (e bons) exemplos de frameworks web como Laravel, Symfony, CodeIgniter, Zend Framework (Laminas Framework?), Yii, CakePHP, Slim e etc.
Atualmente eu trabalho com Laravel e seu ecossistema para desenvolver soluções web mas em essência eu sou desenvolvedor PHP. Decidi decidi desenvolver um pequeno projeto, um pequeno framework do zero para exercitar um pouco e se divertir.
Criei o "My PHP MVC Framework". A ideia é relativamente simples, um projeto com o padrão MVC (Model-View- Controller) com um código simples e com menos dependências externas possíveis. Por que? Quero escrever a maior parte que conseguir para ter mais resultados educacionais e até mesmo de portifólio.
Funcionalidades
- Fácil de usar;
- Simples e intuitivo;
- Suporte para rotas com parâmetros;
- Funções ou métodos associados com rotas
- Gerenciador de banco de dados simples
- Componentes de visualização
Você pode encontrar o repositório aqui ou pode criar um novo projeto via composer:
composer create-project claud/my-php-mvc
Núcleo do framework:
Roteamento
Dentro do diretório _config _ existe um arquivo router.php
e é nele que declaramos nossas rotas.
<?php
require_once __DIR__ . '/../vendor/autoload.php';
use Router\Router\Router;
$router = new Router();
/*
* Add routes here
*/
$router->addRoute('/', 'App\Controller\WelcomeController@render');
return $router;
Implementação de controladores e modelos básicos
Controler\WelcomeController.php
<?php
declare(strict_types=1);
namespace App\Controller;
class WelcomeController
{
public function render()
{
return view("welcome", ['title' => "Bem vindo!"]);
}
}
Entity\User.php
<?php
namespace App\Entity;
class User
{
public int $id;
public string $name;
public string $username;
public string $email;
public string $password;
public ?string $created_at;
public ?string $updated_at;
public function __construct(string $name, string $username, string $email, string $password, ?string $created_at = null, ?string $updated_at = null)
{
$this->name = $name;
$this->username = $username;
$this->email = $email;
$this->password = $password;
$this->created_at = $created_at;
$this->updated_at = $updated_at;
}
public function setId(int $id): void
{
$this->id = $id;
}
public function getId(): ?int
{
return $this->id;
}
}
Repositpry\UserRepository.php
<?php
namespace App\Repository;
use App\Contracts\RepositoryInterface;
use App\Entity\User;
use PDO;
class UserRepository implements RepositoryInterface
{
private PDO $pdo;
public function __construct(PDO $pdo)
{
$this->pdo = $pdo;
}
public function create(object $user): object
{
$sql = 'INSERT INTO users (name, username, email, password) VALUES (?, ?, ?, ?);';
$statement = $this->pdo->prepare($sql);
$statement->bindValue(1, $user->name);
$statement->bindValue(2, $user->username);
$statement->bindValue(3, $user->email);
$statement->bindValue(4, $user->password);
$result = $statement->execute();
if (!$result) {
throw new \RuntimeException('Could not save user');
}
$id = $this->pdo->lastInsertId();
$user->setId(intval($id));
return $user;
}
public function update(int $id, object $user): bool
{
$sql = 'UPDATE users SET name = ?, username = ?, email = ?, password = ? WHERE id = ?;';
$statement = $this->pdo->prepare($sql);
$statement->bindValue(1, $user->name);
$statement->bindValue(2, $user->username);
$statement->bindValue(3, $user->email);
$statement->bindValue(4, $user->password);
$statement->bindValue(5, $user->id);
return $statement->execute();
}
public function delete(int $id): bool
{
$sql = 'DELETE FROM users WHERE id = ?;';
$statement = $this->pdo->prepare($sql);
$statement->bindValue(1, $id);
return $statement->execute();
}
public function all(): array
{
$sql = 'SELECT * FROM users;';
$statement = $this->pdo->prepare($sql);
$statement->execute();
return $statement->fetchAll(\PDO::FETCH_ASSOC);
}
public function findByUsername(string $username)
{
$statement = $this->pdo->prepare('SELECT * FROM users WHERE username = ?;');
$statement->bindValue(1, $username);
$statement->execute();
return $statement->fetch(\PDO::FETCH_ASSOC);
}
public function find(int $id): null|object
{
$statement = $this->pdo->prepare('SELECT * FROM users WHERE id = ?;');
$statement->bindValue(1, $id);
$statement->execute();
$userArr = $statement->fetch(\PDO::FETCH_ASSOC);
$user = new User($userArr['name'], $userArr['username'], $userArr['email'], $userArr['password'], $userArr['created_at'], $userArr['updated_at']);
return $user;
}
}
Exemplo prático:
Criando um site
Criando o projeto
composer create-project claud/my-php-mvc site
Dentro da pasta de Controller
crie o arquivo SiteController.php
<?php
declare(strict_types=1);
namespace App\Controller;
class SiteController
{
public function render(): void
{
view("site.index", ['title' => "Bem vindo!"]);
}
}
O retorno da view renderizar o arquivo index.php dentro da pasta site. Os nomes dos arquivos e pastas ficam a critério do DEV.
Criei uma pastas chamada __partials
para colocar os dados da tag head
Como exemplo, eu peguei um projeto no Git Hub e usei para criar o site.
head.php
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= $title ?></title>
<link rel="stylesheet" href="css/variables.css">
<link rel="stylesheet" href="css/elements.css">
<link rel="stylesheet" href="css/classes.css">
<link rel="stylesheet" href="css/menu.css">
<link rel="stylesheet" href="css/style.css">
</head>
views\site\index.php
<!DOCTYPE html>
<html lang="pt_BR">
<?php include '__partials/head.php' ?>
<body>
<input id="close-menu" class="close-menu" type="checkbox" aria-label="Close menu" role="button">
<label class="close-menu-label" for="close-menu" title="close menu"></label>
<aside class="menu white-bg">
<div class="main-content menu-content">
<h1 onclick="getElementById('close-menu').checked = false;">
<a href="#home">LOGO</a>
</h1>
<nav>
<ul onclick="getElementById('close-menu').checked = false;">
<li><a href="#intro">intro</a></li>
<li><a href="#grid-one">grid-one</a></li>
<li><a href="#gallery">gallery</a></li>
<li><a href="#grid-two">grid-two</a></li>
<li><a href="#pricing">pricing</a></li>
<li><a href="/contact">contact</a></li>
</ul>
</nav>
</div>
</aside>
<div class="menu-spacing"></div>
<section id="home" class="intro main-bg section">
<div class="main-content intro-content">
<div class="intro-text-content">
<h2>January brings us Firefox 85</h2>
<p>To wrap up January, we are proud to bring you the release of Firefox 85. In this version we are bringing you
support for the :focus-visible pseudo-class in CSS and associated devtools, and the complete removal of Flash
support from Firefox.</p>
</div>
<div class="intro-img">
<img src="img/javascript.svg" alt="Logo de HTML, CSS e JS.">
</div>
</div>
</section>
<section id="intro" class="white-bg section">
<div class="main-content top3-content">
<h2>news coverage and some
surprises</h2>
<p>The release of Apple Silicon-based Macs at the end of last year generated a flurry of news coverage and some
surprises at the machine’s performance. This post details some background information on the experience of
porting Firefox to run natively on these CPUs.</p>
<p>We’ll start with some background on the Mac transition and give an overview of Firefox internals that needed to
know about the new architecture, before moving on to the concept of Universal Binaries.</p>
<p>We’ll then explain how DRM/EME works on the new platform, talk about our experience with macOS Big Sur, and
discuss various updater problems we had to deal with. We’ll conclude with the release and an overview of various
other improvements that are in the pipeline.</p>
</div>
</section>
<section id="grid-one" class="grid-one main-bg section">
<div class="main-content grid-one-content">
<h2 class="grid-main-heading">My Grid</h2>
<p class="grid-description">Uma breve descrição.</p>
<div class="grid">
<article>
<h3>Teste 1</h3>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Debitis cum delectus molestias. Atque doloribus
nobis laudantium esse ut, non commodi maxime distinctio veritatis unde, reprehenderit minus ad dolores
provident maiores.</p>
</article>
<article>
<h3>Teste 2</h3>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Debitis cum delectus molestias. Atque doloribus
nobis laudantium esse ut, non commodi maxime distinctio veritatis unde, reprehenderit minus ad dolores
provident maiores.</p>
</article>
<article>
<h3>Teste 2</h3>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Debitis cum delectus molestias. Atque doloribus
nobis laudantium esse ut, non commodi maxime distinctio veritatis unde, reprehenderit minus ad dolores
provident maiores.</p>
</article>
</div>
</div>
</section>
<section id="gallery" class="grid-one white-bg section">
<div class="main-content grid-one-content">
<h2 class="grid-main-heading">Gallery</h2>
<p class="grid-description">Uma breve descrição.</p>
<div class="grid">
<div class="gallery-img">
<img src="http://source.unsplash.com/random/360x360?r=1" alt="random image from unsplash" />
</div>
<div class="gallery-img">
<img src="http://source.unsplash.com/random/360x360?r=2" alt="random image from unsplash" />
</div>
<div class="gallery-img">
<img src="http://source.unsplash.com/random/360x360?r=3" alt="random image from unsplash" />
</div>
<div class="gallery-img">
<img src="http://source.unsplash.com/random/360x360?r=4" alt="random image from unsplash" />
</div>
<div class="gallery-img">
<img src="http://source.unsplash.com/random/360x360?r=5" alt="random image from unsplash" />
</div>
<div class="gallery-img">
<img src="http://source.unsplash.com/random/360x360?r=6" alt="random image from unsplash" />
</div>
</div>
</div>
</section>
<section id="grid-two" class="grid-one main-bg section">
<div class="main-content grid-one-content">
<h2 class="grid-main-heading">My Grid</h2>
<p class="grid-description">Uma breve descrição.</p>
<div class="grid">
<article>
<h3>Teste 1</h3>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Debitis cum delectus molestias. Atque doloribus
nobis laudantium esse ut, non commodi maxime distinctio veritatis unde, reprehenderit minus ad dolores
provident maiores.</p>
</article>
<article>
<h3>Teste 2</h3>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Debitis cum delectus molestias. Atque doloribus
nobis laudantium esse ut, non commodi maxime distinctio veritatis unde, reprehenderit minus ad dolores
provident maiores.</p>
</article>
<article>
<h3>Teste 2</h3>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Debitis cum delectus molestias. Atque doloribus
nobis laudantium esse ut, non commodi maxime distinctio veritatis unde, reprehenderit minus ad dolores
provident maiores.</p>
</article>
</div>
</div>
</section>
<section id="pricing" class="white-bg section">
<div class="main-content top3-content">
<h2>Pricing</h2>
<p>The release of Apple Silicon-based Macs at the end of last year generated a flurry of news coverage and some
surprises at the machine’s performance. This post details some background information on the experience of
porting Firefox to run natively on these CPUs.</p>
<p>We’ll start with some background on the Mac transition and give an overview of Firefox internals that needed to
know about the new architecture, before moving on to the concept of Universal Binaries.</p>
<div class="responsive-table">
<table>
<caption>Pricing table</caption>
<thead>
<tr>
<th>Title 1</th>
<th>Title 2</th>
<th>Title 3</th>
<th>Title 4</th>
<th>Title 5</th>
</tr>
</thead>
<tbody>
<tr>
<td>Content 1</td>
<td>Content 2</td>
<td>Content 3</td>
<td>Content 3</td>
<td>Content 3</td>
</tr>
<tr>
<td>Content 1</td>
<td>Content 2</td>
<td>Content 3</td>
<td>Content 3</td>
<td>Content 3</td>
</tr>
<tr>
<td>Content 1</td>
<td>Content 2</td>
<td>Content 3</td>
<td>Content 3</td>
<td>Content 3</td>
</tr>
</tbody>
<tfoot>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td>Testando</td>
</tr>
</tfoot>
</table>
</div>
</div>
</section>
<section id="contact" class="intro main-bg section">
<div class="main-content intro-content">
<div class="intro-text-content">
<h2>January brings us Firefox 85</h2>
<p>To wrap up January, we are proud to bring you the release of Firefox 85. In this version we are bringing you
support for the :focus-visible pseudo-class in CSS and associated devtools, and the complete removal of Flash
support from Firefox.</p>
</div>
<div class="intro-img">
<img src="img/javascript.svg" alt="Logo de HTML, CSS e JS.">
</div>
<div class="contact-form">
<fieldset class="form-grid">
<legend>Contact me</legend>
<div class="form-group">
<label for="first-name">First name</label>
<input type="text" name="first-name" id="first-name" placeholder="Your name">
</div>
<div class="form-group">
<label for="last-name">last name</label>
<input type="text" name="last-name" id="last-name" placeholder="Your last name">
</div>
<div class="form-group">
<label for="email">E-mail</label>
<input type="email" name="email" id="email" placeholder="Your e-mail">
</div>
<div class="form-group full-width">
<label for="message">Message</label>
<textarea name="message" id="message" cols="30" rows="10" placeholder="Your message"></textarea>
</div>
<div class="form-group full-width">
<button type="submit">Send message</button>
</div>
</fieldset>
</div>
</div>
</section>
<footer id="footer" class="footer white-bg">
<p><a rel="nofollow" target="_blank" href="https://beacons.page/otaviomiranda">Feito com <span class="heart">❤</span> por Otávio Miranda</a></p>
</footer>
<a class="back-to-top" href="#">➤</a>
<script src="js/script.js"></script>
</body>
</html>
Agora tem que criar a rota para exibir a página. No arquivo router.php
dentro da pasta de config vamos escrever o seguinte código:
$router->addRoute('/', 'App\Controller\SiteController@render');
Iniciando o servidor de desenvolvimento
php -S localhost:8000 -t public
Agora vamos acessar a página
http://localhost:8000/
O resultado deve ser esse algo como a imagem abaixo:
O projeto completo está neste repositório.
Próximos passos
Bom, eu estou pretendendo criar um projeto de gerenciamento de tickets para exemplo. A aplicação terá gerenciamento de usuários, chamados e autenticação de usuários.
Quer me ajudar? Assim que criar o repositório eu edito essa postagem e quem sabe escreva outro post.
Conclusão
O My PHP MVC é um pequeno projeto que visa simplificar e estudar PHP para aplicações web simples. Saliento que o principal objetivo dele é para estudo, ou seja, não é aconselhável usa-lo em ambiente de produção - quem sabe um dia.
Espero que este artigo tenha fornecido insights e inspirado você a quem sabe criar o seu próprio framework. Haaa, você pode me ajudar a melhorar o código também! Escreve nos comentários sugestões de _features _ ou melhor, manda um PR! ⭐⭐⭐
Obs: Fiz uso de IA para me ajudar a escrever esse post (correções ortográficas e sugestões de tópicos) 😉
Top comments (0)