How to reorder nested elements in PHP? - php

I have a list of nested items (categories) I would like to reorganize, by ordering and nesting depth. I will try to explain:
When I submit a form with jQuery by clicking on blue button SAVE, it process a function inside models/categories_model.php:
public function organize($items){
if( count($items)){
foreach($items as $order => $item){
if($item['item_id'] != ''){
echo '<pre>'.$order.'</pre>';
$data = array(
'pid' => (int) $item['parent_id'],
'ordering' => $order
);
$this->db
->set($data)
->where($this->_primary_key, $item['item_id'])
->update($this->_table_name);
}
}
}
}
It saves that list like this:
1
2
3
4
5
6
7
8
9
10
Instead of saving it like this:
1
1
2
3
4
2
1
2
3
1
2
1
2
3
3
Instead of getting $order value one by one, it should take into the account it's nested items and start counting the $order from 1, and not continuing a counting from it's parent item.
So when I save it, I want them to be ordered like this, but don't know how to do it:
Someone know how to make it work with nested items pls?

If method recieves items in correct order, this should work:
public function organize($items)
{
if (!empty($items)) {
$orderByPid = array();
foreach ($items as $item) {
if($item['item_id'] != '') {
if (!isset($orderByPid[$item['parent_id']])) {
$orderByPid[$item['parent_id']] = 0;
}
$data = array(
'pid' => (int) $item['parent_id'],
'ordering' => ++$orderByPid[$item['parent_id']]
);
$this->db
->set($data)
->where($this->_primary_key, $item['item_id'])
->update($this->_table_name);
}
}
}
}

Related

Get values from in_array statement

I have an array filled with the result of a query which is as follows:
$ticket[] = array(
'ticket_id' => $row->ticket_id,
'user_id' => $row->user_id,
'raffle_ticket_num' => $row->raffle_ticket_num
);
Now, in a while loop, I check in all arrays inside $tickets if my variable $number equals 'raffle_ticket_num'. I do this with the following code (from #Fuzzy Tree):
$ticket_num=1;
while ($ticket_num <= $total_slots){
foreach($ticket as $test) {
if($test['raffle_ticket_num'] == $ticket_num) {
echo $ticket_num.' claimed by '.$test['user_id'];
}
else{
echo $ticket_num;
}
}
$ticket_num++;
}
The problem that I'm having now is that because I'm using a foreach loop, if more than one results are found, it causes it to echo each result as much as there are rows in $ticket[]... So with 2 rows it echos everything 2 times (because of the foreach). Does anyone know a solution or alternatives for this?
You can see it live here: http://tinyurl.com/hxbhx7y .The bold numbers are slot 21 and 38 which are taken and showing the user_id (1). But as you can see it shows each number 2 times (because foreach has 2 results)
EDIT: Post updated with #Fuzzy Tree's answer
Version 1
foreach see answer by #FuzzyTree
Version 2
array_filter
$number = 1;
$winners = array_filter($tickets, function ($ticket) use ($number) {
return $ticket['raffle_ticket_num'] == $number;
});
// $winners now has all winning tickets.
var_dump($winners);
// Bonus pick a random winner
shuffle($winners);
var_dump(current($winners));
So best and easiest way to do it prepare your tickets array outside of raffles loop.
Solution
<?php
$tickets[] = array(
'ticket_id' => 1,
'user_id' => 2,
'raffle_ticket_num' => 15
);
$tickets[] = array(
'ticket_id' => 2,
'user_id' => 2,
'raffle_ticket_num' => 25
);
$tickets[] = array(
'ticket_id' => 3,
'user_id' => 1,
'raffle_ticket_num' => 21
);
$raffles = range(1, 50);
// Preparing tickets array. Now when I think Flattened is not the best word to describe it :)
$ticketsFlattened = array();
foreach ($tickets as $ticket) {
$ticketsFlattened[$ticket['raffle_ticket_num']] = $ticket;
}
// Could be while if you want
foreach ($raffles as $number) {
if (array_key_exists($number, $ticketsFlattened)) {
echo sprintf(
"Ticket %s is clamed by %s, ticket id %s %s",
$number,
$ticketsFlattened[$number]['user_id'],
$ticketsFlattened[$number]['ticket_id'],
PHP_EOL
);
} else {
echo sprintf("Ticket %s unclaimed %s", $number, PHP_EOL);
}
}
If you need to print the matching user_id and ticket_id then use a foreach to check each ticket one by one and when you see a ticket with the matching #, you can print the other details
foreach($tickets as $ticket) {
if($ticket['raffle_ticket_num'] == $number) {
print 'In array';
print $ticket['user_id'];
print $ticket['ticket_id'];
//break; //uncomment if you just want one
}
}

Associative Array - duplicated key values

I am doing a simple shopping application were user may choose books from the store based on the title, price, quantity etc. At the moment all details of the books are kept in an associative array which looks like this:
$menu = array(
array ('id' => '1', 'catagory' => 'newbooks','title' => 'Alex','price' => 4.95, 'desc' => 'bbbb'),
array('id' => '2','catagory' => 'newbooks','title' => 'David ','price' => 5.95, 'desc' => 'sss'),);
}
What I am trying to achieve is merge or remove duplicated entries of book id's also each book title will be printed in one single row containing 'title', 'id', 'price'
for example:
Alex qty:2 price 4.95
David qty:2 price 5.95
and so on...
And this is my cart file:
$buy = array();
$qty = 0;
$total = 0;
foreach ($_SESSION['buy'] as $id) {
foreach ($menu as $book ) {
if ($book['id'] == $id) {
$buy[] = $book;
$total += $book['price'];
}
}
}
}
if (count($buy) > 0)
if (count($book) > 0)
{
echo"<table>";
echo"<tr><th>Item Description</th><th>Quantity</th><th>Price</th></tr>";
foreach ($buy as $book){
$f_price = sprintf("%0.2f", $book["price"]);
echo"<tr><td>" .$book["title"] ."</td>";
echo"<td>" .$qty."</td><";
echo"<td>" .$f_price."</td></tr>";
}
echo"<tr><td>Total</td><td>".$total."</td></tr>";
echo"</table>";
}
I will be extremely grateful for any help or advice cause I spent long long time to resolve this problem trying use unique functions, for statements foreach loop but in vain I am still novice also please try to be a little tolerant . Cheers!
Untested but this should do what you want using array_count_values to obtain the quantities you were looking for along with a few changes to make the script more efficient and easier to manage.
<?php
//the ids of the items the user wants to buy
$ids=$_SESSION['buy'];
//Your database of items
$menu = array(
array ('id' => '1', 'catagory' => 'newbooks','title' => 'Alex','price' => 4.95, 'desc' => 'bbbb'),
array('id' => '2','catagory' => 'newbooks','title' => 'David ','price' => 5.95, 'desc' => 'sss')
);
//Create a reverse look-up for the database
$lookUp=array();
foreach($menu as $i=>$v)$lookUp[$v['id']]=$menu[$i];
//populate the cart with items, their quantities, and a total
$cart = array();
$total = 0;
foreach (array_count_values($ids) as $id=>$quantity)
{
if (!array_key_exists($id, $lookUp))continue;
$item=$lookUp[$id];
$itemTotal=$item['price']*$quantity;
$total+=$itemTotal;
$cart[]=array_merge($item,array('quantity'=>$quantity,'itemTotal'=>$itemTotal));
}
if(count($cart) > 0)
{
echo '<table>';
echo '<tr><th>Item Description</th><th>Quantity</th><th>Price</th><th>Item Total</th></tr>';
foreach ($cart as $item)
{
echo sprintf('<tr><td>%s</td><td>%s</td><td>%0.2f</td><td>%0.2f</td></tr>',
$item["title"],$item['quantity'],$item['price'],$item['itemTotal']);
}
echo '<tr><td colspan="3" style="text-align:center;">Total</td><td>'.$total.'</td></tr>';
echo '</table>';
}
If it's just based off the id, when you build the array, store the book id as the key in the larger array, so your end array would be:
$books = array(
1=> array ('id' => '1', [...]),
2=> array ('id' => '2', [...])
);
You could also add some code that checks when you try to assign a value to that array to see if the key is already set.
if(!isset($books[$book_id])){
//Add to the array
}else{
//Maybe display an error?
}
PS: Category only has one a in it.
It's a pretty cumbersome way to make a stock overview of the books in your collection, but that aside :)
If i was to work with an array like that, (and i would never do that :)) i would first select all unique keys, and then go through them one by one, counting the number of times it occur in the $menu array.
As follows:
$unique_books = array_unique($menu);
foreach($unique_books as $book){
$qty = array_keys($menu, $book["id"])
$book["qty"] = $qty;
$stock = $book;
}
As you can see, now $stock will be the unique array, with a qty added to it, containing the number of times, the id was seen in $menu.
This is just a fast code block, and should be testet, but the theory works i think. As i mentioned before, the way you'r making this in the first place, is not very smart, and you should really reconsider the whole thing imo :)
If you don't wanna duplicates simple use the book_id as the key of buy array
Example:
$menu = array(
array('id' => '1', 'catagory' => 'newbooks','title' => 'Alex','price' => 4.95, 'desc' => 'bbbb'),
array('id' => '2','catagory' => 'newbooks','title' => 'David ','price' => 5.95, 'desc' => 'sss')
);
$session=array(1,2,2,2,1);
$buy = array();
foreach ($session as $id) {
foreach ($menu as $book) {
if ($book['id'] == $id) {
if(isset($buy[$id])){
$buy[$id]['qty']++; // increase quantity
$buy[$id]['total'] += $book['price']; // total price only for this id
$buy['total'] += $book['price']; // All ids price total
}else{
$buy[$id] = $book; // assign new id to the buy array
$buy[$id]['qty'] = 1; // quantity reset to 1
$buy[$id]['total'] = $book['price']; // id price
$buy['total'] += $book['price']; // total price
}
}
}
}
print_r($buy);
You can test it here!
http://codepad.org/wH2WskLG
Instead of merging duplicates you should add book id and qty as array row to session.
On session start..
if (!isset($_SESSION['buy']))
$_SESSION['buy'] = array();
When adding new..
$new_book = $added_book; // just for example
$qty = $added_qty; // Added qty
// Check if book already exists in shopping cart and update qty
if (array_key_exists($new_book['id'], $_SESSION['buy'])) {
$_SESSION['buy'][$new_book['id']] += $qty;
} else {
$_SESSION['buy'][$new_book['id']] = $qty;
}
Then on shopping cart view just loop thru your items..
foreach($menu as $book ) {
if (array_key_exists($book['id'], $_SESSION['buy'])) {
// User bought this book..
$qty = $_SESSION['buy'][$book['id']];
}
}

How can I do something like this - loop thru an indefinite set of numbers

I'm trying to loop tru a set of numbers based on the initial number. Tried, but cant see a good way to achieve this. The thing goes inside a while loop.
<?php
$this = 1;
//If 1 then 1,4,7
//If 2 then 3
//If 3 then 10
while ( //mySql while loop ) {
if ( $this == 1 ) {
call example() //thrice for 1, 4, 7
}
}
function example($a) {
echo $a+10;
}
?>
Here, based on what $this is, I need to call function example. So if $this = 1, I need to call example thrice - $a value 1, 4, 7. If $this = 2 I need to call it once, value 3.
What would be a good way to achieve this?
Try this:
<?php
$this = 1;
$groups = array(
1 => array(1,4,7),
2 => array(3),
3 => array(10)
);
foreach($groups[$this] as $value)
example($value);
?>
You could use an associative array like so:
vals = array(1 => array(1, 4, 7), 2 => array(3), etc);

Zend framework foreach loop stop ate the first iteration

I'm actually working on ZF. I have a category table with which, I want to create a tree in order to get display the data as below :
Category
--Sub cat 1
--Sub cat 2
----Su sub cat 1
Another Category
--Sub cat 1
//...etc...
I'm using the fetchAll method to get all my data. Everyting works fine. But then I'm now trying to create my tree into a double foreach loop as below :
$tree = array();
foreach($data as $parent){
$tree[$parent->name] = array();
foreach($data as $child){
if($child->parent_id == $parent->id){
$tree[$parent->name][] = $child->name;
}
}
}
The problem is that the loop stop after the main loop first iteration so I'm just getting the first parent and it's sub category but it does not continue to the second parent.
My database table as the following fields :
id, name, parent_id
Any idea?
EDIT
Thanks to you Thibault, it did work using the good old for loop :
for($i=0;$i<count($data);$i++){
$tree[$data[$i]->name] = array();
for($j=0;$j<count($data);$j++){
if($data[$j]->parent_id == $data[$i]->id){
$tree[$data[$i]->name][] = $data[$j]->name;
}
}
}
You may have a conflict between the cursor of both $data variables.
You should use a copy of $data for the second foreach loop.
Or use for loops with $i and $j index, and call them via $data[$i] and $data[$j] to access the array, so the loops don't get messed up.
EDIT
Happy i could help, but after some research, i created this piece of code :
<?
class o {
public $id;
public $name;
public $parent_id;
function __construct($_id,$_name,$_parent) {
$this->id = $_id;
$this->name = $_name;
$this->parent_id = $_parent;
}
}
$data = array(
new o(1,'toto',0),
new o(2,'truc',1),
new o(3,'machin',1),
new o(4,'bidule',2),
new o(5,'titi',3),
new o(6,'tutu',3),
);
$tree = array();
foreach($data as $parent){
$tree[$parent->name] = array();
foreach($data as $child){
if($child->parent_id == $parent->id){
$tree[$parent->name][] = $child->name;
}
}
}
print_r($tree);
And your code works just fine :
(something must be wrong somewhere else ...)
Array
(
[toto] => Array
(
[0] => truc
[1] => machin
)
[truc] => Array
(
[0] => bidule
)
[machin] => Array
(
[0] => titi
[1] => tutu
)
[bidule] => Array
(
)
[titi] => Array
(
)
[tutu] => Array
(
)
)

Array key exists in multidimensional array

I'm trying to arrange a group of pages in to an array and place them depending on their parent id number. If the parent id is 0 I would like it to be placed in the array as an array like so...
$get_pages = 'DATABASE QUERY'
$sorted = array()
foreach($get_pages as $k => $obj) {
if(!$obj->parent_id) {
$sorted[$obj->parent_id] = array();
}
}
But if the parent id is set I'd like to place it in to the relevant array, again as an array like so...
$get_pages = 'DATABASE QUERY'
$sorted = array()
foreach($get_pages as $k => $obj) {
if(!$obj->parent_id) {
$sorted[$obj->id] = array();
} else if($obj->parent_id) {
$sorted[$obj->parent_id][$obj->id] = array();
}
}
This is where I begin to have a problem. If I have a 3rd element that needs to be inserted to the 2nd dimension of an array, or even a 4th element that needs inserting in the 3rd dimension I have no way of checking if that array key exists. So what I can't figure out is how to detect if an array key exists after the 1st dimension and if it does where it is so I can place the new element.
Here is an example of my Database Table
id page_name parent_id
1 Products 0
2 Chairs 1
3 Tables 1
4 Green Chairs 2
5 Large Green Chair 4
6 About Us 0
Here is an example of the output I'd like to get, if there is a better way to do this I'm open for suggestions.
Array([1]=>Array([2] => Array([4] => Array([5] => Array())), [3] => Array()), 6 => Array())
Thanks in advanced!
Well, essentially you are building a tree so one of the ways to go is with recursion:
// This function takes an array for a certain level and inserts all of the
// child nodes into it (then going to build each child node as a parent for
// its respective children):
function addChildren( &$get_pages, &$parentArr, $parentId = 0 )
{
foreach ( $get_pages as $page )
{
// Is the current node a child of the parent we are currently populating?
if ( $page->parent_id == $parentId )
{
// Is there an array for the current parent?
if ( !isset( $parentArr[ $page->id ] ) )
{
// Nop, create one so the current parent's children can
// be inserted into it.
$parentArr[ $page->id ] = array();
}
// Call the function from within itself to populate the next level
// in the array:
addChildren( $get_pages, $parentArr[ $page->id ], $page->id );
}
}
}
$result = array();
addChildren( $get_pages, $result );
print_r($result);
This is not the most efficient way to go but for a small number of pages & hierarchies you should be fine.

Categories