Your SlideShare is downloading. ×
Curso Symfony - Clase 2
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×

Introducing the official SlideShare app

Stunning, full-screen experience for iPhone and Android

Text the download link to your phone

Standard text messaging rates apply

Curso Symfony - Clase 2

6,193
views

Published on

Presentación de la segunda clase de un curso sobre el framework Symfony …

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
13 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
6,193
On Slideshare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
0
Comments
0
Likes
13
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. Frameworks de desarrollo Symfony Clase 2 Javier Eguíluz javier.eguiluz@gmail.com
  • 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. 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. Capítulo 6 Profundizando en  el modelo
  • 5. El objeto Criteria de Propel
  • 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. 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. SELECT * FROM JobeetJob WHERE created_at > time() ‐ 86400 * 30
  • 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. 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. Depurando el código  SQL generado
  • 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. Serializando objetos
  • 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. 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. Personalizando la  configuración
  • 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. apps/frontend/config/app.yml all: dias_activa:  30 sfConfig::get('app_dias_activa')
  • 19. Refactorización
  • 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. 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. 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. Mostrando las  categorías en la  portada
  • 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. 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. Controlador      Vista             Modelo getWithJobs() 1 JobeetCategoryPeer.php getActiveJobs() 3 executeIndex() indexSuccess.php JobeetCategory.php 4 getActiveJobs() 2 $categories JobeetJobPeer.php
  • 27. Limitando los  resultados
  • 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. 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. Archivos de datos  dinámicos
  • 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. Restringir el acceso a  una oferta de trabajo
  • 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. 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. Capítulo 7 La página de cada  categoría
  • 36. La ruta de la  categoría
  • 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. El enlace a la página  de cada categoría
  • 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. 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. xxxPeer::doSelect() xxxPeer::doSelectOne() xxxPeer::doSelectRS() xxxPeer::retrieveByPK() xxxPeer::retrieveByPKs() xxxPeer::doCount() xxxPeer::doInsert() xxxPeer::doDelete() xxxPeer::doUpdate()
  • 42. Creando el módulo  de las categorías
  • 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. nombre slug
  • 45. Actualizando la base  de datos
  • 46. config/schema.yml propel: jobeet_category: id:        ~ name: { type: varchar(255), required: true } slug: type: varchar(255) required: true index: unique getSlug()
  • 47. lib/model/JobeetCategory.php public function setName($name) { parent::setName($name); $this‐>setSlug(Jobeet::slugify($name)); } $ ./symfony propel:build‐all‐load
  • 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. Elementos parciales
  • 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. 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. 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. Paginación
  • 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. getResults() getNbResults() haveToPaginate() getLinks() getPage() getPreviousPage() getNextPage() getLastPage()
  • 56. Capítulo 8 Pruebas unitarias
  • 57. test/ unit/ Prueban funciones y  métodos individualmente functional/ Prueban la aplicación en  su conjunto
  • 58. El framework de  pruebas lime
  • 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. Ejecutando pruebas  unitarias
  • 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. Probando el método  slugify()
  • 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. 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. Pruebas unitarias  para Propel
  • 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. 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. 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. 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. Conjuntos de  pruebas unitarias
  • 71. $ ./symfony test:unit
  • 72. Capítulo 9 Pruebas  funcionales
  • 73. La clase sfBrowser
  • 74. sfBrowser servidor web aplicación  aplicación  Symfony Symfony
  • 75. get() reload() setHttpHeader() post() click() setAuth() call() select() setCookie() back() deselect() removecookie() forward() restart() clearCookie() followRedirect()
  • 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. La clase  sfTestFunctional
  • 78. sfBrowser sfTestFunctional response request user
  • 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. interfaz fluída ___() ___() ___() ___()
  • 81. response request isParameter() checkElement() isFormat() isHeader() isMethod() isStatusCode() hasCookie() isRedirected() isCookie()
  • 82. Ejecutando pruebas  funcionales
  • 83. $ ./symfony test:functional frontend categoryActions
  • 84. Datos de prueba
  • 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. Conjuntos de  pruebas funcionales
  • 87. $ ./symfony test:functional frontend
  • 88. Conjuntos de  pruebas
  • 89. $ symfony test:unit $ symfony test:functional frontend $ symfony test:functional backend + $ symfony test:functional ...... $ symfony test:all
  • 90. Capítulo 10 Los formularios
  • 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. Symfony ya incluye... • Validación (para cada campo) • Widgets (campos del formulario) • Formularios (widgets + validación)
  • 93. Formularios
  • 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. 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. $this‐>mergeForm(new OtroForm()); $this‐>embedForm('name', new OtroForm());
  • 97. Formularios de  Propel
  • 98. schema.yml $ ./symfony propel:build‐forms JobeetJobForm JobeetAffiliateForm JobeetCategoryForm lib/form/
  • 99. class JobeetJobForm extends BaseJobeetJobForm { public function configure() { unset( $this['created_at'], $this['updated_at'], $this['expires_at'], $this['is_activated'] ); } }
  • 100. class JobeetJobForm extends BaseJobeetJobForm { public function configure() { ... $this‐>validatorSchema['email'] =  new sfValidatorEmail();  } }
  • 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. sfWidgetFormChoice multiple expanded widget false false false true false true true true
  • 103. class JobeetJobForm extends BaseJobeetJobForm { public function configure() { ... $this‐>validatorSchema['type'] =  new sfValidatorChoice(array( 'choices' => array_keys(JobeetJobPeer::$types), )); } }
  • 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. 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. 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. 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. 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. 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. Widgets Formulario render() renderRow() renderHiddenFields() render() hasErrors() renderLabel() hasGlobalErrors() renderError() getGlobalErrors() renderHelp() renderGlobalErrors()
  • 111. <?php echo $form ?> <?php foreach ($form as $widget): ?> <?php echo $widget‐>renderRow() ?> <?php endforeach; ?> 
  • 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. 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. 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. apps/frontend/config/routing.yml job: class:        sfPropelRouteCollection options:      { model: JobeetJob, column: token } requirements: { token: w+ } http://localhost.jobeet/job/TOKEN/edit
  • 116. La página de  previsualización
  • 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. Activando y  publicando las  ofertas
  • 119. apps/frontend/config/routing.yml job: class:   sfPropelRouteCollection options: model:          JobeetJob column:         token object_actions: { publish: put } requirements: token: w+
  • 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. Capítulo 11 Probando los  formularios
  • 122. Enviando un  formulario
  • 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. Seguridad
  • 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. $ 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. Tareas de  mantenimiento
  • 128. $ php lib/vendor/symfony/data/bin/symfony
  • 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)); } }