I’ve created a very simple comments system whereby comments are set us a channel, with each (comment )entry related to a main entry from another section. Works fine. I’ve set up editing and deleting the comment via Ajax, but I’m stuck on saving it. I’ve got the form submitting successfully via ajax, but I want to be able to display it immediately after it is saved/posted. The successful response I get gives me the author username, entry id, date etc, but not the actual comment field. I also need to display additional user details.

I’m not quite sure on best way to tackle this. Just looking for a pointer in the right direction. Cheers!

  • The best - and usually most common - way to achieve this is by rendering the needed html in your controller where you prepare your database entry. If you don't use a custom controller, just give me your action url (the route you have in your ajax) and I'll create a controller/plugin for you that does the job tomorrow. Btw were you able to understand the last code I gave you? Oct 25, 2017 at 19:14
  • Hi Robin, thanks. I don't use a custom controller at all. It just going to standard entries/saveEntry (i think!). I do for the most part understand the other code, it's linked to this :) I wanted to get this saving part wrapped up before testing the other out. Thanks!
    – shorn
    Oct 25, 2017 at 19:18

2 Answers 2


Use that as your controller, this is the craft default controller actionSaveEntry that you use right now with a few changes. You can access it in Javascript when you use action: "pluginHandle/entry/saveEntry".

namespace Craft;

class EntryController extends BaseController

     * @var    bool|array Allows anonymous access to this controller's actions.
     * @access protected
    protected $allowAnonymous = array('actionSaveEntry');

     * Saves an entry.
     * @return null
    public function actionSaveEntry()
        // craft default things.. this is actually the exact same function
        // like you call at the moment, it does the same..
        // just go to the part where I have the !Important! note

        $entry = $this->_getEntryModel();

        // Permission enforcement
        $userSessionService = craft()->userSession;
        $currentUser = $userSessionService->getUser();

        if ($entry->id) {
            // Is this another user's entry (and it's not a Single)?
            if (
                $entry->authorId != $currentUser->id &&
                $entry->getSection()->type != SectionType::Single
            ) {
                if ($entry->enabled) {
                    // Make sure they have permission to make live changes to those
                    $userSessionService->requirePermission('publishPeerEntries:' . $entry->sectionId);

        // Populate the entry with post data

        // Even more permission enforcement
        if ($entry->enabled) {
            if ($entry->id) {
                $userSessionService->requirePermission('publishEntries:' . $entry->sectionId);
            } else if (!$currentUser->can('publishEntries:' . $entry->sectionId)) {
                $entry->enabled = false;

        // Save the entry (finally!)
        // !Important! so this is where your action starts
        if (craft()->entries->saveEntry($entry)) {
            $return['success'] = true;
            $return['id'] = $entry->id;
            $return['title'] = $entry->title;

            // after saving the entry you can render your template
            // the documentation can be found here https://craftcms.com/docs/plugins/templates
            // just read this site and you'll know what to do.. 
            // this allows you to render your "comments" template like when you 
            // visit the page the first initially
            // you'll just do $("#idOfYourCommentsContainer").append(ajaxResponse['html']);
            // and thats it^^ 
            // in the template you have access to the variables $entry => the new entry
            // and the user currently.. if you need something else, feel free to ask
            $return['html'] = $this->renderTemplate('/path/to/template', array(
                'entry' => $entry,
                'user' => $entry->getAuthor()

            $return['authorUsername'] = $entry->getAuthor()->username;
            $return['dateCreated'] = DateTimeHelper::toIso8601($entry->dateCreated);
            $return['dateUpdated'] = DateTimeHelper::toIso8601($entry->dateUpdated);
            $return['postDate'] = ($entry->postDate ? DateTimeHelper::toIso8601($entry->postDate) : null);

        } else {
            if (craft()->request->isAjaxRequest()) {
                    'errors' => $entry->getErrors(),
                    'success' => false

If there is an error you get the array with all "mistakes" and error messages. For example if you define the field "headline" as required and the user didn't insert something in the "headline" field your response will be

reponse = [
    "error" => [
        'headline'  => [
            'The Headline field is required //or something like that...'

You could select the headline field by name or class with javascript and append the error message as well

  • Robin this looks great! And is making (some!) sense, I'm going to have a go at getting this in this evening, will report back shortly.
    – shorn
    Oct 25, 2017 at 20:29
  • Ahh I just noticed.. You'll have to copy the _getEntryModel() and the _populateEntryModel() from craft/app/controllers/entrieController as well I forgot that. Craft is (mostly) relativ logical and fun to work with (as soon as you understand it). The most common way to go is reading the craft code how they did something and do the same. If you want, I can add you on Skype and answer more of your questions that don't belong here Oct 25, 2017 at 20:36
  • Been working through this. No luck as of yet, getting 500 error, but then saw comment to include the other two functions. Have done that but still getting a 500 error syntax error, unexpected '$this' (T_VARIABLE), expecting function (T_FUNCTION) - having a look now to see if I can spot anything obvious.
    – shorn
    Oct 25, 2017 at 22:44
  • The one before was bad copy and pasting. I think! Current error is on ...behaviors do not have a method or closure named enforceEditEntryPermissions
    – shorn
    Oct 25, 2017 at 22:53
  • OK, so copied the enforceEditEntryPermissions into it as well and it seems to be working. Haven't fleshed out the template yet, but I'm logging the test div from it! :)
    – shorn
    Oct 25, 2017 at 22:58

What you could do is to use the entries.onSaveEntry to override the ajax response of the EntriesController for your specific entry section.

The actionSaveEntry from the EntriesController save the entry and immediately send back an ajax response so it is pretty harmless to do that. And if there is an error during the saving your code won't be called so Craft will follow its normal flow.

craft()->on('entries.onSaveEntry', function (Event $event) {
    $entry = $event->params['entry'];
    $section = $entry->section->handle;

    if ($section == 'Your specific section') {

       if (craft()->request->isAjaxRequest() && !craft()->request->isCpRequest()) {
          $return['success']   = true;
          $return['id']        = $entry->id;
          $return['title']     = $entry->title;
          $return['comment']     = $entry->comment; // whatever you want

          $return['authorUsername']      = $entry->getAuthor()->username;
          $return['dateCreated'] = DateTimeHelper::toIso8601($entry->dateCreated);
          $return['dateUpdated'] = DateTimeHelper::toIso8601($entry->dateUpdated);
          $return['postDate']    = ($entry->postDate ? DateTimeHelper::toIso8601($entry->postDate) : null);


Hope this helps

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.