En este proyecto, una empresa imaginaria llamada Sympal Builder quiere crear
un CMS para que sus clientes puedan construir sitios web como subdominios de
sympalbuilder.com
. En concreto, el cliente XXX puede ver su sitio web en
xxx.sympalbuilder.com
y hacer uso del área de administración en
xxx.sympalbuilder.com/backend.php
.
Nota El nombre Sympal
se ha tomado prestado del proyecto Sympal
creado por Jonathan Wage, que es un framework de gestores de contenidos (CMF)
desarrollado con Symfony.
Los dos requerimientos básicos del proyecto son:
Los usuarios pueden crear páginas y especificar el título, contenido y URL de esas páginas.
Toda la aplicación debe construirse dentro de un único proyecto de Symfony que gestione el frontend y backend de todos los sitios de los clientes y que obtenga los datos adecuados en función del subdominio utilizado por cada cliente.
Nota Para crear esta aplicación, el servidor debe configurarse para redirigir todos
los subdominios *.sympalbuilder.com
al mismo directorio raíz, que es el
directorio web del proyecto Symfony.
2.1.1. El esquema y los datos
La base de datos del proyecto está formada por clientes (objeto Client
) y
páginas (objeto Page
). Cada cliente representa un sitio web accesible mediante
un subdominio y puede contener varias páginas.
# config/doctrine/schema.yml
Client:
columns:
name: string(255)
subdomain: string(50)
indexes:
subdomain_index:
fields: [subdomain]
type: unique
Page:
columns:
title: string(255)
slug: string(255)
content: clob
client_id: integer
relations:
Client:
alias: Client
foreignAlias: Pages
onDelete: CASCADE
indexes:
slug_index:
fields: [slug, client_id]
type: unique
Nota Aunque los índices de cada tabla no son obligatorios, es mejor añadirlos porque la aplicación va a realizar muchas consultas que utilizan estas columnas.
Para poder probar el funcionamiento del proyecto, añade los siguientes datos de
prueba en el archivo data/fixtures/fixtures.yml
:
# data/fixtures/fixtures.yml
Client:
client_pete:
name: Pete's Pet Shop
subdomain: pete
client_pub:
name: City Pub and Grill
subdomain: citypub
Page:
page_pete_location_hours:
title: Location and Hours | Pete's Pet Shop
content: We're open Mon - Sat, 8 am - 7pm
slug: location
Client: client_pete
page_pub_menu:
title: City Pub And Grill | Menu
content: Our menu consists of fish, Steak, salads, and more.
slug: menu
Client: client_pub
Los datos de prueba crean dos sitios web, cada uno de ellos con una página. La
URL completa de cada página está formada por el valor de la columa subdomain
de la tabla Client
y por el valor de la columna slug
del objeto Page
.
http://pete.sympalbuilder.com/location http://citypub.sympalbuilder.com/menu
2.1.2. El enrutamiento
Todas las páginas de los sitios web creados en Sympal Builder se corresponden
de forma directa con un objeto de tipo Page
del modelo, que define el título
y el contenido que se muestran. Para asociar cada URL con su correspondiente
objeto Page
, se crea una nueva ruta de objetos de tipo sfDoctrineRoute
y
que hace uso del campo slug
. El siguiente código busca automáticamente en la
base de datos un objeto de tipo Page
cuyo campo slug
coincida con el que
incluye la URL:
# apps/frontend/config/routing.yml
page_show:
url: /:slug
class: sfDoctrineRoute
options:
model: Page
type: object
params:
module: page
action: show
La ruta anterior asocia correctamente la página http://pete.sympalbuilder.com/location
con su objeto Page
. Desafortunadamente, la ruta anterior también funciona con
la URL http://pete.sympalbuilder.com/menu
, por lo que el menú del restaurante
se mostraría en el sitio web de Peter. Por el momento, el enrutamiento no es
consciente de la importancia de los subdominios de los clientes.
Para que la aplicación funcione correctamente, el enrutamiento debe ser más
avanzado. El objeto Page
se debe buscar tanto por el campo slug
como por el
campo client_id
. Este último campo se puede determinar comparando el host
(por ejemplo pete.sympalbuilder.com
) con el valor de la columna subdomain
del modelo Client
. Para ello, se va a mejorar el framework de enrutamiento
creando una clase propia de enrutamiento. No obstante, antes de crear esta clase
será necesario repasar cómo funciona el sistema de enrutamiento.