Project structure
A Drupal project has many folders, but only a few of them are meant to be changed. It is important to see at once where core is, where other people's modules are and where your code goes.
Project folders
site/
├── composer.json
├── vendor/
└── web/
├── core/
├── modules/
│ ├── contrib/
│ └── custom/
├── themes/
│ ├── contrib/
│ └── custom/
├── sites/
│ └── default/
│ ├── settings.php
│ └── files/
└── index.phpThe custom folders do not exist after installation — create web/modules/custom and web/themes/custom by hand.
| Folder or file | What it holds |
|---|---|
composer.json | The list of project packages: core, modules, Drush |
vendor | Downloaded PHP packages. Never edited |
web | The site root: the web server points at it |
web/core | Drupal core. Never edited |
web/modules/contrib | Ready-made modules installed through Composer |
web/modules/custom | Your modules |
web/themes/custom | Your themes |
web/sites/default/settings.php | The database connection and site settings |
web/sites/default/files | Files uploaded through the site: images, documents |
What goes into Git
The repository gets composer.json, composer.lock, the custom folders and the configuration files. The vendor, web/core, web/modules/contrib and web/sites/default/files folders are not committed: the first three are restored by composer install, and uploaded files belong to the site content.
The module folder
Each module is a separate folder inside web/modules/custom. In the course it is blog. By the end of the course it looks like this:
web/modules/custom/blog/
├── blog.info.yml module description
├── blog.routing.yml page addresses
├── blog.links.menu.yml menu links
├── blog.permissions.yml permissions
├── blog.services.yml services
├── blog.libraries.yml styles and scripts
├── blog.module hook functions
├── blog.install tables and updates
├── config/ default settings
├── templates/ Twig templates
├── css/ js/ style and script files
└── src/ PHP classes
├── Controller/
├── Form/
└── Plugin/File names start with the module name. Classes live in src, and their namespace starts with Drupal\blog: the file src/Controller/BlogController.php holds the class Drupal\blog\Controller\BlogController.
None of these files except blog.info.yml is required — they are added as the need arises.
Common mistake
Editing files in web/core or web/modules/contrib looks like the shortest way, but the changes disappear at the next composer update. Other code's behaviour is changed from your own module — with hooks and event subscribers — and markup is changed from your own theme.