<p>Where do you want to store your data? Looks like you're trying to create a settings form for a module? Your trouble probably comes from trying to build a module like you would a plugin.</p>
<h3>Plugin settings</h3>
<p>Plugins come with a lot of magic to define, store and read plugin settings (see the <a href="https://craftcms.com/docs/3.x/extend/plugin-settings.html" rel="nofollow noreferrer">documentation for details</a>). The settings are stored in the project config (and a dedicated table) and provided to the plugin on intialization. Modules, however, don't work the same way, so you have to do some steps manually, like finding a place to store your content. Take a look at the last section below (Custom storage solutions) for some pointers if you want to go that way.</p>
<p>Sidenote: If you in fact want to build a plugin, the documentation linked above has everything you need to define your setting fields with default values. Make sure you understand how those interact with the <a href="https://craftcms.com/docs/3.x/project-config.html" rel="nofollow noreferrer">Project Config</a> as well. See <a href="https://craftcms.com/docs/3.x/extend/project-config.html" rel="nofollow noreferrer">Supporting Project Config</a> for details.</p>
<h3>Use Global sets for module settings</h3>
<p>Modules are site-specific, so if you're building a module, the assumption is that you have control over all aspects of the site (which you don't when building a plugin). You can, for instance, create <a href="https://craftcms.com/docs/3.x/globals.html" rel="nofollow noreferrer">Global Sets</a> with custom fields that act as settings for your module. The advantage is that you're using built-in components and user interfaces, so you don't have to write your own model, interface or storage solution.</p>
<p>For example, let's say your module requires two fields, <code>global_username</code> and <code>global_password</code>. You can just create those as regular text fields, then create a global set (<code>global_password_settings</code>) with those two fields.</p>
<p>Then you can access the global set from your module and do whatever you want with the fields:</p>
<pre class="lang-php prettyprint-override"><code>$passwordSettings = \Craft::$app-&gt;getGlobals()-&gt;getSetByHandle('global_password_settings');
$username = $passwordSettings-&gt;global_username;
$password = $passwordSettings-&gt;global_password;
</code></pre>
<h3>Custom storage solutions</h3>
<p>Just some quick pointers if you're looking to store more custom data.</p>
<ul>
<li>You can create Custom <a href="https://craftcms.com/docs/3.x/extend/element-types.html#getting-started" rel="nofollow noreferrer">Elements Types</a> which allow you to use built-in fields and field layouts. Depending on your needs, your custom elements can store their content in the built-in <code>content</code> table or you can roll your own.</li>
<li>If you just need a custom database table without any interface (or you want to build a completely custom interface), you can use the <a href="https://www.yiiframework.com/doc/guide/2.0/en/db-active-record" rel="nofollow noreferrer">Active Record</a> pattern. Yii2 has some base classes you can extend to make this easier. Craft also uses active records for many components that aren't elements.</li>
<li>If you want to create your own custom database table from scratch, you can do that in the special <a href="https://craftcms.com/docs/3.x/extend/migrations.html#plugin-install-migrations" rel="nofollow noreferrer">enter link description here</a>plugin install migration.</li>
</ul>
<p>All those solutions are pretty excessive if all you need is to store two strings.</p>

CC BY-SA 4.0
2021-10-27T14:47:42.450Z
thank you for this clarification. It helps a lot. I indeed had the approach that you mentioned (using global variables) already in place and it's working. But since I'm trying to learn more of craft I decided, as an exercise, to go this route, which implied transferring all the work to the module (including the setting up of the needed fields). I appreciate your notes very much because it gives me a better picture of where a module hits a functionality limit. I think I'll rewind and settle with what I had before. I'm bookmarking your answer! Thanks.
mauricio
CC BY-SA 4.0
2021-10-27T11:08:19.887Z
2021-10-27T11:08:19.887Z
MoritzLost
<p>I read the docs multiple times but I'm stuck.  I need to create a module that shows a form in the CP where an admin can input and save answers to 2 questions. It will always be just one single form. I should also be able to read the saved values from within my code.</p>
<p>This is where I am: I can display  a link in the sidebar, and I can access the form when I click the link. And then my troubles begin:</p>
<ol>
<li>I know that I can get a model to populate the form with previously saved data. But I still haven't figure how to get the saved data. And haven't figured how to avoid error messages the first time the form is accessed and nothing has been saved yet.</li>
<li>The docs show an example, adding 2 routes (new and save) to <code>EVENT_REGISTER_CP_URL_RULES</code>. I imagine I only need one (for saving edits). But my attempts to register the rules haven't succeeded.</li>
</ol>
<p>What I have so far is:</p>
<p><code>Main Class:</code></p>
<pre class="lang-php prettyprint-override"><code>
// CP Routes
        Event::on(
            UrlManager::class,
            UrlManager::EVENT_REGISTER_CP_URL_RULES,
            function(RegisterUrlRulesEvent $event) {
                $event-&gt;rules['password-global'] = 'password-global/settings/index';
            }
        );
// Registers CP Template
        Event::on(
            View::class, 
            View::EVENT_REGISTER_CP_TEMPLATE_ROOTS, 
            function (RegisterTemplateRootsEvent $e) {
            if (is_dir($baseDir = $this-&gt;getBasePath() . DIRECTORY_SEPARATOR . 'templates')) {
                $e-&gt;roots[$this-&gt;id] = $baseDir;
                }
            }
        );
        
// Registers CP Section
        Event::on(
            Cp::class,
            Cp::EVENT_REGISTER_CP_NAV_ITEMS,
            function(RegisterCpNavItemsEvent $event) {
                $event-&gt;navItems[] = [
                    'url' =&gt; 'password-global/settings',
                    'label' =&gt; 'Global Password Protection',
                    'icon' =&gt; '@passwordGlobal/assets/password-lock.svg'
                ];
            }
        );
</code></pre>
<p>There's also:</p>
<ol>
<li>A model: <code>src/models/Settings.php</code></li>
<li>A form template: <code>src/templates/settings/index.html</code></li>
<li>A controller: <code>src/controllers/SettingsController.php</code></li>
</ol>
<p>The controller has a couple actions I defined, but they are kind of useless.</p>
<p>Thank you so much for any help!</p>

<control-panel><controller><modules>
CP Module: saving, editing, and reading a form
# CP Module: saving, editing, and reading a form

(Asked by: username_0 on 2021-10-26)

I read the docs multiple times but I'm stuck.  I need to create a module that shows a form in the CP where an admin can input and save answers to 2 questions. It will always be just one single form. I should also be able to read the saved values from within my code.
This is where I am: I can display  a link in the sidebar, and I can access the form when I click the link. And then my troubles begin:

I know that I can get a model to populate the form with previously saved data. But I still haven't figure how to get the saved data. And haven't figured how to avoid error messages the first time the form is accessed and nothing has been saved yet.
The docs show an example, adding 2 routes (new and save) to EVENT_REGISTER_CP_URL_RULES. I imagine I only need one (for saving edits). But my attempts to register the rules haven't succeeded.

What I have so far is:
Main Class:

// CP Routes
        Event::on(
            UrlManager::class,
            UrlManager::EVENT_REGISTER_CP_URL_RULES,
            function(RegisterUrlRulesEvent $event) {
                $event->rules['password-global'] = 'password-global/settings/index';
            }
        );
// Registers CP Template
        Event::on(
            View::class, 
            View::EVENT_REGISTER_CP_TEMPLATE_ROOTS, 
            function (RegisterTemplateRootsEvent $e) {
            if (is_dir($baseDir = $this->getBasePath() . DIRECTORY_SEPARATOR . 'templates')) {
                $e->roots[$this->id] = $baseDir;
                }
            }
        );
        
// Registers CP Section
        Event::on(
            Cp::class,
            Cp::EVENT_REGISTER_CP_NAV_ITEMS,
            function(RegisterCpNavItemsEvent $event) {
                $event->navItems[] = [
                    'url' => 'password-global/settings',
                    'label' => 'Global Password Protection',
                    'icon' => '@passwordGlobal/assets/password-lock.svg'
                ];
            }
        );

There's also:

A model: src/models/Settings.php
A form template: src/templates/settings/index.html
A controller: src/controllers/SettingsController.php

The controller has a couple actions I defined, but they are kind of useless.
Thank you so much for any help!

**Tags:** control-panel, controller, modules

---

## 1 answers

### Answer by username_1 on 2021-10-27. Score: 2 | Accepted answer

Where do you want to store your data? Looks like you're trying to create a settings form for a module? Your trouble probably comes from trying to build a module like you would a plugin.
Plugin settings
Plugins come with a lot of magic to define, store and read plugin settings (see the documentation for details). The settings are stored in the project config (and a dedicated table) and provided to the plugin on intialization. Modules, however, don't work the same way, so you have to do some steps manually, like finding a place to store your content. Take a look at the last section below (Custom storage solutions) for some pointers if you want to go that way.
Sidenote: If you in fact want to build a plugin, the documentation linked above has everything you need to define your setting fields with default values. Make sure you understand how those interact with the Project Config as well. See Supporting Project Config for details.
Use Global sets for module settings
Modules are site-specific, so if you're building a module, the assumption is that you have control over all aspects of the site (which you don't when building a plugin). You can, for instance, create Global Sets with custom fields that act as settings for your module. The advantage is that you're using built-in components and user interfaces, so you don't have to write your own model, interface or storage solution.
For example, let's say your module requires two fields, global_username and global_password. You can just create those as regular text fields, then create a global set (global_password_settings) with those two fields.
Then you can access the global set from your module and do whatever you want with the fields:
$passwordSettings = \Craft::$app->getGlobals()->getSetByHandle('global_password_settings');
$username = $passwordSettings->global_username;
$password = $passwordSettings->global_password;

Custom storage solutions
Just some quick pointers if you're looking to store more custom data.

You can create Custom Elements Types which allow you to use built-in fields and field layouts. Depending on your needs, your custom elements can store their content in the built-in content table or you can roll your own.
If you just need a custom database table without any interface (or you want to build a completely custom interface), you can use the Active Record pattern. Yii2 has some base classes you can extend to make this easier. Craft also uses active records for many components that aren't elements.
If you want to create your own custom database table from scratch, you can do that in the special enter link description hereplugin install migration.

All those solutions are pretty excessive if all you need is to store two strings.

> **Comments:**
> **username_0**: thank you for this clarification. It helps a lot. I indeed had the approach that you mentioned (using global variables) already in place and it's working. But since I'm trying to learn more of craft I decided, as an exercise, to go this route, which implied transferring all the work to the module (including the setting up of the needed fields). I appreciate your notes very much because it gives me a better picture of where a module hits a functionality limit. I think I'll rewind and settle with what I had before. I'm bookmarking your answer! Thanks.  

---

