Create a module
The scaffold takes a minute. The rest of the half hour goes into the two things it cannot do for you, a migration and a test, and into the commit, which is the only step your colleagues ever see.
Before you start
The stack is up and the working tree of the project is clean, so the commit at the end holds the module and the files grim changed for it, nothing else.
$ grim up
$ git status --shortDecide what the module is. Behaviour only this project needs is a local module: it lives in the project's repository and nowhere else, and it is the usual case. A module other projects will install is a unit, with a repository of its own, grimoiry/grim-<name>, created with the token grim stored. For a unit your GitHub account has to be allowed to create repositories in that organisation. Ask before you start, and check the login with grim auth:whoami. A refusal arrives after the module is already on disk.
Decide the names first. The module name is PascalCase and plural, such as Admissions. The model inside it is singular, such as Entry. Renaming later means touching every generated file.
The ritual, in order
Seven steps. The first six make a local module, and until the sixth is done it exists on your machine only. The seventh is for a unit alone.
Scaffold the module
grim make:modulewritessrc/Admissions/, lists it underlocalingrim.json, writesconfig/modules-available.phpagain and enables the module. For a unit, leave out--no-repo: the command then leaveslocalalone, creates the GitHub repository instead and pushes the scaffold to it from a copy outside the project, and the summary ends with aRemote reporow.$ grim make:module Admissions --with-crud=Entry -d "Course admissions" --no-repo Module Admissions created Path src/Admissions/ Namespace Modules\Admissions Provider AdmissionsProvider Kebab name admissions CRUD yes Model Entry Plural Entries CRUD slug admissions/entries Table admissions_entries GitHub repo skipped (--no-repo) — local module, listed in grim.json "local"Read
Plural,CRUD slugandTablebefore you build on them. The pluralizer knows regular English endings and little else. If a row reads wrong, deletesrc/Admissions/and its entry underlocalingrim.json, or for a unit the new repository, and run it again with--plural. The slug carries a slash, not the dash the option's help text shows. The slash is correct. It is what lets the admin find the pages underPages/Entries/.What landed:
module.json,AdmissionsProvider.php,routes.php, a model, a repository class, four Vue pages, language files, and an emptyDatabase/Migrations/. A unit also gets a workflow under.github/that validates the manifest on everyv*tag; a local module has no tags and gets none. There is no migration and no test. Those are the next steps.Give it a table
The module is enabled and its table does not exist, so the admin page fails until you migrate. Create the migration inside the module, not in the project's
database/directory. Only what lives undersrc/Admissions/travels with the module.$ grim artisan -- make:migration create_admissions_entries_table --path=src/Admissions/Database/Migrations $ grim artisan migrateThe double dash matters. Without it
grim artisandrops--pathwithout a word and the file lands in the project. Write the columns into the new file before you run the second line, then fill infields()andgrid()onsrc/Admissions/Models/Entry.phpto match.See it in the admin
The provider registers the pages, the permissions and a menu entry. Check that the module is enabled, then log in to the admin at
https://shop.testand look for the new entry in the menu.$ grep Admissions storage/config/modules-enabled.php Modules\Admissions\AdmissionsProvider::class,The menu entry sits in a group with a fixed label that the scaffold wrote into
registerMenu()inAdmissionsProvider.php. Change thegroupvalue there by hand. The--menu-groupoption ofmake:moduleis accepted and has no effect, so do not rely on it.Write the first test
The scaffold ships no tests. Put them where the project's PHPUnit configuration looks for module tests,
src/Admissions/Tests/Unitandsrc/Admissions/Tests/Feature, and run that directory alone withgrim test.$ mkdir -p src/Admissions/Tests/Feature $ grim test src/Admissions/Tests $ grim testThe test run migrates
shop_testfirst, so your new migration is applied there as well. Run the whole suite once at the end. A new provider can break something far away.Validate the manifest
grim manifest:validatechecksmodule.jsonagainst the rules the tag workflow enforces. Run it now, and again whenever you add arequiresentry.$ grim manifest:validate src/AdmissionsThe
versioninmodule.jsonwas set to the first version of the train the project is on,2.5.0. Leave it. For a unit, the tag you push in the last step has to say the same.Commit it in the project
The project's repository tracks its whole tree, so the module is committed like any other code of the project.
$ git add src/Admissions grim.json config/modules-available.php $ git commit -m "feat(admissions): entries table, model fields and first test"For a local module this is the end: a colleague gets it with
git pull. Later changes are ordinary commits in the project.grim.jsonlists the module underlocal, andconfig/modules-available.phpcarries its provider, sogrim installon a fresh machine enables it. A colleague whosestorage/config/modules-enabled.phpalready exists adds the provider line from step three once;storage/is not tracked.grim prunenever deletes a local module.Publish a first version (a unit only)
A project resolves units by tag, and
make:modulepushed a branch and no tag.src/Admissions/has no.gitof its own, so the release is cut from a clone outside the project: copy your work into it, commit and tag it with the version frommodule.json.$ git clone https://github.com/grimoiry/grim-admissions /tmp/grim-admissions $ cp -R src/Admissions/. /tmp/grim-admissions/ $ git -C /tmp/grim-admissions add -A $ git -C /tmp/grim-admissions commit -m "feat: entries table, model fields and first test" $ git -C /tmp/grim-admissions tag v2.5.0 $ git -C /tmp/grim-admissions push origin main v2.5.0The tag starts the
Validateworkflow in the new repository. It fails when the tag and the manifest disagree.Then
grim requirefinds the tag, sees thatsrc/Admissions/is already in the project, and writes the module intogrim.jsonandgrim.lock.$ grim require admissions $ git add -A $ git commit -m "feat: add admissions module"grim.jsonnow holds"admissions": "~2.5", and the module is a unit like any other. A change to it goes back to its repository withgrim push Admissionsand returns with the next tag andgrim update.
What you have now
src/Admissions/ a plain directory, committed in the project
module.json name, namespace, version, provider
AdmissionsProvider.php CRUD, permissions, menu entry
Database/Migrations/ your migration
Tests/ your tests
grimoiry/grim-admissions a unit only: branch main and tag v2.5.0
grim.json local: lists admissions under "local"; a unit: under "requires"
grim.lock a unit only: pins v2.5.0When it does not work
“Module directory already exists: src/Admissions/”
An earlier run failed after the scaffold, usually at the GitHub step. The module on disk is complete and enabled. Fix the cause, delete src/Admissions/ and run the command again, or create the repository yourself and push the module to it from a copy outside the project, as in step seven.
“Repository grimoiry/grim-admissions already exists.”
The name is taken. The message goes on to offer --use-existing, which attaches that repository as origin. Use it only for an empty repository. The push that follows fails when main there already has history.
“Heads up: 'Users' is a core module name.”
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.
require says the package is not available
admissions is not available — grimoiry/grim-admissions does not exist, or your token cannot see it. means the repository was created somewhere else, typically with --org. grim resolves every module under grimoiry, so a module created under another organisation cannot be required. Create it without --org.
The admin page answers with an SQL error
The table is missing. Step two was skipped, or the migration landed in the project's database/migrations because the double dash was left out. Move the file into src/Admissions/Database/Migrations/ and run grim artisan migrate.
Next recipe
Create a theme
Scaffold a theme with its own repository, switch the project to it, work on it with the Vite dev server running, validate the manifest, publish a version that holds your work and add the theme to grim.json.
Read itSpells used here
grim make:module
Forge a new module
Scaffold a module under src/ with a provider, routes and a working admin CRUD, local to the project or with a GitHub repository of its own.
grim artisan
Run artisan in the container
Run an artisan command inside the app container, where the app's PHP, extensions and environment are.
grim test
Run the test suite
Run the project's tests in the app container against a separate test database that grim creates and migrates for you.
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 require
Summon a module or theme
Add a module or theme to grim.json, install it with everything it depends on, and rewrite grim.lock, all inside the train the project is on.
Related recipes
Change a module and send it back
Check what kind of module you are in, make, test and commit the change, read the dry run, open the pull request, and take the release when it arrives.
Create a theme
Scaffold a theme with its own repository, switch the project to it, work on it with the Vite dev server running, validate the manifest, publish a version that holds your work and add the theme to grim.json.