Forum

Thread tagged as: Problem, Addons, Add-on-development

Custom app: How do I auto-fill a form with item details from the database

I am trying to build a custom app on top of the Members App. Registered members will be able to manage contracts with said app. They log in, go to the contracts page and see the list of contracts, they have already put in.
Now, when they want to edit a contract they are taken to a form.

        <perch:form id="editContract" method="post" app="frwssr_contracts">

                <div id="vertragspartner">
                    <perch:label for="contractissuer">Vertragspartner</perch:label>
                    <perch:input class="text_input" type="text" id="contractissuer" disabled="disabled">
                </div>

            <perch:input type="submit" name="speichern" value="Daten speichern" id="btn_vertrag_speichern" class="btn_submit">
            <perch:input type="hidden" id="token" />
            <div id="btn_vertrag_abbrechen" class="btn_submit"><a href="?s=<perch:contracts id="contractID" />">abbrechen</a></div>

        </perch:form>

How do I get this form to auto-fill with the contract details from the database?
"contractissuer" is the field alias for the database field "contractIssuer".
I tried to replicate the functionality from the Members App, but seem to be missing the magic detail, that accomplishes what I want.
Here's my code for calling the contact details:

    function frwssr_contracts_details($memberID, $contractID, $opts=array(), $return=false)
    {
        $default_opts = array(
            'template'             => 'details.html',
            'skip-template'        => false,
        );

        if (is_array($opts)) {
            $opts = array_merge($default_opts, $opts);
        }else{
            $opts = $default_opts;
        }

        $API  = new PerchAPI(1.0, 'frwssr_contracts');
        $Contracts = new Frwssr_Contracts($API);

        $Contract = $Contracts->findContractDetails($memberID, $contractID);

        $Template = $API->get('Template');
        $Template->set(PerchUtil::file_path('contracts/'.$opts['template']), 'contracts');

        $r = $Template->render_group($Contract);
        $r = $Template->apply_runtime_post_processing($r, $Contract);

        if ($return) return $r;
        echo $r;

        return false;

    }

I could call <perch:contracts id="contractIssuer" /> as the value of the "contractissuer" field, but that is not, how it is done in the Members App.
What am I missing?
(Sorry, if this sounds confusing. I am somewhat struggling to make my issue clear.)

Nils Mielke

Nils Mielke 3 points

  • 5 years ago
Drew McLellan

Drew McLellan 2638 points
Perch Support

$Template->render_group is rendering the template, so $Contract should be the details you're supplying to the template.

Thanks for the quick answer, Drew. (As always. :)
I guess, that is exactly where I am stuck. $Contract contains (among others) the object [details:protected], which holds the details I want to be displayed. I can't seem to figure out how to feed that object into an array.
Do you have a hint there?

Drew McLellan

Drew McLellan 2638 points
Perch Support

Is it just one item or multiple? render_group() expects multiple items. Use render() if it's just one.

Also make sure you have a to_array() method to transform your object to a template-ready array.

The structure of the $Contract object looks like this:

Array
(
    [0] => Frwssr_Contract Object
        (
            [table:protected] => XXX
            [pk:protected] => contractID
            [static_fields] => Array
                (
                    [0] => memberID
                    [1] => contractID
                    [2] => entryTitle
                    …
                )

            [field_aliases] => Array
                (
                    [user] => memberID
                    [contractid] => contractID
                    [title] => entryTitle
                    …
                )

            [api:protected] => PerchAPI Object
                (
                    [app_id] => frwssr_contracts
                    [version] => 1
                    [Lang:PerchAPI:private] => 
                )

            [db:protected] => PerchDB_MySQL Object
                (
                    [link:PerchDB_MySQL:private] => PDO Object
                        (
                        )

                    [errored] => 
                    [error_msg] => 
                    [dsn] => mysql:host=XXX.com;dbname=XXX;
                )

            [details:protected] => Array
                (
                    [memberID] => 2
                    [contractID] => 14
                    [entryTitle] => Testvertrag 3
                    [entryCreated] => 0000-00-00 00:00:00
                    [contractIssuer] => Anbieter 3
                    [issuerStreet] => 
                    [issuerPostcode] => 0
                    [issuerCity] => 
                    [issuerContactPerson] => 
                    [issuerCustomerID] => 
                    [issuerContractID] => 
                    [desc] => 0
                    [paymentFrequency] => 0
                    [contractDate] => 0000-00-00
                    [contractType] => Vertragsart 3
                    [minimumDuration] => 0
                    [followUpDuration] => 0
                    [autoRenewal] => 0
                    [bankRefID] => 0
                    [initialDuesAmount] => 0
                    [iban] => 
                    [firstPaymentDate] => 0000-00-00
                    [yearlyAdjustment] => 0
                    [tariff] => 
                    [classification] => 
                    [damageDiscount] => 0
                    [nextDuesAmount] => 0
                    [cancelled] => 0
                )

            [index_table:protected] => 
            [event_prefix:protected] => 
            [suppress_events] => 1
            [can_log_resources:protected] => 1
            [modified_date_column:protected] => 
            [pk_is_int:protected] => 1
        )
)

I tried to get a hold of the details with the following code:

    public function to_array()
    {
        $out = $this->details;
        return $out;
    }

But when I try to print_r($Contract->to_array()); the only thing I get is the error message "Fatal error: Call to a member function to_array() on a non-object in /XXX/perch/addons/apps/frwssr_contracts/runtime.php on line 96".

(Sorry for maybe asking for obvious answers, Drew. I am relatively new to object-oriented PHP.)

Drew McLellan

Drew McLellan 2638 points
Perch Support

It's telling you that $Contract is not an object. So you need to look and see where's that's being created and trace back to why it's failing.

$Contracts is filled like this:

        $API  = new PerchAPI(1.0, 'frwssr_contracts');
        $Contracts = new Frwssr_Contracts($API);
        $Contract = $Contracts->findContractDetails($memberID, $contractID);

and the corresponding function looked like this:

    public function findContractDetails($userID, $contractID) {
        $sql = 'SELECT * FROM '.$this->table;
        $sql .= ' WHERE '.$this->user.'='.$userID.' AND '.$this->pk.'='.$contractID;
        $rows = $this->db->get_rows($sql);
        return $this->return_instances($rows);
    }

return_instances seems to have created the non-object.
I now changed the function to read

    public function findContractDetails($userID, $contractID) {
        $sql = 'SELECT * FROM '.$this->table;
        $sql .= ' WHERE '.$this->user.'='.$userID.' AND '.$this->pk.'='.$contractID;
        $rows = $this->db->get_row($sql);
        return $rows;
    }

and thus get an array returned.
Switching render_group($Contract) to render($Contract) then did the trick to fill the form.

Thanks, Drew. You're the best—and the most patient, I suppose. ;)
(Which of your answers should I mark as solution, as all of them led to solving the problem. :D)

Sorry to open this up again. I thought, it would be better for the context, than to open a new thread.
Auto-filling the form worked great after Drew's help.
Now, how do I get the form to save?
Here's my function (details.html being the perch form template):

function frwssr_contracts_details($memberID, $contractID, $opts=array(), $return=false)
{
    $default_opts = array(
        'template'             => 'details.html',
        'skip-template'        => false,
    );

    if (is_array($opts)) {
        $opts = array_merge($default_opts, $opts);
    } else {
        $opts = $default_opts;
    }

    $API = new PerchAPI(1.0, 'frwssr_contracts');
    $Contracts = new Frwssr_Contracts($API);

    if($contractID) {
        $Contract = $Contracts->findContractDetails($memberID, $contractID);
    } else {
        $Contract = '';
    }

    if ($opts['skip-template']) {
        return $Contract;
    }

    $Session = PerchMembers_Session::fetch();
    $Sessiondata = $Session->to_array();

    $toform = array_merge($Contract, $Sessiondata);

    $Template = $API->get('Template');
    $Template->set(PerchUtil::file_path('contracts/'.$opts['template']), 'contracts');

    $r = $Template->render($toform);
    $r = $Template->apply_runtime_post_processing($r, $toform);

    if ($return) return $r;
    echo $r;
}

And here is what debug outputs after submission of the form:

Debug Message
[10] SELECT p.pagePath, pr.routePattern, pr.routeRegExp, p.pageTemplate FROM perch2_pages p LEFT JOIN perch2_page_routes pr ON p.pageID=pr.pageID ORDER BY pr.routeOrder ASC, p.pagePath ASC
Matched page: /contracts/details, so not using routes.
Using master page: /templates/pages/contracts/details.php
[1] SELECT * FROM perch2_members_sessions WHERE sessionID='xxx' AND sessionHttpFootprint='xxx' AND sessionExpires>'2016-08-04 13:44:59' LIMIT 1
User is logged in
[1] SELECT * FROM perch2_pages WHERE pagePath='/contracts/details' LIMIT 1
[22] SELECT DISTINCT settingID, settingValue FROM perch2_settings WHERE userID=0

Array
(
    [contractDate] => format
    [firstPaymentDate] => format
)

Using template: /templates/pages/attributes/default.html
Using sub-template: /templates/pages/attributes/seo.html
[1] SELECT groupID FROM perch2_navigation WHERE groupSlug='hauptmenu' LIMIT 1
[1] SELECT np.pageID, np.pageParentID, p.pagePath, p.pageTitle, p.pageNavText, p.pageNew, p.pageOrder, np.pageDepth, p.pageSortPath, np.pageTreePosition, p.pageAccessTags, p.pageAttributes FROM perch2_navigation_pages np, perch2_pages p WHERE p.pageID=np.pageID AND np.groupID=1 AND p.pageNew=0 AND np.pageDepth >=0 AND np.pageDepth<=1 ORDER BY np.pageTreePosition ASC
[0] SELECT np.pageTreePosition FROM perch2_pages p, perch2_navigation_pages np WHERE np.pageID=p.pageID AND np.groupID=1 AND p.pagePath='/contracts/details' LIMIT 1
[1] Using template: /templates/navigation/item.html
[1] SELECT groupID FROM perch2_navigation WHERE groupSlug='usermenu' LIMIT 1
[3] SELECT np.pageID, np.pageParentID, p.pagePath, p.pageTitle, p.pageNavText, p.pageNew, p.pageOrder, np.pageDepth, p.pageSortPath, np.pageTreePosition, p.pageAccessTags, p.pageAttributes FROM perch2_navigation_pages np, perch2_pages p WHERE p.pageID=np.pageID AND np.groupID=2 AND p.pageNew=0 AND np.pageDepth >=0 AND np.pageDepth<=1 ORDER BY np.pageTreePosition ASC
[1] SELECT np.pageTreePosition FROM perch2_pages p, perch2_navigation_pages np WHERE np.pageID=p.pageID AND np.groupID=2 AND p.pagePath='/contracts/details' LIMIT 1
[2] SELECT pageID FROM perch2_navigation_pages WHERE groupID=2 AND pageTreePosition IN ('000-001-002', '000-001', '000') ORDER BY pageTreePosition DESC
[3] Using template: /templates/navigation/item.html
[2] SELECT regionKey, regionHTML FROM perch2_content_regions WHERE regionPage='/contracts/details' OR regionPage='*' ORDER BY regionPage DESC
[1] SELECT * FROM perch2_frwssr_contracts_contracts WHERE memberID=4 AND contractID=14
Using template: /addons/apps/frwssr_contracts/templates/contracts/details.html
Debug Message
[10] SELECT p.pagePath, pr.routePattern, pr.routeRegExp, p.pageTemplate FROM perch2_pages p LEFT JOIN perch2_page_routes pr ON p.pageID=pr.pageID ORDER BY pr.routeOrder ASC, p.pagePath ASC
Matched page: /contracts/details, so not using routes.
Using master page: /templates/pages/contracts/details.php
[1] SELECT * FROM perch2_members_sessions WHERE sessionID='xxx' AND sessionHttpFootprint='xxx' AND sessionExpires>'2016-08-04 13:44:59' LIMIT 1
User is logged in
[1] SELECT * FROM perch2_pages WHERE pagePath='/contracts/details' LIMIT 1
[22] SELECT DISTINCT settingID, settingValue FROM perch2_settings WHERE userID=0

Array
(
    [contractDate] => format
    [firstPaymentDate] => format
)

Using template: /templates/pages/attributes/default.html
Using sub-template: /templates/pages/attributes/seo.html
[1] SELECT groupID FROM perch2_navigation WHERE groupSlug='hauptmenu' LIMIT 1
[1] SELECT np.pageID, np.pageParentID, p.pagePath, p.pageTitle, p.pageNavText, p.pageNew, p.pageOrder, np.pageDepth, p.pageSortPath, np.pageTreePosition, p.pageAccessTags, p.pageAttributes FROM perch2_navigation_pages np, perch2_pages p WHERE p.pageID=np.pageID AND np.groupID=1 AND p.pageNew=0 AND np.pageDepth >=0 AND np.pageDepth<=1 ORDER BY np.pageTreePosition ASC
[0] SELECT np.pageTreePosition FROM perch2_pages p, perch2_navigation_pages np WHERE np.pageID=p.pageID AND np.groupID=1 AND p.pagePath='/contracts/details' LIMIT 1
[1] Using template: /templates/navigation/item.html
[1] SELECT groupID FROM perch2_navigation WHERE groupSlug='usermenu' LIMIT 1
[3] SELECT np.pageID, np.pageParentID, p.pagePath, p.pageTitle, p.pageNavText, p.pageNew, p.pageOrder, np.pageDepth, p.pageSortPath, np.pageTreePosition, p.pageAccessTags, p.pageAttributes FROM perch2_navigation_pages np, perch2_pages p WHERE p.pageID=np.pageID AND np.groupID=2 AND p.pageNew=0 AND np.pageDepth >=0 AND np.pageDepth<=1 ORDER BY np.pageTreePosition ASC
[1] SELECT np.pageTreePosition FROM perch2_pages p, perch2_navigation_pages np WHERE np.pageID=p.pageID AND np.groupID=2 AND p.pagePath='/contracts/details' LIMIT 1
[2] SELECT pageID FROM perch2_navigation_pages WHERE groupID=2 AND pageTreePosition IN ('000-001-002', '000-001', '000') ORDER BY pageTreePosition DESC
[3] Using template: /templates/navigation/item.html
[2] SELECT regionKey, regionHTML FROM perch2_content_regions WHERE regionPage='/contracts/details' OR regionPage='*' ORDER BY regionPage DESC
[1] SELECT * FROM perch2_frwssr_contracts_contracts WHERE memberID=4 AND contractID=14
Using template: /addons/apps/frwssr_contracts/templates/contracts/details.html
Request time: 0,1276
Process time: 0,1272
Memory: 4,0925

After hitting submit the page gets reloaded with the edited input values. But the changes are not saved in the database and no success message appears (well, obviously as there is no success saving).

Drew McLellan

Drew McLellan 2638 points
Perch Support

I would check out the Example app for an example of how to do this. You need to read the fields in and then pass them to either the create() or update() method in order to commit the change.

Thanks, Drew.
I had done just that, but got stuck anyway.
After another day of trying to track down my mistake I have been successful and the form is now working as desired.