broken image
1.4 The view is responsible for rendering the output correlated to a particular action. In symfony, the view consists of several parts, with each part designed to be easily modified by the person who usually works with it.
Both symfony 1.3 and symfony 1.4 versions have been released at about the same time (at the end of 2009). As a matter of fact, they both have the exact same feature set. The only difference between the two versions is on how they support backward compatibility with older symfony versions.
Symfony Slot 1.4 Games
Symfony Slot 1.4 Download
Read the Getting Started guide if you are new to Symfony. Try the Symfony Demo application to learn Symfony in practice. Master Symfony with the Guides and Tutorials, the Components docs and the Best Practices reference. Join the Symfony Community and meet other members at the Symfony events. Get Symfony support on Stack Overflow.
Authors note: This post was originally published Sun, 07/31/2011 18:14. Since then, I have abandoned Symfony development (both versions 1.4 and 2.x) in favor of developing my own, AJAX-oriented framework which uses YAML files to define database schema and CRUD pages. I call it JAX Framework. You can read about it here: http://www.jaxframework.org and here: http://sourceforge.net/projects/jaxframework
The original article follows.
Ive recently started using the Symfony framework for web application development, and I like it quite a bit. In this article, I share some notes I took for myself while I was learning to use Symfony.
Without any further ado, lets get started. Symfony Slot 1.4 Games
Download the prerequisites checker here: http://sf-to.org/1.4/check.php
Run it like this: php php check_configuration.php
Fix any problems it finds.
Create a project folder, named according to your project. In my examples, Ill typically be using something like sfproject, which is what the Symfony intro uses.
A copy of symfony would typically exist under the lib/vendor/symfony folder under your project folder. So if your project folder is located at /home/sfproject, symfony would be under /home/sfproject/lib/vendor/symfony. If the symfony version is 1.4.1, when you extract the tarball under lib/vendor, youll get a symfony-1.4.1 directory. Rename the directory to make it right. Put all together, youd do something like this (replace with the path to the folder where the symfony tarball is):
cd lib/vendor
tar zxpf /symfony-1.4.1.tgz
mv symfony-1.4.1 symfony
cd ./.
Generate a new project:
php lib/vendor/symfony/data/bin/symfony generate:project
Generate the frontend app (or backend, if there is a backend app):
./symfony generate:app frontend
Fix directory permissions, if needed:
chmod 777 cache log
Enable the rewrite module for apache2. For ubuntu:
a2enmod rewrite
/etc/init.d/apache2 restart (or: service apache2 restart)
Create a virtual host for apache, named according to your project. This example is for a project named sfproject located at /home/sfproject. The filename is /etc/httpd/conf.d/sfproject.conf (change filename to match your project name):
ServerName sfproject
ServerAlias *.sfproject
DocumentRoot /home/sfproject/web
DirectoryIndex index.php
AllowOverride All
Allow from All
Alias /sf /home/sfproject/lib/vendor/symfony/data/web/sf
AllowOverride All
Allow from All
Restart apache when done.
Edit /etc/hosts. Find the line that looks something like this:
127.0.0.1 localhost.localdomain localhost
Add your project name onto the end, like this:
127.0.0.1 localhost.localdomain localhost sfproject
Point a browser at your project (replace sfproject with your project name):
http://sfproject
You should see the symfony startup page, which says something like Symfony Project Created.
Create the database for your project on MySQL or whatever, and make sure you have a good user/password configured on the database server. An example in MySQL:
mysql -u root
drop database if exists sfproject;
create database sfproject;
grant all privileges on *.* to sfproject@ identified by enter-your-password-here;
grant all privileges on *.* to sfproject@localhost identified by enter-your-password-here;
Configure the database. Replace mysql with the database type, if its not mysql. Replace with the hostname or IP address of the database server. Replace with the name of the database. Replace and with the database username and password you configured.
./symfony configure:database mysql:host=;dbname=
Example:
./symfony configure:database mysql:host=localhost;dbname=sfproject sfproject enter-your-password-here
If you have a multiple-module project, do like this for each module (if its a backend module, replace frontend with backend):
./symfony generate:module frontend
NOTE: Typically, one module equals one CRUD page. So for each database table for which youll be creating a CRUD page, there will be one module to handle that tables CRUD. Think of a symfony module as the CRUD application for a database table.
Edit config/doctrine/schema.yml. Define your database schema.
Edit data/fixtures/fixtures.yml, or any other yml file(s) in that directory (read the notes in fixtures.yml). Add your fixtures (default rows which get added to the database when it is initialized).
Build everything related to the model:
./symfony doctrine:build all-classes
./symfony doctrine:generate-migrations-models
./symfony doctrine:migrate
./symfony doctrine:data-load
*** TODO: This part still doesnt seem to work. The ./symfony doctrine:generate-migrations-db says Could not generate migration classes from database. If you use ./symfony doctrine:generate-migrations-models instead, it says it generates the migrations, but they remain untouched in lib/migration/doctrine/, and it fails to do make any changes to the database. This page covers migrations, but there doesnt seem to be any way to easily bring an arbitrary database into sync with the current schema: http://www.denderello.com/publications/guide-to-doctrine-migrations. It may be necessary to rm -f lib/migration/doctrine/* before generating any migrations. Also see: http://www.slideshare.net/weaverryan/the-art-of-doctrine-migrations
Following any subsequent changes to the schemas, do this to update the model classes and database:
rm -f lib/migration/doctrine/*
./symfony doctrine:generate-migrations-db
./symfony doctrine:migrate
./symfony doctrine:build all-classes
Conclusion: Doctrine migrations in Symfony appear to be broken. This is sad, because it really should work as advertised. You should be able to do whatever you need to do to your schema, and then run a single command and have it compare the current schema with that of the database and automatically apply the required DDL changes to the database to bring it into line with the current schema. There should be no need to generate migrations manually. In its current state, the Doctrine migration system should more aptly be called the Doctrine migraine system.
NOTE: If you ever need to drop a table, delete it from schema.yml and then do this:
./symfony doctrine:clean-model-files (or ./symfony doc:clean)
If the migration worked as advertised, you would just do a migration at this point. Alas, youll need to drop the table manually in mysql or whatever database youre using.
NOTE: To show all available doctrine tasks, run:
./symfony list doctrine
Edit apps/frontend/templates/layout.php to define the site-wide layout. This is the top-to-bottom layout (header, template, footer). Do something like this:
?php include_http_metas() ?
?php include_metas() ?
?php include_title() ?
?php include_stylesheets() ?
?php include_javascripts() ?
?php include_component_slot(topNav); ?
?php include_component_slot(leftNav); ?
?php echo $sf_content ?
Put javascript files, css files and image files under web/js, web/css and web/images, respectively.
Edit apps/frontend/config/view.yml and add any stylesheets and/or javascript files (comma-separated) you want to include automatically in every page. Note that main.css is already there by default.
You can add custom per-module stylesheets and javascript files at the module level, or at the page level, by editing apps/frontend/modules//config/view.yml. You can even put custom HTML blocks into the view for that module. You can either add them under the stylesheets: and/or javascripts: entries, or define each action to have additional stylesheets and/or javascript files by creating new entries in the form :. Or, you can just call the use_stylesheet(.css) and/or use_javascript(.css) function inside each individual template in order to minimize the amount of configuration junk you throw into view.yml.
The action controllers are located in apps/frontend/modules//actions/actions.class.php. Each action is a function like this:
public function execute(sfWebRequest $request)
Each action handler function basically does all of the database work, and populates various properties into $this. Example (look for $this-jobeet_jobs):
public function executeIndex(sfWebRequest $request)
$this-jobeet_jobs = Doctrine_Core::getTable(JobeetJob)
-createQuery(a)
-execute();
Playing Blackjack at El Cortez Hotel and Casino Located downtown on Fremont Street, the El Cortez proudly calls itself the longest continuously-running hotel and casino in the city. It has been serving Las Vegas since 1941, and few casinos can claim to have been dealing cards longer than that. El cortez casino blackjack online .
Each view template is located in apps/frontend/modules//templates/Success.php. Each property which was set in $this in the action, is available as a PHP variable for use in the view template, like this (look for $jobeet_jobs):
?php foreach ($jobeet_jobs as $i = $job): ?
?php echo $job-getLocation() ?
?php echo $job-getCompany() ?
?php endforeach ?
NOTE: It is possible use one class per action, as below. Howerver, since each module generally corresponds to a CRUD page for a single table and its relations, it usually wont be necessary.
apps/frontend/modules/pfmgr2/actions/indexAction.class.php
?php
class indexAction extends sfActions
public function executeIndex(sfWebRequest $request)
// put action handler code here.
NOTE: Be careful to keep all model-related code in the model classes themselves, so that the controller is simply calling model functions and setting properties for the view to use.
NOTE: If you have an action URI like posting-types, that will translate to an action named postingtypes, because the non-class-compatible characters will be stripped out. Be careful to name your class file, class, action handler function and action success template file correspondingly. You can also customize the action URIs in the routing.yml file (see below).
In apps/frontend/templates/layout.php, you can define slots (placeholders) which will be filled in with HTML by the templates.
In apps/frontend/templates/layout.php:
?php include_slot(title) ?
To provide a default value for the slot when the template does not provide a value for it:
?php include_slot(title, Jobeet Your best job board) ?
In apps/frontend/modules/job/templates/showSuccess.php:
?php slot(
title,
sprintf(s is looking for a s, $job-getCompany(), $job-getPosition()))
?
URL routing is configured in apps/frontend/config/routing.yml. You can add module-specific routing configuration in apps/frontend/modules//config/routing.yml. Its possible that in some cases, custom routing wont be needed because the default routes look pretty good.
If you want a default module to handle the homepage for the site, do something like this in apps/frontend/config/routing.yml:
homepage:
url: /
param: module: job, action: index
In each routing rule, when you see something like :module, thats assigning a value to a variable. The module and action variables are used by symfony to route to a specific module, and a specific action within that module. The special * character means use the rest of the path as name-value pairs in the form /name/value, so for example /id/37 would set the id variable to 37.
The url_for() helper will take a URL like job/show?id=37 and convert it to /job/show/id/37. The preferred way is to use url_for(@default?module=jobaction=showid=37) which would also return /job/show/id/37.
See here for more info on routing: http://www.symfony-project.org/jobeet/1_4/Doctrine/en/05
Two ways to do a query.
Simple:
$this-jobeet_jobs = Doctrine::getTable(JobeetJob)
-createQuery(a)
-execute();
More control:
$q = Doctrine_Query::create()
-from(JobeetJob j)
-where(j.created_at ?, date(Y-m-d H:i:s, time() 86400 * 30));
$this-jobeet_jobs = $q-execute();
These kinds of custom queries should be created in custom functions inside the model, and called from the actions. Avoid writing ad hoc queries inside the actions.
To customize the model, you just edit the entitys class file. These are located in lib/model/doctrine. The generated base classes should NEVER be edited by hand because they are re-generated by Doctrine when you make a schema change. They are located in lib/model/doctrine/base. Only edit php classes which exist directly in lib/model/doctrine. For each entity you created in the model, there will be two files here:
.class.php
Table.class.php
Normally you would only edit the first one (.class.php). You can add special finder methods which find by specific criteria, or you can override the save(Doctrine_Connection $conn = null) method to do some calculations and assign the results of those calculations to columns before calling:
return parent::save($conn);
You can put application-wide configuration parameters into apps/frontend/config/app.yml:
all:
active_days: 30
You can retrieve these like this (note the app_ prefix):
$activeDays = sfConfig::get(app_active_days);
Component slots are useful for headings, navigation, etc. To do one, edit apps/frontend/templates/layout.php and call include_component_slot(), like this (this shows two separate component slots):
?php include_component_slot(topNav); ?
?php include_component_slot(leftNav); ?
Then edit apps/frontend/config/view.yml and add this under the default section (pfmgr2 is module name; topNav and leftNav are component label names):
default:
components:
topNav: [pfmgr2, topNav]
leftNav: [pfmgr2, leftNav]
Create a components.class.php file to handle the component events (required). This one is in apps/frontend/modules/pfmgr2/actions/components.class.php:
?php
class pfmgr2Components extends sfComponents
public function executeTopNav(sfWebRequest $request)
(put component controller code here)
public function executeLeftNav(sfWebRequest $request)
(put component controller code here)
NOTE: A better way, is to put each components controller code into a separate file, like this:
apps/frontend/modules/pfmgr2/actions/topNavComponent.class.php:
?php
class topNavComponent extends sfComponents
public function executeTopNav(sfWebRequest $request)
if ($this-getUser()-isAuthenticated())
$this-isAuthenticated = true;
$this-fullName = $this-getUser()-getName();
else
$this-isAuthenticated = false;
This one provides a complete navigation menu, removing menu items which the user cant access.
apps/frontend/modules/pfmgr2/actions/leftNavComponent.class.php:
?php
class leftNavComponent extends sfComponents
public static $LEFT_NAV_MENU_GROUPS;
protected $security;
protected $securityByModuleName;
public function executeLeftNav(sfWebRequest $request)
$this-menuGroups = array();
$user = $this-getUser();
$isAuthenticated = $user-isAuthenticated();
$isSuperAdmin = $isAuthenticated $user-isSuperAdmin();
$context = $this-getContext();
$routing = $context-getRouting();
$this-security = array();
$this-securityByModuleName = array();
$group = array();
foreach (self::$LEFT_NAV_MENU_GROUPS as $menuGroup)
foreach ($menuGroup[items] as $menuGroupItem)
$params = $routing-parse($menuGroupItem[uri]);
if (!isset($this-securityByModuleName[$params[module]]))
$this-security = array();
if ($fn = $context-getConfigCache()-checkConfig
(modules/.$params[module]./config/security.yml, true))
require($fn);
$this-securityByModuleName[$params[module]] = $this-security;
$this-security = array();
if ($this-isActionSecure($params[module], $params[action]))
if (!$isAuthenticated)
continue;
if ((!$isSuperAdmin)
(!$user-hasCredential
($this-getActionCredentials($params[module], $params[action]))))
continue;
if (!isset($group[name])) $group[name] = $menuGroup[name];
if (!isset($group[items])) $group[items] = array();
$group[items][] = $menuGroupItem;
if (!empty($group))
$this-menuGroups[] = $group;
$group = array();
protected function getModuleSecurityValue($moduleName, $actionName, $name, $default = null)
$actionName = strtolower($actionName);
if (isset($this-securityByModuleName[$moduleName][$actionName][$name]))
return $this-securityByModuleName[$moduleName][$actionName][$name];
if (isset($this-securityByModuleName[$moduleName][all][$name]))
return $this-securityByModuleName[$moduleName][all][$name];
return $default;
protected function isActionSecure($moduleName, $actionName)
return $this-getModuleSecurityValue($moduleName, $actionName, is_secure, false);
protected function getActionCredentials($moduleName, $actionName)
return $this-getModuleSecurityValue($moduleName, $actionName, credentials);
leftNavComponent::$LEFT_NAV_MENU_GROUPS = array(
array(
name=Setup,
items=array(
array(name=Users, uri=guard/users),
array(name=Groups, uri=guard/groups),
array(name=Posting Types, uri=postingType/index),
array(name=Account Types, uri=accountType/index),
array(name=Accounts, uri=account/index),
array(name=Incomes/Expenses, uri=incomeExpense/index),
),
),
array(
name=Data Entry,
items=array(
array(name=Enter/Reconcile Postings, uri=posting/index),
),
),
array(
name=Reports,
items=array(
array(name=Posting Details, uri=postingDetailReport/index),
array(name=Income/Expenses, uri=incomeExpenseReport/index),
),
),
);
Create php files to generate the components HTML content. You can put HTML and PHP code in these templates; they are part of the view. These are located in apps/frontend/modules/pfmgr2/templates and are named _topNav.php and _leftNav.php respectively.
_topNav.php:
Personal Finance Manager
?php if ($isAuthenticated) ?
Logged in as: ?php echo esc_specialchars($fullName); ?
?php echo link_to(Log out, sf_guard_signout); ?
?php else ?
You are not logged in.
?php ?
_leftNav.php:
?php
echo link_to(Home, homepage);
foreach ($menuGroups as $menuGroup)
echo .esc_specialchars($menuGroup[name]).n n; foreach ($menuGroup[items] as $menuGroupItem) echo
.link_to($menuGroupItem[name], $menuGroupItem[uri]).
n;
echo
n;
Securing an application with a login
Install sfDoctrineGuardPlugin:
./symfony plugin:install sfDoctrineGuardPlugin
Create an administrative user and some permissions (will need to reload or migrate the database).
data/fixtures/010_pfmgr2.yml (filename and data may change, depending on your application):
sfGuardUser:
sgu_admin:
username: admin
password: admin
is_super_admin: true
first_name: Administrator
email_address: admin@some.email.address
sfGuardPermission:
sgp_edit_users: name: Edit Users, description: Edit Users
sgp_edit_groups: name: Edit Groups, description: Edit Groups
sgp_edit_posting_types: name: Edit Posting Types, description: Edit Posting Types
sgp_edit_acct_types: name: Edit Account Types, description: Edit Account Types
sgp_edit_income_expenses: name: Edit Income/Expenses, description: Edit Incomes/Expenses
sgp_edit_accts: name: Edit Accounts, description: Edit Accounts
sgp_edit_postings: name: Edit Postings, description: Edit Postings
sgp_reconcile: name: Reconcile, description: Reconcile
sgp_view_posting_detail_report: name: View Posting Detail Report, description: View Posting Detail Report
sgp_view_income_expense_report: name: View Income Expense Report, description: View Income/Expense Report
Configure symfony to use sfDoctrineGuardPlugin.
apps/frontend/config/settings.yml:
all:
.settings:
enabled_modules: [default, sfGuardAuth, sfGuardUser, sfGuardGroup, sfGuardForgotPassword]
.actions:
login_module: sfGuardAuth To be called when a non-authenticated user
login_action: signin Tries to access a secure page
secure_module: sfGuardAuth
secure_action: secure
apps/frontend/config/security.yml:
default:
is_secure: true
Configure the required credentials for each module (modify this for your application).
apps/frontend/modules/sfGuardUser/config/security.yml:
all:
is_secure: true
credentials: [ Edit Users ]
apps/frontend/modules/sfGuardGroup/config/security.yml:
all:
is_secure: true
credentials: [ Edit Groups ]
apps/frontend/modules/postingType/config/security.yml:
all:
is_secure: true
credentials: [ Edit Posting Types ]
Now any secure page should require login, and any action which requires credentials should prevent the user from executing that action if the user doesnt have the required credentials.
-
Develop a custom form generator
-
apps/frontent/lib/myFormGenerator.class.php:
?php
class myFormGenerator extends sfDoctrineFormGenerator
public function getWidgetClassForColumn($column)
switch ($column-getDoctrineType())
case date:
case timestamp:
return sfWidgetFormTextDateInputJQueryDatePicker;
break;
default:
return parent::getWidgetClassForColumn($column);
public function getWidgetOptionsForColumn($column)
switch ($column-getDoctrineType())
case timestamp:
return array(include_time = true);
break;
default:
return parent::getWidgetOptionsForColumn($column);
-
Rebuilding Model, Forms, etc. After Schema Change
-
./symfony doctrine:build-model
./symfony doctrine:build-filters
./symfony doctrine:build-forms generator-class=myFormGenerator
./symfony cc
Wiping out and Rebuilding the Database
./symfony doctrine:build db and-load
-
Making Plugin Assets Available to Web Pages and Actions
-
./symfony plugin:publish-assets
Generating database administration pages based on the model
To generate an admin page whose URI will be /postingType, for the PfmPostingType entity, do the following:
./symfony doctrine:generate-admin frontend PfmPostingType module=postingType
NOTE: Symfony likes for you to use one module per major entity.
NOTE: You can edit generator.yml for the module, and update various things about the administration.
-
Creating, packaging a plugin
-
Be sure to install sfTaskExtraPlugin, which gives you the extra tasks needed to create and package plugins.
./symfony plugin:install sfTaskExtraPlugin
To create a new plugin:
./symfony generate:plugin myFirstPlugin
To create a module within a plugin:
./symfony generate:plugin-module myFirstPlugin myFirstModule
Package the plugin as a PEAR package for release on symfony-project.org, so other users can install it using ./symfony plugin:install myFirstPlugin. This will create a PEAR-compatible .tar.gz file in the plugins directory, which can be uploaded to symfony-project.org under the plugin admin page (you must have admin permissions for the plugin).
./symfony plugin:package myFirstPlugin
Enter the release number (ie 1.0.0) and stability (ie stable).
Add the plugin to SVN on the symfony-project.org website.
Add each plugin release on symfony-project.org as a PEAR package so others can easily install it.
Making Admin Forms Use AJAX
./symfony plugin:install sfAjaxAdminDoctrineThemePlugin
Follow the instructions in plugin/sfAjaxAdminDoctrineThemePlugin/README.
Making Admin Forms Look Nicer
./symfony plugin:install sfFormExtraPlugin
Edit lib/form/doctrine/.class.php. Add something like the following in the configure() function, as needed:
$this-widgetSchema[posting_date] = new sfWidgetFormJQueryDate();
Clear the cache:
./symfony cc
Thats all for now. I hope this article has given you some good information and pointers on using symfony. Its a really nice, stable framework for developing web applications.
Go forth and write software!
Authors note: This post was originally published Sun, 07/31/2011 18:14. Since then, I have abandoned Symfony development (both versions 1.4 and 2.x) in favor of developing my own, AJAX-oriented framework which uses YAML files to define database schema and CRUD pages. I call it JAX Framework. You can read about it here: http://www.jaxframework.org and here: http://sourceforge.net/projects/jaxframework
The original article follows.
Ive recently started using the Symfony framework for web application development, and I like it quite a bit. In this article, I share some notes I took for myself while I was learning to use Symfony.
Without any further ado, lets get started.
Download the prerequisites checker here: http://sf-to.org/1.4/check.php
Run it like this: php php check_configuration.php
Fix any problems it finds.
Create a project folder, named according to your project. In my examples, Ill typically be using something like sfproject, which is what the Symfony intro uses.
A copy of symfony would typically exist under the lib/vendor/symfony folder under your project folder. So if your project folder is located at /home/sfproject, symfony would be under /home/sfproject/lib/vendor/symfony. If the symfony version is 1.4.1, when you extract the tarball under lib/vendor, youll get a symfony-1.4.1 directory. Rename the directory to make it right. Put all together, youd do something like this (replace with the path to the folder where the symfony tarball is):
cd lib/vendor
tar zxpf /symfony-1.4.1.tgz
mv symfony-1.4.1 symfony
cd ./.
Generate a new project:
php lib/vendor/symfony/data/bin/symfony generate:project
Generate the frontend app (or backend, if there is a backend app):
./symfony generate:app frontend
Fix directory permissions, if needed:
chmod 777 cache log
Enable the rewrite module for apache2. For ubuntu:
a2enmod rewrite
/etc/init.d/apache2 restart (or: service apache2 restart)
Create a virtual host for apache, named according to your project. This example is for a project named sfproject located at /home/sfproject. The filename is /etc/httpd/conf.d/sfproject.conf (change filename to match your project name):
ServerName sfproject
ServerAlias *.sfproject
DocumentRoot /home/sfproject/web
DirectoryIndex index.php
AllowOverride All
Allow from All
Alias /sf /home/sfproject/lib/vendor/symfony/data/web/sf
AllowOverride All
Allow from All
Restart apache when done.
Edit /etc/hosts. Find the line that looks something like this:
127.0.0.1 localhost.localdomain localhost
Add your project name onto the end, like this:
127.0.0.1 localhost.localdomain localhost sfproject
Point a browser at your project (replace sfproject with your project name):
http://sfproject
You should see the symfony startup page, which says something like Symfony Project Created.
Create the database for your project on MySQL or whatever, and make sure you have a good user/password configured on the database server. An example in MySQL:
mysql -u root
drop database if exists sfproject;
create database sfproject;
grant all privileges on *.* to sfproject@ identified by enter-your-password-here;
grant all privileges on *.* to sfproject@localhost identified by enter-your-password-here;
Configure the database. Replace mysql with the database type, if its not mysql. Replace with the hostname or IP address of the database server. Replace with the name of the database. Replace and with the database username and password you configured.
./symfony configure:database mysql:host=;dbname=
Example:
./symfony configure:database mysql:host=localhost;dbname=sfproject sfproject enter-your-password-here
If you have a multiple-module project, do like this for each module (if its a backend module, replace frontend with backend):
./symfony generate:module frontend
NOTE: Typically, one module equals one CRUD page. So for each database table for which youll be creating a CRUD page, there will be one module to handle that tables CRUD. Think of a symfony module as the CRUD application for a database table.
Edit config/doctrine/schema.yml. Define your database schema.
Edit data/fixtures/fixtures.yml, or any other yml file(s) in that directory (read the notes in fixtures.yml). Add your fixtures (default rows which get added to the database when it is initialized).
Build everything related to the model:
./symfony doctrine:build all-classes
./symfony doctrine:generate-migrations-models
./symfony doctrine:migrate
./symfony doctrine:data-load
*** TODO: This part still doesnt seem to work. The ./symfony doctrine:generate-migrations-db says Could not generate migration classes from database. If you use ./symfony doctrine:generate-migrations-models instead, it says it generates the migrations, but they remain untouched in lib/migration/doctrine/, and it fails to do make any changes to the database. This page covers migrations, but there doesnt seem to be any way to easily bring an arbitrary database into sync with the current schema: http://www.denderello.com/publications/guide-to-doctrine-migrations. It may be necessary to rm -f lib/migration/doctrine/* before generating any migrations. Also see: http://www.slideshare.net/weaverryan/the-art-of-doctrine-migrations
Following any subsequent changes to the schemas, do this to update the model classes and database:
rm -f lib/migration/doctrine/*
./symfony doctrine:generate-migrations-db
./symfony doctrine:migrate
./symfony doctrine:build all-classes
Conclusion: Doctrine migrations in Symfony appear to be broken. This is sad, because it really should work as advertised. You should be able to do whatever you need to do to your schema, and then run a single command and have it compare the current schema with that of the database and automatically apply the required DDL changes to the database to bring it into line with the current schema. There should be no need to generate migrations manually. In its current state, the Doctrine migration system should more aptly be called the Doctrine migraine system.
NOTE: If you ever need to drop a table, delete it from schema.yml and then do this:
./symfony doctrine:clean-model-files (or ./symfony doc:clean)
If the migration worked as advertised, you would just do a migration at this point. Alas, youll need to drop the table manually in mysql or whatever database youre using.
NOTE: To show all available doctrine tasks, run:
./symfony list doctrine
Edit apps/frontend/templates/layout.php to define the site-wide layout. This is the top-to-bottom layout (header, template, footer). Do something like this:
?php include_http_metas() ?
?php include_metas() ?
?php include_title() ?
?php include_stylesheets() ?
?php include_javascripts() ? Symfony Slot 1.4 Download
?php include_component_slot(topNav); ?
?php include_component_slot(leftNav); ?
?php echo $sf_content ?
Put javascript files, css files and image files under web/js, web/css and web/images, respectively.
Edit apps/frontend/config/view.yml and add any stylesheets and/or javascript files (comma-separated) you want to include automatically in every page. Note that main.css is already there by default.
You can add custom per-module stylesheets and javascript files at the module level, or at the page level, by editing apps/frontend/modules//config/view.yml. You can even put custom HTML blocks into the view for that module. You can either add them under the stylesheets: and/or javascripts: entries, or define each action to have additional stylesheets and/or javascript files by creating new entries in the form :. Or, you can just call the use_stylesheet(.css) and/or use_javascript(.css) function inside each individual template in order to minimize the amount of configuration junk you throw into view.yml.
The action controllers are located in apps/frontend/modules//actions/actions.class.php. Each action is a function like this:
public function execute(sfWebRequest $request)
Each action handler function basically does all of the database work, and populates various properties into $this. Example (look for $this-jobeet_jobs):
public function executeIndex(sfWebRequest $request)
$this-jobeet_jobs = Doctrine_Core::getTable(JobeetJob)
-createQuery(a)
-execute();
Each view template is located in apps/frontend/modules//templates/Success.php. Each property which was set in $this in the action, is available as a PHP variable for use in the view template, like this (look for $jobeet_jobs):
?php foreach ($jobeet_jobs as $i = $job): ?
?php echo $job-getLocation() ?
?php echo $job-getCompany() ?
?php endforeach ?
NOTE: It is possible use one class per action, as below. Howerver, since each module generally corresponds to a CRUD page for a single table and its relations, it usually wont be necessary.
apps/frontend/modules/pfmgr2/actions/indexAction.class.php
?php
class indexAction extends sfActions
public function executeIndex(sfWebRequest $request)
// put action handler code here.
NOTE: Be careful to keep all model-related code in the model classes themselves, so that the controller is simply calling model functions and setting properties for the view to use.
NOTE: If you have an action URI like posting-types, that will translate to an action named postingtypes, because the non-class-compatible characters will be stripped out. Be careful to name your class file, class, action handler function and action success template file correspondingly. You can also customize the action URIs in the routing.yml file (see below).
In apps/frontend/templates/layout.php, you can define slots (placeholders) which will be filled in with HTML by the templates.
In apps/frontend/templates/layout.php:
?php include_slot(title) ?
To provide a default value for the slot when the template does not provide a value for it:
?php include_slot(title, Jobeet Your best job board) ?
In apps/frontend/modules/job/templates/showSuccess.php:
?php slot(
title,
sprintf(s is looking for a s, $job-getCompany(), $job-getPosition()))
?
URL routing is configured in apps/frontend/config/routing.yml. You can add module-specific routing configuration in apps/frontend/modules//config/routing.yml. Its possible that in some cases, custom routing wont be needed because the default routes look pretty good.
If you want a default module to handle the homepage for the site, do something like this in apps/frontend/config/routing.yml:
homepage:
url: /
param: module: job, action: index
In each routing rule, when you see something like :module, thats assigning a value to a variable. The module and action variables are used by symfony to route to a specific module, and a specific action within that module. The special * character means use the rest of the path as name-value pairs in the form /name/value, so for example /id/37 would set the id variable to 37.
The url_for() helper will take a URL like job/show?id=37 and convert it to /job/show/id/37. The preferred way is to use url_for(@default?module=jobaction=showid=37) which would also return /job/show/id/37.
See here for more info on routing: http://www.symfony-project.org/jobeet/1_4/Doctrine/en/05
Two ways to do a query.
Simple:
$this-jobeet_jobs = Doctrine::getTable(JobeetJob)
-createQuery(a)
-execute();
More control:
$q = Doctrine_Query::create()
-from(JobeetJob j)
-where(j.created_at ?, date(Y-m-d H:i:s, time() 86400 * 30));
$this-jobeet_jobs = $q-execute();
These kinds of custom queries should be created in custom functions inside the model, and called from the actions. Avoid writing ad hoc queries inside the actions.
To customize the model, you just edit the entitys class file. These are located in lib/model/doctrine. The generated base classes should NEVER be edited by hand because they are re-generated by Doctrine when you make a schema change. They are located in lib/model/doctrine/base. Only edit php classes which exist directly in lib/model/doctrine. For each entity you created in the model, there will be two files here:
.class.php
Table.class.php
Normally you would only edit the first one (.class.php). You can add special finder methods which find by specific criteria, or you can override the save(Doctrine_Connection $conn = null) method to do some calculations and assign the results of those calculations to columns before calling:
return parent::save($conn);
You can put application-wide configuration parameters into apps/frontend/config/app.yml:
all:
active_days: 30
You can retrieve these like this (note the app_ prefix):
$activeDays = sfConfig::get(app_active_days);
Component slots are useful for headings, navigation, etc. To do one, edit apps/frontend/templates/layout.php and call include_component_slot(), like this (this shows two separate component slots):
?php include_component_slot(topNav); ?
?php include_component_slot(leftNav); ?
Then edit apps/frontend/config/view.yml and add this under the default section (pfmgr2 is module name; topNav and leftNav are component label names):
default:
components:
topNav: [pfmgr2, topNav]
leftNav: [pfmgr2, leftNav]
Create a components.class.php file to handle the component events (required). This one is in apps/frontend/modules/pfmgr2/actions/components.class.php:
?php
class pfmgr2Components extends sfComponents
public function executeTopNav(sfWebRequest $request)
(put component controller code here)
public function executeLeftNav(sfWebRequest $request)
(put component controller code here)
NOTE: A better way, is to put each components controller code into a separate file, like this:
apps/frontend/modules/pfmgr2/actions/topNavComponent.class.php:
?php
class topNavComponent extends sfComponents
public function executeTopNav(sfWebRequest $request)
if ($this-getUser()-isAuthenticated())
$this-isAuthenticated = true;
$this-fullName = $this-getUser()-getName();
else
$this-isAuthenticated = false;
This one provides a complete navigation menu, removing menu items which the user cant access.
apps/frontend/modules/pfmgr2/actions/leftNavComponent.class.php:
?php
class leftNavComponent extends sfComponents
public static $LEFT_NAV_MENU_GROUPS;
protected $security;
protected $securityByModuleName;
public function executeLeftNav(sfWebRequest $request)
$this-menuGroups = array();
$user = $this-getUser();
$isAuthenticated = $user-isAuthenticated();
$isSuperAdmin = $isAuthenticated $user-isSuperAdmin();
$context = $this-getContext();
$routing = $context-getRouting();
$this-security = array();
$this-securityByModuleName = array();
$group = array();
foreach (self::$LEFT_NAV_MENU_GROUPS as $menuGroup)
foreach ($menuGroup[items] as $menuGroupItem)
$params = $routing-parse($menuGroupItem[uri]);
if (!isset($this-securityByModuleName[$params[module]]))
$this-security = array();
if ($fn = $context-getConfigCache()-checkConfig
(modules/.$params[module]./config/security.yml, true))
require($fn);
$this-securityByModuleName[$params[module]] = $this-security;
$this-security = array();
if ($this-isActionSecure($params[module], $params[action]))
if (!$isAuthenticated)
continue;
if ((!$isSuperAdmin)
(!$user-hasCredential
($this-getActionCredentials($params[module], $params[action]))))
continue;
if (!isset($group[name])) $group[name] = $menuGroup[name];
if (!isset($group[items])) $group[items] = array();
$group[items][] = $menuGroupItem;
if (!empty($group))
$this-menuGroups[] = $group;
$group = array();
protected function getModuleSecurityValue($moduleName, $actionName, $name, $default = null)
$actionName = strtolower($actionName);
if (isset($this-securityByModuleName[$moduleName][$actionName][$name]))
return $this-securityByModuleName[$moduleName][$actionName][$name];
if (isset($this-securityByModuleName[$moduleName][all][$name]))
return $this-securityByModuleName[$moduleName][all][$name];
return $default;
protected function isActionSecure($moduleName, $actionName)
return $this-getModuleSecurityValue($moduleName, $actionName, is_secure, false);
protected function getActionCredentials($moduleName, $actionName)
return $this-getModuleSecurityValue($moduleName, $actionName, credentials);
leftNavComponent::$LEFT_NAV_MENU_GROUPS = array(
array(
name=Setup,
items=array(
array(name=Users, uri=guard/users),
array(name=Groups, uri=guard/groups),
array(name=Posting Types, uri=postingType/index),
array(name=Account Types, uri=accountType/index),
array(name=Accounts, uri=account/index),
array(name=Incomes/Expenses, uri=incomeExpense/index),
),
),
array(
name=Data Entry,
items=array(
array(name=Enter/Reconcile Postings, uri=posting/index),
),
),
array(
name=Reports,
items=array(
array(name=Posting Details, uri=postingDetailReport/index),
array(name=Income/Expenses, uri=incomeExpenseReport/index),
),
),
);
Create php files to generate the components HTML content. You can put HTML and PHP code in these templates; they are part of the view. These are located in apps/frontend/modules/pfmgr2/templates and are named _topNav.php and _leftNav.php respectively.
_topNav.php:
Personal Finance Manager
?php if ($isAuthenticated) ?
Logged in as: ?php echo esc_specialchars($fullName); ?
?php echo link_to(Log out, sf_guard_signout); ?
?php else ?
You are not logged in.
?php ?
_leftNav.php:
?php
echo link_to(Home, homepage);
foreach ($menuGroups as $menuGroup)
echo .esc_specialchars($menuGroup[name]).n n; foreach ($menuGroup[items] as $menuGroupItem) echo
.link_to($menuGroupItem[name], $menuGroupItem[uri]).
n;
echo
n;
Securing an application with a login
Install sfDoctrineGuardPlugin:
./symfony plugin:install sfDoctrineGuardPlugin
Create an administrative user and some permissions (will need to reload or migrate the database).
data/fixtures/010_pfmgr2.yml (filename and data may change, depending on your application):
sfGuardUser:
sgu_admin:
username: admin
password: admin
is_super_admin: true
first_name: Administrator
email_address: admin@some.email.address
sfGuardPermission:
sgp_edit_users: name: Edit Users, description: Edit Users
sgp_edit_groups: name: Edit Groups, description: Edit Groups
sgp_edit_posting_types: name: Edit Posting Types, description: Edit Posting Types
sgp_edit_acct_types: name: Edit Account Types, description: Edit Account Types
sgp_edit_income_expenses: name: Edit Income/Expenses, description: Edit Incomes/Expenses
sgp_edit_accts: name: Edit Accounts, description: Edit Accounts
sgp_edit_postings: name: Edit Postings, description: Edit Postings
sgp_reconcile: name: Reconcile, description: Reconcile
sgp_view_posting_detail_report: name: View Posting Detail Report, description: View Posting Detail Report
sgp_view_income_expense_report: name: View Income Expense Report, description: View Income/Expense Report
Configure symfony to use sfDoctrineGuardPlugin.
apps/frontend/config/settings.yml:
all:
.settings:
enabled_modules: [default, sfGuardAuth, sfGuardUser, sfGuardGroup, sfGuardForgotPassword]
.actions:
login_module: sfGuardAuth To be called when a non-authenticated user
login_action: signin Tries to access a secure page
secure_module: sfGuardAuth
secure_action: secure
apps/frontend/config/security.yml:
default:
is_secure: true
Configure the required credentials for each module (modify this for your application).
apps/frontend/modules/sfGuardUser/config/security.yml:
all:
is_secure: true
credentials: [ Edit Users ]
apps/frontend/modules/sfGuardGroup/config/security.yml:
all:
is_secure: true
credentials: [ Edit Groups ]
apps/frontend/modules/postingType/config/security.yml:
all:
is_secure: true
credentials: [ Edit Posting Types ]
Now any secure page should require login, and any action which requires credentials should prevent the user from executing that action if the user doesnt have the required credentials.
-
Develop a custom form generator
-
apps/frontent/lib/myFormGenerator.class.php:
?php
class myFormGenerator extends sfDoctrineFormGenerator
public function getWidgetClassForColumn($column)
switch ($column-getDoctrineType())
case date:
case timestamp:
return sfWidgetFormTextDateInputJQueryDatePicker;
break;
default:
return parent::getWidgetClassForColumn($column);
public function getWidgetOptionsForColumn($column)
switch ($column-getDoctrineType())
case timestamp:
return array(include_time = true);
break;
default:
return parent::getWidgetOptionsForColumn($column);
-
Rebuilding Model, Forms, etc. After Schema Change
-
./symfony doctrine:build-model
./symfony doctrine:build-filters
./symfony doctrine:build-forms generator-class=myFormGenerator
./symfony cc
Wiping out and Rebuilding the Database
./symfony doctrine:build db and-load
-
Making Plugin Assets Available to Web Pages and Actions
-
./symfony plugin:publish-assets
Generating database administration pages based on the model
To generate an admin page whose URI will be /postingType, for the PfmPostingType entity, do the following:
./symfony doctrine:generate-admin frontend PfmPostingType module=postingType
NOTE: Symfony likes for you to use one module per major entity.
NOTE: You can edit generator.yml for the module, and update various things about the administration.
-
Creating, packaging a plugin
-
Be sure to install sfTaskExtraPlugin, which gives you the extra tasks needed to create and package plugins.
./symfony plugin:install sfTaskExtraPlugin
To create a new plugin:
./symfony generate:plugin myFirstPlugin
To create a module within a plugin:
./symfony generate:plugin-module myFirstPlugin myFirstModule
Package the plugin as a PEAR package for release on symfony-project.org, so other users can install it using ./symfony plugin:install myFirstPlugin. This will create a PEAR-compatible .tar.gz file in the plugins directory, which can be uploaded to symfony-project.org under the plugin admin page (you must have admin permissions for the plugin).
./symfony plugin:package myFirstPlugin
Enter the release number (ie 1.0.0) and stability (ie stable).
Add the plugin to SVN on the symfony-project.org website.
Add each plugin release on symfony-project.org as a PEAR package so others can easily install it.
Making Admin Forms Use AJAX
Shreveport casino poker rooms. ./symfony plugin:install sfAjaxAdminDoctrineThemePlugin
Follow the instructions in plugin/sfAjaxAdminDoctrineThemePlugin/README.
Making Admin Forms Look Nicer
./symfony plugin:install sfFormExtraPlugin
Edit lib/form/doctrine/.class.php. Add something like the following in the configure() function, as needed:
$this-widgetSchema[posting_date] = new sfWidgetFormJQueryDate();
Clear the cache:
./symfony cc
Thats all for now. I hope this article has given you some good information and pointers on using symfony. Its a really nice, stable framework for developing web applications.
Go forth and write software!
broken image