Forum

Home » support » General discussion » Building Own Generator  (4) 7 Vote(s)
Building Own Generator [message #24107] Wed, 21 March 2007 10:55 Go to next message
kubens  is currently offline kubens
Messages: 89
Registered: February 2006
Location: Germany
Member
I like scaffolding, it gives me the possibility to save a lot of time Very Happy 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 Sad I read a lot of docs, but anyway it makes me crazy Sad 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 Go to previous messageGo to next message
kubens  is currently offline 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 Very Happy

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 Very Happy

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 #24411 is a reply to message #24107 ] Sun, 25 March 2007 22:57 Go to previous messageGo to next message
flat stanley  is currently offline flat stanley
Messages: 46
Registered: November 2006
Location: London
Member
I can't answer your questions, but the screen shots look fantastic! Good luck!!


Antique Silver, Hester Bateman Silver, Collecting Silver
Re: Building Own Generator [message #24618 is a reply to message #24107 ] Wed, 28 March 2007 15:47 Go to previous messageGo to next message
Draven  is currently offline Draven
Messages: 760
Registered: January 2006
Faithful Member
Hey kubens, I saw your post over on the Ext boards and thought I'd see if I could give you a hand.

Are you trying to get the admin generator to create an Ext grid for the listing page rather than standard layout? If so, you should concentrate on the admin generator rather than the crud generator as they serve two very different ends.

You may have an easier time, when just starting out, to create a plug-in and copy the admin generator templates from the symfony folder to the new plug-in and modifying them to fit your needs rather than trying to rewrite the generator form scratch.
Re: Building Own Generator [message #24619 is a reply to message #24618 ] Wed, 28 March 2007 16:08 Go to previous messageGo to next message
kubens  is currently offline 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 Very Happy

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 Very Happy As mentioned, it is far away from a final version, but the first results are very impressive Shocked

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 #24622 is a reply to message #24619 ] Wed, 28 March 2007 16:12 Go to previous messageGo to next message
Notorious  is currently offline Notorious
Messages: 131
Registered: March 2007
Senior Member
How did you start on building this generator? I have read the plugins section of the book, but can't get a grasp of how to start on something like this...
Re: Building Own Generator [message #24625 is a reply to message #24107 ] Wed, 28 March 2007 16:24 Go to previous messageGo to next message
kubens  is currently offline 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 #25308 is a reply to message #24625 ] Fri, 06 April 2007 23:18 Go to previous messageGo to next message
kubens  is currently offline kubens
Messages: 89
Registered: February 2006
Location: Germany
Member
In the meantime I setuped a test environment:

http://backoffice.kaffill.de/bookingrequest

Unfortunatelly this app is not finished yet, so keep in mind that this is just a test environment. I will update this periodically and I will keep you informed in this thread. Feel free to play with this early version and of course any idea or hints are welcome.

Br
Wolfgang
Re: Building Own Generator [message #25767 is a reply to message #24107 ] Mon, 16 April 2007 13:35 Go to previous messageGo to next message
ponticelli  is currently offline ponticelli
Messages: 14
Registered: October 2006
Junior Member
Is your generator going ahead? I'd like to see it published as plug-in.
Re: Building Own Generator [message #25768 is a reply to message #25767 ] Mon, 16 April 2007 13:54 Go to previous messageGo to next message
kubens  is currently offline kubens
Messages: 89
Registered: February 2006
Location: Germany
Member
The app which you can see is the direct result of my customized version and it is based on two config files. And yes it is possible to implement this as plugin. But at this moment it isn't finished, because I spent a lot of time into my first steps with Exp. And before I will finalize the generator I will finalize and implement all my fancy ideas which I can now simple realize with Ext Very Happy

Br
Wolfgang
Re: Building Own Generator [message #25778 is a reply to message #25768 ] Mon, 16 April 2007 15:14 Go to previous messageGo to next message
ponticelli  is currently offline ponticelli
Messages: 14
Registered: October 2006
Junior Member
Say a word when you will be ready to publish it. In the next few weeks I'll have to implement the same with Ext and I don't want to reinvent the wheel. I intend to stick to the admin module and not cover by now the scaffolding features.
Re: Building Own Generator [message #25788 is a reply to message #24107 ] Mon, 16 April 2007 16:41 Go to previous messageGo to next message
Martyp  is currently offline Martyp
Messages: 37
Registered: February 2007
Location: Prague, Czech Republic
Member
Hello,

it is really very nice. I'm at a stage where I need to create an Admin section for my app and I'd love to have something like this.

Re: Building Own Generator [message #26241 is a reply to message #24107 ] Wed, 25 April 2007 03:34 Go to previous messageGo to next message
ryang  is currently offline ryang
Messages: 5
Registered: April 2007
Location: London
Junior Member

Hey I second that dude! Really great work - very much looking forward to using this when its ready!
Re: Building Own Generator [message #27841 is a reply to message #24107 ] Tue, 22 May 2007 15:21 Go to previous messageGo to next message
KRavEN  is currently offline KRavEN
Messages: 193
Registered: May 2007
Senior Member
So, I'm very new at symfony. Trying to get the hang of things. I'm am pretty experienced with extjs so I think I could contribute to this effort once I get the symfony stuff figured out.

Can someone please explain how I go about using the files that Wolfgang has provided in this thread? Where do I put them? What do I name them? How do I get the symfony propel-generate-crud to work with them?

Thanks
Re: Building Own Generator [message #34347 is a reply to message #27841 ] Tue, 28 August 2007 16:33 Go to previous messageGo to next message
DrCore  is currently offline 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 #34451 is a reply to message #34347 ] Wed, 29 August 2007 16:12 Go to previous messageGo to next message
lvanderree  is currently offline lvanderree
Messages: 647
Registered: June 2007
Location: Netherlands
Faithful Member
This generator looks very nice indeed!

I would like to combine it with the changes I've made to the theme, like the multisort solution.

Keep us updated Wink


Leon
Re: Building Own Generator [message #34452 is a reply to message #24619 ] Wed, 29 August 2007 16:19 Go to previous messageGo to next message
sfxpt  is currently offline 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 #34453 is a reply to message #34451 ] Wed, 29 August 2007 16:22 Go to previous messageGo to next message
francois  is currently offline francois
Messages: 1591
Registered: October 2005
Faithful Member
I second ponticelli. You must bootstrap a plugin with this generator theme.

If you need advice on how to package it as a plugin, look at existing plugins coming with their generator (e.g. sfControlPanelPlugin) or post a question here.
Re: Building Own Generator [message #34501 is a reply to message #34453 ] Thu, 30 August 2007 08:17 Go to previous messageGo to next message
kubens  is currently offline kubens
Messages: 89
Registered: February 2006
Location: Germany
Member
That was what I suspected. On the one hand their seems to be a lot of people who are interested in results and on the other hand their is a lot of work, especially things which I must learn before and unfortunatelly this is sometimes very timeintensive, at least for me...

I am still working on this issue... My idea is to encapsulate all common functions in separate plugins. Last week I published a little plugin sfPropelTextOutputBehaviorPlugin which provides JSON support for propel objects. This was just one little milestone on my roadmap. Next milestone is to develope a plugin for Ext2.0. I am busy with this, unfortunatelly the guys from Ext are very productive and they are still working on Ext2.0. Unfortunatelly there exists no documentation at this moment for this version. This makes it necessary to look and to understand their source. I have some experiences with Javascript, but I learned a lot from the source. However as far as I have finished the Ext2.0 plugin I will start to redevelop the existing source. That sounds interesting, but my problem is that this could only happens in my freetime, which means that my speed is comparable with a slug :S

If anybody is intersted in all this stuff and if anybody is interested to work on this too, than we should adjust our goals and our next steps together. I am very glad about every support, about every idea.

Br
Wolfgang

Re: Building Own Generator [message #34504 is a reply to message #34501 ] Thu, 30 August 2007 08:58 Go to previous messageGo to next message
lvanderree  is currently offline lvanderree
Messages: 647
Registered: June 2007
Location: Netherlands
Faithful Member
I am!

I want to use extjs to improve my application for my graduation project. In fact it would be perfect to improve the usability of my application and I want to integrate it with other changes I have made to my theme.
I can work on it for more than fulltime, so I would be glad to continue your work!

I'm only working with symfony for something like 3 weeks now, and didn't had any experience with ajax-libraries, but I think I start to understand it all. So if you want to share your current knowlegde and changes, I am here to listen Wink

I was just thinking about implementing extjs1.1 myself, after seeing the results of your work. (I want to implement 1.1 because this seems to be the latest stable, I didn't see anything about 2.0 yet)


Leon
Re: Building Own Generator [message #34524 is a reply to message #34501 ] Thu, 30 August 2007 11:39 Go to previous messageGo to next message
DrCore  is currently offline 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 #34530 is a reply to message #34524 ] Thu, 30 August 2007 12:07 Go to previous messageGo to next message
lvanderree  is currently offline lvanderree
Messages: 647
Registered: June 2007
Location: Netherlands
Faithful Member
The link is broken, but it looks like you want to define a master-detail relationship.

I am also working on this, and it works recursively now, but is not quiet finished yet and I think I want to merge it with extjs-powerrrr... (http://www.symfony-project.com/forum/index.php/m/34489/)

I've started to define my own plugin at the moment. I will add my own functions to it like the master-detail relationship, a multisort option for lists, and a possibility to sort on columns with foreigns values or composite values. I hope I can also add Wolfgangs extjs code in this and then I will release it on the web.

At the moment I am creating the plugin and do some testing with it. Maybe already add the extjs library to see how it works.

I will keep you informed


Leon
Re: Building Own Generator [message #34548 is a reply to message #34530 ] Thu, 30 August 2007 13:49 Go to previous messageGo to next message
DrCore  is currently offline 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 #34561 is a reply to message #34548 ] Thu, 30 August 2007 15:05 Go to previous messageGo to next message
lvanderree  is currently offline lvanderree
Messages: 647
Registered: June 2007
Location: Netherlands
Faithful Member
Am I right in that the only thing that is included in this plugin is the row of searchfields, which do not work (yet)?

I've got something more already (multisort and sort on foreign fields, both working, and a beginning on my implementation of master-detail), but I would like to add a little extjs before I post it as a first alpha....

I think I can upload an alpha tomorrow.


Leon
Re: Building Own Generator [message #34657 is a reply to message #34524 ] Fri, 31 August 2007 12:23 Go to previous messageGo to next message
lvanderree  is currently offline lvanderree
Messages: 647
Registered: June 2007
Location: Netherlands
Faithful Member
Just to keep you all warm, the beginning is there!

I have an action and template defined which outputs the JSON encoded output of the symfony pager, which I can read back in a grid of the extjs library. A small screenshot:

http://fun4me.demon.nl/~leon/extjs-beginning.png
The floating city is me, dragging the column to a new location.

lots of things I still have to do though, first things on the list:

- There is something wrong with the column-width. The width of my header is different than the width of the content. (maybe an Opera bug?)
- Paging (probably) doesn't work yet (haven't looked at it).
- As you can see you now get cityIds instead of city->names so this has to be added to the query of the pager. I don't think I can use city->toString() for that.
- make the solution general, so it can be implemented in the theme (no problems expected so far). I now use the templates in the Berth module folder, to see immdediate results...

AlphaAlpha can be expected soon, but I don't know if I will finish it today...


Leon
Re: Building Own Generator [message #34659 is a reply to message #34657 ] Fri, 31 August 2007 12:31 Go to previous messageGo to next message
ditman  is currently offline ditman
Messages: 33
Registered: August 2007
Location: Spain
Member
Shocked Impressive work

Keep it up, it would be AMAZING to be able to generate admin interfaces that pretty!

Thanks!!
Re: Building Own Generator [message #34663 is a reply to message #34657 ] Fri, 31 August 2007 13:09 Go to previous messageGo to next message
ponticelli  is currently offline ponticelli
Messages: 14
Registered: October 2006
Junior Member
Very, very nice! Continue tha hard working and rele4ase soon Very Happy
Re: Building Own Generator [message #34674 is a reply to message #34657 ] Fri, 31 August 2007 14:49 Go to previous messageGo to next message
DrCore  is currently offline DrCore
Messages: 59
Registered: August 2007
Member
Super cool!

This will definitely boost development of Symfony (business) applications.

Andre
Re: Building Own Generator [message #34683 is a reply to message #34674 ] Fri, 31 August 2007 15:50 Go to previous messageGo to next message
kubens  is currently offline kubens
Messages: 89
Registered: February 2006
Location: Germany
Member
Please find attached a zip file which contains a snapshot of my activities. This version was just a proof of concept, means lot of things in the source were not proved. I suggest that you look into apps/backoffice/modules/bookingrequest/config/generator.yml This file contains my config for the generator. As you can see I am using a property "ext:" on field level. These properties controls the handling inside the Ext part.

As mentioned above I am working on a plugin for Ext2.0 at this moment. I am not sure how much time I will have this weekend, but I hope that I could publish a first preview of the plugin at the begining of the coming week. If this is plugin is finished, or let say if it is good enough to proceed with other issues then I will start to redesign or redevelope the generator part. I am intersted in following features:
  • filter logic based on ajax requests
  • validation inside the form without roundtrips if necessary with ajax requests
  • select boxes which are filled from other table
  • master detail logic of two tables

Sortorder is my favourite priority order Wink

I saw that some of these isues were discussed in other threads and I think that I will look into these threads carefully before I start, because I am absolutely sure that there are further aspects which I did not considered until yet.

@lvanderree
Ext2.0 has a lot of enhancements and you will get access to this version if you at least a developer license. Of course this costs money, but I decided for to use this version for my playground activities. If we could adjust our ideas and coordinate our activities then I assume that we will reach the goal faster and maybe easier. What do you think?

Br
Wolfgang

Re: Building Own Generator [message #34686 is a reply to message #34683 ] Fri, 31 August 2007 16:18 Go to previous messageGo to next message
lvanderree  is currently offline 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 #34688 is a reply to message #34686 ] Fri, 31 August 2007 16:34 Go to previous messageGo to next message
sfxpt  is currently offline sfxpt
Messages: 339
Registered: January 2007
Location: Canada
Faithful Member
Ext2.0 will cost money even for a developer license?

then, personally I don't want to invest my money (and energy) into it either.


http://xpt.sourceforge.net/techdocs/
http://xpt.sourceforge.net/tools/
Re: Building Own Generator [message #34689 is a reply to message #34686 ] Fri, 31 August 2007 16:34 Go to previous messageGo to next message
kubens  is currently offline 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 Go to previous messageGo to next message
lvanderree  is currently offline 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 Wink ) 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 #34691 is a reply to message #24107 ] Fri, 31 August 2007 17:05 Go to previous messageGo to next message
kubens  is currently offline kubens
Messages: 89
Registered: February 2006
Location: Germany
Member
As mentioned above, my approach is a mixture of both. I used the crud templates but my idea is the same as yours. The intention was to get a complete skeleton for an application based on a config file. If this skeleton is saved into cache or into the corresponding module folder is regardless, I would prefere that this should be controled by an individual parameter of the config. May I described my idea mistakable, reason is that my English is not so good. But as far as I understand you, we have the same goal, believe me Wink

As I started with this idea I was neither familiar enough with Symfony nor with Ext. That is the reason why I started now to encapsulate some parts into modules and to redesign the admin generator.

If you look into the web folder, then you will find a php script which acts like the pake task 'propel-init-admin'. The difference is that it takes the templates from the crud folder. But the logic is similar.

Br
Wolfgang
Re: Building Own Generator [message #34696 is a reply to message #34691 ] Fri, 31 August 2007 19:32 Go to previous messageGo to next message
lvanderree  is currently offline 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 Wink 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 Wink


Leon
Re: Building Own Generator [message #34698 is a reply to message #34696 ] Fri, 31 August 2007 20:07 Go to previous messageGo to next message
kubens  is currently offline 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 Sad 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 Wink Worst case you must ask twice if my fancy words seems to be nonsensically ...

Br
Wolfgang




Re: Building Own Generator [message #34713 is a reply to message #34698 ] Sat, 01 September 2007 14:06 Go to previous messageGo to next message
lvanderree  is currently offline lvanderree
Messages: 647
Registered: June 2007
Location: Netherlands
Faithful Member
I've now got paging and sorting working.

However it looks like extjs doesn't support multi-column sorting. I found out that jquery does support this (but don't know since when) and I know that extjs uses jQuery. I don't know if this isn't supported due to the version of jQuery (1.1.1, instead of 1.1.4 now) or that the implementation of extjs doesn't support this at the moment (API to limited?).

If someone can figure this out, I can add that too to the theme. (as said) I've got the symfony part for multi-column sorting completely working, so what I want to know is if it is possible to enable multisorting in extjs (maybe by extending extjs, maybe by adding the new jquery to the extjs library).

I now want to replace the ids I see in the column with text-values from my foreign table and I think I know how to use the toString() functions or partials for that.

After that I will start implementing my results in a alpha-theme so it will be used by the generator and you guys (/girls?) can test it out.

Later on I will probably come back to you people to discuss about the new arguments that have to be added to the generator.yml syntax.


Leon
Re: Building Own Generator [message #34723 is a reply to message #34713 ] Sat, 01 September 2007 20:11 Go to previous messageGo to next message