The settings page of a module
Almost every module has settings: at least a switch «enabled» and some text. The settings page is the admin controller of the extension. It shows a form, and a second method saves the values with the core model setting/setting.
Files
extension/acme_hello/admin/
├── controller/module/acme_hello.php
├── language/en-gb/module/acme_hello.php
└── view/template/module/acme_hello.twigTexts
// extension/acme_hello/admin/language/en-gb/module/acme_hello.php
$_['heading_title'] = 'Acme Hello';
$_['text_success'] = 'Settings saved.';
$_['entry_text'] = 'Greeting text';
$_['entry_status'] = 'Status';
$_['error_permission'] = 'You do not have permission to modify this module.';The controller
// extension/acme_hello/admin/controller/module/acme_hello.php
public function index(): void
{
$this->load->language('extension/acme_hello/module/acme_hello');
$this->document->setTitle($this->language->get('heading_title'));
$token = 'user_token=' . $this->session->data['user_token'];
$data['save'] = $this->url->link('extension/acme_hello/module/acme_hello.save', $token);
$data['back'] = $this->url->link('marketplace/extension', $token . '&type=module');
$data['module_acme_hello_text'] = $this->config->get('module_acme_hello_text');
$data['module_acme_hello_status'] = $this->config->get('module_acme_hello_status');
$data['header'] = $this->load->controller('common/header');
$data['column_left'] = $this->load->controller('common/column_left');
$data['footer'] = $this->load->controller('common/footer');
$this->response->setOutput($this->load->view('extension/acme_hello/module/acme_hello', $data));
}The paths of the language file and the template start with extension/acme_hello/. Current values are read from $this->config. Every admin link carries user_token — the next lesson explains it.
The form
{# extension/acme_hello/admin/view/template/module/acme_hello.twig #}
{{ header }}{{ column_left }}
<div id="content">
<div class="page-header">
<div class="container-fluid">
<div class="float-end">
<button type="submit" form="form-module" class="btn btn-primary">{{ button_save }}</button>
<a href="{{ back }}" class="btn btn-light">{{ button_back }}</a>
</div>
<h1>{{ heading_title }}</h1>
</div>
</div>
<div class="container-fluid">
<form id="form-module" action="{{ save }}" method="post" data-oc-toggle="ajax">
<div class="row mb-3">
<label for="input-text" class="col-sm-2 col-form-label">{{ entry_text }}</label>
<div class="col-sm-10">
<input type="text" name="module_acme_hello_text" value="{{ module_acme_hello_text }}" id="input-text" class="form-control">
</div>
</div>
<div class="row mb-3">
<label class="col-sm-2 col-form-label">{{ entry_status }}</label>
<div class="col-sm-10">
<div class="form-check form-switch form-switch-lg">
<input type="hidden" name="module_acme_hello_status" value="0">
<input type="checkbox" name="module_acme_hello_status" value="1" class="form-check-input"{% if module_acme_hello_status %} checked{% endif %}>
</div>
</div>
</div>
</form>
</div>
</div>
{{ footer }}Open Extensions → Extensions → Modules → Acme Hello → Edit — the page shows a text field and a switch.
Saving
public function save(): void
{
$this->load->language('extension/acme_hello/module/acme_hello');
$this->load->model('setting/setting');
$this->model_setting_setting->editSetting('module_acme_hello', $this->request->post);
$json['success'] = $this->language->get('text_success');
$this->response->addHeader('Content-Type: application/json');
$this->response->setOutput(json_encode($json));
}Press the save button — the message «Settings saved.» appears. editSetting() writes to the setting table every form field whose name starts with the code module_acme_hello.
Reading a setting
Saved values are available in any controller or model, in the admin panel and on the storefront:
$text = $this->config->get('module_acme_hello_text');Common mistake
A field is named text or acme_text — and its value is not saved or is not found later. The name of every field must start with the code passed to editSetting(), and the code for a module is module_ plus the file name: module_acme_hello_text. The key module_acme_hello_status is also what the core checks to decide whether the module is enabled.