- About
- How to Install
- Define Route
- Route Parameter
- Multiple Route Parameters
- Request
- Database and Migration
- Database Seeder
- Database Query Builder
- 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
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.
We can easily set up and install this application with a few steps. Before using this application, a minimum PHP 8.3
version is needed.
- Step 1:
git clone https://github.com/techmahedy/mi.git
or download this application - Step 2: Go to the project directory with this command
cd mi
and runcomposer update
- Step 3: Copy
.env.example
to.env
- Step 4: Start the development server by running this command
php -S localhost:8000
To define the route, navigate to this file and update
<?php
use Mii\Route;
use App\Http\Controllers\ExampleController;
Route::get('/', [ExampleController::class, 'index']);
Route::get('/about', [ExampleController::class, 'about']);
To bind the interface with your service class, just update App\Providers\AppServiceProvider.php
.
<?php
namespace App\Providers;
use Mii\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);
}
}
Now look at that, how you can use dependency injection.
<?php
namespace App\Http\Controllers;
use App\Models\User;
use App\Models\Post;
use Mii\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');
}
}
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 Mii\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,
) {}
}
Now look at Model, how you can use it
use Mii\Model;
class User extends Model
{
/**
* Use any features of Laravel.
*/
}
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')
To work with views, default view file path inside resources/views
. Now passing data with views like
<?php
use Mii\Route;
use Mii\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>
@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
You can pass single or multiple parameter with route as like below
<?php
use Mii\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;
}
}
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 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 Mii\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);
}
}
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 Mii\Request;
use Mii\Middleware\Contracts\Middleware;
class ExampleMiddleware implements Middleware
{
public function __invoke(Request $request, Closure $next)
{
/**
* Code goes here
*/
return $next($request);
}
}
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 Mii\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 Mii\Request;
use Mii\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);
}
}
Mii has its own custom query builder for fetching database query. See the very simple example
<?php
namespace App\Http\Controllers;
use Mii\Database\DB;
use Mii\Controllers\Controller;
class ExampleController extends Controller
{
public function index(Request $request)
{
$data = DB::table('users')
->select(['id', 'name', 'email'])
->where('status', '=', 'active')
->orderBy('name')
->limit(10)
->offset(0)
->get();
return $data; // this is laravel collection. you can use any collection wrapper in this data.
}
}
We can define custom blade directive. To define it, update App\Providers\AppServiceProvider.php
as like below
<?php
namespace App\Providers;
use Mii\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') }}
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 Mii\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 Mii\Request;
use Mii\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>
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>
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 Mii\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 Mii\Route;
Route::get('/', function () {
$collection = collect(['first', 'second']);
$upper = $collection->toUpper();
return $upper; //output ["FIRST","SECOND"]
});
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');
}
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');
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:
<?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.
<?php
declare(strict_types=1);
use Mii\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.
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.
<?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.