M

Manual Rede das Artes

Desenvolvimento

Arquitetura

Visão técnica do Mapas Culturais: stack, estrutura de diretórios, abstrações centrais e padrões de projeto.

O Mapas Culturais é uma aplicação web em PHP 8.3 voltada a mapeamento cultural. Ela é construída sobre Slim 4 como microframework HTTP, Doctrine ORM 2 para acesso a dados e PostgreSQL com PostGIS para dados geoespaciais. Os assets de frontend são gerenciados com pnpm workspaces.

Stack

LayerTechnology
LanguagePHP 8.3 (declare(strict_types=1))
HTTP frameworkSlim 4
ORMDoctrine ORM 2.16
DatabasePostgreSQL + PostGIS
CacheRedis (configurable)
Frontend buildpnpm workspaces
EmailSymfony Mailer
LoggingMonolog
ValidationRespect Validation

Estrutura de diretórios

src/
  core/        → MapasCulturais\      (framework base classes)
  modules/     → MapasCulturais\Modules\ (modular features)
  themes/      → MapasCulturais\Themes\  (installation themes)
  conf/        → configuration loader
  tools/       → CLI tools (apply-updates.php, psysh.php)
config/        → base configuration (PHP arrays)
dev/config.d/  → local development overrides
public/        → web root
tests/         → PHPUnit test suite
helm/          → Helm chart for Kubernetes
docker/        → Docker configuration and entrypoint

PSR-4 namespaces

NamespaceDirectory
MapasCulturais\src/core/
MapasCulturais\Modules\src/modules/
MapasCulturais\Themes\src/themes/
Tests\tests/

Core abstractions

App singleton

The application is accessed as a singleton via App::i(). It holds every top-level service:

$app = MapasCulturais\App::i();

$app->em;           // Doctrine EntityManager
$app->hooks;        // Hook/event system
$app->view;         // Active theme instance
$app->auth;         // Authentication provider
$app->cache;        // Persistent cache (Redis)
$app->log;          // Monolog logger

The singleton pattern means you can retrieve the same application instance from anywhere in the codebase without passing dependencies manually.

Entity

MapasCulturais\Entity is the abstract base class for all Doctrine-managed objects. Entities use PHP 8 attributes for ORM mapping:

use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
#[ORM\Table(name: 'agent')]
class Agent extends MapasCulturais\Entity
{
    // ...
}

All entities share a common set of status constants:

ConstantValueMeaning
Entity::STATUS_ENABLED1Published and visible
Entity::STATUS_DRAFT0Saved but not published
Entity::STATUS_ARCHIVED-2Archived
Entity::STATUS_DISABLED-9Disabled by an admin
Entity::STATUS_TRASH-10Soft-deleted (trash)

Entity instances expose permission checking through canUser() and checkPermission():

// Returns true/false
$agent->canUser('modify');

// Throws PermissionDenied if user cannot perform the action
$agent->checkPermission('remove');

Controller

MapasCulturais\Controller is the abstract base for all route handlers. Action methods are named after the HTTP method they handle:

class MyController extends MapasCulturais\Controller
{
    // Responds to GET /my-controller/index
    public function GET_index(): void
    {
        $this->render('my-controller/index', ['data' => []]);
    }

    // Responds to any HTTP method
    public function ALL_create(): void
    {
        $this->requireAuthentication();
        $this->json(['status' => 'ok']);
    }
}

Register a controller in the application:

$app->registerController('my-controller', MyController::class);

Inside action methods, request data is available through:

  • $this->data — merged URL params + $_REQUEST
  • $this->getData, $this->postData, $this->putData

Hooks

Hooks are the primary extension mechanism. They let modules and themes react to named events throughout the request lifecycle:

$app->hook('entity(Agent).save:before', function() {
    // $this is the Agent entity being saved
});

See the hooks reference for the full naming syntax and available events.

Database migrations

Migrations live in src/db-updates.php and are applied automatically on container startup via docker/entrypoint.sh. You can also apply them manually:

./scripts/db-update.sh

There is no migration versioning tool — each entry in db-updates.php is tracked by a hash and only applied once.

Common request flows

The legacy developer guide spent more time on how requests move across the stack. The three flows below are the ones worth keeping in your mental model when debugging.

Authentication flow

This is why auth bugs often span configuration, provider code, theme-level logout UI, and controller behavior at the same time.

Entity creation flow

If an entity "saves but behaves strangely", inspect hooks and async jobs in addition to the controller itself.

Search flow

Search bugs frequently come from filter syntax, metadata visibility, or geospatial assumptions rather than from a broken endpoint.

Configuration

Configuration is merged from multiple sources at runtime:

config/ — PHP array files with defaults for all settings.

Each theme can contribute its own configuration values through conf-base.php inside the theme directory.

dev/config.d/ — local override files loaded in alphabetical order. The active theme, debug flags, and local credentials go here.

Key variables: DB_HOST, DB_NAME, DB_USER, DB_PASS, REDIS_CACHE, APP_DEBUG, BASE_URL.

Authentication

Authentication providers are pluggable. The active provider is set in configuration and is accessed at runtime through $app->auth. Providers implement the MapasCulturais\AuthProvider interface.

Data layer notes

The data layer is more than "Doctrine over PostgreSQL":

  • PostgreSQL stores the canonical application state
  • PostGIS supports map-aware queries such as proximity filters
  • metadata tables extend base entities without schema changes for every new field
  • Redis can back cache and session storage when enabled

That combination explains a common maintenance pattern: schema issues, metadata issues, and geospatial issues may surface in the same user-facing feature.

Error handling

Use typed exceptions from MapasCulturais\Exceptions\:

ExceptionWhen to use
PermissionDeniedUser lacks the required permission
NotFoundEntity or resource does not exist
WorkflowRequestInvalid state transition in a workflow

Key design patterns

Singleton

App is a singleton accessed via App::i(). Ensures a single shared instance of all services.

Repository (Doctrine)

Entities are queried through Doctrine repositories: $app->repo('Agent')->find($id).

Hook / Event system

Modules and themes extend behavior by registering callbacks on named hooks rather than modifying core files.

Frontend build

Frontend assets are organized as pnpm workspaces under src/:

# Install all workspace dependencies
dev/pnpm.sh -C src install

# Production build
dev/pnpm.sh -C src run build

# Development build with source maps
dev/pnpm.sh -C src run dev

# Watch mode
dev/pnpm.sh -C src run watch

Workspace packages: src/modules/*, src/plugins/*, src/themes/*, src/node_scripts.

Frontend builds must run inside the Docker container or via the dev/pnpm.sh wrapper, which proxies commands into the container where Node.js and pnpm are available.


Esse material é fruto do Programa de Difusão Nacional - Funarte Redes das Artes, realizado pelo Laboratório do Futuro (entidade vinculada à Universidade Federal do Ceará) no ano de 2025.

Felicilab
Mutirão
Lab do Futuro UFC
UFC
Rede das Artes Funarte
Funarte
MinC Governo Federal

On this page