startup: install php and mysql download and run http://sf-to.org/1.4/check.php uses /etc/php5/cli/php.ini installed additionally php5-mysql php5-xsl php-apc set short_open_tag to off in php.ini run check.php via server (not only cli) firefox tries to download it! but renamed it's ok uses /etc/php5/apache2/php.ini changed short_open_tag to Off jobeet: not under web root symfony/jobeet/lib/vendor choose wget from http://www.symfony-project.org/installation tar zxpf file mv symfony-1.4.0 symfony rm symfony-1.4.0.tgz or install from subversion run symfony: php lib/vendor/symfony/data/bin generate:project jobeet generate:app frontend prevents xss and csrf attacks change permissions: chmod 777 cache log (the only dirs symfony writes in) should be ignored by svn or hg: .hgignore syntax: glob cache/** log/** web server config: add website www.jobeet.com.localhost as defined in newsite to /etc/apache2/sites-available and link to it in sites-enabled www.jobeet.com.localhost/frontend_def.php stories in day 1 data model/relational model: config/doctrine/schema.yml doctrine Table: actAs: { Timestampable: ~ } columns: col: type: string(255)/boolean/timestamp/integer/float/decimal/array/object/blob/clob/time/date/enum/gzip notnull: true unique: true primary: true default: 0 relations: OtherTable: class: Table refClass: Table onDelete: CASCADE/SET NULL/RESTRICT local: col foreign: col foreignAlias: Alias database/mysql setup: mysqladmin -uroot -p create jobeet configure:database "mysql:host=localhost;dbname=jobeet" root password better: config/databases.yml (check access mode) doctrine:build --model doctrine:build --sql generates statements in data/sql/schema.sql doctrine:insert-sql doctrine:build --model generates classes for table records in lib/model/doctrine doctrine:build --all --no-confirmation fixture file in data/fixtures/categories.yml wget two pictures load data: doctrine:data-load all together: doctrine:build --all --and-load [--no-confirmation] database/mysql update: generate basic module: doctrine:generate-module --with-show --non-verbose-templates frontend job jobeetjob creates apps/frontend/modules/job actions/actions.class.php index: displays records show: displays fields of a record new: form for creation create edit: form to edit update delete templates module is available in www.jobeet.com.localhost/frontend_dev.php/job jobeet after dbsetup: change layout wget images stylesheets favicon (cool!) add use_stylesheet for indexSuccess and showSuccess changed indexSuccess.php showSuccess.php and actions.class.php (renamed jobeet_job to job) ? changed routing.yml to module job changed link for jobeet logo in layout changed mapping for single job slugify change executeShow: get object from route removed default routes change query: list only active jobs added expired job to fixture (removed expires_at from other two, to see them) added setting in app.yml refactoring: query from control to model (getActiveJobs) sort by expires_at get in categories limiting results securing expired jobs add category page name sluggable create partial create pager, some refactoring (get query) start testing slugify doctrine tests functional test created own functional class JobeetTestFunctional expired jobs are not listed @ fixture: data/fixtures/table.yml Table: entryname: col: value model classes (3 x table): TableElement (customizable) BaseTableElement (rewritten by docttrine:build --model) TableElementTable (class for the table itself - returns collections) model view controller/mvc: decorator design pattern: layout + slots + template layout: apps/frontend/templates/layout.php html page with inline php controller: actions methods of Actions classes (one per module) executeACTION work with model: Doctrine_Core::getTable(name) actions: index: returns Doctrine_Collection public function executeActionName(sfWebRequest $request) show: returns one element API/documentation: http://www.symfony-project.org/api/1_4/ !! http://www.symfony-project.org/doc/1_4/ classes: sfAction sfWebRequest getMethod getUri getReferer getHost getLanguages getCharsets isXmlHttpRequest getHttpHeader getCookie isSecure getFiles getGetParameter getPostParameter getUrlParameter getRemoteAddress response: sfWebResponse setCookie setStatusCode setHttpHeader setContentType addVaryHttpHeader addCacheControlHttpHeader setContent send symfony variables: $sf_contents: html generated by action data manipulation: $job = new JobeetJob(); $job->setPosition('Web developer'); $job->save(); echo $job->getPosition(); $job->delete(); linking: $job->setCategory(new JobeetCategory()); data types: initial data: needed for the app to work test data user data help: help command environments: different exception handling development: web developers, logs all, no cache test: automatical test staging: customer production: end users repository: with svn: svnadmin create repo basic dir structure: svn mkdir -m "created default directory structure" http://svn.example.com/jobeet/trunk http://svn.example.com/jobeet/tags http://svn.example.com/jobeet/branches checkout trunk: svn co http://svn.example.com/jobeet/trunk . remove cach and log content: rm -rf cache/* log/* permissions: chmod 777 cache/ log/ ignore list: svn propedit svn:ignore cache * same for log dir commit: svn import -m "made the initial import" . http://svn.example.com/jobeet/trunk with hg: ?? directory structure: main apps: applications cache: cache config: configuration lib: libraries and classes log: log files plugins: plugins test web: web root created applications: config: app.yml: application specific setings all: { var: val } sfConfig::get('app_var') lib modules (mvc) templates helper functions: include_STH prints get_STH returns include_stylesheets() user_stylesheet(STRING) url_for: generates nice URL for an element: module, action, id url_for('job/show?id='.$job->getId()) url_for('@default?module=job&action=show&id='.$job->getId()) url_for(array('var' => 'val'...)) url_for(array('sf_route' => ROUTENAME, 'sf_subject' => SUBJ)) if routing has been done with sfDoctrineRoute or url_for(ROUTNAME, SUBJ) link_to() generateUrl in layout: include_slot('NAME') forwarding: round trip (no url change) forward404Unless(BOOL): if BOOL=true goes on normally forward404If forward404 forward('default', '404') redirecting: to another url slots: include in layout/template with include_slot('NAME', DEVAULT_VALUE) or if (!include_slot(NAME)): block endif define at beginning of template slot(NAME, sprintf(PATTERN, VARS...)) or slot(NAME) codeblock end_slot() css/stylesheets: what to include: use_stylesheet(), or view.yml view: configures default settings of templates works per module: apps/frontend/config/view.yml apps/frontend/modules/module/config/view.yml format: default: http_metas: content-type: text/html metas: #title: symfony project #description: symfony project #keywords: symfony, project #language: en #robots: index, follow stylesheets: [main.css, print.css: { media: print }] javascripts: [] has_layout: true layout: layout all: templatename: configuration files: default: in framework global: cofig/app.yml local for an app: apps/APP/config/app.yml local for module: apps/APP/modules/MOD/config sfConfig::get('app_VAR') routing: url and uri management better remove default routing internal: MOD/ACTION?id=ID external: MOD/ACTION/id/ID mapping: apps/frontend/config/routing.yml first matching wins ROUTENAME: url: PATTERN class: ROUTECLASS * matches a collection of var/val pairs param: PARAMS requirements: match: pattern (eg id: \d+) change mapping: change also url_for where needed route object/class: sfRoute sfRequestRoute restrict request method: class: sfRequestRoute requirements: sf_method: [get] sfDoctrineRoute: optimized for doctrine objects options: {model: TABLECLASS, type: object/list } method_for_query: method in the element model to retrieve simplifies url_for sfDoctrineRoutecollection: options: { model: JobeetJob } no url, nothing else generates routes for each action! get object from route: $this->getRoute()->getObject(); forwards to 404 if not found. change behavior with allow_empty list routes: app:routes frontend [routename] http methods: symfony supports DELETE and PUT in form hidden sf_method=put method can be given as a parameter to helpers cache: cache:clear slugify/nicer urls: Jobeet::slugify(TEXT){ not needed if "Sluggable": write functions getCOLSlug in routing use parameter /:COL_slug/... doctrine behavior: Sluggable enable in model actAs: Sluggable: fields: [name] query: $q = Doctrine_Query::create() ->from(TABLE) ->where(args..) ->leftJoin() ->limit $result = $q->execute(); you can see query at log/frontend_dev.log or in the toolbar serialization: override save method in the model class public function save(Doctrine_Connection $conn = null) is new: isNew() new feature/module: first url and rule generate:module frontend MOD doctrine:generate-module makes much more create some actions update the model write some templates templates: partial: _NAME include_partial('mod/name', array('var' => val)) paginate: in execute sfDoctrinePager instead of the collection new sfDoctrinePager(ElementClass, limit) setQuery() setPage(page, default) init() getResults() getNbResults haveToPaginate getLinks getPage getPreviousPage getNextPage getLastPage implements iterator and countable automated testing: unit tests: test/unit functional tests: test/functional launch/run test: directly via php or test:unit name add tests whenever you find a bug, then for new features lime testing framework require_once dirname(__FILE__).'/../bootstrap/unit.php'; $t = new lime_test(1); methods: ok is isnt like unlike is_deeply fail pass skip todo comment coverage: test:coverage path/to/test path/to/class needs php5-xdebug on new feature: write test with descripton, then implement doctrine tests: configure:database --name=doctrine --class=sfDoctrineDatabase --env=test "mysql:host=localhost;dbname=jobeet_test" root password updates config/databases.yml mysqladmin -uroot -ppasswor create jobeet_test doctrine:insert-sql --env=test test data: Doctrine_Core::loadData(sfConfig::('sf_test_dir').'/fixtures'); test/fixtures fist create test/bootstrap then test/unit/model/JobeetJobTest.php run all unit tests: test:unit dubious: script ended before eof functional testing: special browser: sfBrowser get post call back forward reload click select deselect restart setHttpHeader setAuth setCookie removeCookie clearCookies followRedirect sfTestFunctional: takes sfBrowser as constructor file: test/functional/frontend/MODActionsTest.php fluent interface $browser = new sfTestfunctional(new sfBrowser()); tester block (more than one method): $b->with('request')->begin()->...->end(); request tester methods: isParameter isFormat isMethod hasCookie isCookie response tester methods: checkElement (css) checkForm debug matches isHeader isStatuscode isRedirected isValid (xml) running functional tests: directly with php or php symfony test:functional frontend categoryActions load data: Doctrine_Core::loadData(sfConfig::get('sf_test_dir').'/fixtures'); here db has already been init by bootstrapping create new functional class: in lib/test debugging: debug method halts script exec run test: test:functional frontend test all: test:all --only-failed