generate needed qcontrols directly

thread: 9 messages  |  last: a year ago  |  started: saturday, june 9, 2007, 3:37 am pdt


#1  |  Igor Butuc (Bucharest) Romania
Saturday, June 9, 2007, 3:37 AM PDT

Hi to all, and, Mike, THANKS for this amazing framework.

I think I basically understood that in philosophy of QCodo framework it is preferred to maximize flexibility and let programmer to customize over the basic form_draft generated by typing code, than by writing configuration files or by naming conventions (of the db table column names for example).

The problem is that form_drafts and form base classes generated, uses only a very few qcontrols available (lbl, lst, txt, cal, chk).

But in my short time while I use Qcodo, I already need to use many other qcontrols bundled with Qcodo or even custom qcontrols (for example FCKEditor, thanks to Martin Kronstad) and for big projects (for witch Qcodo is intended to be used) this is a pain to “customize” site wide, basically by copy-pasting same snippets of code.

Other related problem (simpler one) was to customize design for form draft pages (templates generated) site wide. This one I solved already by overriding codegen templates (db_orm_edit_form_draft_include.tpl and db_orm_list_form_draft_include.tpl). And this is really COOL! Now I save a lot of time of administration part of the application.

But the bigger problem is now to let qcodo somehow know what qcontrol exactly I need for each column and generate it directly. Believe me, this one will save even more time.

I did some research on code generation part of qcodo, but I'm not sure that this problem can be solved just by overriding templates and extending QCodeGenBase and QCodeConvertNotation classes (i.e. not touching qcodo core).

I see 3 ways to “let qcodo somehow know what qcontrol exactly I need for each column” (each one have its pros and cons):
** by suffixing column names in tables in db (just like suffixing table names for _assn and _type). For example we could use “column_name_fck” and qcodo will then know that I want QFCKEditor to be used for that field.
** by putting this info (and may be other config info needed for some qcontrols) in the comment of the column (in db)
** by having a configuration file that will store this infos (this coud be even codegen_settings.xml to have related things be together).

IMHO first one is the simpliets way, but the last one is a cleaner way to do things.

So, finally, hoping that I explained well the problem, my questions are:
1. Is there a way to do this customizations co code generation without touching the qcodo core?
2. Is it appropriate to include generation of needed control in qcodo core (in the future versions)
3. Is there a way to do a reasonable tradeoff between configuration and customization for Qcodo future versions (so that we could automatize repeatable customization tasks for our qcodo applications, doesn't matter that this is for form drafts of for generated data classes).

Thanks!

P.S. Sorry for my English... not my mother tongue.

#2  |  Igor Butuc (Bucharest) Romania
Sunday, June 10, 2007, 5:20 PM PDT

I did a part of the work, only overriding templates.
So now I have:
*_fck column name generates directly a QFCKEditor control
*_img column name generates directly a QFileAsset control of image type

Works great!
But there is 2 more issues:
**these controls have a txt prefix and not a fck or fla
**_fck and _img suffixes are not striped when creating property, variable and controls names, so it's ugly

I did a little research on the forums and on source code of QCodeGenBase class, found that I should override QCodeGenBase class functions in the COMMONLY OVERRIDDEN CONVERSION FUNCTIONS section.
But uhh, these functions are not documented and not so easy for me to figure out from the code what are dependencies.

I tried to do this in my QCodoGen class:

protected function CleanColumnName(QColumn $objColumn)
{
    $objCleanColumn = clone $objColumn;
    $strColumnName = $objCleanColumn->Name;
    if (ereg('_img$', $strColumnName))
        $strColumnName = substr($strColumnName, 0, -4);

    if (ereg('_fck$', $strColumnName))
        $strColumnName = substr($strColumnName, 0, -4);
    
    $objCleanColumn->Name = $strColumnName;
    
    return $objCleanColumn;    
}

protected function PropertyNameFromColumn(QColumn $objColumn) 
{
    return parent::PropertyNameFromColumn($this->CleanColumnName($objColumn));
}
protected function VariableNameFromColumn(QColumn $objColumn) 
{
    return parent::VariableNameFromColumn($this->CleanColumnName($objColumn));
}        

protected function FormControlVariableNameForColumn(QColumn $objColumn) 
{
    if (ereg('_img$', $objColumn->Name))
        return sprintf('fla%s', $objColumn->Reference->PropertyName);

    if (ereg('_fck$', $objColumn->Name))
        return sprintf('fck%s', $objColumn->Reference->PropertyName);
        
    return parent::FormControlVariableNameForColumn($objColumn);
}

but when running codegen.php Apache crashed... :(

Any suggestions please?

#3  |  tronics (VIE, AUT) Austria
Friday, April 4, 2008, 2:08 PM PDT

I think this is a very interesting idea!
Would be great to see your templates!

I think these would make qcodo even more useful for me..

_fck .. richtextedirot
_img .. image/fileupload
_file .. file (download)/fileupload

Regards,
tronics

#4  |  Igor Butuc (Bucharest) Romania
Friday, April 4, 2008, 9:56 PM PDT

I implemented by this time 3 of custom templates (_fck, _img, _audio). Last one uses a control similar to fileasset, it uploads an audio file and displays an audio preview in form of a flash player.

Please note that I use Qcodo 0.3.24 . I didn't have the time to switch all my customizations to the new version of Qcodo. I would like to do this...

What exactly I did:

- put a modified version of db_orm_edit_form_base.tpl in the includes/qcodo/codegen/templates

<template OverwriteFlag="true" DocrootFlag="false" DirectorySuffix="" TargetDirectory="<%= __FORMBASE_CLASSES__ %>" TargetFileName="<%= $objTable->ClassName %>EditFormBase.class.php"/>
<?php
    
/**
     * This is the abstract Form class for the Create, Edit, and Delete functionality
     * of the <%= $objTable->ClassName %> class.  This code-generated class
     * contains all the basic Qform elements to display an HTML form that can
     * manipulate a single <%= $objTable->ClassName %> object.
     *
     * To take advantage of some (or all) of these control objects, you
     * must create a new Form which extends this <%= $objTable->ClassName %>EditFormBase
     * class.
     *
     * Any and all changes to this file will be overwritten with any subsequent re-
     * code generation.
     * 
     * @package <%= QCodeGen::$ApplicationName; %>
     * @subpackage FormBaseObjects
     * 
     */
    
abstract class <%= $objTable->ClassName %>EditFormBase extends QForm {
        
// General Form Variables
        
protected $<%= $objCodeGen->VariableNameFromTable($objTable->Name); %>;
        protected 
$strTitleVerb;
        protected 
$blnEditMode;

        
// Controls for <%= $objTable->ClassName %>'s Data Fields
<% foreach ($objTable->ColumnArray as $objColumn) { %>
        protected $<%= 
$objCodeGen->FormControlVariableNameForColumn($objColumn); %>;
<% } %>

        
// Other ListBoxes (if applicable) via Unique ReverseReferences and ManyToMany References
<% foreach ($objTable->ReverseReferenceArray as $objReverseReference) { %>
    <% if (
$objReverseReference->Unique) { %>
        protected $<%= 
$objCodeGen->FormControlVariableNameForUniqueReverseReference($objReverseReference); %>;
    <% } %>
<% } %>
<% foreach (
$objTable->ManyToManyReferenceArray as $objManyToManyReference) { %>
        protected $<%= 
$objCodeGen->FormControlVariableNameForManyToManyReference($objManyToManyReference); %>;
<% } %>

        
// Button Actions
        
protected $btnSave;
        protected 
$btnCancel;
        protected 
$btnDelete;

        protected function 
Setup<%= $objTable->ClassName %>() {
            
// Lookup Object PK information from Query String (if applicable)
            // Set mode to Edit or New depending on what's found
<% foreach ($objTable->PrimaryKeyColumnArray as $objColumn) { %>
            $<%= 
$objColumn->VariableName %> = QApplication::QueryString('<%= $objColumn->VariableName %>');
<% } %>
            if (<% foreach (
$objTable->PrimaryKeyColumnArray as $objColumn) { %>($<%= $objColumn->VariableName %>) || <% } %><%----%>) {
                
$this-><%= $objCodeGen->VariableNameFromTable($objTable->Name); %> = <%= $objTable->ClassName %>::Load(<% foreach ($objTable->PrimaryKeyColumnArray as $objColumn) { %>($<%= $objColumn->VariableName %>), <% } %><%--%>);

                if (!
$this-><%= $objCodeGen->VariableNameFromTable($objTable->Name) %>)
                    throw new 
Exception('Could not find a <%= $objTable->ClassName %> object with PK arguments: ' . <% foreach ($objTable->PrimaryKeyColumnArray as $objColumn) { %>$<%= $objColumn->VariableName %> . ', ' . <% } %><%----------%>);

                
$this->strTitleVerb QApplication::Translate('Edit');
                
$this->blnEditMode true;
            } else {
                
$this-><%= $objCodeGen->VariableNameFromTable($objTable->Name); %> = new <%= $objTable->ClassName %>();
                
$this->strTitleVerb QApplication::Translate('Create');
                
$this->blnEditMode false;
            }
        }

        protected function 
Form_Create() {
            
// Call Setup<%= $objTable->ClassName %> to either Load/Edit Existing or Create New
            
$this->Setup<%= $objTable->ClassName %>();

            
// Create/Setup Controls for <%= $objTable->ClassName %>'s Data Fields
<% foreach ($objTable->ColumnArray as $objColumn) { %>
            
$this-><%= $objCodeGen->FormControlVariableNameForColumn($objColumn) %>_Create();
<% } %>

            
// Create/Setup ListBoxes (if applicable) via Unique ReverseReferences and ManyToMany References
<% foreach ($objTable->ReverseReferenceArray as $objReverseReference) { %>
    <% if (
$objReverseReference->Unique) { %>
            
$this-><%= $objCodeGen->FormControlVariableNameForUniqueReverseReference($objReverseReference) %>_Create();
    <% } %>
<% } %>
<% foreach (
$objTable->ManyToManyReferenceArray as $objManyToManyReference) { %>
            
$this-><%= $objCodeGen->FormControlVariableNameForManyToManyReference($objManyToManyReference) %>_Create();
<% } %>

            
// Create/Setup Button Action controls
            
$this->btnSave_Create();
            
$this->btnCancel_Create();
            
$this->btnDelete_Create();
        }

        
// Protected Create Methods
<% foreach ($objTable->ColumnArray as $objColumn) { %><%
    
// Use the "control_create_" subtemplates to generate the code
    // required to create/setup the control.
    
$mixArguments = array(
        
'objColumn' => $objColumn,
        
'strObjectName' => $objCodeGen->VariableNameFromTable($objTable->Name),
        
'strClassName' => $objTable->ClassName,
        
'strControlId' => $objCodeGen->FormControlVariableNameForColumn($objColumn)
    );

    
// Figure out WHICH "control_create_" to use
    
if ($objColumn->Identity) {
        
$strTemplateFilename 'identity';
    } else if (
$objColumn->Timestamp) {
        
$strTemplateFilename 'identity';
    } else if (
$objColumn->Reference) {
        if (
$objColumn->Reference->IsType)
            
$strTemplateFilename 'type';
        else
            
$strTemplateFilename 'reference';
    } else if(
ereg('_fck$'$objColumn->Name)) {
        
$strTemplateFilename 'fck';
    } else if(
ereg('_img$'$objColumn->Name)) {
        
$strTemplateFilename 'img';
    } else if(
ereg('_audio$'$objColumn->Name)) {
        
$strTemplateFilename 'audio';
    } else {
        
$strTemplateFilename $objColumn->VariableType;
    }
    
    
// Get the subtemplate and evaluate
    
return $objCodeGen->EvaluateSubTemplate(sprintf('control_create_%s.tpl'$strTemplateFilename), $mixArguments) . "\n\n";
%><% } %>
<% foreach (
$objTable->ReverseReferenceArray as $objReverseReference) { %><%
    if (
$objReverseReference->Unique) { 
        
// Use the "control_create_" subtemplates to generate the code
        // required to create/setup the control.
        
$mixArguments = array(
            
'objReverseReference' => $objReverseReference,
            
'objTable' => $objTable,
            
'strObjectName' => $objCodeGen->VariableNameFromTable($objTable->Name),
            
'strClassName' => $objTable->ClassName,
            
'strControlId' => $objCodeGen->FormControlVariableNameForUniqueReverseReference($objReverseReference)
        );
        
// Get the subtemplate and evaluate
        
return $objCodeGen->EvaluateSubTemplate('control_create_unique_reversereference.tpl'$mixArguments) . "\n\n";
    } else
        return 
null;
%><% } %>
<% foreach (
$objTable->ManyToManyReferenceArray as $objManyToManyReference) { %><%
    
// Use the "control_create_manytomany_reference" subtemplate to generate the code
    // required to create/setup the control.
    
$mixArguments = array(
        
'objManyToManyReference' => $objManyToManyReference,
        
'objTable' => $objTable,
        
'strObjectName' => $objCodeGen->VariableNameFromTable($objTable->Name),
        
'strClassName' => $objTable->ClassName,
        
'strControlId' => $objCodeGen->FormControlVariableNameForManyToManyReference($objManyToManyReference)
    );
    
// Get the subtemplate and evaluate
    
return $objCodeGen->EvaluateSubTemplate('control_create_manytomany_reference.tpl'$mixArguments) . "\n\n";
%><% } %>

        
// Setup btnSave
        
protected function btnSave_Create() {
            
$this->btnSave = new QButton($this);
            
$this->btnSave->Text QApplication::Translate('Save');
            
$this->btnSave->AddAction(new QClickEvent(), new QServerAction('btnSave_Click'));
            
$this->btnSave->PrimaryButton true;
            
$this->btnSave->CausesValidation true;
        }

        
// Setup btnCancel
        
protected function btnCancel_Create() {
            
$this->btnCancel = new QButton($this);
            
$this->btnCancel->Text QApplication::Translate('Cancel');
            
$this->btnCancel->AddAction(new QClickEvent(), new QServerAction('btnCancel_Click'));
            
$this->btnCancel->CausesValidation false;
        }

        
// Setup btnDelete
        
protected function btnDelete_Create() {
            
$this->btnDelete = new QButton($this);
            
$this->btnDelete->Text QApplication::Translate('Delete');
            
$this->btnDelete->AddAction(new QClickEvent(), new QConfirmAction(sprintf(QApplication::Translate('Are you SURE you want to DELETE this %s?'), '<%= $objTable->ClassName %>')));
            
$this->btnDelete->AddAction(new QClickEvent(), new QServerAction('btnDelete_Click'));
            
$this->btnDelete->CausesValidation false;
            if (!
$this->blnEditMode)
                
$this->btnDelete->Visible false;
        }
        
        
// Protected Update Methods
        
protected function Update<%= $objTable->ClassName; %>Fields() {
<% foreach (
$objTable->ColumnArray as $objColumn) { %><%
    if ((!
$objColumn->Identity) && (!$objColumn->Timestamp)) {
        
// Use the "control_create_" subtemplates to generate the code
        // required to create/setup the control.
        
$mixArguments = array(
            
'objColumn' => $objColumn,
            
'strObjectName' => $objCodeGen->VariableNameFromTable($objTable->Name),
            
'strClassName' => $objTable->ClassName,
            
'strControlId' => $objCodeGen->FormControlVariableNameForColumn($objColumn)
        );
    
        
// Figure out WHICH "control_create_" to use
        
if ($objColumn->Reference) {
            if (
$objColumn->Reference->IsType)
                
$strTemplateFilename 'type';
            else
                
$strTemplateFilename 'reference';
        } else if(
ereg('_img$'$objColumn->Name)) {
            
$strTemplateFilename 'img';
        } else if(
ereg('_audio$'$objColumn->Name)) {
            
$strTemplateFilename 'audio';
        } else switch (
$objColumn->VariableType) {
            case 
QType::Boolean:
                
$strTemplateFilename 'checkbox';
                break;
            case 
QType::DateTime:
                
$strTemplateFilename 'calendar';
                break;
            default:
                
$strTemplateFilename 'textbox';
                break;
        }
        
        
// Get the subtemplate and evaluate
        
return $objCodeGen->EvaluateSubTemplate(sprintf('control_update_%s.tpl'$strTemplateFilename), $mixArguments) . "\n";
    } else
        return 
null;
%><% } %>
<% foreach (
$objTable->ReverseReferenceArray as $objReverseReference) { %><%
    if (
$objReverseReference->Unique) {
        
// Use the "control_update_unique_reversereference" subtemplate to generate the code
        // required to create/setup the control.
        
$mixArguments = array(
            
'objReverseReference' => $objReverseReference,
            
'strObjectName' => $objCodeGen->VariableNameFromTable($objTable->Name),
            
'strClassName' => $objTable->ClassName,
            
'strControlId' => $objCodeGen->FormControlVariableNameForUniqueReverseReference($objReverseReference)
        );
    
        
// Get the subtemplate and evaluate
        
return $objCodeGen->EvaluateSubTemplate('control_update_unique_reversereference.tpl'$mixArguments) . "\n";
    } else
        return 
null;
%><% } %>
        }

<% foreach (
$objTable->ManyToManyReferenceArray as $objManyToManyReference) { %><%
        
// Use the "control_update_manytomany_reference" subtemplate to generate the code
        // required to create/setup the control.
        
$mixArguments = array(
            
'objManyToManyReference' => $objManyToManyReference,
            
'strObjectName' => $objCodeGen->VariableNameFromTable($objTable->Name),
            
'strClassName' => $objTable->ClassName,
            
'strControlId' => $objCodeGen->FormControlVariableNameForManyToManyReference($objManyToManyReference)
        );

        
// Get the subtemplate and evaluate
        
return $objCodeGen->EvaluateSubTemplate('control_update_manytomany_reference.tpl'$mixArguments) . "\n";
%><% } %>

        
// Control ServerActions
        
protected function btnSave_Click($strFormId$strControlId$strParameter) {
            
$this->Update<%= $objTable->ClassName; %>Fields();
            
$this-><%= $objCodeGen->VariableNameFromTable($objTable->Name); %>->Save();

<% foreach (
$objTable->ManyToManyReferenceArray as $objManyToManyReference) { %>
            
$this-><%= $objCodeGen->FormControlVariableNameForManyToManyReference($objManyToManyReference); %>_Update();
<% } %>

            
$this->RedirectToListPage
#5  |  tronics (VIE, AUT) Austria
Saturday, April 5, 2008, 9:50 AM PDT

Thanks, Igor that is great!!
This is more then a great starting point to get things rolling :)

I would like to help make this work in the current version of qcodo. I already made the magic columns work with the current version.

I would also love to see the fckeditor and audio implementation. If you don't mind sharing that too, it would really save time :)

--

Currently I'm trying to compare your changes with the original files with winmerge.
But I wasn't able to find the file you are overriding (db_orm_edit_form_base.tpl) yet.
I tried to download the version of qcodo that you are using here to start out, but I cannot find it, I don't see any of the previous versions online..
Probably you can send me the full package to tronics AT gmx.at
As soon as I get this working on my machine, I can make it work in the current version.

Regards,
tronics

#6  |  Igor Butuc (Bucharest) Romania
Sunday, April 6, 2008, 4:00 AM PDT

It would be great if you could make this work in new version on Qcodo, because I really have no time for this now, and I'm missing new features of Qcodo :)

I'll make asap a “clean” copy of the my customized qcodo package and I'll send to you. Sorry I cannot share with you my currently working app as it is, because of the confidentiality issues.

May be you didn't searched in the right place. The files that are overridden are in includes/qcodo/_core/codegen/templates .

#7  |  tronics (VIE, AUT) Austria
Sunday, April 6, 2008, 10:57 PM PDT

Thanks you for your response Igor!

I'm looking forward to the package. I only need the old qcodo version (like it was in the download) and separately the changed templates. So I can test, see the changes and apply and modify them on the new qcodo version.

Or if you don't have the time for that - please just post the other templates here for fck and audio/file. I shall be able to work that out too.

--

I cannot not find the equivalent for db_orm_edit_form_base.tpl (no problem finding the place control_update_**.tpl).
Thought I have found equivalent code pieces in the current version but that were not the right ones..

Thanks.

Regards,
tronics

#8  |  Igor Butuc (Bucharest) Romania
Monday, April 7, 2008, 1:07 AM PDT

I sent you an email with the files you requested.

I hope that you will figure it out.

#9  |  alex94040 (Seattle, WA) United States of America Qcodo Core Contributor
Thursday, February 12, 2009, 10:36 AM PST

Igor, this is extremely interesting. Would you be willing to work with us to integrate your work into a release of QCubed?

QCubed is a community effort to take QCodo further. Join us! <http://qcu.be>



Copyright © 2005 - 2010, Quasidea Development, LLC
This open-source framework for PHP is released under the terms of The MIT License.