grav avanzados, administración

Configuración multisitio

Grav tiene soporte preliminar para multisitio disponible. Sin embargo, los comandos CLI y el plugin Admin aún necesitan actualizarse para admitir completamente configuraciones multisitio. Seguiremos trabajando en esto en versiones posteriores de Grav.

¿Qué es una configuración Multisitio?

Una configuración multisitio te permite crear y administrar una red de múltiples sitios web, todos funcionando en una sola instalación.

Grav tiene soporte multisitio incorporado. Esta funcionalidad extiende la configuración básica de entorno, que te permite definir entornos personalizados para tus sitios de producción y desarrollo.

Una configuración multisitio completa te da el poder de cambiar la forma en que Grav carga todos sus archivos y desde dónde.

Requisitos para una configuración Multisitio en Grav

Lo más importante que necesitarás para ejecutar una red multisitio en Grav es un buen alojamiento web. Si no planeas crear muchos sitios y no esperas muchos visitantes, entonces puedes arreglártelas con un alojamiento compartido. Sin embargo, debido a la naturaleza de los multisitios, probablemente necesitarás un VPS o servidor dedicado a medida que tus sitios crezcan.

Configuración e instalación

Antes de comenzar, querrás asegurarte de que tu servidor web sea capaz de ejecutar múltiples sitios web, es decir, que tengas acceso al directorio raíz de Grav.

Esto es esencial ya que servir múltiples sitios web desde la misma instalación se basa en un archivo setup.php ubicado en la raíz de Grav.

Inicio rápido (para Principiantes)

Una vez creado, el setup.php se llama cada vez que un usuario solicita una página. Para servir múltiples sitios web desde una sola instalación, este script (en términos generales) debe decirle a Grav dónde se encuentran los archivos (para las configuraciones, temas, plugins, páginas, etc.) para un subsitio específico.

Los fragmentos proporcionados a continuación configuran tu instalación de Grav de tal manera que una solicitud como:

http://<subsitio>.ejemplo.com   -->   user/sites/<subsitio>.ejemplo.com

o

http://ejemplo.com/<subsitio>   -->   user/sites/<subsitio>

usará el directorio user/sites como la ruta base "user" en lugar del directorio user.

Si eliges subdirectorios o URLs basadas en rutas para los subsitios, entonces lo único que necesitas es crear un directorio para cada subsitio en el directorio user/sites que contenga al menos las carpetas requeridas config, pages, plugins, y themes.

Si eliges subdominios para estructurar tu red de sitios web, entonces tendrás que configurar subdominios (comodín) en tu servidor además de la configuración de tus subsitios en tu directorio user/sites.

De cualquier manera, decide qué configuración se adapta mejor a ti.

Fragmentos de código

Para subsitios accesibles a través de subdominios copia el archivo setup_subdomain.php, de lo contrario para subsitios accesibles a través de subdirectorios el archivo setup_subdirectory.php en tu setup.php.

El archivo setup.php debe colocarse en la carpeta raíz de Grav, la misma carpeta donde encuentras index.php, README.md y los otros archivos de Grav.

setup_subdomain.php:

<?php
/**
 * Configuración multisitio para subsitios accesibles mediante subdominios.
 *
 * ¡NO EDITES A MENOS QUE SEPAS LO QUE ESTÁS HACIENDO!
 */

use Grav\Common\Utils;

// Obtener el nombre del subsitio del subdominio
$environment = isset($_SERVER['HTTP_HOST'])
    ? $_SERVER['HTTP_HOST']
    : (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'localhost');
// Eliminar el puerto de $environment generado por HTTP_HOST
$environment = strtolower(Utils::substrToString($environment, ':'));
$folder = "sites/{$environment}";

if ($environment === 'localhost' || !is_dir(ROOT_DIR . "user/{$folder}")) {
    return [];
}

return [
    'environment' => $environment,
    'streams' => [
        'schemes' => [
            'user' => [
               'type' => 'ReadOnlyStream',
               'prefixes' => [
                   '' => ["user/{$folder}"],
               ]
            ]
        ]
    ]
];

setup_subdirectory.php:

<?php
/**
 * Configuración multisitio para subdirectorios o URLs basadas en rutas
 * para subsitios.
 *
 * ¡NO EDITES A MENOS QUE SEPAS LO QUE ESTÁS HACIENDO!
 */

use Grav\Common\Filesystem\Folder;

// Obtener ruta relativa desde la raíz de Grav.
$path = isset($_SERVER['PATH_INFO'])
   ? $_SERVER['PATH_INFO']
   : Folder::getRelativePath($_SERVER['REQUEST_URI'], ROOT_DIR);

// Extraer nombre del subsitio de la ruta
$name = Folder::shift($path);
$folder = "sites/{$name}";
$prefix = "/{$name}";

if (!$name || !is_dir(ROOT_DIR . "user/{$folder}")) {
    return [];
}

// Prefijar todas las páginas con el nombre del subsitio
$container['pages']->base($prefix);

return [
    'environment' => $name,
    'streams' => [
        'schemes' => [
            'user' => [
               'type' => 'ReadOnlyStream',
               'prefixes' => [
                   '' => ["user/{$folder}"],
               ]
            ]
        ]
    ]
];

Cuando uses subdirectorios para cambiar contextos de idioma, podrías necesitar cargar diferentes configuraciones dependiendo del idioma. Puedes colocar tus configuraciones específicas de idioma en config/<contexto-idioma>/site.yaml usando el ejemplo de setup_subdir_config_switch.php a continuación. De esta manera tusitio.com/de-AT/index.html cargaría config/de-AT/site.yaml, tusitio.com/de-CH/index.html cargaría config/de-CH/site.yaml y así sucesivamente.

setup_subdir_config_switch.php:

<?php
/**
 * Cambiar configuración basada en el subdirectorio de contexto de idioma
 *
 * ¡NO EDITES A MENOS QUE SEPAS LO QUE ESTÁS HACIENDO!
 */

use Grav\Common\Filesystem\Folder;

$languageContexts = [
    'de-AT',
    'de-CH',
    'de-DE',
];

// Obtener ruta relativa desde la raíz de Grav.
$path = isset($_SERVER['PATH_INFO'])
    ? $_SERVER['PATH_INFO']
    : Folder::getRelativePath($_SERVER['REQUEST_URI'], ROOT_DIR);

// Extraer nombre del subdirectorio de la ruta
$name = Folder::shift($path);

if (in_array($name, $languageContexts)) {
    return [
        'streams' => [
            'schemes' => [
                'config' => [
                    'type' => 'ReadOnlyStream',
                    'prefixes' => [
                        '' => [
                            'environment://config',
                            'user://config/' . $name,
                            'user://config',
                            'system/config',
                        ],
                    ],
                ],
            ],
        ],
    ];
}

return [];

Configuración avanzada (para Expertos)

Una vez creado un setup.php tienes acceso a dos variables importantes: (i) $container, que es la instancia de Grav aún no inicializada correctamente y (ii) $self, que es una instancia de la clase ConfigServiceProvider.

Dentro de este script, puedes hacer cualquier cosa, pero ten en cuenta que el setup.php se llama cada vez que un usuario solicita una página. Esto significa que las operaciones de inicialización que consumen mucha memoria o tiempo pueden ralentizar todo tu sistema y, por lo tanto, deben evitarse.

Al final, el setup.php debe devolver un array asociativo con el nombre opcional del entorno environment y una colección de streams streams (para más información y para configurarlos correctamente, consulta la sección Streams):

return [
  'environment' => '<nombre>',            // Un nombre para el entorno
  'streams' => [
    'schemes' => [
      '<nombre_stream>' => [              // El nombre del stream
        'type' => 'ReadOnlyStream',     // Objeto Stream ej. 'ReadOnlyStream' o 'Stream'
        'prefixes' => [
          '<prefijo>' => [
            '<ruta1>',
            '<ruta2>',
            '<etc>'
          ]
        ],
        'paths' => [                    // Rutas (opcional)
          '<ruta1>',
          '<ruta2>',
          '<etc>'
        ]
      ]
    ]
  ]
]

Ten en cuenta que en esta etapa tan temprana no tienes acceso ni a la configuración ni a la instancia URI y, por lo tanto, cualquier llamada a una clase no inicializada puede terminar en una congelación del sistema, errores inesperados o (completa) pérdida de datos.

Streams

En Grav, los streams son objetos que mapean un conjunto de directorios físicos del sistema a un dispositivo lógico. Se clasifican por su atributo type. Para streams de solo lectura es el tipo ReadOnlyStream y para streams de lectura/escritura es el tipo Stream. Puedes registrar cualquier tipo de stream personalizado y apuntar a él siempre que sea una instancia de la clase de interfaz StreamInterface.

Por defecto, los streams se han configurado así:

  • user:// - carpeta user. ej. user/
  • page:// - carpeta pages. ej. user://pages/
  • image:// - carpeta images. ej. user://images/, system://images/
  • account:// - carpeta accounts. ej. user://accounts/
  • environment:// - ubicación actual del multisitio.
  • asset:// - carpeta de JS/CSS compilados. ej. assets/
  • blueprints:// - carpeta blueprints. ej. environment://blueprints/, user://blueprints/, system://blueprints/
  • config:// - carpeta de configuración. ej. environment://config/, user://config/, system://config/
  • plugins:// - carpeta plugins. ej. user://plugins/
  • themes:// - tema actual. ej. user://themes/
  • theme:// - tema actual. ej. themes://antimatter/
  • languages:// - carpeta languages. ej. environment://languages/, user://languages/, system://languages/
  • user-data:// - carpeta data. ej. user/data/
  • system:// - carpeta system. ej. system/
  • cache:// - carpeta cache. ej. cache/, images/
  • log:// - carpeta log. ej. logs/
  • backup:// - carpeta backup. ej. backups/
  • tmp:// - carpeta temporal. ej. tmp/

El mapeo de directorios físicos a un dispositivo lógico se puede hacer de dos maneras, ya sea configurando paths o prefixes. El primero puede entenderse como un mapeo 1 a 1, mientras que el segundo (como su nombre indica) te permite combinar varias rutas físicas en un solo stream lógico. Digamos que quieres registrar un stream con el nombre "image". Puedes entonces con el stream images:// listar con:

'image' => [
    'type' => 'ReadOnlyStream',
    'paths' => [
        'user/images',
        'system/images'
    ]
];

todas las imágenes ubicadas en las carpetas user/images y system/images. Para prefixes considera el ejemplo:

'cache' => [
    'type' => 'Stream',
    'prefixes' => [
        '' => ['cache'],
        'images' => ['images']
    ]
];

En este caso cache:// se resuelve a cache, pero cache://images se resuelve a images.

Por último, los streams pueden usarse en otros streams. Por ejemplo, dado que existen un stream user y un stream system, el stream "image" anterior también puede escribirse como:

'image' => [
    'type' => 'ReadOnlyStream',
    'paths' => [
        'user://images',
        'system://images'
    ]
];

Configuración Multisitio Basada en Servidor

Grav 1.7 agrega soporte para personalizar el entorno inicial desde la configuración de tu servidor.

Esta característica es útil si deseas usar, por ejemplo, contenedores docker y quieres que sean independientes del dominio que uses. O si no deseas almacenar secretos en la configuración, sino en la configuración de tu servidor.

Las siguientes variables de entorno pueden usarse para personalizar las rutas predeterminadas que Grav usa para configurar el entorno. Después de la inicialización, los streams pueden apuntar a una ubicación diferente.

Nota

*Puedes usar variables de entorno o constantes PHP, pero deben establecerse antes de que Grav se ejecute.

Variable Predeterminado Descripción
GRAV_SETUP_PATH DETECCIÓN AUTOMÁTICA Una ruta personalizada al archivo setup.php incluyendo el nombre del archivo. Por defecto Grav busca el archivo en GRAV_ROOT/setup.php y GRAV_ROOT/GRAV_USER_PATH/setup.php.
GRAV_USER_PATH user Una ruta relativa para el stream user://.
GRAV_CACHE_PATH cache Una ruta relativa para el stream cache://.
GRAV_LOG_PATH logs Una ruta relativa para el stream log://.
GRAV_TMP_PATH tmp Una ruta relativa para el stream tmp://.
GRAV_BACKUP_PATH backup Una ruta relativa para el stream backup://.

Además, hay variables para personalizar los entornos. Puedes encontrar mejor documentación para estas en Configuración de Entorno Basada en Servidor.

Nota

Estas también funcionan desde el archivo setup.php. Puedes hacerlas constantes usando define() o variables de entorno con putenv(). Las constantes tienen preferencia sobre las variables de entorno.

Variable Predeterminado Descripción
GRAV_ENVIRONMENT NOMBRE DE DOMINIO Nombre del entorno. Puede usarse, por ejemplo, en contenedores docker para establecer un entorno personalizado que no dependa del nombre de dominio, como production y develop.
GRAV_ENVIRONMENTS_PATH user://env Ruta de búsqueda para todos los entornos si prefieres algo como user://sites. Puede ser un stream o una ruta relativa desde GRAV_ROOT.
GRAV_ENVIRONMENT_PATH user://env/ENVIRONMENT A veces puede ser útil tener una ubicación personalizada para tu entorno.

Anulaciones de Configuración Basadas en Servidor

Si no deseas almacenar credenciales secretas dentro de la configuración, también puedes proporcionarlas usando variables de entorno desde tu servidor.

Como las variables de entorno tienen requisitos de nomenclatura estrictos (solo pueden contener A-Z, a-z, 0-9 y _), se necesitan algunos trucos para que las anulaciones de configuración funcionen.

Aquí hay un ejemplo de una anulación de configuración simple usando formato YAML para presentación:

GRAV_CONFIG: true                           # Si es false, se ignorará la configuración aquí.

GRAV_CONFIG_ALIAS__GITHUB: plugins.github   # Crear alias GITHUB='plugins.github' para acortar los nombres de variables abajo

GRAV_CONFIG__GITHUB__auth__method: api      # Anular config.plugins.github.auth.method = api
GRAV_CONFIG__GITHUB__auth__token: xxxxxxxx  # Anular config.plugins.github.auth.token = xxxxxxxx

En el ejemplo anterior __ (doble guión bajo) representa una variable anidada, que en twig se representa con . (punto).

También puedes usar variables de entorno en setup.php. Esto te permite, por ejemplo, almacenar secretos fuera de la configuración:

user/setup.php:

<?php

// Usa las siguientes variables de entorno en la configuración de tu servidor:
//
// DYNAMODB_SESSION_KEY: Clave del servidor DynamoDb para el almacenamiento de sesiones PHP
// DYNAMODB_SESSION_SECRET: Secreto del servidor DynamoDb
// DYNAMODB_SESSION_REGION: Región del servidor DynamoDb
// GOOGLE_MAPS_KEY: Clave secreta de Google Maps

return [
    'plugins' => [
        // Este plugin no existe
        'dynamodb_session' => [
            'credentials' => [
                'key' => getenv('DYNAMODB_SESSION_KEY') ?: null,
                'secret' => getenv('DYNAMODB_SESSION_SECRET') ?: null
            ],
            'region' => getenv('DYNAMODB_SESSION_REGION') ?: null
        ],
        // Este plugin no existe
        'google_maps' => [
            'key' => getenv('GOOGLE_MAPS_KEY') ?: null
        ]
    ]
];

! ADVERTENCIA !

setup.php se usa para establecer la configuración inicial. Si el plugin o tu configuración anulan estos valores más tarde, los valores iniciales se pierden.

Después de definir las variables en setup.php, puedes establecerlas en tu servidor:

<VirtualHost 127.0.0.1:80>
    ...

    SetEnv GRAV_SETUP_PATH         user/setup.php
    SetEnv GRAV_ENVIRONMENT        production
    SetEnv DYNAMODB_SESSION_KEY    JBGARDQ06UNJV00DL0R9
    SetEnv DYNAMODB_SESSION_SECRET CVjwH+QkfnPhKgVvJvrG24s0ABi343cJ7WTPxvb7
    SetEnv DYNAMODB_SESSION_REGION us-east-1
    SetEnv GOOGLE_MAPS_KEY         XWIozB2R2GmYInTqZ6jnKuUrdELounUb4BIxYmp
</VirtualHost>

En este ejemplo, el servidor también usará el entorno production almacenado en la carpeta user/env/production.