Skip to content

Liber Tertius · Chapter 5 · Working on modules

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 last step, which is the only one your colleagues ever see.

≈30 min 7 steps A project that installs and runs The right to create repositories in the organisation
Source

Before you start

The stack is up and the working tree of the project is clean. The last step changes grim.json and grim.lock, and you want that commit to hold nothing else.

$ grim up
$ git status --short
$ grim auth:whoami

The module gets a repository of its own, grimoiry/grim-<name>, created with the token grim stored. Your GitHub account has to be allowed to create repositories in that organisation. Ask before you start. 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 creates the module and its repository. The last two publish it, and until they are done the module exists on your machine only.

  1. Scaffold the module

    grim make:module writes src/Admissions/, enables the module, makes the directory a git repository, creates the GitHub repository and pushes the first commit.

    $ grim make:module Admissions --with-crud=Entry -d "Course admissions"
    
      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
      Remote repo    https://github.com/grimoiry/grim-admissions

    Read Plural, CRUD slug and Table before you build on them. The pluralizer knows regular English endings and little else. If a row reads wrong, delete src/Admissions/ and 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 under Pages/Entries/.

    What landed: module.json, AdmissionsProvider.php, routes.php, a model, a repository class, four Vue pages, language files, an empty Database/Migrations/ and a workflow that validates the manifest on every v* tag. There is no migration and no test. Those are the next two steps.

  2. 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 under src/Admissions/ travels with the module.

    $ grim artisan -- make:migration create_admissions_entries_table --path=src/Admissions/Database/Migrations
    $ grim artisan migrate

    The double dash matters. Without it grim artisan drops --path without a word and the file lands in the project. Write the columns into the new file before you run the second line, then fill in fields() and grid() on src/Admissions/Models/Entry.php to match.

  3. 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.test and 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() in AdmissionsProvider.php. Change the group value there by hand. The --menu-group option of make:module is accepted and has no effect, so do not rely on it.

  4. Write the first test

    The scaffold ships no tests. Put them where the project's PHPUnit configuration looks for module tests, src/Admissions/Tests/Unit and src/Admissions/Tests/Feature, and run that directory alone with grim test.

    $ mkdir -p src/Admissions/Tests/Feature
    $ grim test src/Admissions/Tests
    $ grim test

    The test run migrates shop_test first, 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.

  5. Validate the manifest

    grim manifest:validate checks module.json against the rules the tag workflow enforces. Run it now, and again whenever you add a requires entry.

    $ grim manifest:validate src/Admissions

    The version in module.json was set to the first version of the train the project is on, 2.5.0. Leave it. The tag you push next has to say the same.

  6. Publish a first version

    A project resolves modules by tag, and the scaffold pushed a branch and no tag. Commit your work inside the module and tag it with the version from module.json.

    $ git -C src/Admissions add -A
    $ git -C src/Admissions commit -m "feat: entries table, model fields and first test"
    $ git -C src/Admissions tag v2.5.0
    $ git -C src/Admissions push origin main v2.5.0

    The tag starts the Validate workflow in the new repository. It fails when the tag and the manifest disagree. For the next release, raise version in module.json, commit, and tag again.

  7. Add it to the project

    The project's repository ignores src/, so nothing above has reached your colleagues. grim require finds the tag you pushed, sees that the checkout already sits on it, and writes the module into grim.json and grim.lock.

    $ grim require admissions
    $ git add grim.json grim.lock
    $ git commit -m "feat: add admissions module"

    grim.json now holds "admissions": "~2.5". That commit is what makes the module part of the project.

What you have now

src/Admissions/                   its own git repository, on tag v2.5.0
  module.json                     name, namespace, version, provider
  AdmissionsProvider.php          CRUD, permissions, menu entry
  Database/Migrations/            your migration
  Tests/                          your tests
grimoiry/grim-admissions          branch main and tag v2.5.0
grim.json, grim.lock              name the module, committed

A colleague gets the module with git pull and grim install. On a project they had installed before, install does not enable it for them, because storage/config/modules-enabled.php is local to each machine. They add the provider line from step three by hand.

This module is yours, not the monorepo's, so grim push does not apply to it. Changes go out with git from src/Admissions/ and a new tag.

When 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 attach it as origin.

“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.