Skip to content

Forge a new module

$ grim make:module Admissions --with-crud=Entry

The project needs something no existing module does: a list of admissions, a price calculator, a connector to somebody's API. A module is where that code lives, and an empty one is a provider, routes, language stubs and admin pages that all have to agree on a name. grim make:module writes them, so the first thing you type is the part that is actually yours.

By default the module arrives with a complete admin CRUD for one model and with a private GitHub repository of its own. Both can be switched off.

Usage

$ grim make:module Admissions                      # CRUD for a model named Admission
$ grim make:module Admissions --with-crud=Entry    # CRUD for a model named Entry
$ grim make:module Hooks --no-crud --no-repo       # a bare module, nothing on GitHub

Run it from the project directory. The name is PascalCase; admissions or price-list are accepted and turned into Admissions and PriceList. Creating the repository needs a stored GitHub login that may create repositories in the target organisation.

Arguments and options

Scaffold a new module under src/{Name}/ (optionally with CRUD + GitHub repo)

Usage

grim make:module [-d|--description DESCRIPTION] [--disabled] [--with-crud [WITH-CRUD]] [--no-crud] [--plural PLURAL] [--crud-slug CRUD-SLUG] [--table TABLE] [--menu-group MENU-GROUP] [--no-repo] [--private] [--public] [--org ORG] [--use-existing] [--skip-push] [--] <name>

Arguments

Argument Description
name Module name in PascalCase (e.g. Admissions) (required)

Options

Option Description
-d, --description=DESCRIPTION Short description for module.json
--disabled Skip enabling the module in modules-enabled
--with-crud[=WITH-CRUD] Scaffold full CRUD (Model+Repository+Pages+register* methods). Pass a value to override the model name; defaults to singular of module name.
--no-crud Produce a minimal module without CRUD/Model/Pages
--plural=PLURAL Override the derived plural form of the model name
--crud-slug=CRUD-SLUG Override the CRUD slug (default: {module-kebab}-{models-kebab}, e.g. admissions-entries)
--table=TABLE Override the table name (default: {module_snake}_{models_snake})
--menu-group=MENU-GROUP Admin menu group label for the registerMenu() call (default: Obchod)
--no-repo Skip GitHub repository creation + git init
--private Create the GitHub repo as private (default)
--public Create the GitHub repo as public
--org=ORG GitHub org (or user) to create the repo under (default: grimoiry)
--use-existing If the remote repo already exists, reuse it instead of failing
--skip-push Init local git + attach remote but do not push

What it actually does

Each step is printed with a number and its duration. The first one that fails stops the run.

  1. Scaffolds src/<Name>/. module.json, <Name>Provider.php, routes.php, config/, helpers.php, hooks.php, index.ts, Czech and English language files, a public Index action and an empty Database/Migrations/. The version in module.json is the version of the core the project is on.
  2. Adds the CRUD, unless --no-crud: a model, a repository, the Index, Create, Edit and Form pages under Pages/<Models>/, and a provider that registers the CRUD, its permissions and an admin menu entry.
  3. Copies the validation workflow to .github/workflows/validate.yml inside the module. It checks the manifest on every tag pushed to the module's repository.
  4. Enables the module in storage/config/modules-enabled.php, unless --disabled.
  5. Makes it a git repository. git init -b main inside src/<Name>/ and one commit, Initial scaffold from grim-cli.
  6. Creates the GitHub repository grim-<kebab-name> under --org, private unless --public, attaches it as origin over HTTPS and pushes main. --skip-push stops after attaching the remote.
  7. Prints a summary and the next steps. Path, namespace, provider, model, plural, CRUD slug, table and the repository URL.

The names it derives

One module name becomes five others. Check them in the summary, because renaming later means touching every file.

Name Derived as Admissions --with-crud=Entry Override
Model Singular of the module name Entry --with-crud=<Model>
Plural Plural of the model Entries --plural
CRUD slug <module>/<models>, kebab-case admissions/entries --crud-slug
Table <module>_<models>, snake_case admissions_entries --table
Repository grim-<module>, kebab-case acme/grim-admissions with --org=acme --org

The slash in the slug is deliberate. It makes entries a sub-resource of admissions, and it is what lets the admin find the pages under Pages/Entries/.

Pitfalls

The pluralizer is naive

It knows the regular English endings and a handful of irregular words, nothing more. Look at the Plural row of the summary before you build on it. If it reads wrong, delete src/<Name>/ and run again with --plural.

A failed step leaves the directory behind

When the GitHub step fails, the module is already scaffolded and enabled. A second run stops at Module directory already exists: src/Admissions/. Either finish by hand inside src/Admissions/, or delete the directory and start over with the cause fixed.

The repository name is already taken

Repository acme/grim-admissions already exists. Pass --use-existing to reuse it (skips creation, just attaches as remote). The flag attaches the existing repository as origin. The push that follows fails if that repository already has history on main, so use it for an empty one.

The name of a core module

Heads up: 'Users' is a core module name. is a warning, not a refusal. A local module with the name of a core one may shadow it. Pick another name unless that is what you want.

After it finishes

The module is enabled but its table does not exist yet. The summary ends with the same list.

  1. Fill in fields() and grid() on src/Admissions/Models/Entry.php.
  2. Write a migration for admissions_entries into src/Admissions/Database/Migrations/.
  3. Adjust the menu group and the permissions in AdmissionsProvider.php.
  4. Run the migration inside the container.
$ grim shell
$ php artisan migrate

grim manifest:validate confirms the manifest still holds after you edit it.

Recipes that use it

The idea underneath