When developing extensions I have always followed this path when I had a page listing stuff (for example books) and an action link to delete the book:

  1. Create the page and add a link to the form CRM_Utils_System::url(/ "civicrm/myext/form/myform", "action=delete&reset=1", TRUE)
  2. In the form, pick up the delete action in the preProcess method like so:

    if ($this->_action == CRM_Core_ACTION::DELETE) { //do my stuff $url = CRM_Utils_System::url("civicrm/myext/page/mypage", "reset=1", TRUE); CRM_Utils_System::redirect($url); }

I understood that I should not use the CRM_Util_System::redirect() but should use the CRM_Core_Session::singleton->pushUserContext($url) instead. But what is then the recommended way to ignore the rest of my form processing so that the controller returns to the userContext (being my page)?

1 Answer 1


The way I did it

In my most recent extension with a fair amount of CRUD happening via the UI, I used a different form at a specific URL (civicrm/sms/conversation/confirmdelete) for deleting. This gave the user the chance to see what they were deleting and confirm it.

I then set the context in preProcess of that form, deleted the entity in postProcess, and let the controller handle redirection.

$this->context = CRM_Utils_System::url('civicrm/sms/conversation/view', "id={$this->conversationId}");
$this->controller->_destination = $this->context;

IIRC, it was setting $this->controller->_destination = $this->context; that enabled the magic to happen in postProcess, and mean that I didn't have to add any calls to CRM_Utils_System::redirect().

FWIW I generalised this form to allow it to delete any of the entities that my extension was creating.

See the form here: https://github.com/3sd/civicrm-sms-conversation/blob/master/CRM/SmsConversation/Form/ConfirmDelete.php

Is this the CiviCRM way?

Since I wasn't sure how 'CiviCRM' this method was, I just had a quick look at how we handle deleting contacts and membership types.


For contacts, the URLs look as follows: civicrm/contact/view/delete?&reset=1&delete=1&cid=2 which maps to CRM_Contact_Form_Task_Delete, i.e. a special form, just for deleting contacts, which (although I haven't looked in details) seems to be a bit like what I did.

Membership types

civicrm/admin/member/membershipType/add?action=delete&id=1 maps to CRM_Member_Form_MembershipType. Looking at the class and its associated template

preProcess sets an appropriate context (this is where you would put your URL to your 'page listing stuff', I think).

$url = CRM_Utils_System::url('civicrm/admin/member/membershipType', 'reset=1');

It also assigns the action to the template

$this->assign('action', $this->_action);

buildForm checks to see if this is a delete action, and if so, just returns, since it doesn't need to add any form elements

if ($this->_action & CRM_Core_Action::DELETE) {

postProcess does a similar check

public function postProcess() {
  if ($this->_action & CRM_Core_Action::DELETE) {
    try {

And the template has a similar if else

{if $action eq 8}
  <div class="messages status no-popup">
    {ts}WARNING: Deleting this option will result in the loss of all membership records of this type.{/ts} {ts}This may mean the loss of a substantial amount of data, and the action cannot be undone.{/ts} {ts}Do you want to continue?{/ts}
  <div> {include file="CRM/common/formButtons.tpl"}</div>

So what is the right way?

No comment :) You decide.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.