Curso Symfony - Clase 2

6,820 views

Published on

Presentación de la segunda clase de un curso sobre el framework Symfony
--
Slides used in the second class of a symfony framework training.

Published in: Technology
0 Comments
14 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
6,820
On SlideShare
0
From Embeds
0
Number of Embeds
741
Actions
Shares
0
Downloads
0
Comments
0
Likes
14
Embeds 0
No embeds

No notes for slide

Curso Symfony - Clase 2

  1. 1. Frameworks de desarrollo Symfony Clase 2 Javier Eguíluz javier.eguiluz@gmail.com
  2. 2. 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/
  3. 3. 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/
  4. 4. Capítulo 6 Profundizando en  el modelo
  5. 5. El objeto Criteria de Propel
  6. 6. apps/frontend/modules/job/actions/actions.class.php class jobActions extends sfActions { public function executeIndex(sfWebRequest $request) { $this‐>listado = JobeetJobPeer::doSelect( new Criteria() ); } }
  7. 7. 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]]
  8. 8. SELECT * FROM JobeetJob WHERE created_at > time() ‐ 86400 * 30
  9. 9. 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 *
  10. 10. 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 = }
  11. 11. Depurando el código  SQL generado
  12. 12. 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
  13. 13. Serializando objetos
  14. 14. 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); } }
  15. 15. 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 ); }
  16. 16. Personalizando la  configuración
  17. 17. ¿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 = }
  18. 18. apps/frontend/config/app.yml all: dias_activa:  30 sfConfig::get('app_dias_activa')
  19. 19. Refactorización
  20. 20. ¿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 = }
  21. 21. 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(); }
  22. 22. 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); }
  23. 23. Mostrando las  categorías en la  portada
  24. 24. 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); } }
  25. 25. 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; ?> 
  26. 26. Controlador      Vista             Modelo getWithJobs() 1 JobeetCategoryPeer.php getActiveJobs() 3 executeIndex() indexSuccess.php JobeetCategory.php 4 getActiveJobs() 2 $categories JobeetJobPeer.php
  27. 27. Limitando los  resultados
  28. 28. 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); } }
  29. 29. 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
  30. 30. Archivos de datos  dinámicos
  31. 31. 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; ?>
  32. 32. Restringir el acceso a  una oferta de trabajo
  33. 33. 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]
  34. 34. 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); } }
  35. 35. Capítulo 7 La página de cada  categoría
  36. 36. La ruta de la  categoría
  37. 37. 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()); }
  38. 38. El enlace a la página  de cada categoría
  39. 39. 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; ?>
  40. 40. 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.
  41. 41. xxxPeer::doSelect() xxxPeer::doSelectOne() xxxPeer::doSelectRS() xxxPeer::retrieveByPK() xxxPeer::retrieveByPKs() xxxPeer::doCount() xxxPeer::doInsert() xxxPeer::doDelete() xxxPeer::doUpdate()
  42. 42. Creando el módulo  de las categorías
  43. 43. $ ./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
  44. 44. nombre slug
  45. 45. Actualizando la base  de datos
  46. 46. config/schema.yml propel: jobeet_category: id:        ~ name: { type: varchar(255), required: true } slug: type: varchar(255) required: true index: unique getSlug()
  47. 47. lib/model/JobeetCategory.php public function setName($name) { parent::setName($name); $this‐>setSlug(Jobeet::slugify($name)); } $ ./symfony propel:build‐all‐load
  48. 48. 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): ?> ...
  49. 49. Elementos parciales
  50. 50. 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 (_)
  51. 51. 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>
  52. 52. 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()) ) ?> 
  53. 53. Paginación
  54. 54. 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(); }
  55. 55. getResults() getNbResults() haveToPaginate() getLinks() getPage() getPreviousPage() getNextPage() getLastPage()
  56. 56. Capítulo 8 Pruebas unitarias
  57. 57. test/ unit/ Prueban funciones y  métodos individualmente functional/ Prueban la aplicación en  su conjunto
  58. 58. El framework de  pruebas lime
  59. 59. 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)
  60. 60. Ejecutando pruebas  unitarias
  61. 61. 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
  62. 62. Probando el método  slugify()
  63. 63. 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');
  64. 64. 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 ‐'); ...
  65. 65. Pruebas unitarias  para Propel
  66. 66. $ mysqladmin ‐uroot ‐p create jobeet_test $ symfony configure:database ‐‐env=test quot;mysql:host=localhost;dbname=jobeet_testquot; root ConTraSenA config/databases.yml
  67. 67. 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
  68. 68. 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');
  69. 69. 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' );
  70. 70. Conjuntos de  pruebas unitarias
  71. 71. $ ./symfony test:unit
  72. 72. Capítulo 9 Pruebas  funcionales
  73. 73. La clase sfBrowser
  74. 74. sfBrowser servidor web aplicación  aplicación  Symfony Symfony
  75. 75. get() reload() setHttpHeader() post() click() setAuth() call() select() setCookie() back() deselect() removecookie() forward() restart() clearCookie() followRedirect()
  76. 76. $browser = new sfBrowser(); $browser‐> get('/')‐> click('Design')‐> get('/category/programming?page=2')‐> get('/category/programming', array('page' => 2))‐> post('search', array('keywords' => 'php')) ;
  77. 77. La clase  sfTestFunctional
  78. 78. sfBrowser sfTestFunctional response request user
  79. 79. 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() ;
  80. 80. interfaz fluída ___() ___() ___() ___()
  81. 81. response request isParameter() checkElement() isFormat() isHeader() isMethod() isStatusCode() hasCookie() isRedirected() isCookie()
  82. 82. Ejecutando pruebas  funcionales
  83. 83. $ ./symfony test:functional frontend categoryActions
  84. 84. Datos de prueba
  85. 85. 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; } }
  86. 86. Conjuntos de  pruebas funcionales
  87. 87. $ ./symfony test:functional frontend
  88. 88. Conjuntos de  pruebas
  89. 89. $ symfony test:unit $ symfony test:functional frontend $ symfony test:functional backend + $ symfony test:functional ...... $ symfony test:all
  90. 90. Capítulo 10 Los formularios
  91. 91. 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.
  92. 92. Symfony ya incluye... • Validación (para cada campo) • Widgets (campos del formulario) • Formularios (widgets + validación)
  93. 93. Formularios
  94. 94. 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) ), )); } }
  95. 95. 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
  96. 96. $this‐>mergeForm(new OtroForm()); $this‐>embedForm('name', new OtroForm());
  97. 97. Formularios de  Propel
  98. 98. schema.yml $ ./symfony propel:build‐forms JobeetJobForm JobeetAffiliateForm JobeetCategoryForm lib/form/
  99. 99. class JobeetJobForm extends BaseJobeetJobForm { public function configure() { unset( $this['created_at'], $this['updated_at'], $this['expires_at'], $this['is_activated'] ); } }
  100. 100. class JobeetJobForm extends BaseJobeetJobForm { public function configure() { ... $this‐>validatorSchema['email'] =  new sfValidatorEmail();  } }
  101. 101. 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', ); // ... }
  102. 102. sfWidgetFormChoice multiple expanded widget false false false true false true true true
  103. 103. class JobeetJobForm extends BaseJobeetJobForm { public function configure() { ... $this‐>validatorSchema['type'] =  new sfValidatorChoice(array( 'choices' => array_keys(JobeetJobPeer::$types), )); } }
  104. 104. class JobeetJobForm extends BaseJobeetJobForm { public function configure() { ... $this‐>widgetSchema‐>setLabels(array( 'category_id' => 'Category', 'is_public' => 'Public?', 'how_to_apply' => 'How to apply?', )); } }
  105. 105. 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', )); } }
  106. 106. 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.
  107. 107. 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' ); } }
  108. 108. 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
  109. 109. 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>
  110. 110. Widgets Formulario render() renderRow() renderHiddenFields() render() hasErrors() renderLabel() hasGlobalErrors() renderError() getGlobalErrors() renderHelp() renderGlobalErrors()
  111. 111. <?php echo $form ?> <?php foreach ($form as $widget): ?> <?php echo $widget‐>renderRow() ?> <?php endforeach; ?> 
  112. 112. 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 {  ... }
  113. 113. 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); }
  114. 114. 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'] ); } }
  115. 115. apps/frontend/config/routing.yml job: class:        sfPropelRouteCollection options:      { model: JobeetJob, column: token } requirements: { token: w+ } http://localhost.jobeet/job/TOKEN/edit
  116. 116. La página de  previsualización
  117. 117. 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; ?> ...
  118. 118. Activando y  publicando las  ofertas
  119. 119. apps/frontend/config/routing.yml job: class:   sfPropelRouteCollection options: model:          JobeetJob column:         token object_actions: { publish: put } requirements: token: w+
  120. 120. 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)); }
  121. 121. Capítulo 11 Probando los  formularios
  122. 122. Enviando un  formulario
  123. 123. 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()‐> ;
  124. 124. Seguridad
  125. 125. $ 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; 
  126. 126. $ 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>
  127. 127. Tareas de  mantenimiento
  128. 128. $ php lib/vendor/symfony/data/bin/symfony
  129. 129. 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)); } }

×