A marketplace web application where specialists and family members create and share communication cards and game cards for the Talk & Play desktop app. Administrators review each package; approved packages are public and the desktop app can download them.
- Tech stack
- Requirements
- Getting started
- Tests
- Code quality
- Continuous integration
- Releasing
- Deployment
- Sitemap
- Cookie consent
- Debugging
- License
- Backend: Laravel 12, PHP 8.2 or newer (DDEV runs PHP 8.2, CI runs PHP 8.4), MySQL 8.0
- Frontend: Vue 2.7, Vite 7, Bootstrap 5, Sass, jQuery
- Tooling: Pest (PHPUnit), Laravel Pint, Larastan (PHPStan), ESLint
- Local environment: DDEV
- DDEV and Docker (on Windows, inside WSL2)
- Git
DDEV runs PHP, Composer, MySQL and Node.js in containers, so you do not need them on your machine. The Node.js version
comes from .nvmrc (24.21.0); a DDEV start hook keeps nodejs_version in .ddev/config.yaml in sync with it.
-
Clone the repository and start DDEV:
git clone https://github.com/scify/Talk-And-Play-Marketplace.git cd Talk-And-Play-Marketplace ddev startEvery
ddev startrunscomposer setupand then the database migrations.composer setupcreates.envfrom.env.example(only if.envis missing), installs the PHP dependencies and, whenpackage-lock.jsonchanged, the npm dependencies (npm ci), generatesAPP_KEY(only if it is empty), linkspublic/storagetostorage/app/publicand builds the frontend. It is safe to run again at any time, also by hand:ddev composer setup. DDEV writes the database, mail andAPP_URLsettings into.env. -
Edit
.env:DEFAULT_ADMIN_USER_PASSWORD_FOR_SEED: the password of the seeded administratorMAIL_FROM_ADDRESS: the sender of the notification emails (.env.examplesetsnoreply@example.com). DDEV catches all mail in Mailpit (ddev mailpit).
-
Create the starter data:
ddev artisan db:seed
The seeders create the lookup data and the administrator
admin-tnp@scify.org. -
For frontend work, start the Vite dev server (hot reload through port 5179):
ddev npm run dev
| Task | Command |
|---|---|
| Start or stop the environment | ddev start, ddev stop |
| Run Artisan, Composer or npm in the container | ddev artisan ..., ddev composer ..., ddev npm ... |
| Open a MySQL shell | ddev mysql |
| Open Mailpit (all outgoing mail) | ddev mailpit |
| Show URLs and services | ddev describe |
.npmrc hardens npm for this project. CI checks these settings:
ignore-scripts=true: npm does not run install scripts of dependenciesmin-release-age=7: npm installs only package versions that are at least 7 days oldengine-strict=true: npm refuses to install with a Node or npm version outsideenginesinpackage.json
Use npm ci to install exactly what package-lock.json specifies (composer setup does this).
ddev composer test # run the test suite
ddev coverage # the same, with a coverage summary and an HTML report in coverage/The tests run on SQLite in memory (see the <server> values in phpunit.xml). They never use the DDEV MySQL database:
<server> values override the database variables that DDEV sets in the container, tests/TestCase.php refuses to
reset any database except SQLite in memory, and composer test clears a cached configuration first.
ddev coverage turns Xdebug on for the run and then restores its previous state (also when a test fails), because
Xdebug slows down every request. It runs composer test:coverage, which needs a coverage driver such as Xdebug or PCOV.
The project uses three tools:
- Laravel Pint for the PHP code style (rules in
pint.json) - Larastan (PHPStan) for static analysis of
app/(settings inphpstan.neon; known errors are inphpstan-baseline.neon, and new code must not add entries) - ESLint for
resources/js(rules ineslint.config.js)
ddev composer lint:check # run all three tools; report problems, change nothing
ddev composer lint:fix # fix the PHP style and the JavaScript, then report what PHPStan still findslint:check stops at the first tool that reports a problem. PHPStan cannot fix code, so after lint:fix you fix the
remaining PHPStan errors by hand. CI runs the same checks.
The repository uses the shared SciFY workflows:
ci.yml(laravel-ci.yml) runs on pull requests and on pushes tomain: Pint, PHPStan, ESLint, the frontend build and the tests. The required check isci / CI.security.ymlruns on pull requests and every Monday: secret scanning, npm supply-chain checks,composer auditandnpm audit.
Dependabot opens pull requests for security updates only (.github/dependabot.yml). dependabot-auto-merge.yml
enables GitHub auto-merge for every update except a major version; it needs "Allow auto-merge" in the repository settings and the
required check ci / CI in the branch protection of main.
Releases follow Semantic Versioning: increase the major version for changes that break
users or deployments without a migration path, the minor version for new features and compatible changes, and the
patch version for bug fixes only. The version is defined only in package.json; the app shows it in the footer.
Releases are tagged vX.Y.Z. All changes are recorded in CHANGELOG.md.
main accepts changes only through pull requests with a passing ci / CI check (repository ruleset), so a release
goes through a pull request. To release version X.Y.Z from an up-to-date main with green CI:
-
Create a branch, for example
git switch -c chore/release-vX.Y.Z. InCHANGELOG.md, rename## [Unreleased]to## [X.Y.Z] - YYYY-MM-DD, add a new empty## [Unreleased]above it, and update the compare links at the end of the file. -
Set the version (this updates
package.jsonandpackage-lock.json, without a commit or tag):ddev npm version X.Y.Z --no-git-tag-version
-
Commit, push and open a pull request; merge it when CI passes:
git add CHANGELOG.md package.json package-lock.json git commit -m "chore: release vX.Y.Z" git push -u origin chore/release-vX.Y.Z gh pr create --title "chore: release vX.Y.Z" --body "Release vX.Y.Z. See CHANGELOG.md."
-
Tag the merge commit on
mainand push the tag:git switch main && git pull --ff-only git tag -a vX.Y.Z -m "vX.Y.Z" git push origin vX.Y.Z
-
Create the GitHub release, with the changelog section as the release notes:
awk '/^## \[X.Y.Z\]/{found=1; next} /^## \[/{found=0} found' CHANGELOG.md > release-notes.md gh release create vX.Y.Z --title "vX.Y.Z" --notes-file release-notes.md --verify-tag rm release-notes.md
-
Deploy the tag, not the branch (see below).
Deploy a release tag (git checkout vX.Y.Z), not main. Read the upgrade notes of every release in
CHANGELOG.md since the deployed version. A deployment needs these steps:
.envfor production:APP_ENV=production,APP_DEBUG=falseAPP_URLmust match the host name that users open. Outside thelocalenvironment,TrustHostsaccepts only theAPP_URLhost and its subdomains, and answers other hosts with HTTP 400. If a reverse proxy is in front of the app, make sure that it forwards the originalHostheader.MAIL_*, includingMAIL_FROM_ADDRESS: the app sends notifications (submissions, approvals, rejections, reports) by email.SENTRY_LARAVEL_DSNfor backend error reporting.VITE_SENTRY_DSN_PUBLICfor frontend error reporting. Vite readsVITE_*values at build time, so.envmust be complete before the build in step 3.
composer install --no-dev --optimize-autoloader, with scripts: it also publishes the cookie consent assets topublic/vendor/scify, which are not in git and which every page needs.- Node.js 24.21.0 on the build machine; build the frontend with
npm ci && npm run build. The build writes topublic/build. php artisan migrate --force,php artisan storage:link, thenphp artisan config:cache,route:cacheandview:cache.- The web server's user must be able to write to
storage/andbootstrap/cache/.sudo ./set-file-permissions.sh www-data <deploy-user> .sets the owner group, the permissions and the setgid bit on the storage folders, so that folders created by the deploy user keep the web server group. - The web server's document root is
public/. - The notifications are queued. With
QUEUE_CONNECTION=syncthey are sent during the request; with any other queue connection, run a queue worker (php artisan queue:work) under a process manager such as Supervisor.
Each release lists the extra steps for an existing installation under Upgrade notes in CHANGELOG.md. The first release after the move from Laravel Mix to Vite has several.
Spatie Laravel Sitemap creates public/sitemap.xml (not in git):
ddev artisan sitemap:generateSciFY Laravel Cookie Guard shows the cookie consent dialog. Composer
publishes its assets to public/vendor/scify after every install (post-autoload-dump, with --force), so that folder
is not in git and any edit there is lost. Without these files every page fails (the plugin reads their modification
time): after a git pull or a checkout, run ddev composer setup (or composer install with scripts) when pages
answer HTTP 500. To change the colours, override the plugin's CSS variables in
resources/sass/app.scss. The html prefix makes the rule win over the plugin's _variables.css, which loads later:
html #scify-cookies-consent-wrapper,
html #scify-cookie-consent-floating-button {
--scify-cookies-primary-color: #ff5722;
--scify-cookies-primary-hover-color: #e64a19;
}The full list of variables is in vendor/scify/laravel-cookie-guard/public/_variables.css.
Turn on Xdebug in the container with ddev xdebug on (and off again with ddev xdebug off), then start listening for
debug connections in your IDE (for example PhpStorm). With APP_DEBUG=true, the
Laravel Debugbar shows queries and timings at the bottom of each page.
Apache License 2.0. See LICENCE.md.