Created
February 9, 2026 01:07
-
-
Save ryanirelan/cda11be1e8685242a99278f2cd8a9a86 to your computer and use it in GitHub Desktop.
Quiz: Introduction to Extending Craft CMS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "quizzes": [ | |
| { | |
| "title": "Introduction to Extending Craft CMS", | |
| "passingScore": 70, | |
| "timeLimit": 0, | |
| "maxAttempts": 0, | |
| "allowRetakes": 1, | |
| "showCorrectAnswers": 1, | |
| "shuffleQuestions": 1, | |
| "shuffleAnswers": 1, | |
| "hasGuestAccess": 0, | |
| "hasFreeAccountAccess": 0, | |
| "enabled": 1, | |
| "multipleChoiceQuestions": [ | |
| { | |
| "questionText": "What is the primary difference between a Craft CMS plugin and a module?", | |
| "quizOptions": [ | |
| { "optionText": "Plugins are written in PHP while modules use JavaScript", "isCorrect": 0 }, | |
| { "optionText": "Plugins are designed for reusable, distributable functionality while modules are for project-specific code", "isCorrect": 1 }, | |
| { "optionText": "Modules can be listed in the Plugin Store but plugins cannot", "isCorrect": 0 }, | |
| { "optionText": "Plugins don't support event handlers but modules do", "isCorrect": 0 } | |
| ], | |
| "explanation": "Plugins are self-contained, distributable packages designed to be reused across multiple projects, while modules are intended for project-specific customizations tightly coupled to a particular Craft installation." | |
| }, | |
| { | |
| "questionText": "How are modules loaded into a Craft CMS application?", | |
| "quizOptions": [ | |
| { "optionText": "Through the Plugin Store in the Control Panel", "isCorrect": 0 }, | |
| { "optionText": "By adding them to config/general.php", "isCorrect": 0 }, | |
| { "optionText": "Via the config/app.php file", "isCorrect": 1 }, | |
| { "optionText": "Through a dedicated modules.php configuration file", "isCorrect": 0 } | |
| ], | |
| "explanation": "Modules are registered and bootstrapped via the project's main application configuration file, config/app.php, using the modules and bootstrap arrays." | |
| }, | |
| { | |
| "questionText": "What naming convention identifies a private plugin in Craft CMS?", | |
| "quizOptions": [ | |
| { "optionText": "Naming the plugin with a private- prefix", "isCorrect": 0 }, | |
| { "optionText": "Adding \"private\": true to the plugin's composer.json", "isCorrect": 0 }, | |
| { "optionText": "Prefixing the plugin handle with an underscore (e.g., _my-plugin)", "isCorrect": 1 }, | |
| { "optionText": "Placing the plugin in a private-plugins/ directory", "isCorrect": 0 } | |
| ], | |
| "explanation": "The standard way to create a private plugin is by prefixing the plugin's handle with an underscore. This naming convention tells Craft CMS that it's a private plugin, not intended for public distribution." | |
| }, | |
| { | |
| "questionText": "What does the Event::on() method do in Craft CMS?", | |
| "quizOptions": [ | |
| { "optionText": "Creates a new event in the event queue", "isCorrect": 0 }, | |
| { "optionText": "Registers an event listener that triggers a callback when a specific event occurs", "isCorrect": 1 }, | |
| { "optionText": "Disables a built-in Craft event", "isCorrect": 0 }, | |
| { "optionText": "Logs event data to the debug toolbar", "isCorrect": 0 } | |
| ], | |
| "explanation": "Event::on() is the event handler registration method. It accepts the target class, event name, and a callback function that executes when the specified event is triggered." | |
| }, | |
| { | |
| "questionText": "What is the purpose of parent::init() in a module or plugin's init() method?", | |
| "quizOptions": [ | |
| { "optionText": "It installs the module's database tables", "isCorrect": 0 }, | |
| { "optionText": "It registers the module with the Plugin Store", "isCorrect": 0 }, | |
| { "optionText": "It compiles the module's Twig templates", "isCorrect": 0 }, | |
| { "optionText": "It calls the parent class's initialization logic to maintain base functionality", "isCorrect": 1 } | |
| ], | |
| "explanation": "Calling parent::init() invokes the parent class's (BaseModule) initialization method. The child class overrides init() but still needs the parent's initialization logic to function properly." | |
| }, | |
| { | |
| "questionText": "How do modules handle database schema changes?", | |
| "quizOptions": [ | |
| { "optionText": "Through their own dedicated migration track", "isCorrect": 0 }, | |
| { "optionText": "By modifying the database directly in the init() method", "isCorrect": 0 }, | |
| { "optionText": "Through Craft's content migrations", "isCorrect": 1 }, | |
| { "optionText": "Modules cannot make database changes", "isCorrect": 0 } | |
| ], | |
| "explanation": "Modules do not have their own migration track. Database changes required by a module should be handled via Craft's content migrations, as the changes are considered part of the overall project's evolution." | |
| }, | |
| { | |
| "questionText": "What is the role of the bootstrap array in config/app.php?", | |
| "quizOptions": [ | |
| { "optionText": "It defines which CSS framework to use", "isCorrect": 0 }, | |
| { "optionText": "It specifies which modules should be loaded and initialized on every request", "isCorrect": 1 }, | |
| { "optionText": "It lists the database tables to create on installation", "isCorrect": 0 }, | |
| { "optionText": "It configures the front-end build system", "isCorrect": 0 } | |
| ], | |
| "explanation": "Simply registering a module in the modules array makes it available for lazy loading. For a module to be loaded and initialized on every request, it must be added to the bootstrap array." | |
| }, | |
| { | |
| "questionText": "What are the two ways to extend the craft Twig variable?", | |
| "quizOptions": [ | |
| { "optionText": "Using filters and functions", "isCorrect": 0 }, | |
| { "optionText": "Attaching a Behavior or attaching a Service", "isCorrect": 1 }, | |
| { "optionText": "Creating a template hook or extending a controller", "isCorrect": 0 }, | |
| { "optionText": "Using globals and macros", "isCorrect": 0 } | |
| ], | |
| "explanation": "You can extend the craft variable by attaching a Behavior (adds methods directly onto the variable, e.g., craft.myMethod()) or attaching a Service (adds a sub-object, e.g., craft.myServiceHandle.myMethod())." | |
| }, | |
| { | |
| "questionText": "What is the purpose of the schemaVersion property in a Craft plugin?", | |
| "quizOptions": [ | |
| { "optionText": "It defines the minimum Craft CMS version required", "isCorrect": 0 }, | |
| { "optionText": "It sets the GraphQL schema version for the plugin's API", "isCorrect": 0 }, | |
| { "optionText": "It tracks the current version of the plugin's database schema so Craft knows when to run migrations", "isCorrect": 1 }, | |
| { "optionText": "It specifies which version of PHP the plugin requires", "isCorrect": 0 } | |
| ], | |
| "explanation": "The schemaVersion property represents the current version of the plugin's database schema. Craft uses this version number to determine if it should run the plugin's database migrations." | |
| }, | |
| { | |
| "questionText": "What is the recommended approach for plugin development environments using DDEV?", | |
| "quizOptions": [ | |
| { "optionText": "Install the plugin directly into the Craft project's plugins/ directory", "isCorrect": 0 }, | |
| { "optionText": "Keep plugins in a separate directory and use a local path repository in composer.json", "isCorrect": 1 }, | |
| { "optionText": "Create a symbolic link from the plugin to the vendor directory", "isCorrect": 0 }, | |
| { "optionText": "Copy plugin files into each project manually", "isCorrect": 0 } | |
| ], | |
| "explanation": "The recommended setup is to keep plugin projects in a separate directory and use a local path repository in the project's composer.json to pull the plugins in via Composer. This allows managing plugins independently while testing them in Craft environments." | |
| }, | |
| { | |
| "questionText": "Where is the primary location for registering event listeners in a Craft plugin or module?", | |
| "quizOptions": [ | |
| { "optionText": "In the composer.json file", "isCorrect": 0 }, | |
| { "optionText": "In the config/app.php file", "isCorrect": 0 }, | |
| { "optionText": "In the init() method of the main class", "isCorrect": 1 }, | |
| { "optionText": "In a separate events.php configuration file", "isCorrect": 0 } | |
| ], | |
| "explanation": "The init() method is executed early in the request lifecycle and is the primary location for registering event listeners, defining routes, setting up components, and other initialization tasks." | |
| }, | |
| { | |
| "questionText": "What is the purpose of Craft::$app->onInit() in a module or plugin?", | |
| "quizOptions": [ | |
| { "optionText": "It triggers the module's installation process", "isCorrect": 0 }, | |
| { "optionText": "It defers code execution until after Craft is fully initialized", "isCorrect": 1 }, | |
| { "optionText": "It registers the module with the Plugin Store", "isCorrect": 0 }, | |
| { "optionText": "It initializes the module's front-end assets", "isCorrect": 0 } | |
| ], | |
| "explanation": "onInit() tells Craft to run code after the application is fully initialized. This is needed when you want to leverage parts of the application or other plugins that may not be available during the initial init() call." | |
| } | |
| ], | |
| "trueFalseQuestions": [ | |
| { | |
| "questionText": "Modules can be listed and installed via the Craft Plugin Store.", | |
| "correctAnswer": 0, | |
| "explanation": "Modules are not discoverable or installable via the Plugin Store. They are project-specific customizations loaded via config/app.php and are not intended for distribution." | |
| }, | |
| { | |
| "questionText": "A module can be converted to a plugin with minimal effort because they share the same structural patterns.", | |
| "correctAnswer": 1, | |
| "explanation": "Plugins and modules are structurally very similar and follow the same patterns. You could build a module and later convert it to a plugin with a minimal amount of effort since the underlying structure and access to Craft APIs are largely identical." | |
| }, | |
| { | |
| "questionText": "Console controllers in Craft plugins allow you to create custom command-line commands.", | |
| "correctAnswer": 1, | |
| "explanation": "Console controllers allow developers to create custom console commands that can be run from the command line or scheduled via cron jobs, providing CLI access to plugin functionality." | |
| }, | |
| { | |
| "questionText": "Template hooks allow you to inject content into specific locations in Twig templates without editing the template file.", | |
| "correctAnswer": 1, | |
| "explanation": "Template hooks use the {% hook %} tag in Twig to define injection points. Plugins or modules can then register listeners using Craft::$app->getView()->hook() to inject content or modify template context at those points." | |
| }, | |
| { | |
| "questionText": "Services in a Craft plugin can only be called from controllers.", | |
| "correctAnswer": 0, | |
| "explanation": "Services can be called from other parts of the extension, including controllers (very common), templates (as long as you expose them), and even console commands. Services are designed to be reusable across different contexts." | |
| } | |
| ], | |
| "codeChallengeQuestions": [ | |
| { | |
| "questionText": "What does this event handler do?", | |
| "codeSnippet": "Event::on(\n Entry::class,\n Entry::EVENT_BEFORE_SAVE,\n function (ModelEvent $event) {\n $entry = $event->sender;\n $category = $event->sender->category->one();\n if ($entry->firstSave && $category !== null) {\n $entry->slug .= '-' . $category->slug;\n }\n }\n);", | |
| "codeLanguage": "php", | |
| "quizOptions": [ | |
| { "optionText": "Deletes the category when an entry is saved", "isCorrect": 0 }, | |
| { "optionText": "Appends the category slug to the entry's slug on first save", "isCorrect": 1 }, | |
| { "optionText": "Creates a new category every time an entry is saved", "isCorrect": 0 }, | |
| { "optionText": "Prevents entries from being saved without a category", "isCorrect": 0 } | |
| ], | |
| "explanation": "This event handler listens to EVENT_BEFORE_SAVE on entries. When an entry is being saved for the first time (firstSave) and it has a category, it appends the category's slug to the entry's slug with a hyphen separator." | |
| }, | |
| { | |
| "questionText": "What does this config/app.php configuration accomplish?", | |
| "codeSnippet": "return [\n 'modules' => [\n 'my-module' => \\modules\\mymodule\\MyModule::class,\n ],\n 'bootstrap' => ['my-module'],\n];", | |
| "codeLanguage": "php", | |
| "quizOptions": [ | |
| { "optionText": "Installs a plugin from the Plugin Store", "isCorrect": 0 }, | |
| { "optionText": "Creates a new database table for the module", "isCorrect": 0 }, | |
| { "optionText": "Registers and bootstraps a module so it loads on every request", "isCorrect": 1 }, | |
| { "optionText": "Disables the module until manually enabled", "isCorrect": 0 } | |
| ], | |
| "explanation": "The modules array registers the module with Craft by associating an alias with its class. Adding it to the bootstrap array ensures the module is loaded and initialized on every request, rather than being lazy-loaded only when called." | |
| } | |
| ] | |
| } | |
| ] | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment