The data of a subform is not saved, I'm looking for a 'straight forward' code example for a subform?

417 views
Skip to first unread message

Nico van de Kamp

unread,
Jun 27, 2020, 3:03:08 PM6/27/20
to Joomla! General Development
Hello,

I have created a subform which is working so far that is shown the fields in the details page (edit.php).


<?xml version="1.0" encoding="utf-8"?>
<form>
   
<fieldset name="clubpeoplefields">
       
       
<field  type="subform"
               
name="add_people"
               
label="PEOPLE_FIELDS_LABEL"
               
layout="joomla.form.field.subform.repeatable-table"
               
multiple="true"
               
description="PEOPLE_FIELDS_DESC"
               
icon="list"
               
min="1"
               
max="50">
               
<form hidden="true" name="list_addpeople_views_modal" repeat="true">

                   
<field  name="ponr"
                           
type="text"
                           
class="inputbox"
                           
size="16"
                           
label="PEOPLE_FIELD_PONR_LABEL"
                           
description="PEOPLE_FIELD_PONR_DESC"
                           
required="true"
                   
/>
                   
<field name="posc"
                           
type="text"
                           
class="inputbox"
                           
size="16"
                           
label="PEOPLE_FIELD_POSC_LABEL"
                           
description="PEOPLE_FIELD_POSC_DESC"
                           
required="true"
                   
/>
                   
<field name="posn"
                           
type="text"
                           
class="inputbox"
                           
size="64"
                           
label="PEOPLE_FIELD_POSN_LABEL"
                           
description="PEOPLE_FIELD_POSN_DESC"
                           
required="true"
                   
/>
               
</form>
       
</field>
       
   
</fieldset>

   
<field name="state"
           
type="list"
           
label="JSTATUS"
           
description="JFIELD_PUBLISHED_DESC"
           
class="inputbox small"
           
size="1" default="1" >
             
<option value="1">JPUBLISHED</option>
             
<option value="0">JUNPUBLISHED</option>
             
<option value="2">JARCHIVED</option>
             
<option value="-2">JTRASHED</option>
   
</field>
</form>

If I press the save button, at the details page, than an record is added, id value is incremented due auto increment, in the table, the other fields are not populated of the record.
The second time I try to save a details page I get the message "record already exist". After removing the record I can save one time the details page.

I have other admin views working which are working. These I have made by reading the book "Learning Joomla! 3 Extension Development Third Edition" (Tim Plumber)

My first question - for a subform: do I have to to write my 'own' public save function for the controller/model or is the joomla framework maintaining the subform fields which adding, updating,  removing records? My expectation: it is an one of the joomla fields and that this is maintained by Joomla, maintaining the database fields of the subform. But I don't know if my expectation is right.

If not and I have to write my 'own' save function is there a simple straightforward example. I have it realized with the JCB but than I see not something specific which is done for the subform field. On the other hand I see a lot of code which is not 'default' joomla but specific for the JCB, which I don't want to use.

I hope that someone can help me further or out of this issue.

BR.

Nico.

Mark Dexter

unread,
Jun 27, 2020, 6:52:02 PM6/27/20
to Joomla! General Development
Subforms are a way to create a one-to-many relationship. There are at least two common ways to store this type of data. One way is to use a "heading" table and a "detail" table in the SQL database. Another way is to use a JSON string in a column of the table.

If you look at administrator/components/com_users in standard Joomla, the config.xml uses a subform field called "domains". The data from this field is stored as a JSON string in as part of the component parameters in the "params" column of #__extensions. I would guess that this is done as part of the standard way Joomla saves parameters from the form.

In cases where I have used subform fields, I have used the SQL heading/detail table method to store the subform data as rows in a detail table. In this case, I have written a custom override of the save method to handle insert/update/deletes of the detail rows.

Hope that helps.

--
You received this message because you are subscribed to the Google Groups "Joomla! General Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to joomla-dev-gene...@googlegroups.com.
To view this discussion on the web, visit https://groups.google.com/d/msgid/joomla-dev-general/7a93803f-092d-4c2b-8004-709fc3a39542o%40googlegroups.com.

Nico van de Kamp

unread,
Jun 28, 2020, 5:29:28 AM6/28/20
to Joomla! General Development
Hi Mark,

Many thanks.
I have all ready three or four times been looking at com_users and domains/extension. But I didn't see any clue that I thought, "... ah that's the way how to do this, so it works..." I will now look again with this and keep within the back of my head, that 'json'  is doing the 'job', maybe than I realize how it is working and which populating the data on the form again.

I have seen that it is an another array with saving than an normal edit.php which is returned by pressing the save button. This due to the heading table and details table. What I now not understand, why is there a heading table and detail table? I certainly not aware of this and I still not understand why I need it.

To do it with my 'own' save function I have written in another circumstance this. But now understand if I want this I have to make this by myself and there is nothing in the Joomla framework which is maintaining, adding, and/or updating and/or deleting  records for the joomla subform.

What I not understand is, I google on this and I discovered more people are struggling with this, that I can't find a simple tutorial and/or code example even not on the Joomla document site. Only what explain there the forms xml type. And what I have found with googling they are explaining the xml subform type but they do nothing mention about the controller/model.
From there is was doubting, because nothing is said about these specific things like the controller/model, so is the subform by default maintained by Joomla framework or not? If Yes, why is the data not saved???

But ok, I will look again to the com_users component, try to find out if I understand this,

Many thanks again now I understand a bit more what needs to be done about the subform.

BR.

Nico
To unsubscribe from this group and stop receiving emails from it, send an email to joomla-dev-general+unsub...@googlegroups.com.
Message has been deleted

Mark Dexter

unread,
Jun 28, 2020, 1:21:30 PM6/28/20
to Joomla! General Development
Another way to think about this is that the subform handles the user interface for displaying and editing the many-to-one data and it is up to the model to save and retrieve the data from the database.

Again, using the example in com_users, here are a couple of screenshots that might help. The first screenshot is from the com_users options in the Joomla back end. I have entered two domains with a rule and value (which uses using a subform).

image.png
The next screenshot shows the $_REQUEST variable when I save the com_users options. Here we see that the data from the subform has been posted inside the "jform" array as an associative array called "domains", with "domain0" and "domain1" being arrays with the values for "rule" and "name".

image.png

You can read all of the JForm data using a command like

$data   =  Factory::getApplication()->input ->get('jform', array(), 'array');

If you want to save these as a JSON string (like com_users does), you just use json_encode on $data. If you wanted to write the "domains" elements as rows in a detailed SQL table, you would pass the data to the model and have the model insert or delete rows as needed.

Hope that helps.

On Sun, Jun 28, 2020 at 4:27 AM CecioCode <ce...@cxbk.com> wrote:

Hello,
im not an expert but i have always used subforms giving the field type subform as you did, but linkin the file of subform with "formsource" did you try this way?



--
You received this message because you are subscribed to the Google Groups "Joomla! General Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to joomla-dev-gene...@googlegroups.com.
To view this discussion on the web, visit https://groups.google.com/d/msgid/joomla-dev-general/6274d55a-6ba9-478c-bddc-929ec1cbad26n%40googlegroups.com.

Roger at Gmail

unread,
Jun 28, 2020, 2:03:02 PM6/28/20
to joomla-de...@googlegroups.com

On 28 Jun 2020, at 18:21, Mark Dexter <dexter...@gmail.com> wrote:

for a subform: do I have to to write my 'own' public save function for the controller/model

Essentially yes - you extend the parent save() function in the model, same as you have to if you are using the save2copy button in the toolbar

    public function save($data) {
// handle save2copy here (make title and alias unique typically)        
// now do the parent save and then the subform save
        if (parent::save($data)) {
            return $this->storeMySubformData($data[‘whateveritisyouneedtostore’]); 
        }        
        return false;
    }

    function storeMySubformData($data) {
//you may need to start by deleting the existing data and then replacing it
        $db = $this->getDbo();
 // do whatever you need to do to update the related table…
return $result; //true if successful, otherwise false and enqueue an error message
   }

Alejandro Kurczyn

unread,
Jun 28, 2020, 3:45:58 PM6/28/20
to Joomla! General Development
Joomla will return the values from the subform as a multidimensional array, depending on what you want to do with it you have some choices:

If you want to store it on a field of the parent record then you must convert the aray to json with json_encode() either at the model's validate() function or as part of the table check() function. Once stored as a json value, Joomla will read it back to populate the subform the next time with no additional effort. The table field name must match the subform name (i.e. "add_people") and have sufficient length.

If you want to store the array values on a separate detail table, then you can do so at several places. As explained elsewhere, you can modify the table save() method to handle it and you can also do that as part of the model's validate() function I mentioned before but chances are, you might need to have a relationship with the parent record and for this you need the parent record to have an ID assigned already. For that, your best choice might be to use the controller's postSaveHook() function, since it is called after the main record is saved and an ID was assigned. That function provides a $model and $data variables already so you might be ready to go with your custom save code in the model. If for some reason $data does not contain the parent record ID, try doing $record = $model->getItem(); at the beginning. In case you delete a parent record, there's a postDeleteHook() as well.

so:
If you want to save child records along with main record, use json_encode() to keep them on a field.
If you want to save child records first, you might do it with validate().
If you want to save child records after a parent has an ID, you can use postSaveHook().

Nico van de Kamp

unread,
Jun 29, 2020, 10:47:05 AM6/29/20
to Joomla! General Development
Hello Mark, Roger and Alejandro,

Thanks, many thanks. Now I have an idea in which direction I have to look. Guys, what you are telling to me it was never come up in my mind that it works this way, sorry.

On this moment I want to use it for a, I know the dutch word/name for it "stam table = english: basic config data?", which I will use in other admin views as drop down. So I
was not even thinking that json was saved in one table field. I was only thinking that every field/peace of data alsways was save in his own table field.

I will use the SQL variant, because off that I will put in another admin view. And I will put it in one table, for now I don't a 'heading' table. But I have another situation maybe there
I will use a heading table with a childtable.

On this moment I can further. I do not realize every peace of step but I have now a better what I need have to do.

One question, where could I have found this information, is there anyhow some information? Such a things as postSaveHook() what is the function of it and when do I need it!??
Did I not search right , where could I find this information?

But many thanks,

BR.

Nico

Roger at Gmail

unread,
Jun 29, 2020, 1:09:17 PM6/29/20
to joomla-de...@googlegroups.com
My experience is that is very difficult. Often a google search on a question produces links to old versions of the API (1.x, 2.x) and not the new 3.x version (never mind J4).

postSaveHook() for example is a new one to me, and makes me wonder if there are a whole family of ‘hooks’ for Joomla. I thought that hooks were were a Wordpress or Discourse thing and not something that was found in Joomla.

I started by following the same book as you and the Developing an MVC Component official Joomla tutorial online, and then when I saw a component doing something I want I try to puzzle out how it is doing it - trouble is, very often they are either components that were originally Joomla 2.5 or earlier and are not even using facilities that are in the book, or they are free versions of commercial ones and the code is pretty well obscured and buried in general purpose libraries, which makes it hard to follow.

Keep using the search, rephrase the question if you don’t get useful answers. And do keep your own notes of things that you find which you may find useful in the future.

Good luck
Roger

Nico van de Kamp

unread,
Jun 30, 2020, 4:52:53 PM6/30/20
to Joomla! General Development
Hello Mark,

May I ask you a stupid question. Is this what I need to do, to get the opportunity that I can choose an domain for a user? Because in my com_user, I see the subform defined but not a controller/domain for domains. Even if I search with Netbeans on "domain" I find only the subform but further no code in an controller and/or model file.

I'm little bit further, one record is saved with the Json data, but I try it again than an record is added but not the data. One important thing is, what I was not aware off, but you need a column field in the table with the same name as the name of the subfield!
For this admin view I do not have a heading table, just one table with data.

I will struggle further and will find it out, but it is very hard to find the information.

BR.

Nico
To unsubscribe from this group and stop receiving emails from it, send an email to joomla-dev-general+unsub...@googlegroups.com.

Mark Dexter

unread,
Jun 30, 2020, 5:04:42 PM6/30/20
to Joomla! General Development
The example in com_users is nothing to do with the link about Multiple Domains. Please ignore the specifics of the data (it could be ['fruit' => ['apples' => 'yes', 'oranges' => 'no']]). It is just an example using subform to save one-to-many data as a JSON string, in this case in the params column of the #__extensions. In my example, this is the contents of the params column for com_users:

{"allowUserRegistration":"0","new_usertype":"2","guest_usergroup":"9","sendpassword":"0","useractivation":"2","mail_to_admin":"0","captcha":"","frontend_userparams":"0","site_language":"0","change_login_name":"0",
"domains":{"domains0":{"name":"xyz.com","rule":1},"domains1":{"name":"abc.com","rule":0}},"reset_count":"10","reset_time":"1","minimum_length":"8","minimum_integers":"1","minimum_symbols":"1","minimum_uppercase":"0","minimum_lowercase":"0","save_history":"1"
,"history_limit":5,"mailSubjectPrefix":"","mailBodySuffix":"","debugUsers":"1","debugGroups":"1","sef_advanced":0,"custom_fields_enable":"1"}

notice that "domains" is saved as a JSON encoded string, just as it was in the $_REQUEST variable.

In your controller you will retrieve the subform data from the form, just as shown in this example. Then you must decide what to do with that data (for example, save as JSON string or as rows in a detail table).

Hope that helps.

To unsubscribe from this group and stop receiving emails from it, send an email to joomla-dev-gene...@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "Joomla! General Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to joomla-dev-gene...@googlegroups.com.
To view this discussion on the web, visit https://groups.google.com/d/msgid/joomla-dev-general/46e4e7bf-e605-4816-be4d-43272e6a952co%40googlegroups.com.

Alejandro Kurczyn

unread,
Jul 1, 2020, 2:39:20 PM7/1/20
to Joomla! General Development
I'm not sure we have answered your specific question, but I think you almost got it:

if your subform fields are already showing up fine in the edit.php view, then do the following:

1. Make sure I have a field called "add_people" in your database table. Set is as varchar(4096) or TEXT of enough length.
2. On the model, include this function:

    public function validate($form, $data, $group = null)
    {
        $data['add_people'] = json_encode($data['add_people']);

        return parent::validate($form, $data, $group);
    }

This will let joomla save all subform values on a single field and pull them automatically the next time you use edit.php view.

regards.

El sábado, 27 de junio de 2020, 14:03:08 (UTC-5), Nico van de Kamp escribió:

Nico van de Kamp

unread,
Jul 2, 2020, 3:22:34 PM7/2/20
to Joomla! General Development
Hi All,

Ok, I have been looking into the com_users and didn't thought on the users --> options. Sorry about that. But I see it now, sorry for that I haven't look earlier to the options. But at the end, I can't follow it, sorry about it.

I've been looking when, were and what is saved? There is a subform defined with the name 'domains'. But the data is saved in the extensions table and in the field params!!!??? I expect that that it should be saved in the field 'domains'!!??? So there must somewhere code that is twisting this or...

Further I can't find the call to the domains subform. I have been searching through all the backend components but it isn't really there. Only in the language .ini file but further nothing, not in the com_config and nor in the com_users!!?
Do I not looking could and/or do I not understand this or do search in the complete wrong direction?



Yesterday evening I have made a step back, which means I have made it on the conventional way. I mean by that, every record add_people has to be made separately (save and new). This works, if I press save on the details page  (edit.php) an record is created and in the list view I see the records. So this is working perfectly also my main controller etc.

Now back to the issue what I try to learn and to solve:

In this particular case I have only one table, what I mean, I don't have a child table related to a heading table by an FK. I don't know if this is an issue if using a subform? I have also another situation with a heading - child table relation: floor(=heading) - room(=child).

I will put could here beneath:

The code of my controller:

public function save($data = array(), $key = 'id') {
   
//TODO: how tokens work
   
//$this->checkToken();
       
    $input
= JFactory::getApplication()->input;
    $data
= $input->get(jform, array(), 'array' );

    echo
'Controller add_people.php: '.var_dump($data).'<br />';
           
    $model
= $this->getModel();
    $model
->save($data);
   
//return;

   
//TODO: After pressing save, what needs to be the return url? Now it returns to the 'default' list view and away from the details page!!??? (but that is an issue for later)
    $this
->setRedirect(JRoute::_("index.php?option=com_people&view=add_people&layout=edit"), "Records saved");
}
   

protected function postSaveHook(JModelLegacy $model, $validData = array()) {
   
return;
}


-- result in the controller of $data by var_dump($data) after pressing save
array (size=3)
 
'add_people' =>
    array
(size=2)
     
'add_people0' =>
        array
(size=3)
         
'ponr' => string '78' (length=2)
         
'posc' => string 'XY' (length=2)
         
'posn' => string 'Front' (length=5)
     
'add_people1' =>
        array
(size=3)
         
'ponr' => string '88' (length=2)
         
'posc' => string 'YZ' (length=2)
         
'posn' => string 'Behind' (length=6)
 
'state' => string '1' (length=1)
 
'language' => string '' (length=0)
 
The code of my model:
 
public function save($data) {
       
        $add_people
= new JRegistry;
        $add_people
->loadArray($data['add_people']);
        $data
['add_people'] = (string) $add_people;
       
        echo
'1. $data[add_people] Dit is de save functie in de model add_people.php: '.var_dump($data['add_people']).'<br />';
       
       
if (!isset($data['add_people'])) {
                $data
['add_people'] = array();
       
}

   
return parent::save($data);

}
   
public function validate($form, $data, $group = null) {
        $data
['add_people'] = json_encode($data['add_people']);

       
return parent::validate($form, $data, $group);
}
  
result in the model var_dump($data['add_people']:
 '{"add_people0":{"ponr":"78","posc":"XY","posn":"Front"},"add_people1":{"ponr":"88","posc":"YZ","posn":"Behind"}}'
  
result in database field add_people:
{"add_people0":{"ponr":"78","posc":"XY","posn":"Front"},"add_people1":{"ponr":"88","posc":"YZ","posn":"Behind"}}

My code for the edit.php (of course there is more but this is were it goes all about)

<?php echo JLayoutHelper::render('people_field_view.details', $this); ?>

code from the people_field_view.details.php. This code I saw in JoomlaComponentBuilder, so I have taken this, I can't judge this if this is it or just wrong in my case.
$form = $displayData->getForm();
//$form = $forms[0]; //joomla stackoverflow

$layout_path_array
= explode('.', $this->getLayoutId());
$fields_tab_layout
= 'fields_' . $layout_path_array[1];

// get the fields
$fields
= $displayData->get($fields_tab_layout) ?: array(
   
'add_people'
);

$hiddenFields
= $displayData->get('hidden_fields') ?: array();

?>
<?php if ($fields && count((array) $fields)) :?>
<div class="form-vertical">
   
<?php foreach($fields as $field): ?>
       
<?php if (in_array($field, $hiddenFields)) : ?>
               
<?php $form->setFieldAttribute($field, 'type', 'hidden'); ?>
       
<?php endif; ?>
       
<?php echo $form->renderField($field, null, null, array('class' => 'control-wrapper-' . $field)); ?>
   
<?php endforeach; ?>
</div>
<?php endif; ?>

There are a few things remarkble and/or left:
  1. I can only saving one record. If I do 'New', fill the subform - press save, message "records saved". If I look in the database there is still only one record which is not changed or updated.
  2. - For now, I don't have a heading table - or I make a mistake and don't know exactly what is meant by a heading/child table. But doing this without a heading table is that an issue with a subform?
  3. - And the steps ahead, how I get the data back from the database into the fields of the details page (edit.php) after saving? Is my lay out not good?
  4. - The function validate is that called by the 'framework' or do I have to make explicit call to it by the model save function? What is the function of this? I mean in the joomla docs is told about validate but I don't really understand it
  5. - The same for the postSaveHook function. When is this called and why?
  6. - And then some steps further ahead, If the records are saved, how do I get this back in my list view? But I think I can do this by the query and put json string of database field into...? I will see than.
I hope that I have explain were I'm standing and that someone can point in a direction, that I can get it further.

BR.,

Nico

(BTW, all what I know from building a component is from the "LEARNING_JOOMLA_3_EXTENSION_DEVELOPMENTTHIRD_EDITION." of Tim Plummer and following the videos of Robbie Jackson on Joomla docs)

Alejandro Kurczyn

unread,
Jul 2, 2020, 10:52:08 PM7/2/20
to Joomla! General Development
Hmm understanding the basic structure of Joomla components take can be difficult. Perhaps the best learning tool is a basic example. Take a look at the link below to download a small "Movie collection" component I did for you. It is as simple as it gets and I think it will make things simpler to understand. The backend is ready to go, I'll leave the front end up to you ;)

Let me know if you have any question.


Regards,

Roger at Gmail

unread,
Jul 3, 2020, 5:32:37 AM7/3/20
to joomla-de...@googlegroups.com
Hi Nico
As well as the book you might find it worth looking through the online tutorial https://docs.joomla.org/J3.x:Developing_an_MVC_Component/Introduction  (it is available in German, Spanish, French, Dutch and Brazilian Portugese as well as English if that helps)

Although it doesn’t explicitly cover sub-forms it will answer most of your other puzzles.

I presume you have read the description here https://docs.joomla.org/Subform_form_field_type

Sometimes the core joomla components are not the best place to begin to understand things I find. Useful to look at in terms of some specifics - a feature that you want to emulate perhaps - but often a bit obscure. Worth downloading some simple components from the extensions area - not commercial or semi-commercial ones, as even in the free version they will often be using their own libraries etc which confuses the issue. Look for a developer who has contributed just a couple of recent simple components and see how they are doing things.  Avoid things that have been around for a few years, or work with j2.5 or lower, as even if updated they may have legacy code and legacy thinking embedded.

Bon chance

RogerCO
(NOT an expert)

Nico van de Kamp

unread,
Jul 3, 2020, 6:09:18 PM7/3/20
to Joomla! General Development
Hi Alejandro,

Thanks, many thanks again. It is working now, so to my next challenge. Maybe I have some questions later like postSaveHook or postDeleteHook, the function check and the function validate. You have commented out these functions but I want to know why and when is this used! First I will try to find out it by myself by googling about it. For now it is working and I'm very clad. I can go further now and the feeling that I made progression it so fine!!!!

I had not the complete right mindset like heading/child table (=two tables) with FK. Also other small things. Now I have also a kind of heading/child table, but is put in 1 record. It is updated, parts are deleted etc.

Thanks!

Nico

Nico van de Kamp

unread,
Jul 3, 2020, 6:17:54 PM7/3/20
to Joomla! General Development
Hi Roger,

Yes I'm reading this. But the thing is if you read that "Subform_form_field_type" that's not difficult and that I had working. But there they more or less only speak ab form *.xml. If they add a straight forward code example with two fields for example than you can implement that and 'play' with it. You know at least this is working. And from there you can easy modify it and if it not works, than you know ok it is may fault due the change I have made. I have to made a step back.

But the Example of Alejandro helpt me out and I see where I made the 'little' mistakes with the wrong mindset.

I'm very happy now!

Many Thanks to you all.

Nico

Troy Hall

unread,
Jul 4, 2020, 1:17:55 PM7/4/20
to 'Roger at Gmail' via Joomla! General Development
I love this example!
the rem statements make all the difference in the world.
Would you please update the jdocs ( https://docs.joomla.org/Subform_form_field_type )
with this as an example of a save and perhaps another for read?
This would be immensely helpful.
Thanks
Bear ( JBS )
--
You received this message because you are subscribed to the Google Groups "Joomla! General Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to joomla-dev-gene...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages