Академический Документы
Профессиональный Документы
Культура Документы
Symfony
Clase 1
Javier Eguíluz
javier.eguiluz@gmail.com
Esta obra dispone de una licencia de tipo Creative
Commons Reconocimiento‐No comercial‐ Compartir
bajo la misma licencia 3.0
Se prohíbe explícitamente el uso de este material en
actividades de formación comerciales
http://creativecommons.org/licenses/by‐nc‐sa/3.0/es/
This work is licensed under a Creative Commons
Attribution‐Noncommercial‐Share Alike 3.0
http://creativecommons.org/licenses/by‐nc‐sa/3.0/es/
Capítulo 1
Comenzando el
proyecto
¿Qué es
Symfony?
Framework para el
desarrollo de aplicaciones
web con PHP
• El más profesional
• El más documentado
• El mejor
PHP Frameworks
Productividad
Calidad programación
Mantenimiento
Rendimiento aplicación
Curva de aprendizaje
de Symfony
aprendizaje
tiempo
¿Qué es Jobeet?
diciembre 2008
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
La mejor forma de aprender
Symfony 1.2 a través de 24
tutoriales de 1 hora
Un tutorial
diferente
• Serio
• Profesional
• Completo
Prerrequisitos
5.2.4
Instalación de
Symfony
http://www.symfony‐project.org/installation/1_2
symfony‐1.2.4.tgz
check_configuration.php
$ php lib/vendor/symfony/data/bin/symfony
Preparar el
proyecto
frontend backend
proyecto
aplicación jobeet
frontend backend
$ symfony generate:project jobeet
apps/ log/
cache/ plugins/
config/ test/
lib/ web/
frontend/
$ symfony config/
generate:app
jobeet lib/
‐‐escaping‐strategy=on
‐‐csrf‐secret=UniqueSecret modules/
frontend templates/
‐‐escaping‐strategy XSS
‐‐csrf‐secret CSRF
config/ProjectConfiguration.class.php
require_once dirname(__FILE__).
'/../lib/vendor/'.
'symfony/lib/autoload/'.
'sfCoreAutoload.class.php';
Los entornos
• Entorno de desarrollo (dev)
• Entorno de pruebas
• Entorno intermedio
• Entorno de producción (prod)
Errores en el entorno de desarrollo (dev)
Errores en el entorno de producción (prod)
web/index.php
<?php
require_once(dirname(__FILE__).
'/../config/ProjectConfiguration.class.php');
$configuration =
ProjectConfiguration::getApplicationConfiguration(
'frontend',
'prod',
false
);
sfContext::createInstance($configuration)‐>dispatch();
Configurar bien el
servidor web
<VirtualHost 127.0.0.1:80>
ServerName jobeet.localhost
DocumentRoot "/home/sfprojects/jobeet/web"
DirectoryIndex index.php
<Directory "/home/sfprojects/jobeet/web">
AllowOverride All
Allow from All
</Directory>
Alias /sf /home/sfprojects/jobeet/lib/vendor/symfony/data/web/sf
<Directory "/home/sfprojects/jobeet/lib/vendor/symfony/data/web/sf">
AllowOverride All
Allow from All
</Directory>
</VirtualHost>
/etc/hosts
c:\windows\system32\drivers\etc\hosts
127.0.0.1 jobeet.localhost
http://jobeet.localhost/
prod
http://jobeet.localhost/frontend_dev.php
dev
Versionado de
código
Capítulo 2
El proyecto
La idea del
proyecto
Aplicación de software
libre que permite crear
sitios web de búsqueda
de empleo
características
• Completo y personalizable
• Multilingüe
• AJAX, RSS y API
Los escenarios del
proyecto
• administrador (admin)
• usuario (user)
• publicador (poster)
• afiliado (affiliate)
F1
El usuario accede a
la portada y ve las
últimas ofertas de
trabajo activas
F2
El usuario puede
visualizar todas
las ofertas de
trabajo de una
categoría
F3
El usuario refina el
listado mediante
palabras clave
F4
El usuario pincha
sobre una oferta de
trabajo para ver
más información
F5
El usuario publica
una nueva oferta
de trabajo
F6
El usuario quiere
convertirse en un
afiliado
F7
Un usuario afiliado
obtiene la lista de
ofertas de trabajo
activas
B1
El administrador configura el sitio web
B2
El administrador gestiona las ofertas de
trabajo
B3
El administrador gestiona los afiliados
Capítulo 3
El modelo de
datos
El modelo
relacional
relacional objetos
ORM
1. Describir la base de datos
2. Generar las clases PHP
3. Trabajar con objetos en vez
de SQL
El formato YAML
YAML
Formato para serializar datos que
es fácil de leer por las personas y es
compatible con todos los lenguajes
de programación
$casa = array(
'familia' => array(
'apellido' => 'García',
'padres' => array('Antonio', 'María'),
'hijos' => array('Jose', 'Manuel')
),
'direccion' => array(
'numero' => 34,
'calle' => 'Gran Vía',
'ciudad' => 'Cualquiera',
'codigopostal' => '12345'
)
);
casa:
familia:
apellido: García
padres:
‐ Antonio
‐ María
hijos:
‐ Jose
‐ Manuel
direccion:
numero: 34
calle: Gran Vía
ciudad: Cualquiera
codigopostal: "12345"
casa:
familia: { apellido: García, padres: [Antonio,
María], hijos: [Jose, Manuel] }
direccion: { numero: 34, direccion: Gran Vía,
ciudad: Cualquiera, codigopostal: "12345" }
Sintaxis
clave: clave:
clave: valor
clave: valor
clave:
clave: valor
clave:
‐ valor
2 espacios
‐ valor
en blanco
Arrays normales
clave:
‐ valor1
‐ valor2
‐ valor3
clave:[valor1, valor2, valor3]
Arrays asociativos
clave:
clave1: valor1
clave2: valor2
clave3: valor3
clave: { clave1: valor1,
clave2: valor2, clave3: valor3}
<?xml version="1.0"?>
<club>
XML YAML
<players> players:
<player id="kramnik" name="Vladimir Vladimir Kramnik: &kramnik
Kramnik" rating="2700" status="GM" /> rating: 2700
<player id="fritz" name="Deep status: GM
Fritz" rating="2700" status="Computer" Deep Fritz: &fritz
/> rating: 2700
<player id="mertz" name="David
status: Computer
Mertz" rating="1400" status="Amateur"
/> David Mertz: &mertz
</players> rating: 1400
<matches> status: Amateur
<match> matches:
<Date>2002‐10‐04</Date> ‐
<White refid="fritz" /> Date: 2002‐10‐04
<Black refid="kramnik" /> White: *fritz
<Result>Draw</Result>
Black: *kramnik
</match>
<match> Result: Draw
<Date>2002‐10‐06</Date> ‐
<White refid="kramnik" /> Date: 2002‐10‐06
<Black refid="fritz" /> White: *kramnik
<Result>White</Result> Black: *fritz
</match> Result: White
</matches>
</club>
El esquema
config/ schema.yml
propel:
jobeet_category:
id: ~
name: { type: varchar(255), required: true, index: unique }
jobeet_job:
id: ~
category_id: { type: integer, foreignTable: jobeet_category, foreignReference: id, required: true }
type: { type: varchar(255) }
company: { type: varchar(255), required: true }
logo: { type: varchar(255) }
url: { type: varchar(255) }
position: { type: varchar(255), required: true }
location: { type: varchar(255), required: true }
description: { type: longvarchar, required: true }
how_to_apply: { type: longvarchar, required: true }
token: { type: varchar(255), required: true, index: unique }
is_public: { type: boolean, required: true, default: 1 }
is_activated: { type: boolean, required: true, default: 0 }
email: { type: varchar(255), required: true }
expires_at: { type: timestamp, required: true }
created_at: ~
updated_at: ~
jobeet_affiliate:
id: ~
url: { type: varchar(255), required: true }
email: { type: varchar(255), required: true, index: unique }
token: { type: varchar(255), required: true }
is_active: { type: boolean, required: true, default: 0 }
created_at: ~
jobeet_category_affiliate:
category_id: { type: integer, foreignTable: jobeet_category, foreignReference: id, required: true, primaryKey:
true, onDelete: cascade }
affiliate_id: { type: integer, foreignTable: jobeet_affiliate, foreignReference: id, required: true, primaryKey:
true, onDelete: cascade }
config/ schema.yml
propel:
jobeet_category:
id: ~
name: { type: varchar(255),
required: true,
index: unique
}
jobeet_job:
config/ schema.yml
id: ~
category_id: { type: integer, foreignTable: jobeet_category,
foreignReference: id, required: true }
type: { type: varchar(255) }
company: { type: varchar(255), required: true }
logo: { type: varchar(255) }
url: { type: varchar(255) }
position: { type: varchar(255), required: true }
location: { type: varchar(255), required: true }
description: { type: longvarchar, required: true }
how_to_apply: { type: longvarchar, required: true }
token: { type: varchar(255), required: true, index:
unique }
is_public: { type: boolean, required: true, default: 1 }
is_activated: { type: boolean, required: true, default: 0 }
email: { type: varchar(255), required: true }
expires_at: { type: timestamp, required: true }
created_at: ~
updated_at: ~
config/ schema.yml
jobeet_affiliate:
id: ~
url: { type: varchar(255), required: true }
email: { type: varchar(255), required: true, index: unique }
token: { type: varchar(255), required: true }
is_active: { type: boolean, required: true, default: 0 }
created_at: ~
config/ schema.yml
jobeet_category_affiliate:
category_id: {
type: integer,
foreignTable: jobeet_category,
foreignReference: id,
required: true,
primaryKey: true,
onDelete: cascade
}
affiliate_id: {
type: integer,
foreignTable: jobeet_affiliate,
foreignReference: id,
required: true,
primaryKey: true,
onDelete: cascade
}
config/ schema.yml
• type: boolean, tinyint, smallint, integer, bigint, double, float,
real, decimal, char, varchar(size), longvarchar, date, time,
timestamp, blob, clob
• required: true, false
• index: true, false
• primaryKey: true, false
• foreignKey, foreignReference
La base de datos
$ mysqladmin ‐uroot ‐p create jobeet
$ symfony configure:database
“mysql:host=localhost;dbname=jobeet”
root ConTraSenA
config/databases.yml
El ORM
$ symfony propel:build‐sql
data/sql/
$ symfony propel:insert‐sql ‐‐no‐confirmation
$ symfony propel:build‐model
lib/model/
extends
• JobeetJob
• BaseJobeetJob
• JobeetJobPeer extends
• BaseJobeetJobPeer
$job = new JobeetJob();
$job‐>setPosition('Web developer');
$job‐>save();
echo $job‐>getPosition();
$job‐>delete();
$category = new JobeetCategory();
$category‐>setName('Programming');
$ symfony propel:build‐all
$ symfony cc
• Borra la caché de Symfony
• Ejecutar siempre que añades
clases (autoload)
• La solución de casi todos los
errores de los principiantes
$ symfony cache:clear
$ symfony cache:cl
$ symfony ca:c
$ symfony cc
Los datos iniciales
data/fixtures/
• Datos iniciales
• Datos de prueba
• Datos de usuarios
data/fixtures/ 010_categories.yml
JobeetCategory:
design: { name: Design }
programming: { name: Programming }
manager: { name: Manager }
administrator: { name: Administrator }
data/fixtures/ 020_jobs.yml
JobeetJob:
job_sensio_labs:
category_id: programming
type: full‐time
company: Sensio Labs
logo: sensio‐labs.gif
url: http://www.sensiolabs.com/
position: Web Developer
location: Paris, France
description: |
You have already developed websites with symfony and you want
to work with Open‐Source technologies. You have a minimum of
3 years experience in web development with PHP or Java and
you wish to participate to development of Web 2.0 sites using
the best frameworks available.
how_to_apply: |
Send your resume to fabien.potencier [at] sensio.com
is_public: true
is_activated: true
token: job_sensio_labs
email: job@example.com
expires_at: 2010‐10‐10
$ symfony propel:data‐load
$ symfony propel:build‐sql
$ symfony propel:insert‐sql
$ symfony propel:build‐model
$ symfony propel:build‐forms
$ symfony propel:build‐filters
+ $ symfony propel:data‐load
$ symfony propel:build‐all‐load
Probando la
aplicación en el
navegador
proyecto
aplicación jobeet
frontend backend
job
módulo
$ symfony propel:generate‐module
‐‐with‐show ‐‐non‐verbose‐templates
frontend job JobeetJob
frontend/modules/job
actions/
templates/
frontend/modules/job/actions/actions.class.php
index edit
show update
new delete
create
http://jobeet.localhost/frontend_dev.php/job
frontend
_dev
job
Objeto Representación
textual
Categoría
_ _toString()
lib/model/ JobeetCategory.php
El controlador y
la vista
La arquitectura MVC
¿Cómo se programaba con PHP en
el siglo pasado?
acceso a BBDD
generar
código HTML
pagina.php
Modelo
Vista
Controlador
Modelo
Directorio /lib/model
Vista
Directorios templates/
Controlador
Archivos index.php y frontend_dev.php
Archivos actions.class.php
El layout
patrón de diseño decorator
apps/frontend/templates/layout.php
apps/frontend/templates/ layout.php
<!DOCTYPE html PUBLIC "‐//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1‐transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Jobeet ‐ Your best job board</title>
<link rel="shortcut icon" href="/favicon.ico" />
<?php include_javascripts() ?>
<?php include_stylesheets() ?>
</head>
<body>
...
<div class="content">
<?php echo $sf_content ?>
</div>
</body>
</html>
Plantillas Symfony
• Archivos PHP
normales
• Existe un plugin
para Smarty
• Symfony 2.0 podría
incluir plantillas
Las hojas de estilos,
imágenes y archivos
JavaScript
helpers
<head>
...
<?php include_stylesheets() ?>
</head>
apps/frontend/config/ view.yml
default:
http_metas:
content‐type: text/html
metas:
#title: symfony project
#description: symfony project
#keywords: symfony, project
#language: en
#robots: index, follow
stylesheets: [main.css]
javascripts: []
has_layout: on
layout: layout
apps/frontend/config/ view.yml
default:
...
stylesheets: [main.css, jobs.css, job.css]
...
<link rel="stylesheet" type="text/css"
media="screen" href="/css/main.css" />
<link rel="stylesheet" type="text/css"
media="screen" href="/css/jobs.css" />
<link rel="stylesheet" type="text/css"
media="screen" href="/css/job.css" />
apps/frontend/config/ view.yml
default:
...
stylesheets: [main.css, jobs.css, job]
...
<link rel="stylesheet" type="text/css"
media="screen" href="/css/main.css" />
<link rel="stylesheet" type="text/css"
media="screen" href="/css/jobs.css" />
<link rel="stylesheet" type="text/css"
media="screen" href="/css/job.css" />
apps/frontend/config/ view.yml
default:
...
stylesheets: [main.css, /css/v2/jobs.css]
...
<link rel="stylesheet" type="text/css"
media="screen" href="/css/main.css" />
<link rel="stylesheet" type="text/css"
media="screen" href="/css/v2/jobs.css" />
apps/frontend/config/ view.yml
default:
...
stylesheets: [main.css, jobs: { media: print } ]
...
<link rel="stylesheet" type="text/css"
media="screen" href="/css/main.css" />
<link rel="stylesheet" type="text/css"
media="print" href="/css/jobs.css" />
metas:
title: El título 1 Symfony
metas:
title: El título 2 Proyecto
metas:
title: El título 3 Aplicación
metas:
title: El título 4 Módulo
view.yml
Symfony
lib/vendor/symfony/lib/config/config/view.yml
Proyecto
config/view.yml
Aplicación
apps/frontend/config/view.yml
Módulo
apps/frontend/modules/job/config/view.yml
metas:
stylesheets: [job]
<?php
use_stylesheet(‘job.css’) view.yml
?>
plantilla
plantilla
La portada del módulo
de las ofertas de
trabajo
apps/
frontend/
modules/
job/
actions/
actions.class.php
templates/
indexSuccess.php
acción
= +
plantilla
http://jobeet.localhost/frontend_dev.php/job/index
frontend (aplicación)
_dev (entorno)
job (módulo)
index (acción executeIndex y
plantilla indexSuccess)
apps/frontend/modules/job/actions/ actions.class.php
// ...
}
SELECT
[ALL | DISTINCT | DISTINCTROW ]
[HIGH_PRIORITY]
[STRAIGHT_JOIN]
[SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT]
[SQL_CACHE | SQL_NO_CACHE] [SQL_CALC_FOUND_ROWS]
select_expr [, select_expr ...]
[FROM table_references
[WHERE where_condition]
[GROUP BY {col_name | expr | position}
[ASC | DESC], ... [WITH ROLLUP]]
[HAVING where_condition]
[ORDER BY {col_name | expr | position}
[ASC | DESC], ...]
[LIMIT {[offset,] row_count | row_count OFFSET offset}]
[PROCEDURE procedure_name(argument_list)]
[INTO OUTFILE 'file_name' export_options
| INTO DUMPFILE 'file_name'
| INTO var_name [, var_name]]
[FOR UPDATE | LOCK IN SHARE MODE]]
apps/frontend/modules/job/templates/ indexSuccess.php
layout plantilla
<title> </title> Título de la página
layout plantilla
apps/frontend/templates/ layout.php
<title>
<?php include_slot('title') ?>
</title>
apps/frontend/modules/job/templates/ indexSuccess.php
<?php slot('title', 'Título de la página') ?>
<?php slot(
'title',
sprintf('%s is looking for a %s',
$job‐>getCompany(),
$job‐>getPosition()
)
) ?>
La acción de la página
de una oferta de
trabajo
apps/frontend/modules/job/actions/ actions.class.php
$this‐>forward404Unless($this‐>job);
}
La petición y la
respuesta
objeto sfWebRequest
// ...
}
Nombre del método Equivalente de PHP
getMethod() $_SERVER['REQUEST_METHOD']
getUri() $_SERVER['REQUEST_URI']
getReferer() $_SERVER['HTTP_REFERER']
getHost() $_SERVER['HTTP_HOST']
getLanguages() $_SERVER['HTTP_ACCEPT_LANGUAGE']
getCharsets() $_SERVER['HTTP_ACCEPT_CHARSET']
isXmlHttpRequest() $_SERVER['X_REQUESTD_WITH'] == 'XMLHttpRequest'
getHttpHeader() $_SERVER
getCookie() $_COOKIE
isSecure() $_SERVER['HTTPS']
getFiles() $_FILES
getGetParameter() $_GET
getPostParameter() $_POST
getUrlParameter() $_SERVER['PATH_INFO']
getRemoteAddress() $_SERVER['REMOTE_ADDR']
public function executeShow(sfWebRequest $request)
{
...
$request‐>getParameter('id');
...
/ruta1/ruta2/ruta3?id=3&clave1=
valor1&clave2=valor2
objeto sfWebResponse
// ...
}
archivo de configuración
metas:
stylesheets: [job]
plantilla
<?php use_stylesheet('job.css') ?>
acción
$this‐>getResponse()‐>
addStyleSheet('/css/job.css');
Capítulo 5
El sistema de
enrutamiento
URL
internet symfony
URL URI
sistema de
enrutamiento
URI 'job/show?id='.$job‐>getId()
url_for()
URL job/show/id/1
URI
modulo/accion?clave1=
valor1&clave2=valor2&
...
Configurando el
enrutamiento
apps/frontend/config/ routing.yml
homepage:
url: /
param: { module: default, action: index }
default_index:
url: /:module
param: { action: index }
default:
url: /:module/:action/*
nombre
default_index: patrón
url: /:module
param: { action: index }
parámetros
homepage:
url: /
param: { module: default, action: index }
default_index:
url: /:module
param: { action: index }
default:
url: /:module/:action/*
/job
1 /frontend_dev.php/job
aplicación entorno ???
¿módulo? ¿acción?
2 apps/frontend/config/routing.yml
default_index:
/job url: /:module
param: { action: index }
:module = job = módulo
3 apps/frontend/config/routing.yml
default_index:
url: /:module acción = index
param: { action: index }
4 /frontend_dev.php/job
aplicación = frontend módulo = job
entorno = dev acción = index
URI url_for() URL
url_for('job/show?id='.$job‐>getId())
/job/show/id/1
URI url_for() URL
url_for('@default?id='.$job‐>getId())
Personalizando el
enrutamiento
apps/frontend/config/ routing.yml
homepage:
url: /
param: { module: job, action: index }
default_index:
url: /:module
param: { action: index }
default:
url: /:module/:action/*
<h1>
<a href="<?php echo url_for('@homepage') ?>">
<img src="/images/jobeet.gif" alt="Jobeet" />
</a>
</h1>
/job/sensio‐labs/paris‐france/1/web‐developer
1. Identificar el patrón de la URL
2. Incluir la ruta a routing.yml
3. Añadir las restricciones adecuadas
/job/sensio‐labs/paris‐france/1/web‐developer
1. Identificar el patrón de la URL
/job/:company/:location/:id/:position
/job/sensio‐labs/paris‐france/1/web‐developer
2. Incluir la ruta en routing.yml
job_show_user:
url: /job/:company/:location/:id/:position
param: { module: job, action: show }
url_for('job/show?'.
'id='.$job‐>getId().
'&company='.$job‐>getCompany().
'&location='.$job‐>getLocation().
'&position='.$job‐>getPosition()
)
url_for(array(
'module' => 'job',
'action' => 'show',
'id' => $job‐>getId(),
'company' => $job‐>getCompany(),
'location' => $job‐>getLocation(),
'position' => $job‐>getPosition(),
))
Requisitos
/job/sensio‐labs/paris‐france/1/web‐developer
3. Añadir las restricciones adecuadas
job_show_user:
url: /job/:company/:location/:id/:position
param: { module: job, action: show }
requirements:
id: \d+
La clase sfRoute
HTTP Navegadores Symfony
GET
POST
HEAD
PUT
DELETE
job_show_user:
url: /job/:company/:location/:id/:position
class: sfRequestRoute
param: { module: job, action: show }
requirements:
id: \d+
sf_method: [get]
La clase para las
rutas basadas en
objetos
url_for('job/show?'.
'id='.$job‐>getId().
'&company='.$job‐>getCompany().
'&location='.$job‐>getLocation().
'&position='.$job‐>getPosition()
)
job_show_user:
url: /job/:company/:location/:id/:position
class: sfPropelRoute
options:
model: JobeetJob
type: object
param: { module: job, action: show }
requirements:
id: \d+
sf_method: [get]
url_for('job/show?'.
'id='.$job‐>getId().
'&company='.$job‐>getCompany().
'&location='.$job‐>getLocation().
'&position='.$job‐>getPosition()
)
url_for('job_show_user', $job)
url_for(array(
'sf_route' => 'job_show_user',
'sf_subject' => $job
))
Lo que queremos…
/job/sensio‐labs/paris‐france/1/web‐developer
Lo que tenemos…
/job/Sensio+Labs/Paris%2C+France/1/Web+Developer
getId()
id
name getName()
description
... Job getDescription()
getSlug()
schema.yml getShortDescription()
getters
virtuales
slug
Comienza el curso de Symfony en Vitoria‐Gasteiz
www.symfony.es/2009/01/29/comienza‐el‐curso‐de‐symfony‐en‐vitoria‐gasteiz
Twitto, el framework PHP más pequeño
www.symfony.es/2009/01/11/twitto‐el‐framework‐php‐mas‐pequeno/
getId()
id
name getName()
description
... Job getDescription()
getCompanySlug()
schema.yml getPositionSlug()
getLocationSlug()
getters
virtuales
lib/model/ JobeetJob.php
public function getCompanySlug() {
return Jobeet::slugify($this‐>getCompany());
}
return $text;
}
}
job_show_user:
url: /job/:company_slug/:location_slug/:id/:position_slug
class: sfPropelRoute
options: { model: JobeetJob, type: object }
param: { module: job, action: show }
requirements:
id: \d+
sf_method: [get]
class jobActions extends sfActions
{
public function executeShow(sfWebRequest $request)
{
$this‐>job = JobeetJobPeer::retrieveByPk($request‐>getParameter('id'));
$this‐>forward404Unless($this‐>job);
}
// ...
}
// ...
}
Enrutamiento en
acciones y
plantillas
<?php echo link_to(
"Texto del enlace",
'job_show_user',
$job
) ?>
<a href="
<?php echo url_for(
'job_show_user',
$job,
true
); ?>">
Texto del enlace
</a>
class jobActions extends sfActions
{
public function executeIndex(sfWebRequest $request)
{
// ...
$this‐>redirect(
$this‐>generateUrl('job_show_user', $job)
);
}
// ...
}
La clase de las
colecciones de
rutas
job:
class: sfPropelRouteCollection
options: { model: JobeetJob }
job_show_user:
url: /job/:company_slug/:location_slug/:id/:position_slug
class: sfPropelRoute
options: { model: JobeetJob, type: object }
param: { module: job, action: show }
requirements:
id: \d+
sf_method: [get]
$ php symfony app:routes frontend