Larite Framework
Lightweight. Laravel-Inspired. 100% Custom. A modern PHP MVC framework built from scratch for developers who demand performance and simplicity.
Introduction
Larite is a lightweight PHP MVC framework inspired by Laravel, but built entirely from scratch. It's designed for developers who love Laravel's syntax and structure but want full control, performance, and simplicity.
Larite is not a Laravel clone. It's a fresh micro-framework for small to medium web apps, dashboards, admin panels, and educational projects — without Composer bloat or hidden magic.
Why Choose Larite?
- Laravel-style routing, middleware, ORM, relations, query builder, eager loading, and validation
- Laravel-style migrations and seeders
- Custom-built DI container and lifecycle
- CSRF protection and input sanitization
- Auth scaffolding, flash messages, old inputs
- CLI commands for models, controllers, auth scaffolding and migrations
- Useful helpers: dd, dump, asset, url, mail, pagination and many more
- Default exception handler
- Simple, extendable, and easy to read/learn
Security Features
- CSRF Protection:
<?php csrf_field(); ?>inside<form> - Output escaping:
<?= e($value) ?> - File upload validation
- Automatic input sanitization
Core Features
- Auth Scaffolding (
Route::authenticate()) - Pagination:
paginate()/simplePaginate() - Old input repopulation:
old('field') - Flash messages and session management
- Built-in validation system
- Database migrations and seeding
- Blade template engine support (for views)
Installation
Before you start, make sure you have PHP 8.3 or above and Composer installed on your system.
- Create a new project using Composer:
composer create-project larite/larite my-project
- Navigate into your project directory:
cd my-project
- Set up your environment:
Copy the .env.example file to .env and update your database and app configuration.
cp .env.example .env
- Run your local server:
You can now run the application using PHP’s built-in server:
php -S localhost:8000 -t public
✅ You’re all set! Open http://localhost:8000 in your browser to view your application.
Remove /public from URL
By default, Larite's index.php is inside the public directory for security. If you want to remove /public from the URL, you can do it in different ways.
- Method 1: Move
index.php&.htaccessto the root folder
Move both files from public to your project root:
index.php handles the /vendor/autoload.php and /bootstrap/app.php file paths dynamically, no changes are required inside the file.
- Method 2: .htaccess Redirect
Then edit the .htaccess to redirect requests to /public automatically:
RewriteEngine On
# Remove trailing slash
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [L,R=301]
# Send requests to the front controller
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
# Disable directory listing
Options -Indexes
# Allow direct access only to index.php, public assets
<FilesMatch "^(index\.php|favicon\.ico|robots\.txt)$">
Require all granted
</FilesMatch>
# Deny access to sensitive directories
RedirectMatch 403 ^/(app|config|migrations|logs|routes|views|bootstrap|vendor|tests|database|storage|node_modules)(/|$)
# Hide .env file
<Files .env>
Order allow,deny
Deny from all
</Files>
- Method 3: Apache Virtual Host (Recommended)
If you have server control (e.g., Laragon, XAMPP, VPS), set the document root directly to the /public folder:
<VirtualHost *:80>
ServerName larite.local
DocumentRoot "C:/laragon/www/Larite/public"
<Directory "C:/laragon/www/Larite/public">
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
- Method 4: cPanel
On cPanel hosting, simply set the document root of your domain/subdomain to the public folder:
- Go to Domains → Manage.
- Edit the document root path to point to
publicinside your project. - Save changes — no need to move any files.
✅ The most secure way is Method 3 or 4, keeping index.php inside public.
Environment Setup
Rename .env.example to .env and configure your environment:
// Application config
APP_NAME=Larite
APP_URL=http://localhost
APP_ENV=development // development, production
LOG_CHANNEL=single // daily, single
// Mail config
MAIL_DRIVER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_USERNAME=example@gmail.com
MAIL_PASSWORD=secret
MAIL_PORT=587
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=example@gmail.com
MAIL_FROM_NAME=Larite
// Database config
APP_ENV=development
DB_HOST=localhost
DB_DATABASE=Larite
DB_USERNAME=root
DB_PASSWORD=secret
AUTH_TABLE=users
APP_ENV=production to hide error output in production.
Configuration Files
Larite uses these main configuration files located in the config/ directory:
app.php
<?php
// Application configuration settings
return [
/*
|--------------------------------------------------------------------------
| Application Name
|--------------------------------------------------------------------------
| This value is the name of your application.
*/
'name' => env('APP_NAME', 'Larite'),
/* Application URL for console commands */
'url' => env('APP_URL', 'http://localhost'),
/* Environment mode: development or production */
'app_env' => env('APP_ENV', 'development'),
/* Default timezone */
'timezone' => 'Asia/Karachi',
/* Authentication table */
'table' => env('AUTH_TABLE', 'users'),
/* Logging channel (daily, single)*/
'log_channel' => env('LOG_CHANNEL') ?: 'single',
];
database.php
<?php
// Database configuration
return [
/* Default database connection */
'db_connection' => env('DB_CONNECTION', 'mysql'),
/* Database name */
'db_database' => env('DB_DATABASE', 'larite'),
/* Database username */
'db_username' => env('DB_USERNAME', 'root'),
/* Database host */
'db_host' => env('DB_HOST', 'localhost'),
/* Database password */
'db_password' => env('DB_PASSWORD', ''),
];
providers.php
This array lists all of the service providers that will be automatically loaded on the request to your application. Feel free to add your own services to this array to grant expanded functionality to your app.
<?php
// Application service providers configuration
return [
/* App Service Provider: registers core services like cache, mail, etc. */
App\Providers\AppServiceProvider::class,
/* Route Service Provider: registers all route files (web.php, api.php, etc.) */
App\Providers\RouteServiceProvider::class,
/* Custom Service Providers: add your new providers here */
];
mail.php
<?php
// Mail configuration
return [
/* Mail driver (smtp, sendmail, etc.) */
'driver' => env('MAIL_DRIVER', 'smtp'),
/* SMTP server host */
'host' => env('MAIL_HOST', 'smtp.gmail.com'),
/* SMTP username */
'username' => env('MAIL_USERNAME'),
/* SMTP pssword */
'password' => env('MAIL_PASSWORD'),
/* SMTP port */
'port' => env('MAIL_PORT', '587'),
/* From address & name */
'from' => [
'address' => env('MAIL_FROM_ADDRESS', 'example@gmail.com'),
'name' => env('MAIL_FROM_NAME', 'Larite'),
],
/* Mail encryption */
'encryption' => env('MAIL_ENCRYPTION', 'tls'),
];
Pagination
Introduction
Larite provides pagination out of the box. You can paginate query results, loop through paginated items, and render navigation links. Both the new New style pagination and the old-style pagination syntax are supported for backward compatibility.
New Pagination (Recommended)
Use the pagination for cleaner and more expressive syntax:
// In your controller
public function index()
{
$users = User::paginate(10);
return view('users', compact('users'));
}
<!-- In your Blade template -->
@foreach($users as $user)
<p>{{ $user->name }}</p>
@endforeach
<!-- Laravel-style pagination links -->
{!! $users->links() !!}
Old-Style Pagination (Still Supported)
For projects using the older syntax, the classic pagination API is still available.
// In your controller (old style)
public function index()
{
$users = User::paginate(10);
return view('users', compact('users'));
}
<!-- In your Blade template (old style) -->
@foreach($pagination as $user)
<p>{{ $user->name }}</p>
@endforeach
<!-- Old-style pagination links -->
{!! $render->links !!}
Available Methods
The paginate object provides helpful methods and properties:
Methods
links()– Returns pagination HTML (Laravel-style)render()– Alias forlinks()toArray()– Returns pagination data as an arraycount()– Number of items on the current pagegetIterator()– Allows foreach iteration over items
Properties
data– Collection of items for the current pagecurrent_page– Current page numberper_page– Items per pagetotal– Total number of itemslast_page– Last page numberfrom– Starting item indexto– Ending item indexfirst_page_url– URL for the first pagelast_page_url– URL for the last pagenext_page_url– URL for the next pageprev_page_url– URL for the previous pagepath– Base path for pagination
Blade Templating
Introduction
Blade is Larite’s powerful templating engine. It provides clean syntax for writing views with template inheritance, control structures, loops, and even custom directives.
Template Inheritance
Create a base layout and extend it in child views:
<!-- views/layouts/app.blade.php -->
<!doctype html>
<html>
<body>
@yield('content')
</body>
</html>
<!-- views/home.blade.php -->
@extends('layouts.app')
@section('content')
<h1>Welcome Home</h1>
@endsection
Echoing Data
Safely output variables or allow raw HTML:
<!-- Escaped output -->
{{ $name }}
<!-- Raw HTML output -->
{!! $htmlContent !!}
Control Structures
Blade provides shortcuts for common PHP control structures:
@if($user->isAdmin())
<p>Welcome, Admin!</p>
@else
<p>Hello, User!</p>
@endif
Loops
Loop through data collections easily:
@foreach($users as $user)
<li>{{ $user->name }}</li>
@endforeach
Including Partials
Include sub-views with the @include directive:
@include('partials.header')
Using Raw PHP
You can run raw PHP code with @php (both single-expression and block forms):
<!-- Inline PHP expression -->
@php(time())
<!-- Multi-line PHP block -->
@php
$counter = 0;
$message = 'Hello World';
@endphp
Error Handling
Use @error / @enderror to show validation error messages for a specific field:
@error('email')
<div class="alert alert-danger">{{ error('email') }}</div>
@enderror
Push and Stack
Use @push / @endpush to push content into a named stack, and @stack to render it:
@push('scripts')
<script src="/example.js"></script>
@endpush
@stack('scripts')
CSRF Protection
Use @csrf to include a CSRF token in your forms:
<form method="POST" action="/submit">
@csrf
<button type="submit">Submit</button>
</form>
Checking Variables
Use @isset / @endisset and @empty / @endempty for conditional checks:
@isset($records)
<p>Records found.</p>
@endisset
@empty($records)
<p>No records.</p>
@endempty
Unless Statement
The @unless directive is the inverse of @if:
@unless($user->isAdmin())
<p>You are not an admin.</p>
@endunless
Switch Statement
Blade also supports PHP-style switch statements:
@switch($role)
@case('admin')
<p>Admin Panel</p>
@break
@case('user')
<p>User Dashboard</p>
@break
@default
<p>Guest View</p>
@endswitch
Debugging
Use @dump and @dd for quick debugging (prints and optionally stops execution):
@dump($user)
@dd($settings)
Custom Directives
You can register your own Blade directives in AppServiceProvider or a dedicated BladeServiceProvider. Example below adds a simple @datetime directive:
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Blade;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
Blade::directive('datetime',
function ($expression) {
return "<?php echo (".$expression.") ? date('Y-m-d H:i', strtotime(".$expression.")) : ''; ?>";
}
);
}
}
Usage:
@datetime($user->created_at)
Routing
Basic Route Definition
Define routes in routes/web.php:
Route::get('/', [HomeController::class, 'index']);
Route Groups
Group routes with common attributes:
Route::group(['prefix' => 'admin', 'middleware' => ['auth']], function () {
Route::get('dashboard', [DashboardController::class, 'index']);
});
You can also use the prefix() and middleware() methods for a more fluent syntax:
Route::prefix('api')->group(function () {
Route::get('/users', [UserController::class, 'index']);
});
Or combine prefix() with middleware() for chained grouping:
Route::prefix('api')
->middleware('api')
->group(function () {
Route::get('/users', [UserController::class, 'index']);
});
Named Routes
Named routes allow you to generate URLs for specific routes using a name instead of hardcoding the URL. This makes your application more maintainable and flexible.
Defining Named Routes
// In routes/web.php
Route::get('/', [HomeController::class, 'index'])->name('home.index');
Route::get('/about', [HomeController::class, 'about'])->name('about');
Route::get('/users/{id}', [UserController::class, 'show'])->name('users.show');
Using Named Routes
// Generate URL for a named route
echo route('home.index'); // Outputs: /
// Generate URL with parameters
echo route('users.show', ['user' => 5]); // Outputs: /users/5
// Use in views
<a href="<?= route('home.index') ?>">Home</a>
<a href="<?= route('users.show', ['user' => 1]) ?>">View User</a>
// Use in redirects
redirect(route('users.index'));
redirect(route('users.show', ['user' => 5]));
Resource Routes
Resource routes provide a quick way to create all the necessary routes for a resource controller. A resource controller typically handles CRUD operations for a model.
Defining Resource Routes
// In routes/web.php
Route::resource('users', UserController::class);
This single line creates the following routes:
| Method | URI | Name | Action | Description |
|---|---|---|---|---|
GET |
/users |
users.index |
index() |
Display a listing of the resource |
GET |
/users/create |
users.create |
create() |
Show the form for creating a new resource |
POST |
/users |
users.store |
store() |
Store a newly created resource |
GET |
/users/{user} |
users.show |
show() |
Display the specified resource |
GET |
/users/{user}/edit |
users.edit |
edit() |
Show the form for editing the specified resource |
PUT/PATCH |
/users/{user} |
users.update |
update() |
Update the specified resource |
DELETE |
/users/{user} |
users.destroy |
destroy() |
Remove the specified resource |
Route Parameters
Route parameters allow you to capture segments of the URI within your route definition.
Required Parameters
Route::get('/user/{id}', function ($id) {
return 'User ' . $id;
});
Route::get('/posts/{post}/comments/{comment}', function ($postId, $commentId) {
// Handle multiple parameters
});
Optional Parameters
Route::get('/user/{name?}', function ($name = 'Guest') {
return $name;
});
Extending Routes
Register route files in `app/Providers/RouteServiceProvider.php`
public function register(): void
{
$this->routes(function () {
return [
'routes/web.php', // default route file for web routes
'routes/api.php', // default route file for api routes
// Add more route files here...
];
});
}
$this->routes() automatically.
Middleware System
You can create new middleware by running the below command:
php larite make:middleware Authenticate
Register middleware in App\Kernel.php:
public $routeMiddleware = [
'auth' => Authenticate::class,
'web' => WebMiddleware::class,
'api' => ApiMiddleware::class,
];
You can define middleware in any controller's constructor:
$this->middleware(['auth', 'web']);
Middleware per route
Route::get('/profile', [ProfileController::class, 'index'])->middleware('auth');
Middleware with Route Groups
Route::group(['middleware' => ['auth']], function () {
Route::get('dashboard', [DashboardController::class, 'index']);
});
Middleware chaining with API routes
Route::middleware('api')
->group(function () {
Route::get('/users', [UserController::class, 'index']);
});
Service Providers
Service Providers in Larite are the central place to register application services, such as caching, mail, routes, and custom services. All providers are listed in the config/providers.php file.
How to register a Service Provider
To register a service provider, add its class to the providers.php array. Larite will automatically load and resolve them.
<?php
// Providers configuration file
return [
/* App Service Provider: core application services */
App\Providers\AppServiceProvider::class,
/* Route Service Provider: registers all route files */
App\Providers\RouteServiceProvider::class,
/* Custom Service Providers: add new providers here */
];
How to create a new Service Provider
You can create a new service provider using the Larite CLI:
php larite make:provider CacheProvider
This will generate a new provider class CacheProvider inside app/Providers. You can then add its class to config/providers.php to have it automatically loaded during application bootstrapping.
Custom Facades
Facades in Larite provide a simple static interface to services registered in the container, such as cache, mail, or custom services.
Creating a Facade
namespace App\Facades;
use Larite\Support\Facade;
class Cache extends Facade
{
// Define the service key this facade will resolve
protected static functiongetFacadeAccessor()
{
return 'cache';
}
Registering the Facade
To register a facade, you first need to bind the underlying service in a service provider:
<?php
namespace App\Providers;
use App\Services\CustomeService;
use Larite\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
// Bind the service to the container
public function register(): void
{
app()->bind('cache', CustomeService::class); // here define facade name and the related class to bind
}
}
Using the Facade
use App\Facades\Cache;
// Call methods on the service via the facade
Cache::put('key', 'value', 3600);
$value = Cache::get('key');
Facades allow you to use services with a clean static interface without manually resolving them from the container.
Make sure the service is bound in a provider and the facade class defines the correct $service key.
Validation
Larite provides a Validator facade to validate request data easily.
You can define rules for each field, check if validation fails, and redirect back with errors.
Supported rules include required, email, unique, min, max, and more.
Example: validating a registration request with email and password fields.
If validation fails, the user is redirected back with the validation errors.
use Lumite\Support\Facades\Validator;
$rules = [
'email' => 'required|email|unique:users,email',
'password' => 'required|min:6|max:20'
];
$validation = Validator::validate($request->all(), $rules);
if ($validation->fails()) {
return redirect()->back()->withErrors($validation->errors());
}
Mail Support
Here is the Mail facade. Mails can be sent in Larite as shown below:
use Lumite\Support\Facades\Mail;
Mail::send('mail', [], function($mail) {
$mail->subject('Welcome');
$mail->to('admin@example.com', 'Admin');
$mail->from('noreply@example.com', 'Larite');
$mail->attachment('path/to/file.pdf');
});
Migrations
Create a new migration file
php Larite make:migration create_users_table
This will generate a file in the database/migrations/ directory.
Define the schema
Each migration file contains up() and down() methods. You can define your table structure using the Blueprint class inside the up() method:
Migrate::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('name')->nullable();
$table->string('email')->unique();
$table->string('password');
$table->timestamps();
});
Rollback the table
Migrate::dropIfExists('users');
Database Seeding
Larite supports seeders for populating your database with initial or dummy data.
Create a Seeder
php Larite make:seeder AdminSeeder
Example Seeder
use App\Models\User;
class AdminSeeder extends Seeder
{
public function run(): void
{
User::updateOrCreate([
'email' => 'admin@example.com',
], [
'name' => 'Admin',
'password' => bcrypt('password'),
'role' => 'admin',
]);
}
}
Register the Seeder
After creating a seeder, register it inside DatabaseSeeder so it will be executed when you run the seeding command:
class DatabaseSeeder extends Seeder
{
public function run(): void
{
// Call your custom seeders here
$this->call([
AdminSeeder::class
]);
}
}
Run All Seeders
php Larite db:seed
We can run specific seeder like below
php Larite db:seed--class=AdminSeeder
Model Attributes
namespace App\Models;
use Lumite\Database\BaseModel;
class User extends BaseModel
{
// Disable timestamps in fetch/create/update queries
protected $timestamps = false;
// Hide specific fields from query results
protected $hidden = ['password'];
}
You can manage timestamps and hidden fields directly in your model:
$timestamps = false;→ Disables automaticcreated_atandupdated_atfields in ORM operations.$hidden→ An array of attributes you want excluded from query results (e.g., passwords).
Queries & ORM
Larite offers an ORM for interacting with your database using expressive and chainable syntax.
Fetching Data (ORM)
// Truncate table
User::truncate();
// Get all users
$users = User::get();
// Find a specific user by ID
$user = User::find(1);
// Find a specific user by ID or fail
$user = User::findOrFail(1);
// Get first user
$user = User::first();
// Get first or fail user
$user = User::firstOrFail();
// Get users with conditions
$activeUsers = User::where('status', 'active')->get();
// Get user with array conditions
$user = User::where(['age' => '20'])->get();
// First matching result
$user = User::where('email', '=', 'john@example.com')->first();
// Get specific voloumn's values directly
$userName = User::value('name');
// Create a user (returns user object)
$user = User::create([
'name' => 'Test User',
'email' => 'user@gmail.com',
'password' => bcrypt('123456'),
]); // user object
// Insert a user (for single record)
$user = User::insert([
'name' => 'Test User',
'email' => 'user@gmail.com',
'password' => bcrypt('123456'),
]); // true, false
// Insert multiple users (bulk insert)
$users = User::insert([
[
'name' => 'Test User 1',
'email' => 'user1@gmail.com',
'password' => bcrypt('123456'),
],
[
'name' => 'Test User 2',
'email' => 'user2@gmail.com',
'password' => bcrypt('123456'),
],
[
'name' => 'Test User 3',
'email' => 'user3@gmail.com',
'password' => bcrypt('123456'),
],
]); // true, false
// Insert a user and get the inserted ID
$user = User::insertGetId([
'name' => 'Test User',
'email' => 'user@gmail.com',
'password' => bcrypt('123456'),
]); // return user id
Query Builder (DB Facade)
use Lumite\Support\Facades\DB;
// Truncate table
DB::table('users')->truncate();
// Get all users
$users = DB::table('users')->get();
// Get first user
$user = DB::table('users')->first();
// Get first user or fail
$user = DB::table('users')->firstOrFail();
// Find user with id
$user = DB::table('users')->find('id');
// Find or fail user with id
$user = DB::table('users')->findOrFail('id');
// Get single user name
$userName = DB::table('users')->value('name');
// Paginate results
$users = DB::table('users')->paginate(10);
// Get users with conditions
$activeUsers = DB::table('users')->where('status', 'active')->get();
// Create a new user (returns user object)
$user = DB::table('users')->create([
'name' => 'Test User',
'email' => 'user@gmail.com',
'password' => bcrypt('123456'),
]); // user object
// Insert a user (for singel record)
$user = DB::table('users')->insert([
'name' => 'Test User',
'email' => 'user@gmail.com',
'password' => bcrypt('123456'),
]); // true, false
// Insert multiple users (bulk insert)
$users = DB::table('users')->insert([
[
'name' => 'Test User 1',
'email' => 'user1@gmail.com',
'password' => bcrypt('123456'),
],
[
'name' => 'Test User 2',
'email' => 'user2@gmail.com',
'password' => bcrypt('123456'),
],
[
'name' => 'Test User 3',
'email' => 'user3@gmail.com',
'password' => bcrypt('123456'),
],
]); // true, false
// Insert a user and get inserted ID
$user = DB::table('users')->insertGetId([
'name' => 'Test User',
'email' => 'user@gmail.com',
'password' => bcrypt('123456'),
]); // user id
Where Clauses
Larite supports a wide variety of where clauses for filtering data. You can use these with Models (ORM) or directly with the DB facade.
where clauses and use variations like whereYear, whereMonth, whereBetween, orWhere, etc.
ORM (Model) Examples
// Basic where
$users = User::where('status', 'active')->get();
// orWhere
$users = User::where('status', 'active')->orWhere('role', 'admin')->get();
// whereIn
$users = User::whereIn('id', [1, 2, 3])->get();
// whereNull / whereNotNull
$users = User::whereNull('deleted_at')->get();
$users = User::whereNotNull('email_verified_at')->get();
// whereDate / orWhereDate
$users = User::whereDate('created_at', '2025-08-13')->get();
$users = User::orWhereDate('updated_at', '2025-08-01')->get();
// whereMonth / whereYear
$users = User::whereMonth('created_at', 8)->get();
$users = User::whereYear('created_at', 2025)->get();
// whereDay / whereDayOfWeek / whereTime
$users = User::whereDay('created_at', 23)->get();
$users = User::whereDayOfWeek('created_at', 2)->get();
$users = User::whereTime('created_at', 12:00:00)->get();
// whereBetween / whereNotBetween / orWhereBetween / orWhereNotBetween
$users = User::whereBetween('age', [18, 25])->get();
$users = User::whereNotBetween('age', [26, 35])->get();
$users = User::orWhereBetween('score', [50, 100])->get();
$users = User::orWhereNotBetween('score', [0, 49])->get();
// having clause
$users = User::having('total', '>', 100)->get();
Query Builder (DB Facade) Examples
use Lumite\Support\Facades\DB;
// Basic where
$users = DB::table('users')->where('status', 'active')->get();
// whereIn
$users = DB::table('users')->whereIn('id', [1,2,3])->get();
// whereNull / whereNotNull
$users = DB::table('users')->whereNull('deleted_at')->get();
// whereDate / whereMonth / whereYear / whereDay / whereDayOfWeek / whereTime
$users = DB::table('users')->whereDate('created_at', '2025-08-13')->get();
$users = DB::table('users')->whereMonth('created_at', 8)->get();
$users = DB::table('users')->whereYear('created_at', 2025)->get();
$users = DB::table('users')->whereDay('created_at', 22)->get();
$users = DB::table('users')->whereDayOfWeek('created_at', 2)->get();
$users = DB::table('users')->whereTime('created_at', 12:00:00)->get();
// whereBetween / whereNotBetween
$users = DB::table('users')->whereBetween('age', [18,25])->get();
$users = DB::table('users')->orWhereNotBetween('score', [0,49])->get();
// having clause
$users = DB::table('users')->having('total', '>', 100)->get();
Defining Relationships
One-to-One
public function profile()
{
return $this->hasOne(Profile::class, 'user_id');
}
One-to-Many
public function posts()
{
return $this->hasMany(Post::class, 'user_id');
}
Inverse (Belongs To)
public function user()
{
return $this->belongsTo(User::class, 'user_id');
}
Belongs To Many
public function roles()
{
return $this->belongsToMany(Role::class,
'role_user', 'user_id',
'role_id', 'id',
'id');
}
CLI Commands
php Larite make:auth auth
php Larite make:model User
php Larite make:controller PostController
php larite make:controller PostController --resource
php Larite make:migration create_posts_table
php larite make:seeder AdminSeeder // To create seeder
php larite db:seed // to run all seeders
php larite db:seed --class=AdminSeeder // to run specific seeder
php Larite make:provider CacheProvider
php Larite migrate
php Larite migrate:rollback
php larite make:middleware Authenticate
php larite make:command SyncUser
php larite schedule:run
php Larite route:list
php Larite route:list --method=GET // to list specific method's routes
php Larite route:list --method=POST // to list specific method's routes
Task Scheduler
Larite supports a simple scheduler.
public function schedule(Schedule $schedule): void
{
$schedule->command(SyncUser::class)->everyMinute();
}
Supported Schedule Methods
| Method | Cron Expression | Description |
|---|---|---|
everyMinute() |
* * * * * |
Every minute |
everyFiveMinutes() |
*/5 * * * * |
Every 5 minutes |
everyTenMinutes() |
*/10 * * * * |
Every 10 minutes |
everyFifteenMinutes() |
*/15 * * * * |
Every 15 minutes |
everyThirtyMinutes() |
0,30 * * * * |
Every 30 minutes |
hourly() |
0 * * * * |
Once per hour |
daily() |
0 0 * * * |
Once a day at midnight |
twiceDaily() |
0 0,12 * * * |
Once at 12:00 AM and once at 12:00 PM every day |
weekdays() |
0 0 * * * |
Once a week, at midnight (00:00) every weekday — Monday through Friday. |
weekends() |
0 0 * * 6,0 |
Once in weekends, at midnight (00:00) every Saturday and Sunday |
weekly() |
0 0 1 * * |
Once a week (Sunday midnight) |
monthly() |
0 0 * * 0 |
Once a month |
yearly() |
0 0 * * 0 |
Once a year |
Exception Handling
Your global exception handling logic is located at:
// app/Exceptions/Handler.php
protected bool $exception = true; // true, false
public function handle(Throwable $e)
{
$this->render($e, function (Throwable $e) {
if ($e instanceof NotFoundException) {
response()->json(['NotFoundException' => $e->getMessage()], 404);
} elseif ($e instanceof ValidationException) {
response()->json(['ValidationException' => $e->getErrors()], 422);
} elseif ($e instanceof AuthException) {
response()->json('Unauthenticated.', 401);
} else {
response()->json(['Exception' => 'Something went wrong.'], 500);
}
});
return true;
}
Larite Welcome Page
Here is the Larite welcome page view.
ChatGPT Review
Here is the ChatGPT comparison after reviewing the complete Larite's code.
Performance Benchmark
We ran backend performance benchmarks to compare Larite and Laravel under the same environment (Laragon, Apache 2.4, PHP 8.x, MySQL 8.x). The goal was to measure relative framework overhead, not absolute production values.
Test Setup
- Tool: ApacheBench (
ab) - Requests: 1,000
- Concurrency: 50 users
- Database Query: Fetching 20,000 records
Results
| Metric | Larite | Laravel |
|---|---|---|
| Requests per second | 14.24 | 7.24 |
| Average latency (ms) | 70 | 138 |
| Peak memory usage | ~27 MB | ~48 MB |
| Execution time (query load) | 328 ms | 364 ms |
Conclusion
In concurrency benchmarks (1,000 requests with 50 users), Larite handled double the throughput of Laravel while maintaining lower latency and a smaller memory footprint. This confirms Larite as a truly lightweight alternative for building high-performance PHP applications.
Note: These benchmarks were conducted on localhost (Laragon, Windows). Results may vary based on server configuration and production environment. The comparison demonstrates relative framework efficiency under identical conditions.
Contribute
Want to improve this Laravel-style lightweight framework? Submit a PR or open an issue. All contributions are welcome!