2

I'm trying to access unordered list elements in php so I can insert them in a database, I need to be able to access them via position but I'm not sure how to do this in PHP. I'm using jQuery so that the list is sortable on the client side. In Javascript it would be accessed with

alert($("#sortable li:first").text() + ' is first ' + $("#sortable   li:eq(1)").text()    + ' is second ' + $("#sortable li:eq(11)").text() + ' is last');

The list I'm using is on http://jsfiddle.net/mMTtc/

I'm simply looking for help as for how to store those list items in a php variable i.e. lets say I wanted the 6th element based on how the user had ordered the list. How would I do this?

Thanks

user1628206
  • 55
  • 2
  • 7
  • if you are saving the way they sort it, why not use something like `data-index`? You could change the value as they reorder using jQuery's `.data()`, store the integer value of it, then when you pull from the DB to create the list on future loads of the page you could `ORDER BY` that item so the list order remains. – PlantTheIdea May 09 '13 at 21:59
  • The way they sort it is I think irrelevant because it would alter the DOM before PHP is processed, I'm not certain. I'm just looking for a way to access an unordered list in PHP element by element for storing each in a variable. – user1628206 May 09 '13 at 22:11
  • @user1628206: If one of the answers helped you solve your problem, please mark it as "accepted", so users facing a similar problem in the future will be able to spot it easily. – gkalpak May 17 '13 at 18:37

3 Answers3

2

Using the following code you can send updates to the PHP backend as the user changes the order of elements in the front-end:

$("#sortable").on("sortupdate", function() {
    var dataArr = [];
    $("#sortable li").each(function(idx, elem) {
        dataArr[idx] = $(elem).html();
    });
    var dataStr = '{"newOrder":' + JSON.stringify(dataArr) + '}';
    $.ajax({
        url: "<url_to_php_file>",
        data: dataStr
    });
    // alert(dataStr);
});

Live example (frontend part): here

You'll have to replace <url_to_php_file> with the path to your PHP file that does the processing of the elements order (i.e. saving them in the DB). The file will be able to access the user-defined order in a normal PHP Array, using json_decode($_POST["newOrder"]), i.e.

...
$newOrder = json_decode($_POST["newOrder"]);
for ($i = 0; $i < count($newOrder); $i++) {
    echo("The item labeled '" . $newOrder[$i] . "' is placed by the user at index " . $i . ".\n";
    /* 1st item: index 0 */
    /* 2st item: index 1 */
    /* ... */
}

Example: You present a sortable list to the user, containing items: item1, item2, item3 (in this order).
The user places item2 before item1, at which point an AJAX call is made passing to the server the array ["item2", "item1", "item3"] (note the order). The above snippet would echo:

The item labeled 'item2' is placed by the user at index 0.
The item labeled 'item1' is placed by the user at index 1.
The item labeled 'item3' is placed by the user at index 2.

(Of course, instead of echoing anything, you would update the value of an index-field in the DB for each item or do something useful.)

gkalpak
  • 47,844
  • 8
  • 105
  • 118
  • How do I access data items once passed over through json? Say trying to echo out the first item in the array after declaring $value = json_decode($_POST["newOrder"]); – user1628206 May 10 '13 at 11:31
  • 1
    @user1628206 I updated my answer with an example of how to access the data on the server-side. Also, note that my proposed solution is just an illustration of the technic and you should customize it according to your specific requirements. E.g. you might not want to make an AJAX call after every reordering (which might be undone later), but rather have the user reorder the items and then click a "Save" button that triggers the AJAX call etc. – gkalpak May 10 '13 at 12:29
1

You can use DomDocument to parse your HTML. This can be done either via a string using loadHTML(), or loading an external HTML file using loadHTMLFile().

This example uses loadHTML():

<?php

  $html = '<html>
    <body> 
    <ul id="sortable">
      <li class="ui-state-default">1</li>
      <li class="ui-state-default">2</li>
      <li class="ui-state-default">3</li>
      <li class="ui-state-default">4</li>
      <li class="ui-state-default">5</li>
      <li class="ui-state-default">6</li>
      <li class="ui-state-default">7</li>
      <li class="ui-state-default">8</li>
      <li class="ui-state-default">9</li>
      <li class="ui-state-default">10</li>
      <li class="ui-state-default">11</li>
      <li class="ui-state-default">12</li>
    </ul>
        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>    
    </body>
    </html>';

$dom = new DomDocument;
$dom->loadHTML($html);

$li = $dom->getElementsByTagName('li');

// Print first item value
echo $li->item(0)->nodeValue;

// Print third item value
echo $li->item(2)->nodeValue;
Manuel Pedrera
  • 5,347
  • 2
  • 30
  • 47
  • I'm specifically for the text values from each list item i.e the number, is that what is returned from nodeValue or is it just the list item itself? – user1628206 May 09 '13 at 22:25
  • 2
    Yes. See [here](http://www.php.net/manual/en/class.domnode.php#domnode.props.nodevalue). – Brad T. May 09 '13 at 22:27
  • Wouldn't this always return the static value of the original html rather than what's currently in the DOM? i.e. regardless of what the user does to the list, item(0) will always be 1? – user1628206 May 10 '13 at 10:08
  • 1
    Yes, but that's inherit of a server side language, as it is not able to see HTML content after it has been rendered. If you want that to be dynamic, you should be using either pure JavaScript or send an AJAX request to your back-end. Regarding to your particular case, you should send the variable **previously ordered in JavaScript** to PHP. – Manuel Pedrera May 10 '13 at 11:27
1

Here's what I'd do, and it's certainly not the cleanest way, but it should work.

This assumes you're working with your own pages, and not the scenario where you're getting the page html via http request to some external site (e.g. via CURL) and needing to parse it. DOMDocument serves just fine for the latter case. This solution is for the former, as I'm assuming that since you're working with javascript on the client-side of it, it's probably your own page (unless you're injecting that javascript into the page after it's loaded).

First of all, inside each list item, I'd include a server-side accessible input tag. It will serve to keep track of the position and value, and pass it to the server-side script on form submission.

<form method="POST">
  <ul id="sortable">
    <li class="ui-state-default">1
      <input id="the_list_item_1" name="the_list_item[]" type="text" value="1_0" style="display: none;">
    </li>
    ...
  </ul>
</form>

The value is the item's actual value (the example had them ranged 1 - 12) and it's position separated by an underscore (value + "_" + position);

The list needs to be inside a form variable if you only need to submit the list to the server for processing when the user's done. However, if you intend to only use Ajax to get that data to the server, this solution isn't really necessary (as you'd simply just use jquery to get each position and value pair and send them directly in your ajax call).

You'll need to handle updating these input tags as the user drags items and changes the ordering of the list. See here if you need to know how to work with the sortable events. Perhaps, on update, for each list item call this function with the new position:

function update_pos(value,pos)
{
  $("#the_list_item_"+value).val(value+"_"+pos);
}

So on form submit, we're now on the PHP side.

$list_items = $_POST["the_list_item"]; // This is basically an array of all the list_items, thanks to naming all the list items with "the_list_item[]",  note the empty subscript (square braces).

$ordered_list_items = array();  // Let's push them into an associative array.

foreach($list_items as $li)
{
  $li_split = explode("_",$li);
  if(count($li_split) <= 0)
    continue;  // maybe you'd want to handle this situation differently, it really shouldn't happen at all though.  Here, I'm just ignoring nonsensical values.
  $item_id = $li_split[0];
  $pos = $li_split[1];
  $ordered_list_items[$item_id] = $pos;
}

// Then later you can shoot through this list and do whatever with them.
foreach($ordered_list_items as $item_id => $pos)
{
   // postgres perhaps. Insert if not already there, update regardless.
   pg_query("insert into the_list_item (item_id,position) select '$item_id','$pos' where '$item_id' not in (select item_id from the_list_item where '$item_id' = item_id limit 1));
   pg_query("update the_list_item set position = '$pos' where item_id = '$item_id'");
}

Of course, all that said, depending on your needs you may need to be reloading this data onto the page. So looping through your db results (perhaps, for that user), you'd output each list_item into place.

$list_items = pg_fetch_all(pg_query($sql)); // $sql needs to be the query to get the results. Probably should order by position ascending.

$lic = count($list_items);
?>
<html> and stuff

<form method="POST">
  <ul id="sortable">
<?php
for($i = 0; $i < $lic; $i++)
{
  $li = $list_items[$i];
  echo "<li class=\"ui-state-default\">".$li["item_id"]."<input id=\"the_list_item_".$li["item_id"]."\" name=\"the_list_item[]\" type=\"text\" value=\"".$li["item_id"]."_".$li["position"]."\" style=\"display: none;\"></li>";
}
?>
</ul>
</form>
Community
  • 1
  • 1
Brad T.
  • 252
  • 4
  • 17