ikechukwukalu / magicmake
A scaffolding package for an opinionated Laravel coding style.
Requires
- php: ^8.2
- illuminate/console: ^11.0|^12.0|^13.0
- illuminate/support: ^11.0|^12.0|^13.0
- symfony/console: ^7.0|^8.0
- symfony/finder: ^7.0|^8.0
Requires (Dev)
- mockery/mockery: ^1.6
- orchestra/testbench: ^9.0|^10.0|^11.0
- php-parallel-lint/php-parallel-lint: ^1.4
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^10.5|^11.5|^12.5|^13.0
Suggests
- calebporzio/sushi: Required by generated in-memory Eloquent model integrations.
- hisorange/browser-detect: Required by generated browser-detection integrations.
- ikechukwukalu/clamavfileupload: Required by generated file-upload and email-data integrations.
- ikechukwukalu/makeservice: Optional companion service generator.
- knuckleswtf/scribe: Required when generated applications publish API documentation.
- kreait/laravel-firebase: Required by generated Firebase notification integrations.
- laragear/two-factor: Required by generated two-factor authentication artifacts.
- laravel/socialite: Required by generated social authentication artifacts.
- laravel/ui: Required when using the documented Bootstrap UI setup.
- mobiledetect/mobiledetectlib: Required by generated mobile-device detection integrations.
- predis/predis: Required when generated applications use the Predis client.
- pusher/pusher-php-server: Required when generated applications use Pusher broadcasting.
- react/http: Required by generated asynchronous HTTP integrations.
- sentry/sentry-laravel: Required when generated applications use Sentry reporting.
- spatie/laravel-activitylog: Required by generated activity-log artifacts.
- spatie/laravel-permission: Required by generated roles and permissions artifacts.
- stevebauman/location: Required by generated location-detection integrations.
README
A Laravel scaffolding package for an opinionated Laravel coding style.
REQUIREMENTS
The v5.0.0 release candidate has this verified compatibility contract. It remains unpublished until a separately authorized tag and release are created:
| Laravel | PHP | Support tier |
|---|---|---|
| 11 | 8.2–8.4 | Compatibility only; security support ended March 12, 2026 |
| 12 | 8.2–8.5 | Security fixes only through February 24, 2027 |
| 13 | 8.3–8.5 | Supported; security fixes through Q1 2028 |
Every listed Laravel/PHP combination is exercised in the release-candidate CI matrix. Laravel 11 compatibility is retained for established applications, but current Composer policy blocks its known vulnerable releases by default. The legacy jobs consciously bypass resolution blocking only to execute compatibility tests and still report every advisory; Laravel 12–13 jobs retain strict blocking and audit failure. Laravel 12 receives security fixes but its general bug-fix window ended August 13, 2026. PHP 7.3 and Laravel 8–10 were previously declared without matching syntax or CI evidence and are no longer advertised. The php: ^8.2 dependency floor does not advertise unlisted future PHP releases; support is limited to the combinations in this table until their complete CI jobs pass.
Magic Make installs only the dependencies needed by the scaffolder itself. Generated authentication, notification, activity-log, permissions, browser-detection, and other optional integrations require their corresponding suggested Composer packages. Run composer suggests ikechukwukalu/magicmake and install the integrations selected for your application profile.
STEPS TO INSTALL
composer require ikechukwukalu/magicmake
SET UP LARAVEL
php artisan install:api
INIT CLASSES
To initialize prepared classes for a new laravel app. This would only run when env('APP_ENV') === local and env(MAGIC_INIT_LOCK) === false.
php artisan magic:init
Initialization resolves and displays every destination before writing. An unchanged rerun is a no-op, customized files are reported as conflicts, and route includes are appended only once. Use --dry-run to inspect the plan. Use --force only after reviewing the displayed conflicts; it explicitly replaces conflicting generated files. Optional vendor integrations are not published automatically.
Response modes
Initialization creates config/magicmake.php with the application-wide response mode. Supported modes are view, json, and auto; the default is auto.
MAGIC_RESPONSE_MODE=auto
Response mode resolution is deterministic: a method-level override takes precedence over a controller-level override, which takes precedence over the application-wide default. Request content negotiation is consulted only when the resolved mode is auto.
Generated CRUD controllers explicitly use JSON mode to preserve the established API behavior:
use Ikechukwukalu\Magicmake\Response\ResponseMode; protected string|null $responseMode = ResponseMode::JSON;
Set the controller property to ResponseMode::VIEW, ResponseMode::AUTO, or null to select a controller-wide mode or inherit the application default. The generated base-controller methods also accept a final method-level response-mode argument. View mode requires a component name; selecting it without one raises an explicit error instead of silently returning JSON.
Application services remain presentation-agnostic and return ResponseData. Controllers and response helpers decide whether that data becomes a view or JSON response.
MODEL BASED CLASSES
To generate all model based prepared classes.
php artisan magic:model UserKyc
Feature names must be single PascalCase PHP identifiers. The command preflights the model, migration, contract, repository, service, controller, requests, factory, test, and API route as one plan. A conflict prevents every write, an unchanged rerun is non-destructive, and a failed write rolls the feature back. --dry-run displays the plan and --force explicitly overwrites conflicting artifacts.
For an established project, updating the package and running only magic:model does not replace files previously created by magic:init. Existing helpers, the application base controller, and ResponseData remain unchanged, so legacy JSON behavior is preserved. Adopting the newer initialization scaffolding is a separate, explicit migration; see UPGRADE.md.
Generation profiles
--profile=standard remains the default and preserves the established Magic Make feature structure.
lean: model, migration, factory, and focused model test.standard: model, migration, contract, repository, service, controller, create/update/delete/read requests, API route, factory, and feature test.enterprise: Standard plus a dedicated service provider with repository binding. For modular targets, the provider also loads the feature route and migrations.
php artisan magic:model Invoice --profile=lean php artisan magic:model Invoice --profile=standard php artisan magic:model Invoice --profile=enterprise
Modular targets and namespaces
Use a project-relative --path to keep every selected artifact inside a feature boundary. Magic Make derives the namespace from the most specific Composer PSR-4 mapping and displays the resolved profile, target, namespace, artifacts, and file actions before writing.
php artisan magic:model Invoice \
--profile=enterprise \
--path=app/Domains/Billing
With the normal "App\\": "app/" mapping, the resolved namespace is App\Domains\Billing. An explicit namespace may be provided with --namespace; it must match the selected path. A namespace without a path derives its path only when the Composer mapping is unambiguous.
php artisan magic:model Invoice \
--profile=enterprise \
--path=app/Domains/Billing \
--namespace='App\Domains\Billing'
Modular Standard output includes a boundary-local route file but does not register it automatically. Load it from application routing or choose Enterprise and register the generated InvoiceServiceProvider in the host application. Enterprise providers register repository bindings and load their boundary-local route and migration directories. No command mutates bootstrap/providers.php or config/app.php implicitly.
To generate individual model based prepared classes.
php artisan magic:contract UserKyc --variable=userKyc --underscore=user_kyc php artisan magic:repository UserKyc --variable=userKyc --underscore=user_kyc php artisan magic:service UserKyc --variable=userKyc --underscore=user_kyc php artisan magic:controller UserKyc --variable=userKyc --underscore=user_kyc php artisan magic:createRequest UserKyc --variable=userKyc --underscore=user_kyc php artisan magic:updateRequest UserKyc --variable=userKyc --underscore=user_kyc php artisan magic:deleteRequest UserKyc --variable=userKyc --underscore=user_kyc php artisan magic:readRequest UserKyc --variable=userKyc --underscore=user_kyc php artisan magic:api UserKyc --variable=userKyc --underscore=user_kyc php artisan magic:test UserKyc --variable=userKyc --underscore=user_kyc php artisan magic:factory UserKyc --variable=userKyc --underscore=user_kyc
Note
Add this to config/api.php.
'paginate' => [ ... 'user_kyc' => [ 'pageSize' => 10, ], ],
Add this to app/Providers/RepositoryServiceProvider.php.
use App\Contracts\UserKycRepositoryInterface; use App\Repositories\UserKycRepository; public function register(): void { ... $this->app->bind(UserKycRepositoryInterface::class, UserKycRepository::class); }
FINISHING SETUP
If you did run php artisan magic:init.
Add to the composer.json file.
"autoload": { "psr-4": { "App\\": "app/", "Database\\Factories\\": "database/factories/", "Database\\Seeders\\": "database/seeders/" }, "files": [ "app/Http/Helpers.php" ] },
After that run:
composer dump-autoload
Add to bootstrap/providers.php.
/* * Package Service Providers... */ App\Providers\EventServiceProvider::class, App\Providers\MacroServiceProvider::class, App\Providers\RepositoryServiceProvider::class,
Add to bootstrap/app.php
->withMiddleware(function (Middleware $middleware) { // $middleware->alias([ 'check.email.verification' => \App\Http\Middleware\CheckEmailVerification::class, 'check.user.is.admin' => \App\Http\Middleware\CheckIfUserIsAdmin::class, 'check.phone.verification' => \App\Http\Middleware\CheckPhoneVerification::class, ]); })
Add to vite.config.js
import { defineConfig } from 'vite'; import laravel from 'laravel-vite-plugin'; export default defineConfig({ plugins: [ laravel({ input: [ 'resources/sass/app.scss', 'resources/js/app.js', 'resources/css/app.css', ], refresh: true, }), ], });
Project setup
php artisan ui bootstrap npm install composer install php artisan migrate --seed
Run development server
npm run build
php artisan serve
php artisan test
NOTE
Publish notification blade
Add the following line above this code line @if ($emailData->action) in the notification.blade.php file:
@if (isset($emailData->highlightText)) @component('mail::panel', ['style' => 'background-color: #f0f8ff; border-radius: 0.5rem; padding: 16px;text-align: center']) <div style="text-align: center;"> {{ $emailData->highlightText }} </div> @endcomponent @endif
App notification helpers
$userNotificationData = new UserNotificationData($user->id, $title, $text); $user->notify(new DatabaseNotification($userNotificationData->toObject())); $emailData = new EmailData(subject: $title, lines: [$text], from: env('MAIL_FROM_ADDRESS'), remark: null, action: false, action_text: null, action_url: null, attachements: null); $user->notify(new EmailNotification($emailData->toObject())); $smsData = new SmsData($user->name, $text); $user->notify(new SmsNotification($smsData->toObject()));
Advanced search and table filter helpers
Sample usage:
public function getPaginated(int $pageSize): LengthAwarePaginator { /** * Search a parent table via the user_id foreign key on the user_deliveries table */ $search = advancedSearch('user', 'user_id', ['unit_number', 'name', 'email', 'first_name', 'last_name', 'middle_name']); /** * Search a child table via the user_delivery_id foreign key on the user_delivery_items table */ $search2 = advancedSearch('userDeliveryItems', 'user_delivery_id', ['tracking_number', 'name', 'delivery_vendor'], 'user_delivery_items'); return UserDelivery::with(['user', 'userDeliveryItems']) ->search($search) ->search($search2) ->orWhere(function($query) { $query->search('ref_number'); }) ->order() ->date() ->filter($this->whiteList) ->paginate(pageSize($pageSize)); }
LICENSE
The MM package is a software licensed under the Apache 2.0 license.