Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Craft Laravel Package

craftcms/craft

Craft CMS is a flexible, developer-friendly content management system built on PHP and Twig. It offers powerful custom fields, multi-site support, and an intuitive control panel for editors, making it ideal for building bespoke websites and digital experiences.

View on GitHub
Deep Wiki
Context7

Getting Started

craftcms/craft is not a library you require — it's the official Composer starter skeleton for Craft CMS. Think of it as the equivalent of laravel/laravel: you scaffold a project from it, then own the resulting codebase. Craft core itself is craftcms/cms.

Requirements for the current (Craft 5) skeleton: PHP 8.2+, MySQL 8.0+/MariaDB 10.4+/Postgres 13+, Composer 2. No Node build pipeline is included.

Minimal boot:

composer create-project craftcms/craft my-site
cd my-site
cp .env.example .env          # set CRAFT_DB_*, CRAFT_SECURITY_KEY, CRAFT_PRIMARY_SITE_URL
php craft setup              # interactive: DB creds, admin user, site URL
php craft serve             # or point a vhost/DDEV at ./web

With DDEV (the path almost every Craft dev uses):

mkdir my-site && cd my-site
ddev config --project-type=craftcms --docroot=web --php-version=8.2
ddev composer create craftcms/craft
ddev craft setup

Where to look first, in order:

  1. composer.json — pins the Craft version (craftcms/cms) and plugin set. This is your upgrade surface.
  2. config/general.php, config/db.php, config/app.php — the only three config files you'll touch regularly.
  3. config/project/ (or project.yaml on older 3.x) — Craft's project config, the serialized source of truth for sections, fields, entry types, sites, and most plugin settings.
  4. templates/ — Twig templates. web/index.php is the front controller; the docroot must be web/.
  5. modules/ — where you put app-specific PHP (Craft's equivalent of app/).
  6. storage/logs/ — web.log and console.log are your debugging ground truth.

First meaningful use case: create a "News" section + entry type + a couple of fields in the control panel, add one entry, then render it:

{# templates/news/index.twig -> /news #}
{% set entries = craft.entries().section('news').orderBy('postDate DESC').limit(10).all() %}
{% for entry in entries %}
  <a href="{{ entry.url }}">{{ entry.title }}</a>
{% endfor %}

Laravel mental-model mapping:

Laravel Craft
php artisan php craft (or ddev craft)
Blade Twig (auto-escaped, no @ directives)
Eloquent models Elements + Query API (craft.entries(), Entry::find())
env('X') App::env('X') (Craft's dotenv, not Laravel's helper)
config/*.php config/*.php + project config YAML for content modeling
Migrations php craft migrate/* (schema for plugins, data moves)
Service container / facades Craft::$app
Supervisor queues Queue driver in config/queue.php + php craft queue/listen

Implementation Patterns

Project config as the deployment contract. The workflow that matters day-to-day:

# after any CP change (fields, sections, plugins)
php craft project-config/write
git add config/project && git commit

# on the next environment / after pull
php craft project-config/apply
php craft migrate/all
php craft clear-caches/all

Craft 4.4+ has php craft up, which chains migrate + project-config apply + cache clear — use that in your deploy script.

Custom logic lives in a module. Create modules/site/Site.php extending craft\base\Module, then register it in config/app.php:

return [
    'modules' => ['site' => \modules\site\Site::class],
    'bootstrap' => ['site'],
];

Add "modules\\site\\": "modules/site/" to composer.json autoload.psr-4 and composer dump-autoload -o. This is where you put service classes, Twig extensions, event listeners, and console controllers — not in templates and not by forking the skeleton.

Event hooks instead of patching core:

use craft\elements\Entry;
use yii\base\Event;

Event::on(Entry::class, Entry::EVENT_AFTER_SAVE, function (Event $e) {
    /** @var Entry $entry */
    if ($entry->section->handle === 'news') {
        Craft::$app->queue->push(new \modules\site\jobs\NotifyJob(['entryId' => $entry->id]));
    }
});

Eager-load relations in Twig — this is the single biggest perf lever:

{% set posts = craft.entries()
    .section('blog')
    .with(['featureImage', 'author', 'topics'])
    .limit(12)
    .all() %}

Console commands extend craft\console\Controller inside your module and are exposed as php craft <module-id>/<controller>/<action> — the closest thing to artisan make:command.

Headless / API surface: install craftcms/element-api for JSON endpoints (config/element-api.php), or use built-in GraphQL with schemas under config/graphql/schemas/ (project-config managed). For a Laravel front end consuming Craft, GraphQL with a scoped public schema + bearer token is the cleanest pattern.

Front-end build: the skeleton ships without one. Add craftcms/vite (or nystudio107/craft-vite) and use {{ craft.vite.script('src/main.js') }} in your layout.

Queue: set runQueueAutomatically => false in config/general.php and run php craft queue/listen under supervisor — otherwise jobs are kicked off by web requests and failures are silent. php craft queue/info and queue/retry are your friends.

Plugin installation is two steps: composer require vendor/plugin then php craft plugin/install <handle>, and commit the resulting project-config diff. Skipping the second step is the classic "works locally, broken on deploy."

Gotchas and Tips

  • Never composer require craftcms/craft. It's create-project only. Installing it into an existing app just drops a nested skeleton into vendor/.
  • Project config merge conflicts are inevitable on teams. YAML files conflict on UUIDs and ordering. Resolve by taking one side, then php craft project-config/apply and re-save the component in the CP and commit the clean output. If DB and YAML drift badly, php craft project-config/rebuild regenerates YAML from the database — run it locally only and review the diff before committing.
  • allowAdminChanges => false in production is correct and non-negotiable, but it means the CP becomes read-only for schema. Plan every field/section change as a deploy. Gate it with App::env('CRAFT_ENVIRONMENT').
  • devMode off in prod. It leaks stack traces and disables template caching in places. Gate it in config/general.php off CRAFT_ENVIRONMENT, not a hardcoded boolean.
  • Use App::env() not env(). Laravel's helper doesn't exist here; Craft's dotenv populates $_ENV/$_SERVER and App::env is the supported accessor. CRAFT_* vars (CRAFT_DB_DATABASE, CRAFT_SECURITY_KEY, CRAFT_LICENSE_KEY) are read by the framework; your own vars are freeform.
  • Element queries are lazy, not results. craft.entries().section('news') returns a query object. Forgetting .all() / .one() / .count() produces silent empty output or "not iterable" errors. one() returns null, not an exception — null-check before {{ entry.title }}.
  • File permissions on storage/, web/cpresources/, and config/ are the #1 cause of a 500 on first boot, especially in containers. The PHP user must be able to write there for caches, CP assets, and project-config writes.
  • ./craft must run from the project root and via php craft on Windows (it's a PHP script, not a shell wrapper). With DDEV, always ddev craft.
  • Docroot must be web/. If index.php is served from the project root you get exposed .env and config/ — a real security incident, not just a misconfiguration.
  • Licensing bites late. Solo is free (single CP user); Pro is paid and required for multi-user, GraphQL in some configurations, and more. Set CRAFT_LICENSE_KEY in .env or claim it in the CP before go-live.
  • Debugging sequence: confirm devMode on → php craft clear-caches/all → read storage/logs/web.log / console.log → php craft queue/info for stuck jobs → php craft project-config/apply if the CP shows fields that "don't exist."
  • Extension points, in order of preference: config override → install a plugin → project config → custom module → custom plugin. Forking the skeleton or patching vendor/ will be destroyed by the next composer update.
  • Major upgrades (4→5) need the official upgrade guide, composer update craftcms/cms, plugin updates, php craft migrate/all, then project-config/apply. Start new projects from the matching branch/tag (5.x) or you'll scaffold an outdated project structure.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect