mii
mii copied to clipboard
MII is a web application framework with expressive, elegant syntax
How to use
- About
- How to Install
- Define Route
- Route Parameter
- Multiple Route Parameters
- Request
- Database and Migration
- Database Seeder
- Binding Interface to Service Class
- Controller Method Dependency Injection
- Constructor Dependency Injection
- Model
- Database Connection
- Views
- Global Middleware
- Route Middleware
- Custom Blade Directivee
- From Validation
- CSRF Token
- Collection & Macro
- Session Flash Message
- Log
About
MII, A basic PHP MVC framework designed in a way that you feel like you are working in a Laravel application. In this framework, you will get all the basic features of a web application like routing, middleware, dependency injection, eloquent relationship, model, blade template engine interface injection, and many more. Test it and if you like, please give it a star.
How to Install
We can easily set up and install this application with a few steps. Before using this application, a minimum PHP 8.1 version is needed.
- Step 1:
git clone https://github.com/techmahedy/mi.gitor download this application - Step 2: Go to the project directory with this command
cd miand runcomposer update - Step 3: Copy
.env.exampleto.env - Step 4: Start the development server by running this command
php -S localhost:8000
Define Route
To define the route, navigate to this file and update
routes/web.php
<?php
use App\Core\Route;
use App\Http\Controllers\ExampleController;
Route::get('/', [ExampleController::class, 'index']);
Route::get('/about', [ExampleController::class, 'about']);
Binding Interface to Service Class
To bind the interface with your service class, just update App\Providers\AppServiceProvider.php.
<?php
namespace App\Providers;
use App\Core\Container;
use App\Services\StripePaymentService;
use App\Contracts\PaymentServiceContract;
class AppServiceProvider extends Container
{
public function register()
{
//Remember, the global request() helper is available here. You can get input value here like
//request()->input('payment_type')
$this->bind(PaymentServiceContract::class, StripePaymentService::class);
//or
$this->singleton(PaymentServiceContract::class, StripePaymentService::class);
}
}
Dependency Injection
Now look at that, how you can use dependency injection.
<?php
namespace App\Http\Controllers;
use App\Models\User;
use App\Models\Post;
use App\Core\Request;
use App\Contracts\PaymentServiceContract;
class ExampleController extends Controller
{
/**
* You can pass as many class as you want as parameter
*/
public function index(
Request $request, //class dependency injection
User $user, //class dependency injection
Post $post, //class dependency injection
PaymentServiceContract $payment //interface dependency injection
) {
//Use any eloquent query of Laravel
}
public function about()
{
return view('about.index');
}
}
Constructor Dependency Injection
Now look at that, how you can use dependency injection using constructor.
<?php
namespace App\Http\Controllers;
use App\Models\User;
use App\Models\Post;
use App\Core\Request;
use App\Contracts\PaymentServiceContract;
class ExampleController extends Controller
{
/**
* Look at that, we are passing interface, models. How cool it is
*/
public function __construct(
public PaymentServiceContract $payment,
public User $user,
public Post $post,
) {}
}
Model
Now look at Model, how you can use it
use App\Core\Model;
class User extends Model
{
/**
* Use any features of Laravel.
*/
}
Database Connection
Connect your database like that, just pass your credentials to .env
DB_CONNECTION=mysql
DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=
DB_USERNAME=
DB_PASSWORD=
And you can print configuration value like $_ENV['DB_CONNECTION'] or you can use env('DB_CONNECTION')
Views
To work with views, default view file path inside resources/views. Now passing data with views like
<?php
use App\Core\Route;
use App\Core\Application;
Route::get('/', function () {
$version = Application::VERSION;
return view('welcome', compact('version')); //for nested folder file view: home.index
});
This will load welcome.blade.php file. We can print this value like
<h1>{{ $version }}</h1>
Avaiable blade systex
@section('looping-test')
<p>Let's print odd numbers under 50:</p>
<p>
@foreach($numbers as $number)
@if($number % 2 !== 0)
{{ $number }}
@endif
@endforeach
</p>
@endsection
For mastering template
@include('shared.header')
<body>
<div id="container">
<h3>Welcome to <span class="reddish">{{ $title }}</span></h3>
<p>{{ $content }}</p>
<p>Master file</p>
@yield('looping-test')
</div>
@include('shared.footer')
</body>
You can use any blade systex as you want like laravel framework
Route Parameters
You can pass single or multiple parameter with route as like below
<?php
use App\Core\Route;
use App\Http\Controllers\ProfileController;
Route::get('/user/{id}', [ProfileController::class, 'index']);
Now accept this param in your controller like:
<?php
namespace App\Http\Controllers;
class ProfileController extends Controller
{
public function index($id)
{
return $id;
}
}
Multiple Route Parameters
You can pass multiple parameter with route as like below
<?php
use App\Http\Controllers\ProfileController;
Route::get('/user/{id}/{username}', [ProfileController::class, 'index']);
Now accept this multiple param in your controller like:
<?php
namespace App\Http\Controllers;
class ProfileController extends Controller
{
public function index($id, $username)
{
return $id;
return $username;
}
}
Request
Request is most important thing when we work in a web application. We can use Request in this application like
<?php
namespace App\Http\Controllers;
use App\Core\Request;
class ExampleController extends Controller
{
public function store(Request $request)
{
//asume we have a url like http://www.example.com/?name=mahedi. Now we can check.
if($request->has('name')){
}
//We can also check form request data like
if($request->has('name') && $request->has('email')){
}
//Now get the value from request like:
$name = $request->input('name');
$email = $request->input('email');
//You can also use global request() helper like:
$name = request()->input('name');
//or
if(request()->has('name')){
}
//get all the input as an array
$input = $request->all();
dd($input);
}
}
Global Middleware
We can define multiple global middleware. To define global middleware, just update the App\Http\Kernel.php file's $middleware array as like below
<?php
/**
* Application global middleware
*/
public $middleware = [
\App\Http\Middleware\ExampleMiddleware::class,
];
Now update your middleware like
<?php
namespace App\Http\Middleware;
use Closure;
use App\Core\Request;
use App\Core\Middleware\Contracts\Middleware;
class ExampleMiddleware implements Middleware
{
public function __invoke(Request $request, Closure $next)
{
/**
* Code goes here
*/
return $next($request);
}
}
Route Middleware
We can define multiple route middleware. To define route middleware, just update the App\Http\Kernel.php file's $routeMiddleware array as like below
<?php
/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* @var array<string, class-string|string>
*/
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
];
And update your route like:
<?php
use App\Core\Route;
use App\Http\Controllers\ProfileController;
Route::get('/', [ProfileController::class,'index'])->middleware('auth');
Now update your middleware like
<?php
namespace App\Http\Middleware;
use Closure;
use App\Core\Request;
use App\Core\Middleware\Contracts\Middleware;
class Authenticate implements Middleware
{
/**
* handle.
*
* @param Request $request
* @param Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
/**
* code
*/
return $next($request);
}
}
Custom Blade Directive
We can define custom blade directive. To define it, update App\Providers\AppServiceProvider.php as like below
<?php
namespace App\Providers;
use App\Core\Container;
class AppServiceProvider extends Container
{
public function register()
{
$this->directive('capitalize', function ($text) {
return "<?php echo strtoupper($text) ?>";
});
}
}
And now we can call it in a blade file like
{{ capitalize('hello') }}
From Validation
We can validate from and can show error message in blade file very easily. To validate from , just assume we have two routes
<?php
use App\Core\Route;
use App\Http\Controllers\ExampleController;
Route::get('/register', [ExampleController::class, 'index']);
Route::post('/register', [ExampleController::class, 'store']);
And now we can update App\Http\Controllers\ExampleController.php like
<?php
namespace App\Http\Controllers;
use App\Core\Request;
use App\Core\Controllers\Controller;
class ExampleController extends Controller
{
public function index()
{
return view('user.index');
}
public function store(Request $request)
{
$request->validate([
'email' => 'required|email|unique:user|min:2|max:100', //unique:user -> here [user] is model
'password' => 'required|min:2|max:100',
]);
//save the data
return redirect()->url('/test'); //or redirect()->back()
}
}
Now update the resources/user/index.blade.php like
<!-- Showing All Error Messages -->
@if (session()->has('errors'))
@foreach (session()->get('errors') as $error)
@foreach ($error as $item)
<li>{{ $item }}</li>
@endforeach
@endforeach
@endif
<form action="/register" method="post">
@csrf
<div class="mb-3">
<label for="exampleInputEmail1" class="form-label">Email address</label>
<input type="email" class="form-control" name="email">
<!-- Show Specific Error Message -->
@if (session()->has('email'))
{{ session()->get('email') }}
@endif
</div>
<div class="mb-3">
<label for="exampleInputPassword1" class="form-label">Password</label>
<input type="password" class="form-control" name="password">
<!-- Show Specific Error Message -->
@if (session()->has('password'))
{{ session()->get('password') }}
@endif
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
CSRF Token
If you submit a post request form, then you must be provide csrf_token with your request like below, otherwise it will throw an exception error.
<form action="/" method="post">
@csrf
<input type="submit" value="submit">
</form>
Collection & Macro
Like Laravel framework, in this MII framework, you can also work with Laravel collection and you can create your own custom macro. To create a custom macro, just update service provider App\Providers\AppServiceProvider.php like:
<?php
namespace App\Providers;
use App\Core\Container;
use Illuminate\Support\Collection;
class AppServiceProvider extends Container
{
/**
* register.
*
* Register any application services.
* @return void
*/
public function register()
{
Collection::macro('toUpper', function () {
return $this->map(function ($value) {
return strtoupper($value);
});
});
}
}
And now we can use it like:
<?php
use App\Core\Route;
Route::get('/', function () {
$collection = collect(['first', 'second']);
$upper = $collection->toUpper();
return $upper; //output ["FIRST","SECOND"]
});
Session Flash Message
When we work with form submit then we need to show validation error message or success message. We can show session flash message very easily like
<?php
//Set the session flash value like
session->flash('key', 'value to be printed');
//Now you can print this value lie
if(session()->has('key')){
echo session()->get('key');
}
Log
We can easily print important messages in a log file which is located inside storage\logs\mii.log. To print a message, mii provide logger() helper function, you just need to follow this
<?php
//logger() is a global helper function
logger()->info('Hello');
Database and Migration
MII allow you to create migration. To create migration, MII uses CakePHP's phinx. So to create a migration file first you need to update the configuration file environments array like:
config.php
<?php
return [
'environments' => [
'default_migration_table' => 'phinxlog',
'your_database_name' => [
'adapter' => 'mysql',
'host' => 'localhost',
'name' => 'your_database_name',
'user' => 'your_database_username',
'pass' => 'your_database_password',
'port' => '3306'
]
]
];
Now run the below command in your project terminal like:
php vendor/bin/phinx create Post -c config.php
Here Post is the model name.
Now this command will generate a migration file in the following path with the empty change() method.
app\database\migration\20231111144423_post.php
<?php
declare(strict_types=1);
use App\Core\Migration\Migration;
final class Post extends Migration
{
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
public function change(): void
{
$table = $this->table('posts');
$table->addColumn('title', 'string', ['limit' => 20])
->addColumn('body', 'text')
->addColumn('cover_image', 'string')
->addTimestamps()
->addIndex(['title'], ['unique' => true]);
$table->create();
}
}
Now run the migration command:
php vendor/bin/phinx migrate -c config.php.
Now see the documentation of phinx Documentation to learn more.
Database Seeder
MII allow you to create database seeder file to generate fake date. To create seeder, MII uses CakePHP's phinx. So to create a seeder file first you need run below command. Assume we are going to create PostSeeder:
Now run the below command in your project terminal like:
php vendor/bin/phinx seed:create PostSeeder -c config.php
Here PostSeeder is the seeder class name.
Now this command will generate a seeder file in the following path with the empty run() method.
app\database\seeds\PostSeeder.php
<?php
declare(strict_types=1);
use Phinx\Seed\AbstractSeed;
class PostSeeder extends AbstractSeed
{
/**
* Run Method.
*
* Write your database seeder using this method.
*
* More information on writing seeders is available here:
* https://book.cakephp.org/phinx/0/en/seeding.html
*/
public function run(): void
{
//you can use fake() helper here as well as your entire application
$data = [
[
'title' => fake()->title(),
'body' => fake()->title(),
'created_at' => date('Y-m-d H:i:s'),
]
];
$posts = $this->table('posts');
$posts->insert($data)
->saveData();
// empty the table
// $posts->truncate();
}
}
Now run the seeder command:
php vendor/bin/phinx seed:run -c config.php.
Or you can run specific seeder class file lie
php vendor/bin/phinx seed:run -s PostSeeder -c config.php
Now see the documentation from phinx Documentation to learn more.