v1.4.0

Configuration

Introduction

This refers to the various ways in which the configuration of your application can be handled in code at runtime. A good example is in the use of environmental variables, typically in an env file. It also involves all the knowledge around setting up your work station and tech stack for optimal performance. Therefore, see Configuration to mean Settings. It also involves information on the best practice for organising your project directory and file structure.


The directory structure has its own page — see Directory Structure.


Dorguzen Configuration System

The Dorguzen configuration system provides a centralized, cached, extensible, and environment-aware way to manage application settings across the entire framework.

It is designed around three core principles:

Single source of truth - configuration is loaded once and reused everywhere.

Zero re-instantiation - configuration is never reloaded or re-created.

Multiple access styles - global, helper-based, and DI-based access are all supported.

Files Involved

src/config/ConfigLoader.php

Responsible for discovering, loading, merging, and caching all configuration files.

Reads configuration files from:

configs/

configs/modules/

Supports multiple formats:

PHP

XML

YAML / YML

Handles config caching for performance

Returns a merged configuration array

This class does not expose configuration globally. It only loads it.

src/config/Config.php

The configuration repository.

This class:

Stores the final merged configuration array

Provides read/write access to config values

Acts as the single source of truth for config data

Common methods include:

all()

get(string $key, mixed $default = null)

has(string $key)

set(string $key, mixed $value)

getConfig() (backward compatibility)

getFileRootPath()

getHomePage()

…and other framework-specific helpers

Important: Config does not load files. It only stores and retrieves configuration data.

src/config/EnvLoader.php

Responsible for loading .env files and exposing their values globally.

Reads .env key/value pairs

Injects them into $_ENV

Works seamlessly with the env() helper

You do not need to understand its internals to use it — only how to consume .env values.

Directories Involved

src/config/

Contains all core configuration-related classes:

Config

ConfigLoader

EnvLoader

configs/

The primary configuration directory for your application.

Example files:

configs/
├── app.php
├── database.php
├── mail.yaml
└── cache.xml

configs/modules/

Optional but recommended directory for module-specific configuration.

Example:

configs/modules/
├── blog.php
├── auth.yaml
└── payments.xml

All module configs are:

Automatically discovered

Automatically merged

Namespaced by filename

bootstrap/cache/ ⚠️ Required

This directory must exist and be writeable.

Used for:

Cached merged configuration

Cache metadata/signatures

bootstrap/cache/
├── config.php
└── config.php.meta

If this directory is missing or not writeable, configuration caching will fail.

How the System Works

Configuration Bootstrap (bootstrap/config.php)

The configuration system is bootstrapped early in the application lifecycle.

Key steps:

// Safety check
if (!defined('DGZ_BASE_PATH')) {
    throw new DGZ_Exception(
        'config.php must be loaded via index.php',
        DGZ_Exception::INVALID_CONFIG
    );
}

// Paths
$configDir = DGZ_BASE_PATH . '/configs';
$cacheFile = DGZ_BASE_PATH . '/bootstrap/cache/config.php';

// 1) Load .env (optional)
$envLoader = new EnvLoader(DGZ_BASE_PATH);
$envLoader->load('.env');

// 2) Load config files (with caching)
$loader = new ConfigLoader($configDir, $cacheFile, true);
$configArray = $loader->load();

// 3) Create config repository
$GLOBALS['config'] = new Config($configArray);

// 4) Register in DI container
$container = $GLOBALS['container'];
$container->singleton(Config::class, fn () => $GLOBALS['config']);

// 5) Return repository
return $GLOBALS['config'];

Accessing Configuration Data

Rule: ONLY ONE INSTANCE of Config should ever exist. Never instantiate it manually.

Dorguzen provides three safe and supported access methods.

1️⃣ Via $GLOBALS

$GLOBALS['config']->getConfig();
$GLOBALS['config']->get('app');
$appName = $GLOBALS['config']->get('app.name');
$debug = $GLOBALS['config']->get('app.debug', false);
$GLOBALS['config']->all();

✔ Direct ✔ Fast ✖ Verbose

2️⃣ Via the config() Helper (Recommended)

Defined in bootstrap/helpers.php.

config('app.name');
config('database.connections.mysql.host');
config('app.debug', false);

This is functionally equivalent to:

$GLOBALS['config']->get('app.name');

✔ Clean ✔ Readable ✔ Preferred in application code

3️⃣ Via the DI Container

use Dorguzen\Config\Config;

$config = container(Config::class);

$config->get('app.name');
$config->getConfig(); // backward compatibility

✔ Ideal for services, models, and controllers ✔ Test-friendly ✔ Framework-consistent

Why bootstrap/config.php Returns the Config Object

At the end of bootstrap/config.php:

$GLOBALS['config'] = new Config($configArray);
return $GLOBALS['config'];

This enables two valid usage styles.

Style 1: Global (Current)

require_once 'bootstrap/config.php';
config('app.name');

Style 2: Explicit (Future-Proof)

$config = require 'bootstrap/config.php';
$config->get('app.name');

Benefits

CLI compatibility

Testability

Optional DI usage

Zero downside

Matches patterns used by Laravel, Symfony, and Slim

👉 Think of bootstrap/config.php as a factory file, not just a script.


Dorguzen Environment Configuration (.env)

Dorguzen uses environment variables to store sensitive and environment-specific configuration such as database credentials, cache drivers, API keys, and runtime modes (local, staging, production).

This system is powered by:

Dorguzen's EnvLoader

The battle-tested vlucas/phpdotenv package

The global env() helper

As a framework user, you do not need to understand the internals — only how to use .env files correctly.

1. Getting Started: .env.example

Dorguzen ships with two files named:

.env.example
.env.local.example

These files serve as a template showing all environment variables your application expects.

First step after installing Dorguzen

cp .env.example .env

and

cp .env.local.example .env.local

You will now work only with .env and .env.local, while keeping .env.example and .env.local.example as references only.

✅ .env.example and .env.local.example are committed to Git ❌ .env and .env.local must never be committed

2. The .env Files

The .env files contain key-value pairs, one per line.

Supported syntax

Both quoted and unquoted strings, as well as booleans (true/false) are supported:

APP_NAME="ProjectName"
APP_ENV=local
APP_SLOGAN="The Best Service in Town"
CACHE_DRIVER=file

✔ Quotes are optional ✔ Spaces are allowed inside quoted strings ✔ Comments are allowed using #

3. Environment Detection (APP_ENV)

The variable APP_ENV tells Dorguzen which environment the application is running in.

Example:

APP_ENV=local

Common values include:

local

staging

production

This value is used by Dorguzen to determine which additional .env files to load.

4. Environment-Specific .env Files

Dorguzen supports multiple environment files, loaded in a predictable order.

Load order (important)

.env (base defaults)

.env.{APP_ENV} (environment-specific)

.env.local (local machine overrides)

Example

If your .env contains:

APP_ENV=local
CACHE_DRIVER=file

And your .env.local contains:

CACHE_DRIVER=redis

Then the effective value will be:

env('CACHE_DRIVER'); // redis

Common patterns

File Purpose
.env Base configuration
.env.local Local developer overrides
.env.staging Staging server config
.env.prod Production server config

You can introduce as many environments as you like — Dorguzen will automatically load the matching file based on APP_ENV.

5. Git & Security Best Practices

These files must be ignored:

.env
.env.local
.env.staging
.env.prod

This file must be committed:

.env.example

This ensures:

Secrets never leak to GitHub

New developers know which variables are required

Production credentials remain server-only

In production, .env files are typically created directly on the server or injected via deployment tools, Docker, or CI/CD pipelines.

6. Using Environment Variables in Config Files

Environment variables are accessed using the global env() helper.

Example: configs/app.php

return [
    'appName' => env('APP_NAME'),
    'environment' => env('APP_ENV'),
    'cache_driver' => env('CACHE_DRIVER', 'file'),
];

Example: Database credentials

'localDBcredentials' => [
    'username'          => env('DB_LOCAL_USERNAME'),
    'pwd'               => env('DB_LOCAL_PASSWORD'),
    'db'                => env('DB_LOCAL_DATABASE'),
    'host'              => env('DB_LOCAL_HOST'),
    'connectionType'    => env('DB_LOCAL_CONNECTION'),
    'key'               => env('DB_LOCAL_KEY'),
],

The second argument to env() is an optional fallback value.

7. How env() Works (Conceptual)

The env() helper reads from PHP's environment:

getenv()

$_ENV

Fallback value (if provided)

Once loaded at bootstrap:

Environment variables are available globally

No container access is required

No performance cost at runtime

This makes environment variables ideal for:

Secrets

Credentials

Feature flags

Environment detection

8. Important Guidelines

✅ Do

Use env() only inside config files

Store secrets in .env, not in PHP code

Use .env.local for personal overrides

❌ Don't

Call env() deep inside application logic

Commit .env files

Hard-code credentials in configs

Once config files are loaded, your application should rely on config() — not env().

9. Summary

Dorguzen uses .env files for environment configuration

.env.example is your starting point

.env.local and .env.{APP_ENV} allow safe overrides

APP_ENV controls environment selection

env() bridges .env values into config files

Sensitive data never touches Git

10. URL and Path Variables per Environment

Dorguzen's Config class uses several .env variables to serve the correct URLs and asset paths depending on which environment the app is running in. It is important to set these correctly in every environment, or assets, links, and emails will point to the wrong place.

Key variables and their purpose:

Variable               Purpose
---------------------------------------------------------------------------
APP_ENV                Identifies the environment. Recognised values: local,
                       testing, staging, qa, prod, production.

APP_URL                The full base URL of the application in this environment.
                       Used by Config::getHomePage() and anywhere an absolute
                       URL is needed (e.g. unsubscribe links in emails).
                       Set this to the correct URL for every environment.

                         local:    APP_URL=http://localhost/myapp
                         staging:  APP_URL=https://staging.myapp.com
                         prod:     APP_URL=https://myapp.com

APP_FILE_ROOT_PATH_LOCAL   The URL path prefix used when the app runs locally
                           under a web server sub-folder (e.g. MAMP).
                           Example: /myapp/
                           Used by Config::getFileRootPath() when APP_ENV is
                           local or testing.

APP_FILE_ROOT_PATH_LIVE    The URL path prefix used on deployed servers.
                           Typically / (the app lives at the domain root).
                           Used by Config::getFileRootPath() for staging, qa,
                           prod, and production.

How Config::getFileRootPath() decides which to use:

  APP_ENV=local or testing  → returns APP_FILE_ROOT_PATH_LOCAL  (e.g. /myapp/)
  Any other environment     → returns APP_FILE_ROOT_PATH_LIVE   (e.g. /)

How Config::getHomePage() works:

  Always returns APP_URL for the current environment, trimmed of trailing slash.
  This means you only need to set APP_URL correctly in each environment's .env
  and all absolute URL generation (links, emails, redirects) will work everywhere.

Common mistake to avoid:

  Before Dorguzen supported multiple named environments, the live/local switch
  was driven by a boolean LIVE_ENV flag. If you see LIVE_ENV or APP_LIVE in an
  older .env file, those are legacy — APP_ENV is the correct switch now.

  Also ensure your .env variable names match exactly what configs/app.php reads.
  For example, FILE_ROOT_PATH_LOCALL (two L's) is a typo that silently falls
  through to the default — always verify against configs/app.php.

11. Server Upload Limits and the PHP SAPI (.htaccess and .user.ini)

This section explains how Dorguzen controls PHP upload and memory limits, why two separate configuration files are used, and what you need to know when deploying to different server environments.

What is a PHP SAPI?

SAPI stands for Server API. It is the layer that connects your web server (Apache, Nginx, etc.) to PHP — in other words, it is the mechanism by which Apache hands an incoming HTTP request to PHP for processing.

There are two SAPIs you will commonly encounter:

  mod_php
    PHP is loaded directly inside Apache as a module. Apache and PHP are tightly
    coupled. Configuration for PHP can be set inside .htaccess using the php_value
    directive. This was the standard approach for many years and is still used on
    some servers.

  PHP-FPM (FastCGI Process Manager)
    PHP runs as a completely separate process from Apache. Apache passes requests
    to PHP-FPM over a FastCGI connection. They are decoupled. This is the modern
    standard and what most up-to-date servers (including newer versions of MAMP)
    use by default.

    Because PHP is a separate process with PHP-FPM, Apache's php_value directives
    in .htaccess have no effect — Apache cannot reach across into the PHP process
    to set configuration values. A different mechanism is needed.

The key question: which SAPI is my server using?

  If you are using a modern version of MAMP, the answer is almost certainly PHP-FPM.
  If you are on a traditional cPanel shared hosting plan, it may be mod_php.
  If you are on a modern VPS or cloud server (DigitalOcean, AWS, etc.) it is
  almost certainly PHP-FPM.

  You do not need to be certain — Dorguzen ships both configuration files so the
  correct one is always present regardless of the environment.

What .htaccess does (and what it does not do)

The .htaccess file in the project root has two jobs:

  1. URL rewriting — redirect every request to index.php (the front controller),
     unless the request is for a real file on disk (CSS, images, JS, etc.).
     This is handled by Apache's mod_rewrite module and works identically on both
     mod_php and PHP-FPM. The SAPI does not affect URL rewriting at all.

  2. PHP settings (mod_php only) — set upload limits and memory using php_value
     directives inside <IfModule mod_php8.c> blocks. These are silently ignored
     when the server uses PHP-FPM.

Dorguzen's .htaccess currently contains:

  # ------------------------------- RULE: catch-all (any depth)
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteRule ^ index.php [L]

  <IfModule mod_php7.c>
     php_value upload_max_filesize 50M
     php_value post_max_size 50M
     php_value memory_limit 256M
  </IfModule>
  <IfModule mod_php8.c>
     php_value upload_max_filesize 50M
     php_value post_max_size 50M
     php_value memory_limit 256M
  </IfModule>

The <IfModule> wrapper is important: it tells Apache to only apply those directives if the named module is loaded. Without it, Apache would throw a 500 error on any server that does not have mod_php installed. With it, the block is simply skipped on PHP-FPM servers — safely and silently.

Blocks for both mod_php7.c and mod_php8.c are included to cover PHP 7.x and 8.x mod_php installations respectively.

What .user.ini does (PHP-FPM only)

PHP-FPM reads a special file called .user.ini from the document root of each project. Think of it as the PHP-FPM equivalent of the php_value directives in .htaccess. It uses standard PHP ini syntax — plain key = value pairs, no Apache directives, no module wrappers.

Dorguzen's .user.ini currently contains:

  upload_max_filesize = 50M
  post_max_size = 50M
  memory_limit = 256M

This file is read automatically by PHP-FPM on every request and requires no special setup. It also works on shared hosting running PHP-FPM where you do not have access to the server's main php.ini — making it the correct solution for the majority of modern hosting environments.

Why both files exist

Dorguzen ships both .htaccess and .user.ini so that the same upload limits apply regardless of which SAPI the server uses:

  Server uses mod_php  → .htaccess php_value blocks take effect
                          .user.ini is ignored (PHP-FPM is not running)

  Server uses PHP-FPM  → .user.ini takes effect
                          .htaccess php_value blocks are silently skipped

You do not need to choose one or delete the other. Keep both files in place and the correct one will be picked up automatically.

What happens when an upload exceeds the limit

PHP has two relevant ini settings that work together:

  post_max_size       — the maximum size of the entire HTTP POST body (all form
                        fields plus all uploaded files combined).

  upload_max_filesize — the maximum size of a single uploaded file.

post_max_size must always be equal to or larger than upload_max_filesize, because the uploaded file travels inside the POST body.

If a request body exceeds post_max_size, PHP silently discards the entire $_POST array before your application runs. This has an important side effect: the CSRF token (which travels in $_POST) also disappears, causing the CSRF middleware to appear to fail with a misleading "Invalid or missing CSRF token" error.

Dorguzen's CsrfPsrMiddleware detects this situation before checking the CSRF token. When it finds that the incoming Content-Length exceeds post_max_size, it throws a clear exception:

  "Uploaded file exceeds server max size limit"
  "The data you submitted (X MB) exceeds the server's maximum allowed POST size
  (50M). Please upload a smaller file."

This means you will always get a meaningful error instead of a confusing CSRF failure when a file is too large.

Note: PHP emits its own low-level warning about this ("POST Content-Length of X bytes exceeds the limit of Y bytes in Unknown on line 0") before your application code runs. This warning appears in the browser on local/development environments because display_errors is on. It is suppressed automatically in production because Dorguzen calls setupErrorHandling($env) in index.php, which turns display_errors off for any environment that is not local.

12. Key Application Variables Reference

The narrative above explains how the .env system works. The table below is the concrete reference of the application-level variables Dorguzen reads in configs/app.php (and configs/logging.php). Each is consumed via env() with the fallback shown, so an unset variable degrades to a safe default rather than failing.

Variable                  configs key             Purpose / notes
-----------------------------------------------------------------------------
APP_LOCAL_URL             app.localUrl            Base URL when running locally
                                                  (e.g. http://localhost/myapp/).
APP_LIVE_URL              app.liveUrl             Base URL on the deployed server.
APP_LIVE_URL_SECURE       app.liveUrlSecure       HTTPS base URL for the live site.
APP_EMAIL                 app.appEmail            Admin recipient address used by
                                                  DGZ_Messenger. MUST NOT be empty —
                                                  it is also the default From/Reply-To
                                                  (localHeaderFrom, liveHeaderFrom,
                                                  headerReply-To all read APP_EMAIL).
APP_CONTACT_TEL           app.site_contact_tel    Public contact phone number.
APP_POST_ADDRESS          app.site_postal_address Public postal address.
APP_JWT_SECRET            app.jwt-secret-key      Secret key for signing API JWTs.
APP_JWT_ENCODING          app.encoding_algorithm  JWT signing algorithm (e.g. HS256).
APP_API_CSRF_EXCEPTION    app.csrf_except         Path prefix exempt from CSRF
                                                  validation. Set to '/api/' to exempt
                                                  all API routes (array_filter drops it
                                                  when unset, so no blanket exemption).
QUEUE_DRIVER              app.queue_driver        Queue backend: sync (dev) | db |
                                                  rabbitmq (production).
ALLOW_REGISTRATION        app.allow_registration  Gates public self-service signup;
                                                  new registrants default to 'member'.
APP_LOG_DRIVER            logging driver          Where logs go: file | db | both.
APP_LOG_FORMAT            logging format          Log line format: text | json.
API_DOCS_ENABLED          (read directly)         When true, exposes the API docs
                                                  endpoint (DocsController). Leave
                                                  false/unset in production.
SLACK_WEBHOOK_URL         (read by Slack channel) Incoming-webhook URL for Slack
                                                  notifications.
SLACK_DEFAULT_CHANNEL     (read by Slack channel) Optional override of the webhook's
                                                  own default channel (e.g. #general).

Module on/off flags follow the same pattern in app.modules:

MODULES_SEO_STATUS=on        # on | off
MODULES_PAYMENTS_STATUS=off
MODULES_SMS_STATUS=off
MODULES_GALLERY_STATUS=off
MODULES_VIDEOS_STATUS=off
MODULES_BLOG_STATUS=off
MODULES_TESTIMONIALS_STATUS=off

Beyond the format examples shown earlier, the configs/ directory ships these real PHP config files: app.php (identity, URLs, upload dirs, JWT, queue driver, module toggles, role permissions), database.php (DB credentials/connections), events.php (event → listener map), and logging.php (log driver/format).


Role Permissions

Fine-grained admin permissions are defined in configs/app.php under the permissions key. Each entry maps a capability to the list of roles allowed to use it:

// configs/app.php
'permissions' => [
    'seo'          => ['admin', 'admin_gen', 'super_admin'],
    'payments'     => ['admin_gen', 'super_admin'],
    'manage_users' => ['admin', 'admin_gen', 'super_admin'],
    'settings'     => ['super_admin'],
],

Read these in code via the config repository (e.g. config('app.permissions.settings')) to check whether the current user's role is authorised for an action. Note that settings is restricted to super_admin — the most privileged role — while broader capabilities like seo and manage_users also admit admin and admin_gen.


Customising a New Application After Cloning

When you clone or install a fresh Dorguzen application, there are two kinds of customisation you will want to do immediately: rename the application and set its colour theme. These are handled in two different places by design.

1. Rename Your Application

Open your .env file and update the following variables to reflect your new project:

    APP_NAME=my-app-slug          # used internally (e.g. for logging)
    APP_BUSINESS_NAME="My App"    # the human-readable brand name shown in the UI
                                  # and in email headers
    APP_SLOGAN="Your tagline here"
    APP_URL=http://localhost/my-app   # base URL for this environment

These values are read by configs/app.php via env() and are available throughout the application via $this->config->get('app.appName') etc., and are injected automatically into email layouts.

Do NOT hardcode the app name in layout files or views — always read it from config so a single .env change renames the whole application consistently.

2. Set the Colour Theme

The colour theme is NOT set in .env. It is controlled at runtime through the admin panel and stored in the baseSettings database table.

After logging in as an admin, navigate to:

    Admin Dashboard → Settings

Find the app_color_theme field and enter a valid CSS colour value — any hex code, rgb(), or named colour works:

    Examples:  #e63946   #3a86ff   #2ecc71   rgb(52, 152, 219)

How the override mechanism works:

  Every layout file (seoMasterLayout.php, adminLayout.php, etc.) reads the
  app_color_theme value from the database via $this->config->getAppColorTheme()
  and injects it as an inline <style> block in the page <head>:

      <style>:root { --site-theme: #3a86ff; }</style>

  This inline declaration overrides the default value defined in assets/css/style.css:

      :root {
          --site-theme: #fd7e14;  /* default orange — overridden by DB value at runtime */
      }

  Because inline <style> has higher specificity than an external stylesheet, the
  DB value always wins on every page load. No CSS file needs to be edited, no
  deployment is required — the change is instant and affects the entire site.

  Email layouts (layouts/email/defaultEmailLayout.php) use the same value via the
  $accentColour variable, which is injected by DGZ_Messenger::renderEmail() the
  same way.

What --site-theme controls:

  The CSS variable --site-theme is referenced throughout assets/css/style.css for:
  - Primary button background and border colours (.btn-primary, .bg-primary)
  - Outline button colours (.btn-outline-primary)
  - Navigation link and nav-item colours
  - Text highlights (.text-primary, .text-secondary)
  - Hero section button accent colours
  - Team card and testimonial carousel accent colours
  - Breadcrumb separator and back-to-top button icon
  - Email header background and footer accent bar

  Changing app_color_theme in the admin panel therefore recolours the entire
  application at once with no code changes.

3. Summary: Two Different Customisation Paths

  What you're changing         Where to change it
  ---------------------------------------------------------------
  App name, slogan, URL        .env  (APP_NAME, APP_BUSINESS_NAME, APP_URL, etc.)
  Colour theme                 Admin Dashboard → Settings → app_color_theme
  Layout structure / markup    layout PHP files in layouts/
  CSS overrides beyond colour  assets/css/style.css

Never hardcode the app name or the colour value in templates — keep both as single points of truth so they stay consistent across views, emails, and admin.


Security Recommendations

✅ Never commit .env to Git

✅ Add .env to .gitignore

✅ Use .env.example for documentation

✅ Store secrets only in .env

✅ Load secrets into config via env()

❌ Do not hard-code credentials in config files

In production:

Use environment-level variables

Lock down file permissions

Ensure bootstrap/cache/ is writable but protected

Summary

✔ Centralized configuration ✔ Cached for performance ✔ Module-aware ✔ Environment-aware ✔ Single instance ✔ Multiple access styles


Runtime Database Settings (baseSettings)

What baseSettings Is

Dorguzen ships with a database table called baseSettings. Its purpose is to hold a small number of settings that a site administrator needs to be able to change at runtime through the admin panel — without touching any config files, redeploying, or restarting the server.

These settings are loaded lazily by the Config class when first needed and are kept entirely separate from the file-based configuration loaded at bootstrap. They do not live in configs/app.php, they are not in .env, and they are not part of the config cache. They are always read live from the database.

The Two-Tier Settings Design

Dorguzen intentionally splits application settings into two tiers:

  Tier 1 — File-based config (bootstrap time)
    Loaded from `configs/` files and `.env` at the very start of every request.
    Cached for performance. Controlled by developers and deployment pipelines.
    Accessed via config('app.key') or $this->config->get('app.key').

  Tier 2 — Database settings (runtime, admin-controlled)
    Stored in the `baseSettings` DB table.
    Loaded lazily on first use during a request.
    Controlled by site administrators through the admin panel.
    Accessed via $this->config->getBaseSettings()['key'].

The division is a deliberate design decision: settings that are infrastructure or code decisions belong in files, while settings that a non-developer site admin might legitimately want to adjust belong in the database.

What Belongs in File Config (Bootstrap Settings)

Use configs/app.php and .env for settings that:

  - Are set once at deployment and rarely change
  - Require a server-level or code-level change to modify safely
  - Contain sensitive data (database credentials, API keys, JWT secrets)
  - Must be available before any DB connection is established
  - Are developer decisions, not admin decisions

Examples:

  - APP_NAME, APP_URL, APP_ENV
  - Database credentials (DB_HOST, DB_USERNAME, DB_PASSWORD)
  - JWT secret key
  - Mail/SMTP credentials
  - Whether the app is live or local
  - Default locale and fallback locale
  - Module on/off flags (seo, payments, sms)
  - allow_registration (a deployment decision, not a runtime toggle — gates public
    self-service signup; new registrants default to the 'member' role. See
    "Public registration & the default member role" under User Roles.)

What Belongs in baseSettings (Runtime Settings)

Use the baseSettings DB table for settings that:

  - A site admin should be able to change through the UI without code access
  - Affect the visual appearance or behaviour of the site, not its infrastructure
  - Do not contain sensitive data
  - Are safe for a non-developer to change
  - Take effect immediately on the next page load without any deployment step

Examples of settings that belong here:

  - brand_slider_source — the path to the directory containing the slider images
  - app_color_theme     — the CSS colour theme applied to the site
  - Any future UI toggle an admin should control (e.g. maintenance mode banner,
    show/hide a promotions section, active site theme name)

A good rule of thumb: if changing the setting requires the admin panel, it belongs in baseSettings. If changing it requires editing a file or running a deployment, it belongs in file config.

Accessing baseSettings in Code

The Config class provides a dedicated method for reading baseSettings. It is lazy-loaded — the database is only queried the first time it is called on a given request.

    // In a controller, layout, or view partial:
    $baseSettings = $this->config->getBaseSettings();

    $colorTheme = $this->config->getAppColorTheme();
    // equivalent to $this->config->getBaseSettings()['app_color_theme']

Important: do NOT mix the two tiers. Do not call config('app.app_color_theme') — that key does not exist in the file config. And do not call getBaseSettings() to read database credentials — they are not in the DB table. Each tier has its own access method for a reason.

The baseSettings DB Table

The table has a simple structure:

    settings_id    INT  (primary key, auto-increment)
    settings_name  VARCHAR  (the key, e.g. 'brand_slider_source')
    settings_value VARCHAR  (the value, e.g. 'true')

Current entries:

    brand_slider_source  e.g. 'assets/images/gallery'
                         The path (relative to the app root) of the directory
                         from which the brand slider pulls its images.
                         See the Brand Slider section below for full details.

    app_color_theme      e.g. 'dark-blue'
                         The name of the CSS colour theme file to load.
                         Available themes are the CSS files in assets/css/color/.
                         The value here must match a filename in that directory
                         (without the .css extension).

Managing baseSettings via the Admin Panel

Log in as an admin and navigate to:

    Admin Dashboard → Settings

This page reads all rows from the baseSettings table and renders a form field for each one. Submitting the form updates each setting's value in the database. Changes take effect on the next page load — no restart or deployment required.

To add a new runtime setting:

  1. Insert a row directly into the `baseSettings` table:
         INSERT INTO baseSettings (settings_name, settings_value)
         VALUES ('my_new_setting', 'default_value');
  2. Add a corresponding form field for it in views/admin/manageSettings.php.
  3. Read it in your code via $this->config->getBaseSettings()['my_new_setting'].

The Brand Slider

What It Is

The brand slider is a horizontal image carousel that sits between the main page content and the footer. It is designed to showcase a set of images — typically brand logos, product photos, or portfolio images — in a continuously scrolling strip. It is powered by Owl Carousel.

It is a per-page opt-in feature: it is off by default on every page. Each controller method that wants the slider must explicitly enable it. The two supported layouts are:

  - layouts/seoMaster/seoMasterLayout.php   (the recommended default layout)
  - layouts/dorguzApp/dorguzAppLayout.php   (the older full-featured layout)

How to Enable It (Per Page)

The brand slider is controlled from the controller via the inherited setImageSlider() method. Call it before rendering the view:

    public function home(): void
    {
        $this->setImageSlider(true);   // show the slider on this page only

        $view = DGZ_View::getView('home', $this, 'html');
        $view->show($this->homeService->homePayload());
    }

Pages that do not call $this->setImageSlider(true) will never show the slider. The layout checks the $this->showImageSlider property (which defaults to false) and skips the carousel HTML entirely — no empty space, no placeholder.

This is preferable to a global DB toggle because:

  - Different pages can make independent decisions
  - A developer can clearly see from the controller whether a page has a slider
  - No database round-trip is needed just to decide whether to render a section

How to Configure the Image Source (brand_slider_source)

The brand_slider_source setting tells the slider where to find its images. Its value is a path relative to the application root — not a URL, not an absolute server path. For example:

    assets/images/gallery

This means the slider will look for images inside:

    /your-app-root/assets/images/gallery/

To change it, update the DB value:

    UPDATE baseSettings SET settings_value = 'assets/images/myimages'
    WHERE settings_name = 'brand_slider_source';

Or change it through the admin panel Settings page.

The path must be relative to the application root (the directory containing index.php). Do not include a leading or trailing slash. Do not use a URL — the framework resolves the file system path and the browser URL separately from this single relative value.

Adding Your Images

Simply drop image files into the directory pointed to by brand_slider_source. The slider automatically picks up every .jpg, .jpeg, .png, .gif, and .webp file in that directory. There are no filenames to configure and no array to maintain.

Steps:

  1. Decide on your image directory (e.g. assets/images/gallery/).
  2. Update brand_slider_source in the DB to match (e.g. 'assets/images/gallery').
  3. Drop your images into that directory.
  4. Call $this->setImageSlider(true) in any controller method that should show it.
  5. Load that page — the slider appears between the content and the footer.

To remove an image from the slider, delete the file from the directory. To add more images, drop them into the directory. No code changes needed.

Dorguzen ships with a default gallery directory at assets/images/gallery/ seeded with six demo images. These are placeholder images and should be replaced with your own before going live.

For best visual results use images that are the same dimensions as each other. Wide landscape images (e.g. 300×200px) work best for brand/logo sliders.

How It Works Internally

In both seoMasterLayout and dorguzAppLayout, the brand slider block:

  1. Checks $this->showImageSlider (a boolean property on DGZ_Layout, defaulting
     to false). This value is set by the framework automatically from the
     controller's own $showImageSlider property, which the developer sets via
     $this->setImageSlider(true) before calling $view->show().

  2. If $this->showImageSlider is false, nothing is rendered.

  3. Calls $this->config->getBaseSettings() to retrieve brand_slider_source,
     then builds the absolute file system path from DGZ_BASE_PATH and that value,
     using PHP's glob() to discover all image files in that directory.

  4. Renders an <img> tag for each file found, building the browser URL from
     getFileRootPath() (which resolves correctly for both local and live
     environments) and the relative source path.

  5. The #brands-carousel div is initialised by Owl Carousel, which is already
     loaded by both layouts' html_dependencies files.

The brand slider shares the same Owl Carousel library used by other carousels in the layouts — no additional JavaScript or CSS dependencies are needed.


The SEO module has its own page — see SEO Module.


The assetVer() Helper — Cache-Busting for CSS & JavaScript

When a browser loads your site, it saves ("caches") your CSS and JavaScript files on the visitor's device so the next visit loads faster. The catch: after you edit a file like style.css and upload the new version, many browsers keep serving the OLD cached copy for a while — so your visitors don't see your change. This is the classic "I changed the CSS but the old style still shows" problem, and it's especially stubborn on mobile (iPhone Safari).

  • Cache: a local copy a browser keeps of a file so it doesn't re-download it every visit.
  • Cache-busting: a trick that makes the browser treat an updated file as a brand-new file, so it downloads the fresh copy straight away.

Dorguzen solves this with the global helper assetVer() (defined in bootstrap/helpers.php). It returns the asset's URL with a version "stamp" appended — the file's last-modified time:

/assets/css/style.css?v=1719357141

Because the number after ?v= changes the moment you edit the file, the URL changes too, so every browser (phones included) treats it as a new file and fetches the latest version. No visitor ever has to clear their cache.

Use it in your layouts and views for any LOCAL css or js file, in place of the raw path:

Before:
<link href="<?= $this->config->getFileRootPath() ?>assets/css/style.css" rel="stylesheet">
<script src="<?= $this->config->getFileRootPath() ?>js/main.js"></script>

After:
<link href="<?= assetVer('assets/css/style.css') ?>" rel="stylesheet">
<script src="<?= assetVer('js/main.js') ?>"></script>

The path you pass is relative to your project root (e.g. 'assets/css/style.css'). The helper safely checks the file exists before stamping it, so a wrong path never throws an error. Use it only for your OWN files — external CDN links (e.g. Bootstrap loaded from a CDN) already carry a version in their URL, so they don't need it.