How does postSaveHook work with an overriden save in the controller/model?

278 views
Skip to first unread message

Nico van de Kamp

unread,
May 7, 2021, 5:19:47 PM5/7/21
to Joomla! General Development
Hi again,

I working again on my component I'm not the first one I've seen with the postSaveHook question but I can't find  out how this work.
I have overriden the save function in the subcontroller like:
[CODE]
public function save($data = array(), $key = 'id') {
       
        $input = JFactory::getApplication()->input;
        $data = $input->get(jform, array(), 'array' );

        $model = $this->getModel();
        if ($model->save($data)) {

            $this->setRedirect(JRoute::_("index.php?option=com_component&view=viewName&layout=edit&id=".$data['id'], false), "Records saved");
        } else {
            //return;
            $this->setRedirect(JRoute::_( "index.php?option=com_component&view=viewName&layout=edit&id=".$data['id'] , false));
        }
    } //END public function save_new($data = array(), $key = 'id')
[/CODE]

This is working fine for an update of an existing record. But if I add a new record than the $data['id'] is 0. (The record is saved in the database).

So I thought I need add the postSaveHook in the subcontroller like:
[CODE]
    protected function postSaveHook($model, $validData) {
        $item    = $model->getItem();
        $itemid  = $item->get('id');
        
       // echo '$itemid: '.$itemid;
        //echo "<pre>";
         //   print_r($data);
        //echo "</pre>";
        return true;
    }
[/COD]

But when I add a new record, than it doesn't  do anything. What I'm doing wrong and do not understand?
I have tried to save the id in $data['id'], but that doesn't help me.
I have been looking at 'examples' from the category,.php, user.php and style.php but that is different what I have here, at least I do not see a relation with that from me.

I do not know how the postSaveHook is related to my save function in the subcontroller. Ok it is invoked, if I understand, just after the save in my model, but than...

nicova...@gmail.com

unread,
May 8, 2021, 12:47:17 PM5/8/21
to Joomla! General Development
I'm searching again, maybe I don't understand postSaveHook related to the Save function.

The idea that I have, and I will try to follow the Joomla framework, that :
  1. I invoke the overriden save function in the subcontroller
  2. From the save function in the subcontroller I need to call the save function in the model
  3. After saving returning from my save function in the model returning to the save function in the controller with the redirect with the right id.
But with invoking postSaveHook() function, step 3 is not needed anymore and the redirection is put in this postSaveHook insteadoff the save function in the subcontroller? (It will not return after saving the records in the save function of the subcontroller) 
Is this the idea how it should work according to the Joomla framework? (I will try to understand the idea of the Joomla framework)

Op vrijdag 7 mei 2021 om 23:19:47 UTC+2 schreef ni...@nicovandekamp.nl:

Nico van de Kamp

unread,
May 11, 2021, 4:26:04 PM5/11/21
to Joomla! General Development
Nobody?

I have overriden the save function in the child-contoller and model. This working fine except when I add an new record. This due that in the redirect in the save function of child-controller is unknown ($data['íd'] = 0) .

How can I solve this on the Joomla way? I thought this should be done by the postSaveHook() function, but how? How is the postSaveHook() related to the save function in the child-controller?

Or am I thinking in the complete wrong direction?

I hope that someone can give me an idea how this should be solved in the Joomla framework.

Nico

Hannes Papenberg

unread,
May 11, 2021, 5:53:45 PM5/11/21
to joomla-de...@googlegroups.com
I have to admit that I'm not sure what you mean and what you are trying
to achieve, but:

The postSaveHook() method is an easy way to do additional work after
saving a record without overriding the save() method of your model. That
would be stuff like deleting a custom cache or similar stuff.
postSaveHook() shouldn't be used to redirect or set the redirect. Both
are supposed to be done in the controller.

Hannes

Am 11.05.2021 um 22:26 schrieb Nico van de Kamp:
> Nobody?
>
> I have overriden the save function in the child-contoller and model.
> This working fine except when I add an new record. This due that in the
> redirect in the save function of child-controller is unknown
> ($data['íd'] = 0) .
>
> How can I solve this on the Joomla way? I thought this should be done by
> the postSaveHook() function, but how? How is the postSaveHook() related
> to the save function in the child-controller?
>
> Or am I thinking in the complete wrong direction?
>
> I hope that someone can give me an idea how this should be solved in the
> Joomla framework.
>
> Nico
>
> On Saturday, May 8, 2021 at 6:47:17 PM UTC+2 nicova...@gmail.com wrote:
>
> I'm searching again, maybe I don't understand postSaveHook related
> to the Save function.
>
> The idea that I have, and I will try to follow the Joomla framework,
> that :
>
> 1. I invoke the overriden save function in the subcontroller
> 2. From the save function in the subcontroller I need to call the
> save function in the model
> 3. After saving returning from my save function in the model
> returning to the save function in the controller with the
> redirect with the right id.
>
> But with invoking postSaveHook() function, step 3 is not needed
> anymore and the redirection is put in this postSaveHook insteadoff
> the save function in the subcontroller? (It will not return after
> saving the records in the save function of the subcontroller)
> *Is this the idea how it should work according to the Joomla
> framework? (I will try to understand the idea of the Joomla framework)*
> --
> 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
> <mailto:joomla-dev-gene...@googlegroups.com>.
> To view this discussion on the web, visit
> https://groups.google.com/d/msgid/joomla-dev-general/f0b2c7ac-e1aa-4d32-a07c-d103d84116d4n%40googlegroups.com
> <https://groups.google.com/d/msgid/joomla-dev-general/f0b2c7ac-e1aa-4d32-a07c-d103d84116d4n%40googlegroups.com?utm_medium=email&utm_source=footer>.

Roger Creagh

unread,
May 12, 2021, 3:42:11 AM5/12/21
to joomla-de...@googlegroups.com
On Tue, 2021-05-11 at 13:26 -0700, Nico van de Kamp wrote:
I have overriden the save function in the child-contoller and model. This working fine except when I add an new record. This due that in the redirect in the save function of child-controller is unknown ($data['íd'] = 0) .

Do you actually need to override the save function in the controller? 
If you do need to  are you still calling the parent function to do the save? Perhaps you should be?

You probably do need to have a save() function in the model, but are you calling the parent save before returning? Check out the HelloWorld tutorial:
https://docs.joomla.org/J3.x:Developing_an_MVC_Component/
...
		$result = parent::save($data);
		return $result;
	}

nicova...@gmail.com

unread,
May 12, 2021, 5:58:30 AM5/12/21
to Joomla! General Development
Hello Roger,

Thanks. 
I think I need to overwrite the save function because I have made a multi-tab detail page and I'm saving also data in related child tables.
I have also created a save function in the model file.

In the save function of the model I save first my data to the parent table with:
if (!(parent::save($data))) {
            return false;
 }

If this succeeds, than I save the data for the child tables. This is all working fine for existing records. And also if it is a new record it is saved as well to the database. So this is working well.

But with creating a new record returning from the model save function tot the save function in the controller the id is unknown, which I understand. 

I thought that the postSaveHook() function, in child controller,  is made for this situation. 
I've been debugging yesterday evening, but it is not 'automatically' called by the framework, after the save function in the model is executed (which I thought by reading on the internet)..

(I'm following the tutorial of Robbie Jackson, but this more or less special..)
(An total other question: how did you add that code block in you're post?, in the old 'Google groups' there was option to add code, but with the new groups, I don't see and/or have that option!???)


Op woensdag 12 mei 2021 om 09:42:11 UTC+2 schreef roger...@googlemail.com:

Roger Creagh

unread,
May 12, 2021, 10:08:36 AM5/12/21
to joomla-de...@googlegroups.com
On Wed, 2021-05-12 at 02:58 -0700, nicova...@gmail.com wrote:
In the save function of the model I save first my data to the parent table with:
if (!(parent::save($data))) {
            return false;
 }

If this succeeds, than I save the data for the child tables. This is all working fine for existing records. And also if it is a new record it is saved as well to the database. So this is working well.

Okay, I have a very similar situation and this is what works for me.
There is no override of a save() function in the Controller - I'm pretty sure you don't need that - all the controller is doing is calling the appropriate view or model depending what's occurring (model-view-controller MVC)

In the model I have this:

	public function save($data) {

//... other stuff before saving that isn't done in the table check() function. 
// eg setting empty dates to NULL as J3 still insists on setting them to zero which is now heavily deprecated in MySql.

		if (parent::save($data)) {
			$pid = $this->getState('person.id');
			//a function that saves the linked table data
			$this->storePersonJobs($pid, $data['joblist']); 
			//... other stuff
			return true;
		}		
		return false;
}

The key thing is that after the save you can get the id from the state for the view.

$this->getState('viewname.id');

 I honestly can't remember if there is something else that sets this
In this case the view is 'person' and at the top of the model is the line

public $typeAlias = 'com_componentname.person';

which might have something to do with it.

Hope this helps
Roger

Roger Creagh

unread,
May 12, 2021, 10:14:00 AM5/12/21
to joomla-de...@googlegroups.com
On Wed, 2021-05-12 at 02:58 -0700, nicova...@gmail.com wrote:
(An total other question: how did you add that code block in you're post?, in the old 'Google groups' there was option to add code, but with the new groups, I don't see and/or have that option!???)

Yeah - that surprised me as normally it doesn't seem to. In this case I copied the code from a webpage so I guess the formatting was picked up as html.

If I copy from eclipse IDE it looses all the formatting - in the previous message I then manually flagged it as preformatted and then coloured the comments green having copied from eclipse to the mail editor.

I tried copying from github.com but that only picked up the  'pre' tag and none of the syntax highlighting

It would be nice to be able to use markdown in emails.

Roger

nicova...@gmail.com

unread,
May 24, 2021, 6:26:48 AM5/24/21
to Joomla! General Development
Hello Hannes and Roger,

What I want, which I think is necessary for my situation, making an override of the save function of an component for the detail page.
I  have created a save function in my child-controller file and in my model file. Saving of a new record it self and updating of a record is working fine, what I mean by this, the data is saved to the database.

@Hannes
The issue what I have: when I create a NEW record from the details page (edit.php), and the new record is saved by save function child-controller --> save function of my model file, when it returns from the the save function of my model file to the save function of my child controller the id of the NEW record is unknown  in my child-controller, it is in $data[íd']= 0!
Than the redirection "$this->setRedirect(JRoute::_("index.php?option=com_component&view=viewName&layout=edit&id=".$data['id'], false), "Records saved");" goes wrong.

This is understandable for me, but I don't know how to solve this within the Joomla framework.
If I read documentation, site's than the postsavehook() should be the key in this, to get the id known of the new record in the save function of the controller. Unfortunately I can't find a simple working solution.

@Roger
From the view direct calling the save function is new to me. I thought that I have always to go via the controller --> model.

I'm now looking in save functions of existing joomla code, and indeed there are some components who have only a (overriden) save function in the model file. But how do I come from the view straight to the model.

What a struggle is this.

Op dinsdag 11 mei 2021 om 23:53:45 UTC+2 schreef Hannes Papenberg:

Roger Creagh

unread,
May 24, 2021, 8:48:43 AM5/24/21
to joomla-de...@googlegroups.com
So just to be clear I understand that your situation is this:

You have an edit form - (incidentally is this on the admin side (backend) or site (frontend)?) for a table

You may be either updating an existing record or creating a new record on the edit form

As well as the main table you are also wanting to update related child tables, and to do this you need to know the id of a newly created record of course. Other data for the child tables may also entered on the edit form.

Is that correct?

The way I do this is by using the subform field type https://docs.joomla.org/Subform_form_field_type for the child table data and then in the model for the edit view I have a save function which calls the parent save function and then gets the id

To get the id of the just saved record, whether new or updated, you get it from the state. 

$this->getState('film.id');

where, "film" is the name of the model/view as set in the class definition. For example

class XbfilmsModelFilm extends JModelAdmin {
public $typeAlias = 'com_xbfilms.film';
//...rest of model code
You can then use this id to call another function, in your model (or elsewhere), to save the child data passing it the main id and the subform data thus in your save function:

if (parent::save($data)) {
$fid = $this->getState('film.id');
$this->storeFilmCast($fid, $data['castlist']);
where 'castlist' is the name of the subform. In this case is collecting the names of an actors and the roles they are playing, 
and   storeFilmCast()  is a function that saves the data in the child table.

No changes in the default controller, no use of postsavehook() - everything done in the model. Very simple. Let Joomla handle it.

Incidentally if you are using a Save As Copy button in the toolbar for your edit page, then the model save() function is also where you can handle adjusting an item alias or other unique required fields before calling parent::save() so as to avoid duplicate errors .

Subform is very flexible and designed for exactly this situation. It can be presented as a div or a table and can include any field types and allow for multiple sub items - eg in the case above the sub form allows entering multiple actors and roles. 

Sometimes if your subform includes multiple rows and includes all the content from the child table (eg if it is a many-to-may link table) it may be easier to delete all the existing content in the child table and replace it with all the content from the subform rather than have to identify whether every row in the subform is an update or insert, but apart from that the subform save function (eg storeFilmCast() ) will be a simple insert query.

Maybe your situation is somehow more complex and you need something different, but this works fine for me.

Roger
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/0a8171c6-3581-421d-bd29-97c7aaae150en%40googlegroups.com.

Roger Creagh

unread,
May 24, 2021, 8:55:37 AM5/24/21
to joomla-de...@googlegroups.com
On Mon, 2021-05-24 at 03:26 -0700, nicova...@gmail.com wrote:
From the view direct calling the save function is new to me. I thought that I have always to go via the controller --> model.

PS Don't you simply use a standard Save button on the edit view toolbar? This does indeed call the controller to get to the model save function (and all the other stuff it needs like the check() function for the table) but it can be pretty invisible to you unless you need to override something.

Nico van de Kamp

unread,
Jun 2, 2021, 8:15:54 AM6/2/21
to Joomla! General Development
Hello Roger,

Thanks for the answer.
I'm further but it is I think due a lack of experience with OO/joomla and on this moment I do not know all the things that 'exist' in joomla and which not.. At least it is not a procedural 3th generation language, I realize.

The controller is not a subclass of the model of course, I know. So something as:
(int) $this->getState('<subcontroller/model-name>.id')
, which is returning in the model the right id,  is not working in the sub-controller!!! Which I understand, because getState() is from the model.

At the sub-controller I had already made an instance of the model in the save function of the sub-controller:
$model = $this->getModel();
if ($model->save($data)) {...}

Instead off using $this I need to use $model like: (int) $model->getState('<subcontrollername>.id'), which is returning the right id of the new added parent record in the sub-controller. So this is an good learning moment of realizing things in OO/Joomla.

But now...
I'm running into next problem.
Initial at saving of the record the $data['id']=0 and after saving is for example id=36, for the new added record.
Than I get this error at the redirection in the save function of the sub-controller:
Error
You are not permitted to use that link to directly access that page (#36).
And it is redirected to the list view!

I think I know why, because 'suddenly' the https request is changed from id= 0 (before saving of the new record) to id=36.
I expect it has something to do with the session...?
But does anybody have an idea how to solve this?
(After saving the records in the save function of the model I'm using this: $this->cleanCache(); But this doesn't help me.)

I hope that someone can help further.
@Roger, give the answers in red on you're questions. I hope that it is good readable

Nico

On Monday, May 24, 2021 at 2:48:43 PM UTC+2 roger...@googlemail.com wrote:
So just to be clear I understand that your situation is this:

You have an edit form - (incidentally is this on the admin side (backend) or site (frontend)?) for a table Yes that's right and it's on the backend.

You may be either updating an existing record or creating a new record on the edit form Yes that's right

As well as the main table you are also wanting to update related child tables, and to do this you need to know the id of a newly created record of course. Other data for the child tables may also entered on the edit form.

Is that correct?
Yes that's right and this is working fine in my model file. I do save the parent record with the child-record in the model file. For child table I'm using subform but this is working as wel. I get the id of the parent table in the model by: (int) $this->getState($this->getName() . '.id')

The way I do this is by using the subform field type https://docs.joomla.org/Subform_form_field_type for the child table data and then in the model for the edit view I have a save function which calls the parent save function and then gets the id

To get the id of the just saved record, whether new or updated, you get it from the state. 

$this->getState('film.id'); Ok, but I was not aware of this, that this is possible/exist in Joomla. Thanks, I'm using it now
 
where, "film" is the name of the model/view as set in the class definition. For example

class XbfilmsModelFilm extends JModelAdmin {
public $typeAlias = 'com_xbfilms.film';
//...rest of model code
You can then use this id to call another function, in your model (or elsewhere), to save the child data passing it the main id and the subform data thus in your save function: Ok, but this is not an issue for me now. Just the redirection at the save funtion of the sub-controller is giving me the issue. But for the child data I'm using
(int) $this->getState($this->getName() . '.id'), which is working.

if (parent::save($data)) {
$fid = $this->getState('film.id');
$this->storeFilmCast($fid, $data['castlist']);
where 'castlist' is the name of the subform. In this case is collecting the names of an actors and the roles they are playing, 
and   storeFilmCast()  is a function that saves the data in the child table.

No changes in the default controller, no use of postsavehook() - everything done in the model. Very simple. Let Joomla handle it. Yes ok, but how do you call from the edit.php with an javascript direct the model. I'm done this like with Ajax:
var getUrl          = "index.php?option=<com_componentname>&task=<controllerName>.checkTitleExist&format=json";

and build the javascript function for another controller:
Joomla.submitbutton = function(task) {
   ...
    Joomla.submitform(task, document.getElementById('adminForm'));
}


Incidentally if you are using a Save As Copy button in the toolbar for your edit page, then the model save() function is also where you can handle adjusting an item alias or other unique required fields before calling parent::save() so as to avoid duplicate errors . This is not an issue at the moment

Subform is very flexible and designed for exactly this situation. It can be presented as a div or a table and can include any field types and allow for multiple sub items - eg in the case above the sub form allows entering multiple actors and roles. 

Sometimes if your subform includes multiple rows and includes all the content from the child table (eg if it is a many-to-may link table) it may be easier to delete all the existing content in the child table and replace it with all the content from the subform rather than have to identify whether every row in the subform is an update or insert, but apart from that the subform save function (eg storeFilmCast() ) will be a simple insert query. I have chosen for the little more complex method, so that I can see what is saved at what time...

Nico van de Kamp

unread,
Jun 3, 2021, 11:10:45 AM6/3/21
to Joomla! General Development
Hello I'm further with fixing the issue:
Error
You are not permitted to use that link to directly access that page (#36). 

The error has to do with checkout/checkin. An tip from Robbie Jackson). First I thought I update the checked_out, checked_out_time fields. But it is Joomla framesork and it goes far more beyound these fields. So I found 'the example' in com_finder/controllers/filter.php.

So I have add the following lines:
            $app = JFactory::getApplication();
            // Set the record data in the session.
            $context = "$this->option.edit.$this->context";
            $recordId = $model->getState($this->context . '.id');
            $this->holdEditId($context, $recordId);
            $app->setUserState($context . '.data', null);
            $this->setRedirect(JRoute::_("index.php?option=com_component&view=viewName&layout=edit&id=".$data['id'], false), "Records saved");

I do not understand everything, but I realize that I think it has to do with the Table class and the formController, FormModel etc. and these classes.

I have not test everything but it looks good now and willl extend for save&new, save&copy like filter.php

It's a struggle but I think I'm getting it managed.

Nico
Reply all
Reply to author
Forward
0 new messages