SlideShare a Scribd company logo
1 of 135
Frameworks de desarrollo

Symfony
Clase 2

                       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 




    The use of these slides in commercial courses or
            trainings is explicitly prohibited

 http://creativecommons.org/licenses/by‐nc‐sa/3.0/es/
Capítulo 6

Profundizando en 
el modelo
El objeto Criteria
    de Propel
apps/frontend/modules/job/actions/actions.class.php



class jobActions extends sfActions
{
  public function executeIndex(sfWebRequest $request)
  {
    $this‐>listado = JobeetJobPeer::doSelect(
      new Criteria()
    );
  }
}
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]]
SELECT *
FROM JobeetJob
WHERE created_at > time() ‐ 86400 * 30
apps/frontend/modules/job/actions/actions.class.php


public function executeIndex(sfWebRequest $request)
{
  $criteria = new Criteria();
  $criteria‐>add( WHERE
     JobeetJobPeer::CREATED_AT,
                                        condición
     time() ‐ 86400 * 30,
     Criteria::GREATER_THAN
  );
                       JobeetJobPeer::doSelect($criteria);
    $this‐>listado =
}              FROM JobeetJob                     SELECT *
apps/frontend/modules/job/actions/actions.class.php


public function executeIndex(sfWebRequest        $request)

{
  $criteria = new Criteria();
  $criteria‐>add(
     JobeetJobPeer::COMPANY,
     'Empresa ACME'
  );
                       JobeetJobPeer::doSelect($criteria);
    $this‐>listado =
}
Depurando el código 
   SQL generado
log/frontend_dev.log

Dec 6 15:47:12 symfony [debug] {sfPropelLogger} exec: SET NAMES 'utf8‘

Dec 6 15:47:12 symfony [debug] {sfPropelLogger} prepare: SELECT 
jobeet_job.ID, jobeet_job.CATEGORY_ID, jobeet_job.TYPE, 
jobeet_job.COMPANY, jobeet_job.LOGO, jobeet_job.URL, 
jobeet_job.POSITION, jobeet_job.LOCATION, jobeet_job.DESCRIPTION, 
jobeet_job.HOW_TO_APPLY, jobeet_job.TOKEN, jobeet_job.IS_PUBLIC, 
jobeet_job.CREATED_AT, jobeet_job.UPDATED_AT FROM ''jobeet_job'' 
WHERE jobeet_job.CREATED_AT>:p1
                                            SQL Injection
Dec 6 15:47:12 symfony [debug] {sfPropelLogger} Binding '2008‐11‐06 
15:47:12' at position :p1 w/ PDO type PDO::PARAM_STR
Serializando objetos
lib/model/JobeetJob.php

class JobeetJob extends BaseJobeetJob
{
  public function save(PropelPDO $con = null)
  {
    if ($this‐>isNew() && !$this‐>getExpiresAt())
    {
      $now = $this‐>getCreatedAt() ?
             $this‐>getCreatedAt('U') :
             time();
      $this‐>setExpiresAt($now + 86400 * 30); 
    }

        return parent::save($con);
    }
}
apps/frontend/modules/job/actions/actions.class.php

public function executeIndex(sfWebRequest $request)
{
  $criteria = new Criteria();
  $criteria‐>add(
     JobeetJobPeer::CREATED_AT EXPIRES_AT,
     time(),
     Criteria::GREATER_THAN
  );

    $this‐>listado = JobeetJobPeer::doSelect(
       $criteria
    );
}
Personalizando la 
  configuración
¿Dónde está el problema?
public function executeIndex(sfWebRequest $request)
{
  $criteria = new Criteria();
  $criteria‐>add(
     JobeetJobPeer::CREATED_AT,
                                   mejor como opción 
     time() ‐ 86400 * 30,
                                   de configuración
     Criteria::GREATER_THAN
  );
                       JobeetJobPeer::doSelect($criteria);
    $this‐>listado =
}
apps/frontend/config/app.yml
all:
 dias_activa:  30

                     sfConfig::get('app_dias_activa')
Refactorización
¿Dónde está el problema?
                                   Controlador (MVC)
public function executeIndex(sfWebRequest $request)
{
  $criteria = new Criteria();
  $criteria‐>add(
     JobeetJobPeer::CREATED_AT, Modelo (MVC)
     time() ‐ 86400 * 30,
     Criteria::GREATER_THAN
  );
                       JobeetJobPeer::doSelect($criteria);
    $this‐>listado =
}
lib/model/JobeetJobPeer.php
Modelo
class JobeetJobPeer extends BaseJobeetJobPeer {
  static public function getActiveJobs() {
    $criteria = new Criteria();
    $criteria‐>add(
      self::EXPIRES_AT,
      time(),
      Criteria::GREATER_THAN
    );

        return self::doSelect($criteria);
    }
}
                   apps/frontend/modules/job/actions/actions.class.php
Controlador
public function executeIndex(sfWebRequest $request) {
  $this‐>jobeet_job_list = JobeetJobPeer::getActiveJobs();
}
static public function getActiveJobs()
{
  $criteria = new Criteria();
  $criteria‐>add(
     self::EXPIRES_AT,
     time(),
     Criteria::GREATER_THAN
  );
    $criteria‐>addDescendingOrderByColumn(
       self::EXPIRES_AT  ORDER BY ___ DESC
    );
    return self::doSelect($criteria);
}
Mostrando las 
categorías en la 
    portada
lib/model/JobeetCategoryPeer.php

class JobeetCategoryPeer extends BaseJobeetCategoryPeer
{
  static public function getWithJobs()
  {
    $criteria = new Criteria();
    $criteria‐>addJoin(self::ID, JobeetJobPeer::CATEGORY_ID);
    $criteria‐>add(
      JobeetJobPeer::EXPIRES_AT,
      time(),
      Criteria::GREATER_THAN
    );
                                       SELECT DISTINCT
        $criteria‐>setDistinct();

        return self::doSelect($criteria);
    }
}
apps/frontend/modules/job/actions/actions.class.php

public function executeIndex(sfWebRequest $request) {
  $this‐>categories = JobeetCategoryPeer::getWithJobs();
}


      apps/frontend/modules/job/templates/indexSuccess.php
<?php foreach ($categories as $category): ?>
  ...
  <h1><?php echo $category ?></h1>
  ...
  <?php foreach ($category‐>getActiveJobs() as $i => $job): ?>
    ...
  <?php endforeach; ?>
  ...
<?php endforeach; ?> 
Controlador      Vista             Modelo
                          getWithJobs()
      1
                                          JobeetCategoryPeer.php
                                  getActiveJobs()
                            3

executeIndex()      indexSuccess.php      JobeetCategory.php

                                                    4   getActiveJobs()
                            2
            $categories
                                           JobeetJobPeer.php
Limitando los 
  resultados
lib/model/JobeetJobPeer.php

class JobeetJobPeer extends BaseJobeetJobPeer {
  static public function getActiveJobs($max = 10) {
    $criteria = new Criteria();
    $criteria‐>add(           mejor en un archivo 
       self::EXPIRES_AT,        de configuración
       time(),
       Criteria::GREATER_THAN
    );
    $criteria‐>setLimit($max); 

        return self::doSelect($criteria);
    }
}
apps/frontend/modules/job/templates/indexSuccess.php

<?php foreach ($category‐>getActiveJobs(
 sfConfig::get('app_max_jobs_on_homepage')
) as $i => $job): ?> 


                           apps/frontend/config/app.yml

all:
  active_days:          30
  max_jobs_on_homepage: 10
Archivos de datos 
   dinámicos
data/fixtures/020_jobs.yml
JobeetJob:
<?php for ($i = 100; $i <= 130; $i++): ?>
  job_<?php echo $i ?>:
    category_id: programming
    company: Company <?php echo $i.quot;nquot; ?>
    position: Web Developer
    location: Paris, France
    description: |
      Lorem ipsum dolor sit amet,
      consectetur adipisicing elit.
    how_to_apply: |
      Send your resume to lorem.ipsum
      [at] company_<?php echo $i ?>.sit
    is_public: true
    is_activated: true
    token: job_<?php echo $i.quot;nquot; ?>
    email: job@example.com
<?php endfor; ?>
Restringir el acceso a 
una oferta de trabajo
apps/frontend/config/routing.yml

job_show_user:
 url: /job/:company_slug/:location_slug/:id/:position_slug
 class: sfPropelRoute
  options:
    model: JobeetJob
    type:  object
    method_for_criteria: doSelectActive
param: { module: job, action: show }
requirements:
 id: d+
 sf_method: [get]
lib/model/JobeetJobPeer.php


class JobeetJobPeer extends BaseJobeetJobPeer
{
  static public function doSelectActive(Criteria $criteria)
  {
    $criteria‐>add(
      JobeetJobPeer::EXPIRES_AT,
      time(),
      Criteria::GREATER_THAN
    );
    return self::doSelectOne($criteria);
  }
}
Capítulo 7

La página de cada 
categoría
La ruta de la 
 categoría
apps/frontend/config/routing.yml
category:
  url:      /category/:slug
  class:    sfPropelRoute
  param:    { module: category, action: show }
  options:  { model: JobeetCategory, type: object }



                        lib/model/JobeetCategory.php

public function getSlug() {
  return Jobeet::slugify($this‐>getName());
}
El enlace a la página 
 de cada categoría
apps/frontend/modules/job/templates/indexSuccess.php
<h1>
  <?php echo link_to($category,
                     'category',
                     $category) ?>
</h1>


$total = $category‐>countActiveJobs() ‐
         sfConfig::get('app_max_jobs_on_homepage')

<?php if ($total > 0): ?>
  ...
  echo link_to($count, 'category', $category)
  ...
<?php endif; ?>
DRY (Don’t Repeat Yourself)
 A process philosophy aimed at reducing
 duplication, particularly in computing.
 […]
 When the DRY principle is applied successfully, a modification of 
 any single element of a system does not change other logically‐
 unrelated elements. Additionally, elements that are logically 
 related all change predictably and uniformly, and are thus kept 
 in sync.
xxxPeer::doSelect()
xxxPeer::doSelectOne()
xxxPeer::doSelectRS()
xxxPeer::retrieveByPK()
xxxPeer::retrieveByPKs()
xxxPeer::doCount()
xxxPeer::doInsert()
xxxPeer::doDelete()
xxxPeer::doUpdate()
Creando el módulo 
 de las categorías
$ ./symfony propel:generate‐module frontend category
      actions/               templates/
                                indexSuccess
      index      edit
                                editSuccess
      show       update
                                newSuccess
                 delete
      new
      create


$ ./symfony generate:module frontend category
      actions/               templates/
                                indexSuccess
      index
nombre   slug
Actualizando la base 
     de datos
config/schema.yml

propel:
  jobeet_category:
    id:        ~
    name: { type: varchar(255), required: true }
    slug:
      type: varchar(255)
      required: true
      index: unique
                                    getSlug()
lib/model/JobeetCategory.php

public function setName($name)
{
  parent::setName($name);

    $this‐>setSlug(Jobeet::slugify($name));
}




$ ./symfony propel:build‐all‐load
apps/frontend/modules/category/actions/actions.class.php
class categoryActions extends sfActions
{
  public function executeShow(sfWebRequest $request)
  {
    $this‐>category = $this‐>getRoute()‐>getObject();
  }
}



apps/frontend/modules/category/templates/showSuccess.php

<h1><?php echo $category ?></h1>
...
<?php foreach ($category‐>getActiveJobs() as $i => $job): ?>
...
Elementos parciales
elementos parciales

      “trozos de código de plantilla que se 
      pueden reutilizar en varias plantillas”


 Son iguales que las plantillas en todo salvo que 
 su nombre empieza por un guión bajo (_)
apps/frontend/modules/job/templates/_list.php
<table class=quot;jobsquot;>
  <?php foreach ($jobs as $i => $job): ?>
  <tr class=quot;<?php echo fmod($i, 2) ? 'even' : 'odd' ?>quot;>
    <td class=quot;locationquot;>
      <?php echo $job‐>getLocation() ?>
    </td>
    <td class=quot;positionquot;>
      <?php echo link_to($job‐>getPosition(),
                          'job_show_user',
                          $job) ?>
    </td>
    <td class=quot;companyquot;>
      <?php echo $job‐>getCompany() ?>
    </td>
  </tr>
<?php endforeach; ?>
</table>
apps/frontend/modules/job/templates/indexSuccess.php
<?php include_partial(
  'job/list',
  array('jobs' => $category‐>getActiveJobs(
     sfConfig::get('app_max_jobs_on_homepage')
  ))
) ?>


     apps/frontend/modules/job/templates/showSuccess.php
<?php include_partial(
  'job/list',
  array('jobs' => $category‐>getActiveJobs())
) ?> 
Paginación
apps/frontend/modules/category/actions/actions.class.php

public function executeShow(sfWebRequest $request)
{
  $this‐>category = $this‐>getRoute()‐>getObject();

    $this‐>pager = new sfPropelPager(
                                               app.yml
       'JobeetJob',
       sfConfig::get('app_max_jobs_on_category')
    );
    $this‐>pager‐>setCriteria(
       $this‐>category‐>getActiveJobsCriteria()
    );
    $this‐>pager‐>setPage($request‐>getParameter('page', 1)); 
    $this‐>pager‐>init();
}
getResults()
getNbResults()
haveToPaginate()
getLinks()
getPage()
getPreviousPage()
getNextPage()
getLastPage()
Capítulo 8

Pruebas unitarias
test/
    unit/
     Prueban funciones y 
     métodos individualmente

   functional/
     Prueban la aplicación en 
     su conjunto
El framework de 
  pruebas lime
require_once dirname(__FILE__).'/../bootstrap/unit.php';
$t = new lime_test(1, new lime_output_color());

                        número de pruebas 
                        esperadas

      ok($condicion)
      is($valor1, $valor2)
      isnt($valor1, $valor2)
      like($cadena, $expresionRegular)
      unlike($cadena, $expresionRegular)
      is_deeply($array1, $array2)
Ejecutando pruebas 
     unitarias
test/unit/JobeetTest.php
require_once dirname(__FILE__).'/../bootstrap/unit.php';

$t = new lime_test(1, new lime_output_color());
$t‐>pass('This test always passes.');




$ ./symfony test:unit Jobeet
Probando el método 
     slugify()
Sensio Labs                 sensio‐labs
       Paris, France               paris‐france

                                   test/unit/JobeetTest.php
require_once dirname(__FILE__).'/../bootstrap/unit.php'; 


$t = new lime_test(6, new lime_output_color()); 

$t‐>is(Jobeet::slugify('Sensio'), 'sensio'); 
$t‐>is(Jobeet::slugify('sensio labs'), 'sensio‐labs'); 
$t‐>is(Jobeet::slugify('sensio labs'), 'sensio‐labs'); 
$t‐>is(Jobeet::slugify('paris,france'), 'paris‐france'); 
$t‐>is(Jobeet::slugify(' sensio'), 'sensio'); 
$t‐>is(Jobeet::slugify('sensio '), 'sensio');
test/unit/JobeetTest.php
require_once dirname(__FILE__).'/../bootstrap/unit.php'; 


$t = new lime_test(6, new lime_output_color()); 

$t‐>comment('::slugify()');
$t‐>is(Jobeet::slugify('Sensio'), 'sensio',
       '::slugify() pasa la cadena de texto a minúsculas');
$t‐>is(Jobeet::slugify('sensio labs'), 'sensio‐labs',
       '::slugify() sustituye los espacios en blanco por ‐');
...
Pruebas unitarias 
   para Propel
$ mysqladmin ‐uroot ‐p create jobeet_test


$ symfony configure:database ‐‐env=test
quot;mysql:host=localhost;dbname=jobeet_testquot;
root ConTraSenA

                 config/databases.yml
config/databases.yml
dev:
  propel:
     class: sfPropelDatabase
     param:
       classname: DebugPDO

test:
  propel:
    class: sfPropelDatabase
    param:
      classname: DebugPDO
      dsn:       'mysql:host=localhost;dbname=jobeet_test'

all:
  propel:
     class: sfPropelDatabase
     param:
       dsn:      'mysql:host=localhost;dbname=jobeet'
       username: root
       password: null
test/bootstrap/propel.php

include(dirname(__FILE__).'/unit.php');

$configuration = ProjectConfiguration::getApplicationConfiguration(
  'frontend',
  'test',
   true
);

new sfDatabaseManager($configuration);

$loader = new sfPropelData();
$loader‐>loadData(sfConfig::get('sf_test_dir').'/fixtures');
test/unit/model/JobeetJobTest.php


include(dirname(__FILE__).'/../../bootstrap/propel.php');

$t = new lime_test(1, new lime_output_color());

$t‐>comment('‐>getCompanySlug()');
$job = JobeetJobPeer::doSelectOne(new Criteria());
$t‐>is(
   $job‐>getCompanySlug(),
   Jobeet::slugify($job‐>getCompany()),
   '‐>getCompanySlug() devuelve el slug del nombre de la empresa'
);
Conjuntos de 
pruebas unitarias
$ ./symfony test:unit
Capítulo 9

Pruebas 
funcionales
La clase sfBrowser
sfBrowser



servidor web



 aplicación     aplicación 
  Symfony        Symfony
get()       reload()    setHttpHeader()
post()      click()     setAuth()
call()      select()    setCookie()
back()      deselect() removecookie()
forward()   restart()   clearCookie()
                        followRedirect()
$browser = new sfBrowser();

$browser‐>
  get('/')‐>
  click('Design')‐>
  get('/category/programming?page=2')‐>
  get('/category/programming', array('page' => 2))‐>
  post('search', array('keywords' => 'php'))
;
La clase 
sfTestFunctional
sfBrowser


      sfTestFunctional


                      response
request     user
test/functional/frontend/categoryActionsTest.php

include(dirname(__FILE__).'/../../bootstrap/functional.php');
$browser = new sfTestFunctional(new sfBrowser());

$browser‐>
  get('/category/index')‐>

    with('request')‐>begin()‐>
      isParameter('module', 'category')‐>
      isParameter('action', 'index')‐>
    end()‐>

    with('response')‐>begin()‐>
      isStatusCode(200)‐>
      checkElement('body', '!/This is a temporary page/')‐>
    end()
;
interfaz fluída


___()                     ___()
        ___()   ___()
response
request
isParameter()   checkElement()
isFormat()      isHeader()
isMethod()      isStatusCode()
hasCookie()     isRedirected()
isCookie()
Ejecutando pruebas 
    funcionales
$ ./symfony test:functional frontend categoryActions
Datos de prueba
lib/test/JobeetTestFunctional.class.php

class JobeetTestFunctional extends sfTestFunctional
{
  public function loadData()
  {
    $loader = new sfPropelData();
    $loader‐>loadData(
       sfConfig::get('sf_test_dir').'/fixtures'
    );
    return $this;
  }
}
Conjuntos de 
pruebas funcionales
$ ./symfony test:functional frontend
Conjuntos de 
  pruebas
$ symfony test:unit
    $ symfony test:functional frontend
    $ symfony test:functional backend
+   $ symfony test:functional ......

    $ symfony test:all
Capítulo 10

Los formularios
Crear código HTML del formulario
1.

     Definir reglas de validación para los datos
2.

     Procesar valores enviados por el usuario
3.

     Guardar la información en la base de datos
4.

     Mostrar posibles mensajes de error
5.

     Volver a mostrar los datos en el formulario
6.
Symfony ya incluye...

• Validación (para cada campo)

• Widgets (campos del formulario)

• Formularios (widgets + validación)
Formularios
class ContactForm extends sfForm
{
  public function configure()
  {
    $this‐>setWidgets(array(
      'email'   => new sfWidgetFormInput(),
      'message' => new sfWidgetFormTextarea(),
    ));

        $this‐>setValidators(array(
          'email'   => new sfValidatorEmail(),
          'message' => new sfValidatorString(array(
                         'max_length' => 255)
                       ),
        ));
    }
}
sfWidgetFormChoice               sfWidgetFormInputHidden
sfWidgetFormChoiceMany           sfWidgetFormInputPassword
sfWidgetFormDate                 sfWidgetFormPropelChoice
sfWidgetFormDateRange            sfWidgetFormPropelChoiceMany
sfWidgetFormDateTime             sfWidgetFormPropelSelect
sfWidgetFormFilterDate           sfWidgetFormPropelSelectMany
sfWidgetFormFilterInput          sfWidgetFormSchema
sfWidgetFormI18nDate             sfWidgetFormSchemaDecorator
sfWidgetFormI18nDateTime         sfWidgetFormSchemaForEach
sfWidgetFormI18nSelectCountry    sfWidgetFormSchemaFormatter
sfWidgetFormI18nSelectCurrency   sfWidgetFormSelect
sfWidgetFormI18nSelectLanguage   sfWidgetFormSelectCheckbox
sfWidgetFormI18nTime             sfWidgetFormSelectMany
sfWidgetFormInput                sfWidgetFormSelectRadio
sfWidgetFormInputCheckbox        sfWidgetFormTextarea
sfWidgetFormInputFile            sfWidgetFormTime
sfWidgetFormInputFileEditable
$this‐>mergeForm(new OtroForm());
$this‐>embedForm('name', new OtroForm());
Formularios de 
   Propel
schema.yml


  $ ./symfony propel:build‐forms


JobeetJobForm                        JobeetAffiliateForm
                JobeetCategoryForm

                  lib/form/
class JobeetJobForm extends BaseJobeetJobForm
{
  public function configure()
  {
    unset(
       $this['created_at'],
       $this['updated_at'],
       $this['expires_at'],
       $this['is_activated']
    );
  }
}
class JobeetJobForm extends BaseJobeetJobForm
{
  public function configure()
  {
    ...
    $this‐>validatorSchema['email'] = 
      new sfValidatorEmail(); 
  }
}
class JobeetJobForm extends BaseJobeetJobForm
{
  public function configure()
  {
    ...
    $this‐>widgetSchema['type'] =
      new sfWidgetFormChoice(array(
        'choices' => JobeetJobPeer::$types,
        'expanded' => true,
      ));
  }                         class JobeetJobPeer extends BaseJobeetJobPeer {

}                              static public $types = array(
                                  'full‐time' => 'Full time',
                                                     'part‐time' => 'Part time',
                                                     'freelance' => 'Freelance',
                                                );
                                                // ...
                                            }
sfWidgetFormChoice
multiple    expanded       widget
              false
 false

              false
 true

 false        true

              true
 true
class JobeetJobForm extends BaseJobeetJobForm
{
  public function configure()
  {
    ...
        $this‐>validatorSchema['type'] = 
          new sfValidatorChoice(array(
            'choices' => array_keys(JobeetJobPeer::$types),
          ));
    }
}
class JobeetJobForm extends BaseJobeetJobForm
{
  public function configure()
  {
    ...
    $this‐>widgetSchema‐>setLabels(array(
      'category_id' => 'Category',
      'is_public' => 'Public?',
      'how_to_apply' => 'How to apply?',
    ));
  }
}
class JobeetJobForm extends BaseJobeetJobForm
{
  public function configure()
  {
    ...
    $this‐>widgetSchema['logo'] = 
      new sfWidgetFormInputFile(array(
        'label' => 'Company logo',
      ));

        $this‐>validatorSchema['logo'] = 
          new sfValidatorFile(array(
            'required'   => false,
            'label'      => sfConfig::get('sf_upload_dir').'/jobs',
            'mime_types' => 'web_images',
          ));
    }
}
sfValidatorFile

      Valida que el archivo subido sea una 
1.
     imagen
      Cambia el nombre del archivo por un 
2.
     valor único
     Guarda el archivo en la ruta indicada
3.

     Actualiza el valor de la columna logo
4.
class JobeetJobForm extends BaseJobeetJobForm
{
  public function configure()
  {
    ...
    $this‐>widgetSchema‐>setHelp(
       'is_public',
       'Indica si la oferta de trabajo se puede
        publicar en sitios web de afiliados'
    );
  }
}
apps/frontend/modules/job/templates/newSuccess.php



<?php use_stylesheet('job.css') ?>

<h1>Post a Job</h1>

<?php
   include_partial('form', array('form' => $form))
?>

                            parcial  _form
apps/frontend/modules/job/templates/_form.php

<?php include_stylesheets_for_form($form) ?>
<?php include_javascripts_for_form($form) ?>
                                    method, enctype
<?php echo form_tag_for($form, '@job') ?>
  <table id=quot;job_formquot;>
    <tfoot><tr><td colspan=quot;2quot;>
      <input type=quot;submitquot; value=quot;Preview jobquot; /> 
    </td></tr></tfoot>
    <tbody>
     <?php echo $form ?>
    </tbody>
  </table>
</form>
Widgets
  Formulario
render()               renderRow()
renderHiddenFields()   render()
hasErrors()            renderLabel()
hasGlobalErrors()      renderError()
getGlobalErrors()      renderHelp()
renderGlobalErrors()
<?php echo $form ?>



<?php foreach ($form as $widget): ?>
  <?php echo $widget‐>renderRow() ?>
<?php endforeach; ?> 
apps/frontend/modules/job/actions/actions.class.php

                  executeNew(sfWebRequest   $request) {
public function
     ...
}
                  executeCreate(sfWebRequest    $request) {
public function
     ...
}
                  executeEdit(sfWebRequest    $request) {
public function
     ...
}
                  executeUpdate(sfWebRequest    $request) {
public function
     ...
}
                  executeDelete(sfWebRequest    $request) {
public function
     ...
}
                     processForm(sfWebRequest   $request, sfForm $form)
protected function
{ 
     ...
}
apps/frontend/modules/job/actions/actions.class.php




public function executeNew(sfWebRequest $request)
{
  $job = new JobeetJob();
  $job‐>setType('full‐time');

    $this‐>form = new JobeetJobForm($job);
}
lib/model/JobeetJob.php
public function save(PropelPDO $con = null)
{
  // ...
  if (!$this‐>getToken()) {
    $this‐>setToken(
      sha1($this‐>getEmail().rand(11111, 99999))
    );
  }

    return parent::save($con);
}
                           lib/form/JobeetJobForm.class.php
class JobeetJobForm extends BaseJobeetJobForm {
  public function configure() {
    unset( $this['token'] );
  }
}
apps/frontend/config/routing.yml

job:
  class:        sfPropelRouteCollection
  options:      { model: JobeetJob, column: token }
  requirements: { token: w+ }




 http://localhost.jobeet/job/TOKEN/edit
La página de 
previsualización
apps/frontend/modules/category/templates/showSuccess.php

<?php if($sf_request‐>getParameter('token') == $job‐>getToken()): ?>
  <?php include_partial('job/admin', array('job' => $job)) ?>
<?php endif; ?>



                            apps/frontend/modules/job/templates/_admin.php
<h3>Admin</h3>
  <ul>
  <?php if (!$job‐>getIsActivated()): ?>
    <li><?php echo link_to('Edit', 'job_edit', $job) ?></li> 
    <li><?php echo link_to('Publish', 'job_edit', $job) ?></li>
  <?php endif; ?>
  ...
  <?php if ($job‐>isExpired()): ?>
    Expired
  <?php else: ?>
    Expires in <strong>
      <?php echo $job‐>getDaysBeforeExpires() ?></strong>
    days
  <?php endif; ?>
  ...
Activando y 
publicando las 
   ofertas
apps/frontend/config/routing.yml
job:
  class:   sfPropelRouteCollection
  options:
     model:          JobeetJob
     column:         token
   object_actions: { publish: put }
  requirements:
    token: w+
apps/frontend/modules/job/actions/actions.class.php

public function executePublish(sfWebRequest $request)
{
  $request‐>checkCSRFProtection();

    $job = $this‐>getRoute()‐>getObject();
    $job‐>publish();

    $this‐>getUser()‐>setFlash(
       'notice',
       sprintf('Your job is now online for %s days.', 
               sfConfig::get('app_active_days'))
    );

    $this‐>redirect($this‐>generateUrl('job_show_user', $job));
}
Capítulo 11

Probando los 
formularios
Enviando un 
 formulario
test/functional/frontend/jobActionsTest.php
$browser‐>info('3 ‐ Post a Job page')‐>
  info(' 3.1 ‐ Submit a Job')‐>

    get('/job/new')‐>
      with('request')‐>begin()‐>
        isParameter('module', 'job')‐>
        isParameter('action', 'new')‐>
                                                   Preview your job
    end()‐>

    click('Preview your job', array('job' => array(
      'company' => 'Sensio Labs',
      'url' => 'http://www.sensio.com/',
      'logo' => sfConfig::get('sf_upload_dir').'/jobs/sensio‐labs.gif', 
      'position' => 'Developer',
      'location' => 'Atlanta, USA',
      'is_public' => false,
    )))‐>

    with('request')‐>begin()‐>
      isParameter('module', 'job')‐>
      isParameter('action', 'create')‐>
    end()‐>
;
Seguridad
$ symfony generate:app jobeet ‐‐escaping‐strategy=on ‐‐csrf‐secret=secreto frontend


                                                             XSS
                                              &lt;p&gt;Soy un 
<p>Soy un usuario 
                                              usuario 
malvado</p>
                                              malvado&lt;/p&gt;
y voy a meter JS 
                                              y voy a meter JS
<script 
                                              &lt;script
type=quot;text/javascript
                                              type=&quot;text/javas
quot;>document.write(quot;Hol
                                              cript&quot;&gt;docume
a!quot;)</script>
                                              nt.write(&quot;Hola!&
                                              quot;)&lt;/script&gt; 
$ symfony generate:app jobeet ‐‐escaping‐strategy=on ‐‐csrf‐secret=secreto frontend


                                                                      CSRF
                                              <form>
                                               <input type=quot;hiddenquot; 
<form>
                                                         name=quot;_csrf_tokenquot; 
  <input type=quot;textquot; .../>
                                                         value=quot;...quot; /> 
  <input type=quot;textquot; .../>
                                               <input type=quot;textquot; .../>
  ...
                                               <input type=quot;textquot; .../>
</form>
                                               ...
                                              </form>
Tareas de 
mantenimiento
$ php lib/vendor/symfony/data/bin/symfony
lib/task/JobeetCleanupTask.class.php
class JobeetCleanupTask extends sfBaseTask {
  protected function configure() {
    $this‐>addOptions(array(
      new sfCommandOption('env', null, sfCommandOption::PARAMETER_REQUIRED, 
                          'The environement', 'prod'),
      new sfCommandOption('days', null, sfCommandOption::PARAMETER_REQUIRED, 
                          '', 90), ));

      $this‐>namespace = 'jobeet';
      $this‐>name = 'cleanup';
      $this‐>briefDescription = 'Cleanup Jobeet database';

    $this‐>detailedDescription = <<<EOF
The [jobeet:cleanup|INFO] task cleans up the Jobeet database:    [./symfony 
jobeet:cleanup ‐‐env=prod ‐‐days=90|INFO]
EOF;
  }

    protected function execute($arguments = array(), $options = array()) {
      $databaseManager = new sfDatabaseManager($this‐>configuration);
      $nb = JobeetJobPeer::cleanup($options['days']);
      $this‐>logSection('propel', sprintf('Removed %d stale jobs', $nb));
    }
}

More Related Content

What's hot

Symfony 4 Workshop - Limenius
Symfony 4 Workshop - LimeniusSymfony 4 Workshop - Limenius
Symfony 4 Workshop - LimeniusIgnacio Martín
 
Apostrophe (improved Paris edition)
Apostrophe (improved Paris edition)Apostrophe (improved Paris edition)
Apostrophe (improved Paris edition)tompunk
 
November Camp - Spec BDD with PHPSpec 2
November Camp - Spec BDD with PHPSpec 2November Camp - Spec BDD with PHPSpec 2
November Camp - Spec BDD with PHPSpec 2Kacper Gunia
 
PHPUnit Episode iv.iii: Return of the tests
PHPUnit Episode iv.iii: Return of the testsPHPUnit Episode iv.iii: Return of the tests
PHPUnit Episode iv.iii: Return of the testsMichelangelo van Dam
 
Working With The Symfony Admin Generator
Working With The Symfony Admin GeneratorWorking With The Symfony Admin Generator
Working With The Symfony Admin GeneratorJohn Cleveley
 
Design how your objects talk through mocking
Design how your objects talk through mockingDesign how your objects talk through mocking
Design how your objects talk through mockingKonstantin Kudryashov
 
Unit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBeneluxUnit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBeneluxMichelangelo van Dam
 
Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)Fabien Potencier
 
Decoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DICDecoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DICKonstantin Kudryashov
 
Love and Loss: A Symfony Security Play
Love and Loss: A Symfony Security PlayLove and Loss: A Symfony Security Play
Love and Loss: A Symfony Security PlayKris Wallsmith
 
Symfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il clienteSymfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il clienteLeonardo Proietti
 
Crafting beautiful software
Crafting beautiful softwareCrafting beautiful software
Crafting beautiful softwareJorn Oomen
 
Apigility reloaded
Apigility reloadedApigility reloaded
Apigility reloadedRalf Eggert
 
Min-Maxing Software Costs - Laracon EU 2015
Min-Maxing Software Costs - Laracon EU 2015Min-Maxing Software Costs - Laracon EU 2015
Min-Maxing Software Costs - Laracon EU 2015Konstantin Kudryashov
 
WordPress Queries - the right way
WordPress Queries - the right wayWordPress Queries - the right way
WordPress Queries - the right wayAnthony Hortin
 
Introduction to Zend framework
Introduction to Zend framework Introduction to Zend framework
Introduction to Zend framework Matteo Magni
 

What's hot (20)

Symfony 4 Workshop - Limenius
Symfony 4 Workshop - LimeniusSymfony 4 Workshop - Limenius
Symfony 4 Workshop - Limenius
 
Apostrophe (improved Paris edition)
Apostrophe (improved Paris edition)Apostrophe (improved Paris edition)
Apostrophe (improved Paris edition)
 
A dive into Symfony 4
A dive into Symfony 4A dive into Symfony 4
A dive into Symfony 4
 
November Camp - Spec BDD with PHPSpec 2
November Camp - Spec BDD with PHPSpec 2November Camp - Spec BDD with PHPSpec 2
November Camp - Spec BDD with PHPSpec 2
 
PHPUnit Episode iv.iii: Return of the tests
PHPUnit Episode iv.iii: Return of the testsPHPUnit Episode iv.iii: Return of the tests
PHPUnit Episode iv.iii: Return of the tests
 
Working With The Symfony Admin Generator
Working With The Symfony Admin GeneratorWorking With The Symfony Admin Generator
Working With The Symfony Admin Generator
 
Design how your objects talk through mocking
Design how your objects talk through mockingDesign how your objects talk through mocking
Design how your objects talk through mocking
 
Unit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBeneluxUnit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBenelux
 
Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)
 
Decoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DICDecoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DIC
 
What's new with PHP7
What's new with PHP7What's new with PHP7
What's new with PHP7
 
Love and Loss: A Symfony Security Play
Love and Loss: A Symfony Security PlayLove and Loss: A Symfony Security Play
Love and Loss: A Symfony Security Play
 
Symfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il clienteSymfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il cliente
 
Crafting beautiful software
Crafting beautiful softwareCrafting beautiful software
Crafting beautiful software
 
Symfony2 - WebExpo 2010
Symfony2 - WebExpo 2010Symfony2 - WebExpo 2010
Symfony2 - WebExpo 2010
 
Apigility reloaded
Apigility reloadedApigility reloaded
Apigility reloaded
 
Min-Maxing Software Costs - Laracon EU 2015
Min-Maxing Software Costs - Laracon EU 2015Min-Maxing Software Costs - Laracon EU 2015
Min-Maxing Software Costs - Laracon EU 2015
 
WordPress Queries - the right way
WordPress Queries - the right wayWordPress Queries - the right way
WordPress Queries - the right way
 
Introduction to Zend framework
Introduction to Zend framework Introduction to Zend framework
Introduction to Zend framework
 
PHP MVC
PHP MVCPHP MVC
PHP MVC
 

Viewers also liked

Symfony Live 09 Symfony 2
Symfony Live 09 Symfony 2Symfony Live 09 Symfony 2
Symfony Live 09 Symfony 2narkoza
 
Building A Platform From Open Source At Yahoo
Building A Platform From Open Source At YahooBuilding A Platform From Open Source At Yahoo
Building A Platform From Open Source At Yahoonarkoza
 
History of english literature sajid
History of english literature sajidHistory of english literature sajid
History of english literature sajidDr. Cupid Lucid
 
A comprehensive grammar of the english language quirk greenbaum leech svartvik
A comprehensive grammar of the english language quirk greenbaum leech svartvikA comprehensive grammar of the english language quirk greenbaum leech svartvik
A comprehensive grammar of the english language quirk greenbaum leech svartvikIvana Jovanovic
 
PPT ON ENGLISH
PPT ON ENGLISHPPT ON ENGLISH
PPT ON ENGLISHTime Rahul
 

Viewers also liked (6)

Symfony Live 09 Symfony 2
Symfony Live 09 Symfony 2Symfony Live 09 Symfony 2
Symfony Live 09 Symfony 2
 
Building A Platform From Open Source At Yahoo
Building A Platform From Open Source At YahooBuilding A Platform From Open Source At Yahoo
Building A Platform From Open Source At Yahoo
 
30 Symfony Best Practices
30 Symfony Best Practices30 Symfony Best Practices
30 Symfony Best Practices
 
History of english literature sajid
History of english literature sajidHistory of english literature sajid
History of english literature sajid
 
A comprehensive grammar of the english language quirk greenbaum leech svartvik
A comprehensive grammar of the english language quirk greenbaum leech svartvikA comprehensive grammar of the english language quirk greenbaum leech svartvik
A comprehensive grammar of the english language quirk greenbaum leech svartvik
 
PPT ON ENGLISH
PPT ON ENGLISHPPT ON ENGLISH
PPT ON ENGLISH
 

Similar to Curso Symfony - Clase 2

Curso Symfony - Clase 3
Curso Symfony - Clase 3Curso Symfony - Clase 3
Curso Symfony - Clase 3Javier Eguiluz
 
Using Geeklog as a Web Application Framework
Using Geeklog as a Web Application FrameworkUsing Geeklog as a Web Application Framework
Using Geeklog as a Web Application FrameworkDirk Haun
 
Using of TDD practices for Magento
Using of TDD practices for MagentoUsing of TDD practices for Magento
Using of TDD practices for MagentoIvan Chepurnyi
 
Why is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenariosWhy is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenariosDivante
 
Best Practice Testing with Lime 2
Best Practice Testing with Lime 2Best Practice Testing with Lime 2
Best Practice Testing with Lime 2Bernhard Schussek
 
Как получить чёрный пояс по WordPress? v2.0
Как получить чёрный пояс по WordPress? v2.0Как получить чёрный пояс по WordPress? v2.0
Как получить чёрный пояс по WordPress? v2.0Yevhen Kotelnytskyi
 
Easy rest service using PHP reflection api
Easy rest service using PHP reflection apiEasy rest service using PHP reflection api
Easy rest service using PHP reflection apiMatthieu Aubry
 
Как получить чёрный пояс по WordPress?
Как получить чёрный пояс по WordPress?Как получить чёрный пояс по WordPress?
Как получить чёрный пояс по WordPress?Yevhen Kotelnytskyi
 
Introduction to Zend Framework web services
Introduction to Zend Framework web servicesIntroduction to Zend Framework web services
Introduction to Zend Framework web servicesMichelangelo van Dam
 
Empowering users: modifying the admin experience
Empowering users: modifying the admin experienceEmpowering users: modifying the admin experience
Empowering users: modifying the admin experienceBeth Soderberg
 
OSDC 2009 Rails Turtorial
OSDC 2009 Rails TurtorialOSDC 2009 Rails Turtorial
OSDC 2009 Rails TurtorialYi-Ting Cheng
 
Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ EtsyNishan Subedi
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For BeginnersJonathan Wage
 
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)arcware
 
Bag Of Tricks From Iusethis
Bag Of Tricks From IusethisBag Of Tricks From Iusethis
Bag Of Tricks From IusethisMarcus Ramberg
 

Similar to Curso Symfony - Clase 2 (20)

Curso Symfony - Clase 3
Curso Symfony - Clase 3Curso Symfony - Clase 3
Curso Symfony - Clase 3
 
Using Geeklog as a Web Application Framework
Using Geeklog as a Web Application FrameworkUsing Geeklog as a Web Application Framework
Using Geeklog as a Web Application Framework
 
Using of TDD practices for Magento
Using of TDD practices for MagentoUsing of TDD practices for Magento
Using of TDD practices for Magento
 
Why is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenariosWhy is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenarios
 
Best Practice Testing with Lime 2
Best Practice Testing with Lime 2Best Practice Testing with Lime 2
Best Practice Testing with Lime 2
 
Get AngularJS Started!
Get AngularJS Started!Get AngularJS Started!
Get AngularJS Started!
 
PHP Unit Testing
PHP Unit TestingPHP Unit Testing
PHP Unit Testing
 
Как получить чёрный пояс по WordPress? v2.0
Как получить чёрный пояс по WordPress? v2.0Как получить чёрный пояс по WordPress? v2.0
Как получить чёрный пояс по WordPress? v2.0
 
Easy rest service using PHP reflection api
Easy rest service using PHP reflection apiEasy rest service using PHP reflection api
Easy rest service using PHP reflection api
 
Как получить чёрный пояс по WordPress?
Как получить чёрный пояс по WordPress?Как получить чёрный пояс по WordPress?
Как получить чёрный пояс по WordPress?
 
Symfony 1, mi viejo amigo
Symfony 1, mi viejo amigoSymfony 1, mi viejo amigo
Symfony 1, mi viejo amigo
 
Introduction to Zend Framework web services
Introduction to Zend Framework web servicesIntroduction to Zend Framework web services
Introduction to Zend Framework web services
 
Empowering users: modifying the admin experience
Empowering users: modifying the admin experienceEmpowering users: modifying the admin experience
Empowering users: modifying the admin experience
 
PHPSpec BDD Framework
PHPSpec BDD FrameworkPHPSpec BDD Framework
PHPSpec BDD Framework
 
OSDC 2009 Rails Turtorial
OSDC 2009 Rails TurtorialOSDC 2009 Rails Turtorial
OSDC 2009 Rails Turtorial
 
Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ Etsy
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
 
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
 
Test driven development_for_php
Test driven development_for_phpTest driven development_for_php
Test driven development_for_php
 
Bag Of Tricks From Iusethis
Bag Of Tricks From IusethisBag Of Tricks From Iusethis
Bag Of Tricks From Iusethis
 

More from Javier Eguiluz

deSymfony 2017: Symfony 4, Symfony Flex y el futuro de Symfony
deSymfony 2017: Symfony 4, Symfony Flex y el futuro de SymfonydeSymfony 2017: Symfony 4, Symfony Flex y el futuro de Symfony
deSymfony 2017: Symfony 4, Symfony Flex y el futuro de SymfonyJavier Eguiluz
 
New Symfony Tips & Tricks (SymfonyCon Paris 2015)
New Symfony Tips & Tricks (SymfonyCon Paris 2015)New Symfony Tips & Tricks (SymfonyCon Paris 2015)
New Symfony Tips & Tricks (SymfonyCon Paris 2015)Javier Eguiluz
 
Mastering Twig (DrupalCon Barcelona 2015)
Mastering Twig (DrupalCon Barcelona 2015)Mastering Twig (DrupalCon Barcelona 2015)
Mastering Twig (DrupalCon Barcelona 2015)Javier Eguiluz
 
Symfony tips and tricks
Symfony tips and tricksSymfony tips and tricks
Symfony tips and tricksJavier Eguiluz
 
Twig, el nuevo motor de plantillas de Drupal 8
Twig, el nuevo motor de plantillas de Drupal 8Twig, el nuevo motor de plantillas de Drupal 8
Twig, el nuevo motor de plantillas de Drupal 8Javier Eguiluz
 
Silex, desarrollo web ágil y profesional con PHP
Silex, desarrollo web ágil y profesional con PHPSilex, desarrollo web ágil y profesional con PHP
Silex, desarrollo web ágil y profesional con PHPJavier Eguiluz
 
Twig, los mejores trucos y técnicas avanzadas
Twig, los mejores trucos y técnicas avanzadasTwig, los mejores trucos y técnicas avanzadas
Twig, los mejores trucos y técnicas avanzadasJavier Eguiluz
 
Twig avanzado (sf2Vigo)
Twig avanzado (sf2Vigo)Twig avanzado (sf2Vigo)
Twig avanzado (sf2Vigo)Javier Eguiluz
 
Desymfony 2012 - Concurso de diseño
Desymfony 2012 - Concurso de diseñoDesymfony 2012 - Concurso de diseño
Desymfony 2012 - Concurso de diseñoJavier Eguiluz
 
Desymfony 2011 - Tutorial #5: Backend
Desymfony 2011 - Tutorial #5: BackendDesymfony 2011 - Tutorial #5: Backend
Desymfony 2011 - Tutorial #5: BackendJavier Eguiluz
 
Desymfony 2011 - Tutorial #1: Instalacion y primeros pasos
Desymfony 2011 - Tutorial #1: Instalacion y primeros pasosDesymfony 2011 - Tutorial #1: Instalacion y primeros pasos
Desymfony 2011 - Tutorial #1: Instalacion y primeros pasosJavier Eguiluz
 
Desymfony 2011 - Introducción a Symfony2
Desymfony 2011 - Introducción a Symfony2Desymfony 2011 - Introducción a Symfony2
Desymfony 2011 - Introducción a Symfony2Javier Eguiluz
 
Symfony2, Jornadas Symfony
Symfony2, Jornadas SymfonySymfony2, Jornadas Symfony
Symfony2, Jornadas SymfonyJavier Eguiluz
 
Curso Symfony - Anexos
Curso Symfony - AnexosCurso Symfony - Anexos
Curso Symfony - AnexosJavier Eguiluz
 
Curso Symfony - Clase 5
Curso Symfony - Clase 5Curso Symfony - Clase 5
Curso Symfony - Clase 5Javier Eguiluz
 

More from Javier Eguiluz (20)

deSymfony 2017: Symfony 4, Symfony Flex y el futuro de Symfony
deSymfony 2017: Symfony 4, Symfony Flex y el futuro de SymfonydeSymfony 2017: Symfony 4, Symfony Flex y el futuro de Symfony
deSymfony 2017: Symfony 4, Symfony Flex y el futuro de Symfony
 
New Symfony Tips & Tricks (SymfonyCon Paris 2015)
New Symfony Tips & Tricks (SymfonyCon Paris 2015)New Symfony Tips & Tricks (SymfonyCon Paris 2015)
New Symfony Tips & Tricks (SymfonyCon Paris 2015)
 
Mastering Twig (DrupalCon Barcelona 2015)
Mastering Twig (DrupalCon Barcelona 2015)Mastering Twig (DrupalCon Barcelona 2015)
Mastering Twig (DrupalCon Barcelona 2015)
 
Symfony tips and tricks
Symfony tips and tricksSymfony tips and tricks
Symfony tips and tricks
 
Twig, el nuevo motor de plantillas de Drupal 8
Twig, el nuevo motor de plantillas de Drupal 8Twig, el nuevo motor de plantillas de Drupal 8
Twig, el nuevo motor de plantillas de Drupal 8
 
Silex al límite
Silex al límiteSilex al límite
Silex al límite
 
Twig tips and tricks
Twig tips and tricksTwig tips and tricks
Twig tips and tricks
 
Silex, desarrollo web ágil y profesional con PHP
Silex, desarrollo web ágil y profesional con PHPSilex, desarrollo web ágil y profesional con PHP
Silex, desarrollo web ágil y profesional con PHP
 
Twig, los mejores trucos y técnicas avanzadas
Twig, los mejores trucos y técnicas avanzadasTwig, los mejores trucos y técnicas avanzadas
Twig, los mejores trucos y técnicas avanzadas
 
Wallpaper Notifier
Wallpaper NotifierWallpaper Notifier
Wallpaper Notifier
 
Backend (sf2Vigo)
Backend (sf2Vigo)Backend (sf2Vigo)
Backend (sf2Vigo)
 
Twig avanzado (sf2Vigo)
Twig avanzado (sf2Vigo)Twig avanzado (sf2Vigo)
Twig avanzado (sf2Vigo)
 
Desymfony 2012 - Concurso de diseño
Desymfony 2012 - Concurso de diseñoDesymfony 2012 - Concurso de diseño
Desymfony 2012 - Concurso de diseño
 
Desymfony 2011 - Twig
Desymfony 2011 - TwigDesymfony 2011 - Twig
Desymfony 2011 - Twig
 
Desymfony 2011 - Tutorial #5: Backend
Desymfony 2011 - Tutorial #5: BackendDesymfony 2011 - Tutorial #5: Backend
Desymfony 2011 - Tutorial #5: Backend
 
Desymfony 2011 - Tutorial #1: Instalacion y primeros pasos
Desymfony 2011 - Tutorial #1: Instalacion y primeros pasosDesymfony 2011 - Tutorial #1: Instalacion y primeros pasos
Desymfony 2011 - Tutorial #1: Instalacion y primeros pasos
 
Desymfony 2011 - Introducción a Symfony2
Desymfony 2011 - Introducción a Symfony2Desymfony 2011 - Introducción a Symfony2
Desymfony 2011 - Introducción a Symfony2
 
Symfony2, Jornadas Symfony
Symfony2, Jornadas SymfonySymfony2, Jornadas Symfony
Symfony2, Jornadas Symfony
 
Curso Symfony - Anexos
Curso Symfony - AnexosCurso Symfony - Anexos
Curso Symfony - Anexos
 
Curso Symfony - Clase 5
Curso Symfony - Clase 5Curso Symfony - Clase 5
Curso Symfony - Clase 5
 

Recently uploaded

Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Patryk Bandurski
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationSlibray Presentation
 
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Wonjun Hwang
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brandgvaughan
 
SAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxSAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxNavinnSomaal
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):comworks
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsMark Billinghurst
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 3652toLead Limited
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Mark Simos
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationRidwan Fadjar
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Scott Keck-Warren
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfAlex Barbosa Coqueiro
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek SchlawackFwdays
 
Vertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsVertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsMiki Katsuragi
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLScyllaDB
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Commit University
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubKalema Edgar
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsRizwan Syed
 

Recently uploaded (20)

Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck Presentation
 
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brand
 
SAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxSAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptx
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR Systems
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 Presentation
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdf
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
 
Vertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsVertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering Tips
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQL
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding Club
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL Certs
 

Curso Symfony - Clase 2