Forge a new module
$ grim make:module Admissions --with-crud=Entry --no-repo
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, and for behaviour only this project needs the repository is the one to switch off: --no-repo makes a local module, part of the project and committed with it.
Usage
$ grim make:module Admissions --no-repo # a local module, CRUD for a model named Admission
$ grim make:module Hooks --no-crud --no-repo # a bare local module
$ grim make:module Admissions --with-crud=Entry # a unit: CRUD for Entry and a repository on GitHubRun 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. --no-repo needs no login at all.
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 |
Local module: no GitHub repository and no .github/; listed in grim.json "local" and committed in the instance |
--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.
- Scaffolds
src/<Name>/.module.json,<Name>Provider.php,routes.php,config/,helpers.php,hooks.php,index.ts, Czech and English language files, a publicIndexaction and an emptyDatabase/Migrations/. Theversioninmodule.jsonis the version of the core the project is on. - Adds the CRUD, unless
--no-crud: a model, a repository, theIndex,Create,EditandFormpages underPages/<Models>/, and a provider that registers the CRUD, its permissions and an admin menu entry. - Copies the validation workflow to
.github/workflows/validate.ymlinside the module, or with--no-repolists the module ingrim.jsonlocalinstead. The workflow checks the manifest on every tag pushed to the module's repository; a local module has none, so it gets no.github/. Listing it also writesconfig/modules-available.phpagain. A name the project already requires or has ingrim.lockis refused: a local module needs a name of its own. - Enables the module in
storage/config/modules-enabled.php, unless--disabled. - Commits the scaffold in a copy, unless
--no-repo. The copy sits in the system temp directory:git init, one commit,Initial scaffold from grim-cli.src/<Name>/itself stays a plain directory of the project with no.gitinside, and the copy is thrown away when the command ends. - Creates the GitHub repository
grim-<kebab-name>under--org, private unless--public, attaches it to the copy asoriginover HTTPS and pushesmain.--skip-pushcreates the repository and pushes nothing; the copy is thrown away all the same, so the repository stays empty and its first commit is yours to push from a clone, as in Create a module. No tag is pushed: the first release is yours to cut, see Create a module. - Prints a summary and the next steps. Path, namespace, provider, model, plural, CRUD slug, table and the repository URL.
A local module or a unit
The project repository holds its whole tree, so a module needs no repository to be kept, reviewed and shared. Choose by who else needs it.
| Local module | Unit | |
|---|---|---|
| Made with | grim make:module <Name> --no-repo |
grim make:module <Name> |
| For | Behaviour only this project needs | A module other projects will install |
| Lives | In the project's git, nowhere else | In grimoiry/grim-<name>, a copy in each project |
In grim.json local |
Yes | No |
In grim.lock |
No | Yes, after grim require |
| A change goes | Into a commit in the project; grim push says so |
Where it is released from, with grim push <Name> |
Both have "type": "module" in module.json; what tells them apart is local in grim.json against the entry in grim.lock. grim require refuses a name listed in local. A module with "type": "core-module" belongs to core, and a change to one goes with grim push --core.
Commit a local module like any other code of the project, together with the two files the command changed.
$ git add src/Hooks grim.json config/modules-available.php
$ git commit -m "feat(hooks): scaffold the hooks module"grim.json lists a local module under local, and config/modules-available.php carries its provider, so grim install enables it on a fresh machine. A machine whose storage/config/modules-enabled.php already exists needs the provider line added once; storage/ is not tracked. Prune never deletes a local module, and grim install and grim update resolve the units it requires. A local module made by an older grim still carries .github/ and is missing from local: delete .github/, commit, and the next grim prune offers to list it.
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; only the copy outside the project is gone. 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.
- Fill in
fields()andgrid()onsrc/Admissions/Models/Entry.php. - Write a migration for
admissions_entriesintosrc/Admissions/Database/Migrations/. - Adjust the menu group and the permissions in
AdmissionsProvider.php. - Run the migration inside the container.
$ grim shell
$ php artisan migrateThen commit src/<Name>/ in the project. For a unit, the recipe Create a module goes on to its first release and grim require.
grim manifest:validate confirms the manifest still holds after you edit it.
Related spells
grim make:theme
Weave a new theme
Scaffold a frontend or admin theme in the project, with its layouts and entry points, and publish it to a GitHub repository of its own.
grim manifest:validate
Check a manifest
Validate module.json, theme.json and core.json against the manifest schema on your machine, before CI does it for you.
grim push
Send a change upstream
Turn the edits you made to one installed module or theme into a pull request against the monorepo it is released from.
grim shell
Step inside a container
Open an interactive bash session in the app container, or in another service of the local stack.
grim auth:login
Log grim in to GitHub
Give grim a GitHub token, from your gh login or a personal access token, and hand the same token to Composer and Docker.