#1
  1. No Profile Picture
    Super Moderator
    Devshed Intermediate (1500 - 1999 posts)

    Join Date
    Jun 2009
    Location
    Hartford, WI
    Posts
    1,531
    Rep Power
    111

    Returning PHP/JS via AJAX


    Well, as long as I'm returning one or the other straight, things seemed fine, but when calling an action that needs to embed something in a page, the action fails. Overall, it seems the way I am trying to embed the HTML on the PHP side before returning the javascript back to AJAX.

    Initial JS action: (Worked)
    Code:
    } else if(page == 'invoice' && action == 'select') {
      this.html(data[0]);
      this.append($('<input />', { type: 'hidden', value: values }));
    Current Version: (Simplified to rule out many if/else conditions)
    Code:
    eval(data[0]);
    Initial PHP: (Worked)
    PHP Code:
    case 'select':
      
    $select select_account(key($_POST));
      echo 
    json_encode(array($selectNULL));
      break; 
    An Attempt:
    PHP Code:
    case 'select':
      
    $select select_account(key($_POST));
      
    $return "this.html('" $select "'); this.append($('<input />', { type: 'hidden', value: values }));";
      echo 
    json_encode(array($returnNULL));
      break; 
    The overall intent is to move all the JS/jQuery actions from the numerous if/else conditions over to the PHP file, and just have it return that action as well with its initial return.
    Last edited by Triple_Nothing; December 16th, 2017 at 10:33 AM.
    He who knows not that he knows not is a fool, ignore him. He who knows that he knows not is ignorant, teach him. He who knows not that he knows is asleep, awaken him. He who knows that he knows is a leader, follow him.
  2. #2
  3. Maddening Moderator
    Devshed Supreme Being (6500+ posts)

    Join Date
    Mar 2007
    Location
    Washington, USA
    Posts
    16,495
    Rep Power
    9645
    You can't safely put $select into a Javascript string. Use JSON. In fact you can use the JSON array you're already working with.
    PHP Code:
    case 'select'
      
    $select select_account(key($_POST)); 
      
    $return "(function($0, select) { this.html(select); this.append($('<input />', { type: 'hidden', value: values })); })";
      echo 
    json_encode(array($return$select));
      break; 
    Code:
    eval(data[0]).apply(this, data);
    data[0] is a function because it needs access to the data variable but shouldn't have to depend on the variable actually being named "data". So that's passed as arguments. Same sort of deal for whatever the "this" object should be.

    (I'm fine with the general approach above but I would spend more time refactoring it into something nicer. Like, having to put the whole "function(...) { }" stuff in PHP is annoying.)

    And "values" looks undefined. Was it set earlier? I don't remember.
    Last edited by requinix; December 16th, 2017 at 07:00 PM.
  4. #3
  5. No Profile Picture
    Super Moderator
    Devshed Intermediate (1500 - 1999 posts)

    Join Date
    Jun 2009
    Location
    Hartford, WI
    Posts
    1,531
    Rep Power
    111
    Most of this is intending to run more direct actions such as reloading, redirecting, or running a preloaded function. This is the only case with an attempt to embed something, which is to be a select/drop-down. Below is the beginning of my JavaScript file. You can see my initial coding commented out. As the project was to continue, there would end up being a whole handful of conditions in the if/else if tree. This is why I just wanted PHP to return the basic string to execute, since it is not to really return more than such, other than this instance.
    Code:
    $.fn.extend({
    
      actionCall: function(page, action, values) {
        $.ajax({
          url     : './inc/ajax.php?page=' + page + '&action=' + action,
          context : this,
          type    : 'post',
          dataType: 'json',
          data    : values,
          success : function(data) {
            alert(data[1]);
            eval(data[0]);
    /*        if((page == 'invoice' && action == 'add') || (page == 'invoice' && action == 'edit' && data[3] == 1) || (page == 'account' && action == 'add')) {
              location.reload(true);
            } else if(page == 'invoice' && action == 'select') {
              this.html(data[0]);
              this.append($('<input />', { type: 'hidden', value: values }));
            } else if(page == 'invoice') {
              $().updateTotals(data);
            } else if(page == 'login' && data[0] == 'success') {
              window.location.href='?page=invoice';
            }*/
          },
          error   : function(xhr, err) {
            alert('Request Status: ' + xhr.status + ' Status Text: ' + xhr.statusText + ' ' + xhr.responseText);     
          }
        });    
      },
    
      formatCurrency: function(n) {
        return '$' + n.toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g, '$1,');
      },
    
      updateTotals: function(values) {
        $.ajax({
          url     : './inc/ajax.php?page=' + page + '&action=totals',
          type    : 'post',
          dataType: 'json',
          data    : 'values=' + values,
          success : function(data) {
            $('#grand_' + data[0]).html($().formatCurrency(data[1]));
            $('#remain_' + data[0]).html($().formatCurrency(data[2]));
            $('#adj_' + data[0]).html($().formatCurrency(data[3]));
          },
          error   : function(xhr, err) {
            alert('Request Status: ' + xhr.status + ' Status Text: ' + xhr.statusText + ' ' + xhr.responseText);     
          }
        });    
      }
    
    });
    Here is a more broad example of where this would now land. As far as the returned array, so far, its 2 items are array(JavaScript, Alert), so the JavaScript would land in the eval(), and the Alert would make the initial alert().
    PHP Code:
    switch($_GET['page']) {
      case 
    'account':
        switch(
    $_GET['action']) {
          case 
    'add':
            
    $_POST['active'] = isset($_POST['active']) ? 0;
            
    $stmt $db->prepare('INSERT INTO `account` (`name`, `active`) VALUES (?, ?);');
            
    $stmt->execute(array($_POST['name'], $_POST['active']));
            
    $link->closeDB();
            echo 
    json_encode(array('location.reload(true);''Account Successfully added!'));
            break; 
    I think the trick to this may be the fact this is the only time something is embedded, while every other time the returned code needs nothing more than to be evaluated and executed...

    As far as the values, the below is a call to actionCall() when clicking an 'Add' button, and the values are from a form.
    Code:
    $(this).actionCall(page, 'add', $(this).closest('form').serialize());
    He who knows not that he knows not is a fool, ignore him. He who knows that he knows not is ignorant, teach him. He who knows not that he knows is asleep, awaken him. He who knows that he knows is a leader, follow him.
  6. #4
  7. Maddening Moderator
    Devshed Supreme Being (6500+ posts)

    Join Date
    Mar 2007
    Location
    Washington, USA
    Posts
    16,495
    Rep Power
    9645
    Hmm. I get why you want to do this but I'm not a fan of the idea.

    By moving that code into PHP you do reduce the amount of Javascript, but you increase the amount of PHP. There isn't much of a gain. In fact I say it's a loss.

    The PHP code isn't naturally aware of the page structure. Like, you can't scroll through the .php file and see "oh, right, this element is named 'foo'" and then write that into your code. You have the definitions off in the HTML and Javascript but an important thing that needs that information is somewhere else. When updating the HTML or Javascript you have to remember to visit the PHP and make sure that you aren't accidentally breaking it.

    Worse (IMO) than that is how you have to come up with this awkward scheme of passing Javascript code through an AJAX request. It's one of those "if the answer is eval then you're asking the wrong question" sort of issues.

    There wasn't much Javascript code to begin with so I would just keep it. The same amount of code is required in one place or another, so why not put it in the place that actually uses it. You could make a bit of the process more abstract - the AJAX could say "this requires a page refresh" or "display this message" in a more generic way,
    PHP Code:
    json_encode(array("refresh" => true"message" => "Account Successfully added!")) 
    and even the DOM replacement is a fairly generic action
    PHP Code:
    json_encode(array("content" => $select)) 
    but stuff like
    Code:
    this.append($('<input />', { type: 'hidden', value: values }));
    is very specialized.

    This is all an interesting thought experiment but I would stick with the earlier code. (Or something similar to it.) Yes, it's more complex, but it is easier to understand and maintain and uses far less coupling.
  8. #5
  9. No Profile Picture
    Super Moderator
    Devshed Intermediate (1500 - 1999 posts)

    Join Date
    Jun 2009
    Location
    Hartford, WI
    Posts
    1,531
    Rep Power
    111
    I'm not sure why I never thought to name my indexes in my returned array. That would actually make a few things easier. And even defining a returned value of an action would help keep all the actual JS in the JavaScript file, and just be told which to execute... Thanks for the suggestion!

    Edit: Just an update... I decided to keep my initial if/else tree, but instead of the lengthy conditions, I decided to just return the action from the AJAX call as a single item. Keeps the nice tree, and makes the conditions ever so simple. Sometimes I think we just start to overthink the simplest of things... ^_^
    Code:
    success : function(data) {
      if(date[0] == 'reload') {
        location.reload(true);
      } else if(data[0] == 'populateSelect') {
        this.html(data[0]);
        this.append($('<input />', { type: 'hidden', value: values }));
      } else if(data[0] == 'updateTotals') {
        $().updateTotals(data);
      } else if(data[0] == 'redirect') {
        window.location.href='?page=invoice';
      }
    },
    Last edited by Triple_Nothing; December 28th, 2017 at 01:17 PM.
    He who knows not that he knows not is a fool, ignore him. He who knows that he knows not is ignorant, teach him. He who knows not that he knows is asleep, awaken him. He who knows that he knows is a leader, follow him.

IMN logo majestic logo threadwatch logo seochat tools logo