diogodg / neoframework
NeoFramework รฉ um framework simples e facil de usar para php
Requires
- diogodg/neoorm: ^1.0
- filp/whoops: ^2.17
- guzzlehttp/guzzle: ^7.9
- league/flysystem: ^3.29
- matthiasmullie/minify: ^1.3
- monolog/monolog: ^3.8
- nesbot/carbon: ^3.8
- php-di/php-di: ^7.0
- phpmailer/phpmailer: ^6.9
- respect/validation: ^2.4
- symfony/cache: ^7.2
README
NeoFramework is a modern and robust PHP framework that offers a complete structure for web application development. It provides a clean and organized architecture with advanced features for routing, validation, caching, and much more.
Main Features
- ๐ Advanced Routing System with Attribute Support
- ๐ Integrated Security System
- ๐ง Email Management
- ๐ Template System
- ๐พ Cache and File Storage
- ๐ Data Validation
- ๐ Session Management
- ๐ Logging System
- ๐จ Asset Bundler
- ๐ Dependency Injection Container
Requirements
- PHP 8.0 or higher
- Composer
- Web server (Apache/Nginx)
Installation
- Clone the repository:
composer create-project diogodg/neoframework your-project-name
- Install dependencies:
composer install
- Configure your environment:
- Copy the
.env.example
file to.env
- Adjust settings as needed
cp .env.example .env
Project Structure
your-project/
โโโ App/
โ โโโ Controllers/ # Application controllers
โ โโโ Models/ # Data models
โ โโโ View/ # View templates
โ โโโ Services/ # Business logic services
โ โโโ Middleware/ # Request/Response middleware
โ โโโ Helpers/ # Helper functions
โ โโโ Enums/ # Enumeration classes
โโโ Config/ # Configuration files
โโโ Logs/ # Application logs
โโโ Resources/ # Frontend assets
โโโ Cache/ # Cache files
โโโ public/ # Public directory (web root)
โโโ vendor/ # Composer dependencies
Basic Usage
Routing with Attributes
use NeoFramework\Core\Attributes\Route; class UserController { #[Route("index",['GET','POST'])] public function index():Response { // Controller logic return $this->response; } #[Route("show/{:any}/{:num:optional}")] public function show(string $srt,int|float $id):Response { // Controller logic return $this->response; } }
Middleware System
Middleware Class
public function __construct( private bool $Auth = true, ) { } public function before(Controller $controller): Controller { $response = $controller->getResponse(); if($Auth){ $response->addContent("Hello"); }else{ $response->go("login"); $response->send(); } $controller->setResponse($response); return $controller; } public function after(Response $response): Response { $response->addContent("Bye"); return $response; }
Controller
use NeoFramework\Core\Attributes\Route; class UserController { #[Route("index",['GET','POST'])] #[Middleware(new Auth(true))] public function index() { // Controller logic } #[Route("show/{:any}/{:num:optional}")] public function show(string $srt,int|float $id) { // Controller logic } }
Data Validation
use NeoFramework\Core\Validator; use Respect\Validation\Validator as v; use NeoFramework\Core\Message; $validator = new Validator(); $data = [ 'email' => "test@test.com" 'phone' => "48554115467" ] $rules = [ 'email' => v::allOf(v::email(), v::uniqueDb(new User, "email")), 'phone' => v::allOf(v::phone()) ] $messages = [ 'email' => "Email invalid", 'phone' => "Phone invalid", ]; $validator->make($this->getArrayData(), $rules, $messages); if ($validator->hasError()) { //set flash message Message::setError(...$validator->getErrors()); }
Cache
class Company extends model { public function get($value = "", string $column = "id", int $limit = 1, bool $cache = true): array|object { if ($cache === true && $value == 1 && $column == "id") { return Cache::get("company_1", function (ItemInterface $item) use ($value, $column, $limit) { $item->expiresAfter(3600); $item->tag('company'); return parent::get($value, $column, $limit); }); } return parent::get($value, $column, $limit); } public function set(): self|null { //logic if ($this->store()) { Message::setSuccess("Successfully saved"); Cache::delete("company_1"); return $this; } return null; } }
Templates
namespace App\View\Layout; use App\Helpers\Functions; use NeoFramework\Core\Abstract\Layout; use NeoFramework\Core\Message as CoreMessage; class Message extends Layout { public function __construct() { $this->setTemplate("Message.html"); $Messages = []; $Messages[] = CoreMessage::getError(); $Messages[] = CoreMessage::getSuccess(); $Messages[] = CoreMessage::getMessage(); $i = 0; foreach ($Messages as $Message){ foreach ($Message as $text){ if($text){ if ($i == 0){ $this->tpl->alert = "#f8d7da"; }elseif ($i == 1){ $this->tpl->alert = "#d1e7dd"; }else{ $this->tpl->alert = "#fff3cd"; } $this->tpl->message = $text; $this->tpl->block("BLOCK_MESSAGE"); } } $i++; } } }
<div id="message"> <div class="fixed left-0 top-3 w-full z-[1000]"> <!-- BEGIN BLOCK_MESSAGE --> <div id="alert-{id}" class="alert mx-2 alert-dismissible mt-1 flex justify-between items-center alert_back p-4 text-sm text-gray-800 rounded-[10px]" role="alert" style="background-color: {alert};"> <div>{message}</div> <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> <button type="button" class="btn-close d-none" onclick="document.querySelector('#alert-{id}').remove()"> <i class="fa-solid fa-xmark"></i> </button> </div> <!-- END BLOCK_MESSAGE --> </div> </div>
Models
Selecting Records
Select by ID
// Returns an object with all table columns based on the provided $id $result = (new Scheduling)->get($id);
Select by Name
// Returns an object with all table columns based on the provided $name $result = (new Scheduling)->get($name, "name");
Select All Records
// Returns an array of objects with all columns and table records $result = (new Scheduling)->getAll();
Select with Filters
// Returns an array of objects with all table columns based on the provided filters $db = new Scheduling; $results = $db->addFilter("dt_ini", ">=", $dt_start) ->addFilter("dt_fim", "<=", $dt_end) ->addFilter("id_agenda", "=", intval($id_agenda)) ->addFilter("status", "!=", $status) ->selectAll();
Select with Joins and Filters
// Returns an array of objects with the specified columns, based on added filters and joins $db = new Scheduling; $result = $db->addJoin("LEFT", "user", "user.id", "scheduling.id_user") ->addJoin("INNER", "schedule", "schedule.id", "scheduling.id_schedule") ->addJoin("LEFT", "client", "client.id", "scheduling.id_client") ->addJoin("INNER", "employee", "employee.id", "scheduling.id_employee") ->addFilter("schedule.id_company", "=", $id_company) ->selectColumns("scheduling.id", "user.cpf_cnpj", "client.name as cli_name", "user.name as user_name", "user.email", "user.phone", "schedule.name as schedule_name", "employee.name as employee_name", "dt_ini", "dt_fim");
Select with Filters and Limit
// Returns an array of objects with specified columns that match the provided values, based on filters and specified limit $db = new City; $result = $db->addFilter("name", "LIKE", "%" . $name . "%") ->addLimit(1) ->selectByValues(["uf"], [$id_uf], true);
Insert/Update Records
$values = new Employee; // If $values->id is null, empty, or 0, it will attempt an INSERT command. Otherwise, it will attempt an UPDATE. $values->id = null; // or "" or 0 $values->id_user = $id_user; $values->name = $name; $values->cpf_cnpj = $cpf_cnpj; $values->email = $email; $values->phone = $phone; $values->hour_ini = $hour_ini; $values->hour_fim = $hour_fim; $values->lunch_hour_ini = $lunch_hour_ini; $values->lunch_hour_fim = $lunch_hour_fim; $values->days = $days; // Returns false or the record ID $return = $values->store();
Deleting Records
Delete by Filter
$db = new Employee; // Returns true or false $return = $db->addFilter("name", "=", "Diogo")->deleteByFilter();
Delete by ID
$id = 1; $db = new employee; // Returns true or false $return = $db->delete($id);
Using Transactions
try{ connection::beginTransaction(); if ($schedule->set()){ $scheduleUser = new scheduleUser; $scheduleUser->id_user = $user->id; $scheduleUser->id_schedule = $schedule->id; $scheduleUser->set(); if($schedule->id_employee){ $scheduleEmployee = new scheduleEmployee; $scheduleEmployee->id_employee = $schedule->id_employee; $scheduleEmployee->id_schedule = $schedule->id; $scheduleEmployee->set(); } connection::commit(); } }catch (\exception $e){ connection::rollBack(); }
Other Examples
Using the DB Class Directly
$id = 1; $db = new db("tb_employee"); // Returns true or false $return = $db->delete($id);
Database Creation/Modification
Create a Table
Inside the app/models folder, create a class that will represent your database table as in the example below:
<?php namespace App\Models; use Diogodg\Neoorm\Abstract\Model; use Diogodg\Neoorm\Migrations\Table; use Diogodg\Neoorm\Migrations\Column; class State extends Model { //mandatory parameter that will define the table name in the database public const table = "state"; //mandatory to be in this way public function __construct() { parent::__construct(self::table); } //method responsible for creating the table public static function table(){ return (new Table(self::table,comment:"State table")) ->addColumn((new Column("id","INT"))->isPrimary()->setComment("City ID")) ->addColumn((new Column("name","VARCHAR",120))->isNotNull()->setComment("State name")) ->addColumn((new Column("uf","VARCHAR",2))->isNotNull()->setComment("UF name")) ->addColumn((new Column("country","INT"))->isNotNull()->setComment("country id of the state")) ->addForeignKey(Country::table,column:"country") ->addColumn((new Column("ibge","INT"))->isUnique()->setComment("IBGE id of the state")) ->addColumn((new Column("ddd","VARCHAR",50))->setComment("DDDs separated by , of the UF")); } //method responsible for inserting initial data in the table public static function seed(){ $object = new self; if(!$object->addLimit(1)->selectColumns("id")){ $object->name = "Acre"; $object->uf = "AC"; $object->country = 1; $object->ibge = 12; $object->ddd = "68"; $object->store(); $object->name = "Alagoas"; $object->uf = "AL"; $object->country = 1; $object->ibge = 27; $object->ddd = "82"; $object->store(); $object->name = "Amapรก"; $object->uf = "AP"; $object->country = 1; $object->ibge = 16; $object->ddd = "96"; $object->store(); $object->name = "Amazonas"; $object->uf = "AM"; $object->country = 1; $object->ibge = 13; $object->ddd = "92,97"; $object->store(); } } }
After creating all classes
just call the following command
php migrate
Bundler
your-project/
โโโ Resources/
โ โโโ Js/ # JS files for your project
โ โโโ Css/ # CSS files for your project
After placing the files in this folder just call the following command
php build
They will be compiled to the public folder
your-project/
โโโ Config/
โ โโโ bundler.config.php # Here you can configure which files will be compiled
<?php return [ "js" => [ "site" => ["htmx.min.js","swiper-bundle.min.js","zmain.js","aos.js"], "admin" => ["htmx.min.js","bootstrap.bundle.min.js","chart.js","choices.min.js","zadmin.js"] ], "css" => [ "site" => ["all.min.css","swiper-bundle.min.css","tailwind.css","aos.css"], "admin" => ["choices.min.css","bootstrap.min.css","all.min.css","zadmin.css"] ] ];
Configuration
The framework uses environment variables for configuration. Configure your .env
file with the following variables:
ENVIRONMENT=dev PATH_CONTROLLERS=App/Controllers
Security
- Protection against SQL Injection
- Protection against CSRF
- Protection against XSS
- Data validation
- Secure session management
- Encryption of sensitive data
License
This project is licensed under the MIT License - see the LICENSE file for details.