Home » support » General discussion » Building Own Generator ( ) 7 Vote(s)
| Building Own Generator [message #24107] |
Wed, 21 March 2007 10:55  |
kubens Messages: 89 Registered: February 2006 Location: Germany |
Member |
|
|
I like scaffolding, it gives me the possibility to save a lot of time Unfortunatelly I would like to modify the layout and in parts the logic of the scaffolding templats and actions. Well I think a mixture of admin and crud would be the best for me ... So far so good the idea to build my own generator was born ...
Actually I am wondering why my class for example does not support the following call:
<?php foreach ($this->getColumns('list.display') as $column): ?>
<?php echo $column->getName() ?>
<?php endforeach; ?>
Neither an error message nor any output I read a lot of docs, but anyway it makes me crazy May someone give me some hints or even a little guideline, that would be great...
Thanks in advance
Wolfgang
Generator
<?php
class wakPropelCrudGenerator extends sfPropelCrudGenerator {
public function generate($params = array()){
/*
* load generate.yml
*/
if (array_key_exists('application', $params) && array_key_exists('moduleName', $params)) {
$params = array_merge(
$params,
sfYaml::load(
sfConfig::get('sf_root_dir') .DIRECTORY_SEPARATOR.
sfConfig::get('sf_apps_dir_name') .DIRECTORY_SEPARATOR.
$params['application'] .DIRECTORY_SEPARATOR.
sfConfig::get('sf_app_module_dir_name') .DIRECTORY_SEPARATOR.
$params['moduleName'] .DIRECTORY_SEPARATOR.
sfConfig::get('sf_app_config_dir_name') .DIRECTORY_SEPARATOR.
'generator.yml'
)
);
}
$data = parent::generate($params);
return $data;
}
}
?>
Generate
<?php
define('SF_ROOT_DIR', realpath(dirname(__FILE__).'/..'));
define('SF_APP', 'backoffice');
define('SF_ENVIRONMENT', 'dev');
define('SF_DEBUG', true);
require_once(SF_ROOT_DIR.DIRECTORY_SEPARATOR.'apps'.DIRECTORY_SEPARATOR.SF_APP.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'config.php');
require_once(SF_ROOT_DIR.DIRECTORY_SEPARATOR.'apps'.DIRECTORY_SEPARATOR.SF_APP.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'config.php');
require_once(SF_ROOT_DIR.DIRECTORY_SEPARATOR.'lib'.DIRECTORY_SEPARATOR.'wakPropelCrudGenerator.class.php');
define('SF_LIB_DIR', sfConfig::get('sf_symfony_lib_dir'));
require_once(SF_LIB_DIR.DIRECTORY_SEPARATOR.'vendor/pake/pakeFunction.php');
require_once(SF_LIB_DIR.DIRECTORY_SEPARATOR.'vendor/pake/pakeGetopt.class.php');
$author_name = 'Wolfgang Kubens';
$project = 'symfoy';
$app = 'backoffice';
$model_class = 'WAKBookingRequest';
$module = 'bookingrequest';
$theme = 'default';
$module_dir = SF_ROOT_DIR.DIRECTORY_SEPARATOR.sfConfig::get('sf_apps_dir_name').DIRECTORY_SEPARATOR.$app.DIRECTORY_SEPARATOR.sfConfig::get('sf_app_module_dir_name').DIRECTORY_SEPARATOR.$module;
$tmp_dir = SF_ROOT_DIR.DIRECTORY_SEPARATOR.'cache'.DIRECTORY_SEPARATOR.'tmp'.DIRECTORY_SEPARATOR.md5(uniqid(rand(), true));
sfConfig::set('sf_module_cache_dir', $tmp_dir);
$generator_manager = new sfGeneratorManager();
$generator_manager->initialize();
$generator_manager->generate('wakPropelCrudGenerator', array('model_class' => $model_class, 'moduleName' => $module, 'application' => $app));
$constants = array(
'PROJECT_NAME' => $project,
'APP_NAME' => $app,
'MODULE_NAME' => $module,
'MODEL_CLASS' => $model_class,
'AUTHOR_NAME' => $author_name,
);
// customize php and yml files
$finder = pakeFinder::type('file')->name('*.php', '*.yml');
pake_replace_tokens($finder, $tmp_dir, '##', '##', $constants);
pake_replace_tokens($finder, $tmp_dir, '', '', array('auto'.ucfirst($module) => $module));
// copy our generated module
// delete temp files
$finder = pakeFinder::type('any');
pake_mirror($finder, $tmp_dir.'/auto'.ucfirst($module), $module_dir);
pake_remove($finder, SF_ROOT_DIR.DIRECTORY_SEPARATOR.'cache'.DIRECTORY_SEPARATOR.'tmp');
?>
|
|
|
| Re: Building Own Generator [message #24410 is a reply to message #24107 ] |
Sun, 25 March 2007 21:46   |
kubens Messages: 89 Registered: February 2006 Location: Germany |
Member |
|
|
Just to quicken the appetite, may anybody is intersted in this ... As posted in my last posting I am interested in a custom generator. Especially I am interested in a generator which implements the features of Jack Slocum's great Ext UI Library. Here you can see the first results of my endevaours
Unfortunatelly I have no test server at this moment available, for that reason I can only provide you a screenshot.
And of course any hints and ideas would be very appreciate 
Br
Wolfgang
wakPropelCrudGenerator
<?php
/*
* This file is part of the symfony package.
* (c) 2004-2006 Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* CRUD generator.
*
* This class generates a basic CRUD module.
*
* @package symfony
* @subpackage generator
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @version SVN: $Id: sfPropelCrudGenerator.class.php 2342 2006-10-06 07:19:22Z chtito $
*/
class wakPropelCrudGenerator extends sfPropelCrudGenerator
{
public function generate($params = array()){
/*
* load generate.yml
*/
if (array_key_exists('application', $params) && array_key_exists('moduleName', $params)) {
$params = array_merge(
$params,
sfYaml::load(
sfConfig::get('sf_root_dir') .DIRECTORY_SEPARATOR.
sfConfig::get('sf_apps_dir_name') .DIRECTORY_SEPARATOR.
$params['application'] .DIRECTORY_SEPARATOR.
sfConfig::get('sf_app_module_dir_name') .DIRECTORY_SEPARATOR.
$params['moduleName'] .DIRECTORY_SEPARATOR.
sfConfig::get('sf_app_config_dir_name') .DIRECTORY_SEPARATOR.
'generator.yml'
)
);
}
$data = parent::generate($params);
return $data;
}
public function getColumns($paramName, $category = 'NONE') {
$columns = array();
if ($paramName == 'ALL') {
foreach ($this->tableMap->getColumns() as $column) {
$columns[] = $column;
}
}
else {
$value = $this->getValueFromKey($paramName);
$fields = array();
if (is_array($value)) { $fields = $value; };
if (is_string($value)) { $fields = implode($value); };
foreach ($fields as $fieldName) {
$columns[] = $this->tableMap->getColumn($fieldName);
}
}
return $columns;
}
public function getPrimaryKeyColumn () {
foreach ($this->tableMap->getColumns() as $column) {
if ($column->isPrimaryKey()) return $column;
}
}
public function getExtGridExpandColumnId($paramName, $expandColumn=false) {
$value = $this->getValueFromKey($paramName);
$fields = array();
if (is_array($value)) { $fields = $value; };
if (is_string($value)) { $fields = implode($value); };
$id = count($value);
if ($expandColumn==false) $id--;
return $id;
}
public function getExtDataStore ($paramName, $expandColumn=false) {
$ext = '';
foreach ($this->getColumns($paramName) as $column) {
$ext .= sprintf('%s{%s}', (strlen($ext) == 0 ? '' : ','), $this->getExtDataStoreOfField($column->getColumnName()));
}
if ($expandColumn) {
$ext = $ext . ',{name:"_expandColumn",mapping:"id"}';
}
return $ext;
}
protected function getExtDataStoreOfField ($fieldName) {
$ext = '';
$extParams = array('type','dateFormat');
$fieldName = strtolower($fieldName);
$fieldParams = $this->getValueFromKey('generator.param.fields.'.$fieldName);
if (is_array($fieldParams) && array_key_exists('ext',$fieldParams)) {
$ext = sprintf('name:"%s",mapping:"%s"', $fieldName, $fieldName);
foreach ($fieldParams['ext'] as $key => $value) {
if (in_array($key, $extParams)) {
$ext .= sprintf('%s%s:"%s"', (strlen($ext) == 0 ? '' : ','), $key, $value);
}
}
}
return $ext;
}
public function getExtColumnModel ($paramName, $expandColumn=false) {
$ext = "";
foreach ($this->getColumns($paramName) as $column) {
$ext .= sprintf('%s{%s}', (strlen($ext) == 0 ? '' : ','), $this->getExtColumnModelOfField($column->getColumnName()));
}
if ($expandColumn) {
$ext = $ext . ',{header:"",dataIndex:"_expandColumn",locked:false,renderer:function(){return "";},sortable:false}';
}
return $ext;
}
protected function getExtColumnModelOfField ($fieldName) {
$ext = "";
$extParams = array(
'locked' =>'locked:%s',
'renderer' =>'renderer:%s',
'sortable' =>'sortable:%s',
'width' =>'width:%s'
);
$fieldName = strtolower($fieldName);
$fieldParams = $this->getValueFromKey('generator.param.fields.'.$fieldName);
if (is_array($fieldParams) && array_key_exists('ext',$fieldParams)) {
$ext = sprintf('header:"[?php echo __("%s.%s"); ?]",dataIndex:"%s"', $this->getModuleName(), $fieldName, $fieldName);
foreach ($fieldParams['ext'] as $key => $value) {
if (array_key_exists($key, $extParams)) {
$ext .= sprintf('%s'.$extParams[$key], (strlen($ext) == 0 ? '' :','), $value);
}
}
}
return $ext;
}
}
listSuccess.php
<script type="text/javascript">
<!--
Ext.onReady(function(){
/*
* @text translations
* GridView
* PagingToolbar
*/
Ext.apply(Ext.grid.GridView.prototype, {
sortAscText: '[?php echo __('ext.grid.gridview.sortAscText'); ?]',
sortDescText: '[?php echo __('ext.grid.gridview.sortDescText'); ?]',
lockText: '[?php echo __('ext.grid.gridview.lockText'); ?]',
unlockText: '[?php echo __('ext.grid.gridview.unlockText'); ?]',
columnsText: '[?php echo __('ext.grid.gridview.columnsText'); ?]'
});
Ext.apply(Ext.PagingToolbar.prototype, {
displayMsg: '[?php echo __('ext.pagingtoolbar.displayMsg', array('%1%'=>__('<?php echo $this->getValueFromKey('generator.param.list.title'); ?>'))); ?]',
emptyMsg: '[?php echo __('ext.pagingtoolbar.emptyMsg', array('%1%'=>__('<?php echo $this->getValueFromKey('generator.param.list.title'); ?>'))); ?]',
beforePageText: '[?php echo __('ext.pagingtoolbar.beforePageText'); ?]',
afterPageText: '[?php echo __('ext.pagingtoolbar.afterPageText'); ?]',
firstText: '[?php echo __('ext.pagingtoolbar.firstText'); ?]',
prevText: '[?php echo __('ext.pagingtoolbar.prevText'); ?]',
nextText: '[?php echo __('ext.pagingtoolbar.nextText'); ?]',
lastText: '[?php echo __('ext.pagingtoolbar.lastText'); ?]',
refreshText: '[?php echo __('ext.pagingtoolbar.refreshText'); ?]'
});
/*
* @enhancements
* grid.getRecordId - returns id of selected row. if no row is selected it returns null
* toolbar.refresh - disables or enables all buttons pending of attribute idRequired and selection of linked grid
*/
Ext.apply(Ext.grid.Grid.prototype, {
getRecordId: function() {
if (this.getSelectionModel().hasSelection()) {
return this.getSelectionModel().getSelected().id;
}
else {
return null;
}
}
});
Ext.apply(Ext.Toolbar.prototype, {
refresh: function () {
var id = this.grid.getRecordId();
for(var i=0, len=this.items.length; i < len; i++) {
var item = this.items.get(i);
if (item.action) {
if (!item.idRequired) {
item.enable();
}
else {
if (id) {
item.enable();
}
else {
item.disable();
}
}
}
}
}
});
/*
* @actions
* actionCreate - opens mask with empty form
* actionEdit - opens mask and loads data from selected row
* actionCopy - opens mask and loads data from selected row into new form
* actionDelete - deletes the selected row
*/
function actionCreate() {
openPanel(-1, '/<? echo strtolower($this->getModuleName()); ?>/create/');
}
function actionEdit() {
var id = grid.getRecordId();
openPanel(id, '/<? echo strtolower($this->getModuleName()); ?>/edit/id/'+id);
}
function actionCopy() {
var id = grid.getRecordId();
openPanel(id, '/<? echo strtolower($this->getModuleName()); ?>/copy/id/'+id);
}
function actionDelete() {
var id = grid.getRecordId();
}
/*
* @handler
* onClick - handler for buttons of toolbar
* onClickContextMenu - handler for items of context menu
*/
function onClick(e) {
if (e.action == 'create') actionCreate();
if (e.action == 'edit') actionEdit();
if (e.action == 'copy') actionCopy();
if (e.action == 'delete') actionDelete();
}
function onClickContextMenu (e) {
gridContextMenu.hide();
onClick(e);
}
/*
* @misc
* openPanel - creates a new panel or open an existing panel
*/
function openPanel(id,url) {
layout.add('south', new Ext.ContentPanel('ifrm', {fitToFrame:true, title:id, closable:true, autoCreate:{tag:'iframe', src:url, id:'ifrm'+id, name:'ifrm'+id, frameborder: 'no'} }));
}
/*
* @renderer for grid
*/
function rendererDate (value){
var d = new Date(Date.parse(value));
return d ? d.dateFormat('d.m.Y') : '';
}
/*
* @datastore for grid
*/
var ds = new Ext.data.Store({
proxy: new Ext.data.HttpProxy({ url: 'http://v2.symfony.intra.net/bookingrequest/ajaxList/' }),
reader: new Ext.data.JsonReader({
root: 'bookingrequests',
totalProperty: 'totalCount',
id: '<?php echo strtolower($this->getPrimaryKeyColumn()->getColumnName()); ?>'
}, [
<?php echo $this->getExtDataStore('generator.param.list.display', false); ?>
]),
remoteSort: false
});
ds.load({params:{start:0, limit:<?php echo $this->getValueFromKey('generator.param.list.paging.pageSize',20); ?>}});
/*
* @columnmodel for grid
*/
var colModel = new Ext.grid.ColumnModel([<?php echo $this->getExtColumnModel('generator.param.list.display', false); ?>]);
/*
* @grid
*/
var grid = new Ext.grid.Grid('htmlPanelGrid', {
ds: ds,
cm: colModel
});
function onGridClick(grid, rowIndex, e) {
toolbar.refresh();
}
function onGridContextMenuClick(grid, rowIndex, e) {
toolbar.refresh();
}
function onGridRowDBLClick(grid, rowIndex, e) {
e.stopEvent();
e.action = 'edit';
onClick(e);
}
function onGridRowContextMenu(grid, rowIndex, e) {
e.stopEvent();
var coords = e.getXY();
gridContextMenu.showAt([coords[0], coords[1]]);
}
var gridContextMenu = new Ext.menu.Menu({
id: 'gridContextMenu',
items: [
{ text: '[?php echo __('action.edit'); ?]', action: 'edit', handler: onClickContextMenu, cls: 'btn_edit' },
{ text: '[?php echo __('action.copy'); ?]', action: 'copy', handler: onClickContextMenu, cls: 'btn_copy' },
'-',
{ text: '[?php echo __('action.delete'); ?]', action: 'delete', handler: onClickContextMenu, cls: 'btn_delete' }
]
});
grid.addListener('click', onGridClick);
grid.addListener('contextmenu', onGridContextMenuClick);
grid.addListener('rowdblclick', onGridRowDBLClick);
grid.addListener('rowcontextmenu', onGridRowContextMenu);
grid.render();
// create Paging-Toolbar for Grid
var gridFoot = grid.getView().getFooterPanel(true);
var paging = new Ext.PagingToolbar(gridFoot, ds, {
pageSize: <?php echo $this->getValueFromKey('generator.param.list.paging.pageSize',20); ?>,
displayInfo: <?php echo $this->getValueFromKey('generator.param.list.paging.displayInfo','0'); ?>
});
/*
* @toolbar
*/
toolbar = new Ext.Toolbar('htmlPanelToolbar');
toolbar.grid = grid;
toolbar.addButton({ text: '[?php echo __('action.create'); ?]', action: 'create', idRequired: false, handler: onClick, cls: 'x-btn-text-icon btn_create' });
toolbar.addSeparator();
toolbar.addButton({ text: '[?php echo __('action.edit'); ?]', action: 'edit', idRequired: true, handler: onClick, cls: 'x-btn-text-icon btn_edit', disabled: true });
toolbar.addButton({ text: '[?php echo __('action.copy'); ?]', action: 'copy', idRequired: true, handler: onClick, cls: 'x-btn-text-icon btn_copy', disabled: true });
toolbar.addSeparator();
toolbar.addButton({ text: '[?php echo __('action.delete'); ?]', action: 'delete', idRequired: true, handler: onClick, cls: 'x-btn-text-icon btn_delete', disabled: true });
/*
* @layout
*/
panelToolbar = new Ext.ContentPanel('htmlPanelToolbar');
panelGrid = new Ext.GridPanel(grid,'[?php echo __('<?php echo $this->getValueFromKey('generator.param.list.title'); ?>'); ?]');
var layout = Ext.BorderLayout.create({
north: {
titlebar: false,
collapsible: false,
animate: false,
split:false,
initialSize:30
},
south: {
titlebar: false,
collapsible: true,
animate: false,
split:true,
initialSize:400,
tabPosition: 'bottom',
alwaysShowTabs: true,
closeOnTab: true
},
center: {
titlebar: false
}
});
layout.beginUpdate();
layout.add('north', panelToolbar);
layout.add('center', panelGrid);
layout.endUpdate();
});
//-->
</script>
<div id="htmlPanelToolbar"></div>
<div id="htmlPanelGrid" class="x-layout-inactive-content"></div>
|
|
| | |
| Re: Building Own Generator [message #24619 is a reply to message #24618 ] |
Wed, 28 March 2007 16:08   |
kubens Messages: 89 Registered: February 2006 Location: Germany |
Member |
|
|
Hi Draven,
thanks for your annotations and for you offer. Believe me that I will contact you, if I could not see the wood for the trees 
At this time I try to understand both logics behind admin and crud. Actually my generator is a mixture of both. It use a generator.yml and deploys all files into a module folder. Here is my generator.yml:
generator:
class: wakPropelCrudGenerator
param:
model_class: WAKBookingRequest
theme: default
fields:
id: { name: bookingrequest.id, ext: {width: 30, type: int, sortable: true}}
ip_address: { name: bookingrequest.ip_address, ext: {width: 100, type: string, sortable: true} }
travel_duration: { name: bookingrequest.travel_duration, ext: {width: 100, type: int, sortable: true}}
travel_start_date: { name: bookingrequest.travel_start_date, params: date_format=dd.MM.yyyy, ext: {width: 100, type: date, sortable: true, renderer: rendererDate, dateFormat: Y-m-d h:i:s}}
travel_end_date: { name: bookingrequest.travel_end_date, params: date_format=dd.MM.yyyy, ext: {width: 100, type: date, sortable: true, renderer: rendererDate, dateFormat: Y-m-d h:i:s}}
airport_departure: { name: bookingrequest.airport_departure, ext: {width: 100, type: string, sortable: true}}
number_adults: { name: bookingrequest.number_adults, ext: {width: 80, type: int, sortable: true}}
number_childs: { name: bookingrequest.number_childs, ext: {width: 80, type: int, sortable: true}}
contact_first_name: { name: bookingrequest.contact_first_name, ext: {width: 140, type: string, sortable: true}}
contact_last_name: { name: bookingrequest.contact_last_name, ext: {width: 140, type: string, sortable: true}}
contact_mail: { name: bookingrequest.contact_mail, ext: {width: 140, type: string, sortable: true}}
contact_zip: { name: bookingrequest.contact_zip, ext: {width: 60, type: string, sortable: true}}
contact_city: { name: bookingrequest.contact_city, ext: {width: 140, type: string, sortable: true}}
created_at: { name: bookingrequest.created_at, params: date_format=dd.MM.yyyy, ext: {width: 100, type: date, sortable: true}}
updated_at: { name: bookingrequest.updated_at, ext: {width: 100, type: date, dateFormat: n/j h:ia, sortable: true}}
list:
title: bookingrequest.title.list
paging:
pageSize: 20
displayInfo: 1
display: [id, contact_first_name, contact_last_name, travel_duration, travel_start_date, travel_end_date, number_adults, number_childs]
filters: [id, contact_first_name, contact_last_name, travel_duration, travel_start_date, travel_end_date, number_adults, number_childs]
edit:
title: bookingrequest.title.edit
The screenshot which I posted before is from the module which was generated by the custom generator As mentioned, it is far away from a final version, but the first results are very impressive
Unfortunatelly I did not read the plugin chapter of the symfony book. I will read this chapter tonight, may this is an alternative for the generator approach.
Br
Wolfgang
|
|
| |
| Re: Building Own Generator [message #24625 is a reply to message #24107 ] |
Wed, 28 March 2007 16:24   |
kubens Messages: 89 Registered: February 2006 Location: Germany |
Member |
|
|
To generate a module through a generator is very simple:
$generator_manager = new sfGeneratorManager();
$generator_manager->initialize();
$generator_manager->generate('wakPropelCrudGenerator', array('model_class' => $model_class, 'moduleName' => $module, 'application' => $app));
But this is not the complete job, because we have some pake calls which are replacing infos inside the files and which are deploying the files to the correct folders.
// customize php and yml files
$finder = pakeFinder::type('file')->name('*.php', '*.yml');
pake_replace_tokens($finder, $tmp_dir, '##', '##', $constants);
pake_replace_tokens($finder, $tmp_dir, '', '', array('auto'.ucfirst($module) => $module));
// copy our generated module
// delete temp files
$finder = pakeFinder::type('any');
pake_mirror($finder, $tmp_dir.'/auto'.ucfirst($module), $module_dir);
pake_remove($finder, SF_ROOT_DIR.DIRECTORY_SEPARATOR.'cache'.DIRECTORY_SEPARATOR.'tmp');
Unfortunatelly I never worked with pake before therefore I decided to create just a simple action which overtakes for me the pake job.
Br
Wolfgang
|
|
| | | | | | | |
| Re: Building Own Generator [message #34347 is a reply to message #27841 ] |
Tue, 28 August 2007 16:33   |
DrCore Messages: 59 Registered: August 2007 |
Member |
|
|
Looking at the examples I would like to contribute an idea: implementing the filters in the column header to save space (no filter window).
A preliminary trial of this concept is included in an admin theme. To use, unpack the file in your plugin directory. Specify in your admin generate.yml the following code:
generator:
class: sfPropelAdminGenerator
param:
model_class: xxx
theme: batchfilterTheme
It is far from perfect though. For example there is still a problem where the filter values are not redisplayed after filtering.
Cheers,
Andre
P.s. this theme also includes the changes for batch actions (see http://trac.symfony-project.com/trac/ticket/2100). However I have not yet found a method to add a changed lib/generator/sfAdminGenerator.class.php file in the theme.
|
|
| |
| Re: Building Own Generator [message #34452 is a reply to message #24619 ] |
Wed, 29 August 2007 16:19   |
sfxpt Messages: 339 Registered: January 2007 Location: Canada |
Faithful Member |
|
|
| kubens wrote on Wed, 28 March 2007 10:08 | . . . my generator is a mixture of both. It use a generator.yml and deploys all files into a module folder. . .
|
I like this idea very much. Personally, I'd prefer this than the current admin gen approach.
BTW, it has been a while since you've been working on this, have you get a version stable enough for a preview release?
thanks and looking forward to it.
http://xpt.sourceforge.net/techdocs/
http://xpt.sourceforge.net/tools/
|
|
| | | |
| Re: Building Own Generator [message #34524 is a reply to message #34501 ] |
Thu, 30 August 2007 11:39   |
DrCore Messages: 59 Registered: August 2007 |
Member |
|
|
In addition to the admin theme of two days ago, I would like to propose some other ideas:
I'm currently busy writing a workflow application with many different forms and sub-forms. I want to use the admin generator to build and control the content of these forms.
From my experiments I found that as each admin form is a module, linking the forms into sub-forms becomes quickly very messy. I tried to use a session parameter to store the main element ID which needs to be used for filtering and creating sub form lists and elements. To implement this a lot of code needs to be added to each sub-form. This does not seem to be the way to go.
To adhere to the Symfony mind-set, the sub-forms should actually be created in the module. Therefore I'm proposing to extend the generator.yml syntax with sub-form descriptions. This could look like this:
generator:
class: sfSubformGenerator
param:
model_class: parent
list:
<usual syntax>
edit:
<usual syntax>
subform:
childform1: [ parent_field: xxx, child_field: xxx ]
childform2: [ parent_field: xxx, child_field: xxx ]
subformmenu:
childform1: [ subactions: xx,xx ]
For each child, a childformx_generator.yml file would be present containing the usual directives of a generator.yml. When the childforms are build, the lists are automatically filtered on the parent_field = child_field value. For the edit form, the child_field value will be prefilled with the parent_field. When more childforms are present, a tabbed menu (styled through templates) will be generated to select a childform. Through the subformmenu element sub actions can be defined. The menu could look like this http://labs.silverorange.com/archives/2004/may/updatedsimple
Let me know your thoughts.
Cheers,
Andre
EDIT: fixed link
[Updated on: Thu, 30 August 2007 13:43]
|
|
| |
| Re: Building Own Generator [message #34548 is a reply to message #34530 ] |
Thu, 30 August 2007 13:49   |
DrCore Messages: 59 Registered: August 2007 |
Member |
|
|
If you have some code which you are willing to share already as a alpha version, I would very much appreciate this.
I basically come as far as creating a plugin based upon the previously presented admin theme. To use this plugin, install the code in your project plugin folder and change the following directives in generate.yml:
generator:
class: sfAdminSubformGenerator
param:
theme: sfAdminSubform
Andre
|
|
| | | | | | |
| Re: Building Own Generator [message #34686 is a reply to message #34683 ] |
Fri, 31 August 2007 16:18   |
 |
lvanderree Messages: 647 Registered: June 2007 Location: Netherlands |
Faithful Member |
|
|
Collaboration of course would be great (what else are we visiting this forum for...) So adjusting and coordinating our ideas is fine by me.
I am using this for my graduation project and I can and want to put a lot of time ant effort in this, however I don't want to invest money in it. I have seen the developers license and it isn't that much money, but besides the money, the switch to 2.0 will mean we can't release the results as plugin for symfony isn't it.... (until version 2.0 is also released under a open source license)
As said, things I have already figured out in symfony are multi-sort and sort on foreign keys, which you can be defined in my generator.yml file. I figured these things out with the help of the forum and the snippets, the results can already be found in trac. I also have a proof of concept of a master-detail implementation which can be implemented in the theme, but to improve the usability I want to use extjs.
I have to play a little more to find out how I can reuse as much of power of symfony, while using extjs for presentation.
I will now take a look at your code and try to combine it with my plugin to get the best of both...
As said I can spend some time on this, but together with other ideas and knowledge we probably get the best out of this. In 3 weeks I don't know the complete symfony framework, but together with this great community we should definitely be able to produce a nice plugin.
Leon
|
|
| |
| Re: Building Own Generator [message #34689 is a reply to message #34686 ] |
Fri, 31 August 2007 16:34   |
kubens Messages: 89 Registered: February 2006 Location: Germany |
Member |
|
|
I checked my source and I think that it will work with Ext1.1 too. I am using a config file which contains the default syntax of the different components and if is possible to establish one separate config file for each version then this could work.
sfConfig::set('sf_wakext_tabpanel_attributes', array(
'resizeTabs' => 'true',
'minTabWidth' => '100',
'tabWidth' => '150',
'width' => '"100%"',
'renderTo' => 'document.body',
'activeTab' => '0',
'defaults' => '{autoScroll: true, autoHeight: true, autoWidth: true}'
));
Otherwise some special features must postponed as long as Ext2.0 is not finalized and not published.
If you are very familiar with the generator part then I would invest more time into the Ext part. Let me know for which Ext component you need a helper class and I will try to fullfill your wishes.
What do you think?
Br
Wolfgang
|
|
|
| Re: Building Own Generator [message #34690 is a reply to message #34689 ] |
Fri, 31 August 2007 16:53   |
 |
lvanderree Messages: 647 Registered: June 2007 Location: Netherlands |
Faithful Member |
|
|
I compared your admin-generator-theme with mine but found out that you didn't changed that at all yet.
I am now going to compare the rest of the code, but just to be sure about our expectations. You are interessted in a admin-generator-extension, isn't it? I see some files regarding CRUD, but personally I am not very interested in CRUD (at the moment). I think the power is in the admin-generator, which should generate modules based on your generator.yml-file. This generator.yml file should remain compatible with the current cheat-sheet, but should get extra options to improve its capabilities.
Of course I am also making my proofs-of-concept in the modules themselves before I modify the generator in the theme, but this just to make sure we want the same thing.
I agree that we should find a solution which is independed of the version of extjs. So maybe we should define a extjs-version-option which makes sure the correct code is generated, but this depends on how backwards-compatible extjs2.0 is (or how forward-compatible Extjs1.1 is). Maybe if Extjs2.0 only contains extra's we can use the same generator, which outputs code which Extjs1.0 simply ignores. I will look into that as well, although you are probably the expert on that field.
I will now first look into your code (in the train, so there will be a short radio silence ) and get a little more familiar with extjs, before I can tell what I think has to be done.
Cheers!
Leon
|
|
| |
| Re: Building Own Generator [message #34696 is a reply to message #34691 ] |
Fri, 31 August 2007 19:32   |
 |
lvanderree Messages: 647 Registered: June 2007 Location: Netherlands |
Faithful Member |
|
|
I looked at your code, and there are a lot of things which I still had to figure out, so this is definitely going to save me some time.
I had a problem with the JSON conversion, I looked at your solution and it looks like you had that problem too You loop though the results of the pager and then loop through all elements of the object and converted them to JSON-text.
I think I have a simpler solution, but this depends on PHP5.2. I also loop through all objects of the pager, but use the object->toArray() method to convert all individual objects to an array. This array gets added to one big-array which again contains all objects. But now I can use the PHP(>=5.2) method json_encode to convert them all at once to a JSON-text.
Did you knew this method exists? Or haven't you got PHP5.2, because I think my version is "cleaner", but your version also works when people don't have PHP5.2
Tonight I am not at home, but tomorrow I'll be back and probably get and alpha-alpha version working.
Btw Ich verstehe auch (ein bißchen) Deutsch, aber Ich weiß nicht wie die andere leute das finden
Leon
|
|
|
| Re: Building Own Generator [message #34698 is a reply to message #34696 ] |
Fri, 31 August 2007 20:07   |
kubens Messages: 89 Registered: February 2006 Location: Germany |
Member |
|
|
As I started I was not familiar with this json features of PHP5.2, but in the meatime I read a lot of this. I decided me against this build in features for two reasons:
1. Build in features converts everytime the whole array. But sometimes I just want to get one or two attributes
2. PHP5.2 is not available at every ISP
Unfortunatelly json_encode is the fastest way to convert data into json with php, so I will everytime lose performance at this moment, but that is the compromise, either as generic and flexible at possible, but not so fast or very fast, but not so generic and flexible However, did you looked at this plugin. I planed to overwork this again, because I used reflection and that is not so fast as call_user_func_array. At least what I read in the WWW. But before I will modify the source I will test this by myself.
Thanks for your offer that I can nerve you in German, but then we will exclude to much from the other non speaking German users. And I assume that this is the majority. However I will improve my skills with every thread, so you must suffer Worst case you must ask twice if my fancy words seems to be nonsensically ...
Br
Wolfgang
|
|
| | |
|