😅Build a Simple Module Laravel Project (ok)

https://github.com/balanchi/Simple-Module-Laravel-Project/tree/master

Example 1: Module Category

C:\xampp82\htdocs\lva4\Modules\Category\Database\Migrations\2023_10_23_043505_create_categories_table.php

<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
return new class extends Migration
{
  /**
   * Run the migrations.
   *
   * @return void
   */
  public function up()
  {
    Schema::create('categories', function (Blueprint $table) {
      $table->id();
      $table->string('name');
      $table->text('description');
      $table->timestamps();
    });
  }
  /**
   * Reverse the migrations.
   *
   * @return void
   */
  public function down()
  {
    Schema::dropIfExists('categories');
  }
};

C:\xampp82\htdocs\lva4\Modules\Category\Entities\Category.php

<?php
namespace Modules\Category\Entities;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Category extends Model
{
  use HasFactory;
  public $timestamps  = false;
  protected $fillable = [
    'name',
    'description'
  ];
  protected $guarded = ['id'];
  protected static function newFactory()
  {
    // return \Modules\Category\Database\factories\CategoryFactory::new();
  }
}

C:\xampp82\htdocs\lva4\Modules\Category\Http\Controllers\CategoryController.php

<?php
namespace Modules\Category\Http\Controllers;
use Illuminate\Contracts\Support\Renderable;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Modules\Category\Entities\Category;
use Modules\Category\Http\Requests\CreateCategoryRequest;
use Modules\Category\Http\Requests\UpdateCategoryRequest;
class CategoryController extends Controller
{
  /**
   * Display a listing of the resource.
   * @return Renderable
   */
  public function index()
  {
    $categories = Category::all();
    return view('category::index', compact('categories'));
  }
  /**
   * Show the form for creating a new resource.
   * @return Renderable
   */
  public function create()
  {
    return view('category::create');
  }
  /**
   * Store a newly created resource in storage.
   * @param Request $request
   * @return Renderable
   */
  public function store(CreateCategoryRequest $request)
  {
    $inputs = $request->all();
    Category::create($inputs);
    return to_route('category.index');
  }
  /**
   * Show the specified resource.
   * @param int $id
   * @return Renderable
   */
  public function show($id)
  {
    return view('category::show');
  }
  /**
   * Show the form for editing the specified resource.
   * @param int $id
   * @return Renderable
   */
  public function edit(Category $category)
  {
    return view('category::edit', compact('category'));
  }
  /**
   * Update the specified resource in storage.
   * @param Request $request
   * @param int $id
   * @return Renderable
   */
  public function update(UpdateCategoryRequest $request, Category $category)
  {
    $inputs = $request->all();
    $category->update(['name' => $inputs['name'], 'description' => $inputs['description']]);
    return to_route('category.index');
  }
  /**
   * Remove the specified resource from storage.
   * @param int $id
   * @return Renderable
   */
  public function destroy(Category $category)
  {
    $category->delete();
    return back();
  }
}

C:\xampp82\htdocs\lva4\Modules\Category\Http\Requests\CreateCategoryRequest.php

<?php
namespace Modules\Category\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class CreateCategoryRequest extends FormRequest
{
  /**
   * Get the validation rules that apply to the request.
   *
   * @return array
   */
  public function rules()
  {
    return [
      'name' => 'required|max:120|min:2|regex:/^[ا-یa-zA-Z0-9\-۰-۹ء-ي., ]+$/u',
      'description' => 'required|max:1200|min:2|regex:/^[ا-یa-zA-Z0-9\-۰-۹ء-ي., ]+$/u',
    ];
  }
  /**
   * Determine if the user is authorized to make this request.
   *
   * @return bool
   */
  public function authorize()
  {
    return true;
  }
}

C:\xampp82\htdocs\lva4\Modules\Category\Http\Requests\UpdateCategoryRequest.php

<?php
namespace Modules\Category\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class UpdateCategoryRequest extends FormRequest
{
  /**
   * Get the validation rules that apply to the request.
   *
   * @return array
   */
  public function rules()
  {
    return [
      'name' => 'required|max:120|min:2|regex:/^[ا-یa-zA-Z0-9\-۰-۹ء-ي., ]+$/u',
      'description' => 'required|max:1200|min:2|regex:/^[ا-یa-zA-Z0-9\-۰-۹ء-ي., ]+$/u',
    ];
  }
  /**
   * Determine if the user is authorized to make this request.
   *
   * @return bool
   */
  public function authorize()
  {
    return true;
  }
}

C:\xampp82\htdocs\lva4\Modules\Category\Resources\views\layouts\master.blade.php

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Module Category</title>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
  @if ($errors->any())
  <div class="text-bg-danger p-4 m-3 rounded">
    @foreach ($errors->all() as $error)
    <div>{{ $error }}</div>
    @endforeach
  </div>
  @endif
  @yield('content')
</body>
</html>

C:\xampp82\htdocs\lva4\Modules\Category\Resources\views\create.blade.php

@extends('category::layouts.master')
@section('content')
<div class="container mt-3">
  <h2>Create Category</h2>
  <form action="{{ route('category.store') }}" method="post">
    @csrf
    <div class="mb-3 mt-3">
      <label for="name">Name:</label>
      <input type="text" class="form-control" id="name" placeholder="Enter Name" name="name">
    </div>
    <div class="mb-3">
      <label for="description">Description:</label>
      <input type="text" class="form-control" id="description" placeholder="Enter Description" name="description">
    </div>
    <button type="submit" class="btn btn-primary">Submit</button>
  </form>
</div>
@endsection

C:\xampp82\htdocs\lva4\Modules\Category\Resources\views\edit.blade.php

@extends('category::layouts.master')
@section('content')
<div class="container mt-3">
  <h2>Edit Category</h2>
  <form action="{{ route('category.update', $category) }}" method="post">
    @csrf
    @method('PUT')
    <div class="mb-3 mt-3">
      <label for="name">Name:</label>
      <input type="text" class="form-control" id="name" value="{{ old('name', $category->name) }}" name="name">
    </div>
    <div class="mb-3">
      <label for="description">Description:</label>
      <input type="text" class="form-control" id="description" value="{{ old('description', $category->description) }}"
        name="description">
    </div>
    <button type="submit" class="btn btn-primary">Submit</button>
  </form>
</div>
@endsection

C:\xampp82\htdocs\lva4\Modules\Category\Resources\views\index.blade.php

@extends('category::layouts.master')
@section('content')
<div class="container mt-3">
  <h2>Category</h2>
  <div class="d-flex justify-content-end">
    <a class="btn btn-info" href="{{ route('category.create') }}">create</a>
  </div>
  <table class="table table-striped">
    <thead>
      <tr>
        <th>#</th>
        <th>Name</th>
        <th>Description</th>
        <th>Setting</th>
      </tr>
    </thead>
    <tbody>
      @foreach ($categories as $category)
      <tr>
        <td>{{ $loop->iteration }}</td>
        <td>{{ $category->name }}</td>
        <td>{{ $category->description }}</td>
        <td>
          <form class="d-inline" action="{{ route('category.destroy', $category)}}" method="post">
            @csrf
            @method('DELETE')
            <button class="btn btn-danger">Delete</button>
          </form>
          <a class="btn btn-warning" href="{{ route('category.edit', $category) }}">Edit</a>
        </td>
      </tr>
      @endforeach
    </tbody>
  </table>
</div>
@endsection

C:\xampp82\htdocs\lva4\Modules\Category\Routes\web.php

<?php
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
use Modules\Category\Http\Controllers\CategoryController;
Route::resource('category', CategoryController::class);

Example 2: Module Post

Install

C:\xampp82\htdocs\lva4\composer.json

{
  "name": "laravel/laravel",
  "type": "project",
  "description": "The Laravel Framework.",
  "keywords": [
    "framework",
    "laravel"
  ],
  "license": "MIT",
  "require": {
    "php": "^8.1",
    "guzzlehttp/guzzle": "^7.2",
    "laravel/framework": "^10.0",
    "laravel/sanctum": "^3.2",
    "laravel/tinker": "^2.8",
    "laravel/ui": "^4.2",
    "laravelcollective/html": "^6.4",
    "nwidart/laravel-modules": "^10.0",
    "spatie/laravel-medialibrary": "^10.13",
    "unisharp/laravel-filemanager": "^2.6"
  },
  "require-dev": {
    "barryvdh/laravel-debugbar": "^3.8",
    "fakerphp/faker": "^1.9.1",
    "laravel/pint": "^1.0",
    "laravel/sail": "^1.18",
    "mockery/mockery": "^1.4.4",
    "nunomaduro/collision": "^7.0",
    "phpunit/phpunit": "^10.0",
    "spatie/laravel-ignition": "^2.0",
    "sven/artisan-view": "^3.6"
  },
  "autoload": {
    "psr-4": {
      "App\\": "app/",
      "Database\\Factories\\": "database/factories/",
      "Database\\Seeders\\": "database/seeders/",
      "Modules\\": "Modules/"
    }
  },
  "autoload-dev": {
    "psr-4": {
      "Tests\\": "tests/"
    }
  },
  "scripts": {
    "post-autoload-dump": [
      "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
      "@php artisan package:discover --ansi"
    ],
    "post-update-cmd": [
      "@php artisan vendor:publish --tag=laravel-assets --ansi --force"
    ],
    "post-root-package-install": [
      "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
    ],
    "post-create-project-cmd": [
      "@php artisan key:generate --ansi"
    ]
  },
  "extra": {
    "laravel": {
      "dont-discover": []
    }
  },
  "config": {
    "optimize-autoloader": true,
    "preferred-install": "dist",
    "sort-packages": true,
    "allow-plugins": {
      "pestphp/pest-plugin": true,
      "php-http/discovery": true
    }
  },
  "minimum-stability": "stable",
  "prefer-stable": true
}

C:\xampp82\htdocs\lva4\Modules\Post\Database\Migrations\2023_10_23_061430_create_posts_table.php

<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
return new class extends Migration
{
  /**
   * Run the migrations.
   *
   * @return void
   */
  public function up()
  {
    Schema::create('posts', function (Blueprint $table) {
      $table->id();
      $table->string('name');
      $table->string('slug')->nullable();
      $table->text('content')->nullable();
      $table->string('featured_image')->nullable();
      $table->timestamps();
    });
  }
  /**
   * Reverse the migrations.
   *
   * @return void
   */
  public function down()
  {
    Schema::dropIfExists('posts');
  }
};

C:\xampp82\htdocs\lva4\Modules\Post\Entities\Post.php

<?php
namespace Modules\Post\Entities;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Post extends Model
{
  use HasFactory;
  public $timestamps  = false;
  protected $fillable = [
    'name',
    'slug',
    'content',
    'featured_image'
  ];
  protected static function newFactory()
  {
    // return \Modules\Post\Database\factories\PostFactory::new();
  }
}

C:\xampp82\htdocs\lva4\Modules\Post\Http\Controllers\PostController.php

<?php
namespace Modules\Post\Http\Controllers;
use Illuminate\Contracts\Support\Renderable;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Modules\Post\Entities\Post;
use Modules\Post\Http\Requests\PostsRequest;
class PostController extends Controller
{
  /**
   * Display a listing of the resource.
   * @return Renderable
   */
  public function index()
  {
    $posts = Post::all();
    return view('post::index', compact('posts'));
  }
  /**
   * Show the form for creating a new resource.
   * @return Renderable
   */
  public function create()
  {
    return view('post::create');
  }
  /**
   * Store a newly created resource in storage.
   * @param Request $request
   * @return Renderable
   */
  public function store(PostsRequest $request)
  {
    $inputs = $request->except('files');
    Post::create($inputs);
    return back();
  }
  /**
   * Show the specified resource.
   * @param int $id
   * @return Renderable
   */
  public function show($id)
  {
    return view('post::show');
  }
  /**
   * Show the form for editing the specified resource.
   * @param int $id
   * @return Renderable
   */
  public function edit($id)
  {
    return view('post::edit');
  }
  /**
   * Update the specified resource in storage.
   * @param Request $request
   * @param int $id
   * @return Renderable
   */
  public function update(Request $request, $id)
  {
    //
  }
  /**
   * Remove the specified resource from storage.
   * @param int $id
   * @return Renderable
   */
  public function destroy($id)
  {
    //
  }
}

C:\xampp82\htdocs\lva4\Modules\Post\Http\Requests\PostsRequest.php

<?php
namespace Modules\Post\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class PostsRequest extends FormRequest
{
  /**
   * Determine if the user is authorized to make this request.
   *
   * @return bool
   */
  public function authorize()
  {
    return true;
  }
  /**
   * Get the validation rules that apply to the request.
   *
   * @return array
   */
  public function rules()
  {
    return [
      'name' => 'required|max:191',
      'slug' => 'nullable|max:191',
      'content' => 'required',
      'featured_image' => 'required|max:191'
    ];
  }
}

C:\xampp82\htdocs\lva4\Modules\Post\Resources\views\layouts\master.blade.php

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Module Category</title>
  <script src="{{ asset('vendor/jquery/jquery-3.6.4.min.js') }}"></script>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet">
  @stack('after-styles')
</head>
<body>
  @if ($errors->any())
  <div class="text-bg-danger p-4 m-3 rounded">
    @foreach ($errors->all() as $error)
    <div>{{ $error }}</div>
    @endforeach
  </div>
  @endif
  @yield('content')
  @stack('after-scripts')
</body>
</html>

C:\xampp82\htdocs\lva4\Modules\Post\Resources\views\create.blade.php

@extends('post::layouts.master')
@section('content')
<div class="container mt-3">
  <h2>Create Category</h2>
  <form action="{{ route('post.store') }}" method="post">
    @csrf
    <div class="mb-3 mt-3">
      <label for="name">Name:</label>
      <input type="text" class="form-control" id="name" placeholder="Enter Name" name="name">
    </div>
    <div class="mb-3">
      <label for="slug">Description:</label>
      <input type="text" class="form-control" id="slug" placeholder="Enter Slug" name="slug">
    </div>
    <div class="mb-3">
      <label for="content">Description:</label>
      <textarea class="form-control" name="content" id="content" placeholder="Content" required></textarea>
    </div>
    <div class="mb-3">
      <label for="featured_image">Description:</label>
      <div class="form-group">
        <label for="featured_image">Featured Image</label> <span class="text-danger">*</span>
        <div class="input-group mb-3">
          <input class="form-control" type="text" name="featured_image" id="featured_image" placeholder="Featured Image"
            required aria-label="Image" aria-describedby="button-image">
          <div class="input-group-append">
            <button class="btn btn-info" type="button" id="button-image" data-input="featured_image"><i
                class="fas fa-folder-open"></i> Browse</button>
          </div>
        </div>
      </div>
    </div>
    <button type="submit" class="btn btn-primary">Submit</button>
  </form>
</div>
@endsection
@push('after-styles')
<!-- File Manager -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/summernote/0.8.20/summernote-lite.min.css" rel="stylesheet">
<style>
  .note-editor.note-frame :after {
    display: none;
  }
  .note-editor .note-toolbar .note-dropdown-menu,
  .note-popover .popover-content .note-dropdown-menu {
    min-width: 180px;
  }
</style>
@endpush
@push ('after-scripts')
<script type="module" src="https://cdnjs.cloudflare.com/ajax/libs/summernote/0.8.20/summernote-lite.min.js"></script>
<script type="module">
  // Define function to open filemanager window
var lfm = function(options, cb) {
  var route_prefix = (options && options.prefix) ? options.prefix : '/laravel-filemanager';
  window.open(route_prefix + '?type=' + options.type || 'file', 'FileManager', 'width=900,height=600');
  window.SetUrl = cb;
};
// Define LFM summernote button
var LFMButton = function(context) {
  var ui = $.summernote.ui;
  var button = ui.button({
    contents: '<i class="note-icon-picture"></i> ',
    tooltip: 'Insert image with filemanager',
    click: function() {
      lfm({
        type: 'image',
        prefix: '/laravel-filemanager'
      }, function(lfmItems, path) {
        lfmItems.forEach(function(lfmItem) {
          context.invoke('insertImage', lfmItem.url);
        });
      });
    }
  });
  return button.render();
};
$('#content').summernote({
  height: 120,
  toolbar: [
    ['style', ['style']],
    ['font', ['fontname', 'fontsize', 'bold', 'underline', 'clear']],
    ['color', ['color']],
    ['para', ['ul', 'ol', 'paragraph']],
    ['table', ['table']],
    ['insert', ['link', 'lfm', 'video']],
    ['view', ['codeview', 'undo', 'redo', 'help']],
  ],
  buttons: {
    lfm: LFMButton
  }
});
</script>
<script type="module" src="{{ asset('vendor/laravel-filemanager/js/stand-alone-button.js') }}"></script>
<script type="module">
  $('#button-image').filemanager('image');
</script>
@endpush

C:\xampp82\htdocs\lva4\Modules\Post\Resources\views\index.blade.php

@extends('category::layouts.master')
@section('content')
<div class="container mt-3">
  <h2>Category</h2>
  <div class="d-flex justify-content-end">
    <a class="btn btn-info" href="{{ route('post.create') }}">create</a>
  </div>
  <table class="table table-striped">
    <thead>
      <tr>
        <th>#</th>
        <th>Name</th>
        <th>Slug</th>
        <th>Featured</th>
        <th>Actions</th>
      </tr>
    </thead>
    <tbody>
      @foreach ($posts as $post)
      <tr>
        <td>{{ $loop->iteration }}</td>
        <td>{{ $post->name }}</td>
        <td>{{ $post->slug }}</td>
        <td>{{ $post->featured_image }}</td>
        <td>
          <form class="d-inline" action="{{ route('post.destroy', $post)}}" method="post">
            @csrf
            @method('DELETE')
            <button class="btn btn-danger">Delete</button>
          </form>
          <a class="btn btn-warning" href="{{ route('post.edit', $post) }}">Edit</a>
        </td>
      </tr>
      @endforeach
    </tbody>
  </table>
</div>
@endsection

C:\xampp82\htdocs\lva4\Modules\Post\Routes\web.php

<?php
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
use Modules\Post\Http\Controllers\PostController;
Route::resource('post', PostController::class);

Last updated