Can someone help me on the way with a SELECT (or more if needed) and some php loops. I've been trying to come up with a solution for 2 days now.
There's this 12 column grid I use which is movable and resizable. I store the data of it, such as x-axis, y-axix, width, height.
Here is an example of the database table elements:
| id | page_id | element_type | element_x | element_y | width | height | element_content
----------------------------------------------------------------------------------------
| 45 | 1 | title | 0 | 0 | 12 | 1 | Content
| 70 | 1 | button | 6 | 2 | 6 | 1 | Content
| 23 | 1 | form | 4 | 1 | 4 | 1 | Content
| 55 | 1 | rich-textfield 0 | 1 | 4 | 1 | Content
| 101| 1 | gallery | 8 | 1 | 4 | 1 | Content
As you can see the height doesn't matter because it's always 1.
This example structure would show this in my resizable grid:
So far I got this to SELECT the grid items:
$id = 1;
$selectElements = $conn->prepare("SELECT * FROM `elements` WHERE `page_id` = :id");
$selectElements->bindParam(':id', $id, PDO::PARAM_STR);
$selectElements->execute();
What I have in mind is something like
foreach element_y
<div class="row">
// loop to loop thru all element_x's per element_y
</div>
endforeach
Note that every element_y number needs to be outputted just one time each. so instead of:
0 - 1 - 1 - 1 - 2 it should be 0 - 1 - 2
Is it possible to make what I have in mind or am I thinking the complete wrong way? :) Highly appreciated!
P.S. If the question is not clear enough tell me, I will change my question!
I'd suggest something along the lines of the following:
Make sure your SQL orders by element_y and element_x:
SELECT * FROM `elements`
WHERE `page_id` = :id
ORDER BY `element_y` ASC, `element_x` ASC
That way, you'll be sure to have the correct order already.
Then, transform your array into a slightly different structure with something like this:
$elements_grouped = [];
foreach ($elements as $key => $element) {
$elements_grouped[$element['element_y']][$key] = $element;
}
That will put your elements in an array with element_y keys holding their respective elements.
Then, you can loop over that, and in in a second loop loop over the elements (using your pseudocode):
foreach elements_grouped as row
<div class="row">
foreach row as element
// output element
endforeach
</div>
endforeach
You should use multi dimensional array ( key and value) to handle all data first
$handleData = array();
foreach($result as $item){
$handleData[$item['element_y'] => array(
$item['element_x'] => array(
'element_type' => $item['element_type'],
'width' => $item['width '],
'element_content' => $item['element_content']
)
)
}
After that, you just need to printout $handleData
Related
SOLUTION: Make sure you don't 'use up' any $responses->fetch_assoc()s before the while loop.
I performed mysqli_fetch_array($responses);.
In php I have this sql query (simplified for your convenience, but the problem remains)
$sql = "SELECT id, content FROM responses ORDER BY RAND()";
$responses = $conn->query($sql);
where the responses table looks like this:
+----+----------+--------+------+
| id | content | userId | part |
+----+----------+--------+------+
| 4 | peewee | 31 | 1 |
| 5 | tallinn | 31 | 1 |
| 6 | dewey | 31 | 1 |
| 7 | stanford | 31 | 1 |
+----+----------+--------+------+
That doesn't format properly so all you need to know is that the id and content rows are different for each entry while the rest is the same for each.
The problem is, when I do a while loop on $responses like so:
while ($row = $responses->fetch_assoc()) {
$responseId = $row["id"];
$content = $row["content"];
echo " id: ".$responseId;
echo " content: ".$content;
}
I always get 1 record fewer than there are. In this case, since there are 4 rows, I would only see 3 echoed. However, it is not always the same 3, nor are they in the same order. If I remove the ORDER BY RAND() clause, then it is always the first record which is left out.
Thanks in advance
Cheers
I am saving many waypointIDs, bearings, and distances in three columns in a single row of my database. Each column stores it's respective item separated by a comma.
WaypointIds: 510,511,512
Bearings: 65,50,32
Distances: 74,19,14
I think I might have coded myself into a corner! I now need to pull them from the database and run through adding them to a table to output on screen.
I'm using the following to put the corresponding columns back into an array but am a little stuck with where to go after that, or if that is even the way to go about it.
$waypointArrays = explode (",", $waypointsString);
$bearingArrays = explode (",", $bearingsString);
$waypointsString & $bearingsStrings are the variables set by my database call.
I think I need to add them all together so I can iterate through each one in turn.
For example, waypointId 510, will have bearing 065, and distance 0.74.
I think I need an associative array but not sure how to add them all together so I can run through each waypoint ID and pull out the rest of the entry.
e.g. for each waypointId give the corresponding bearing and waypoint.
I have checks in place to ensure that as we add waypoints/bearings/distances that they don't fall out of step with each other so there will always be the same number of entries in each column.
Don't continue with this design: your database is not normalised and therefore you are not getting any benefit from the power that a database can offer.
I don't think working around this problem by extracting the information in PHP using explode, ..etc is the right way, so I will clarify how your database should be normalised:
Currently you have a table like this (possibly with many more columns):
Main table: route
+----+---------+-------------+----------+-----------+
| id | Name | WaypointIds | Bearings | Distances |
+----+---------+-------------+----------+-----------+
| 1 | myroute | 510,511,512 | 65,50,32 | 74,19,14 |
| .. | .... | .... | .... | .... |
+----+---------+-------------+----------+-----------+
The comma-separated lists violate the first normal norm:
A relation is in first normal form if and only if the domain of each attribute contains only atomic (indivisible) values, and the value of each attribute contains only a single value from that domain.
You should resolve this by creating a separate table for each of these three columns, which will have one record for each atomic value
Main table: route
+----+---------+
| id | Name |
+----+---------+
| 1 | myroute |
| .. | .... |
+----+---------+
new table route_waypoint
+----------+-------------+------------+----------+
| route_id | waypoint_id | bearing_id | distance |
+----------+-------------+------------+----------+
| 1 | 510 | 65 | 74 |
| 1 | 511 | 50 | 19 |
| 1 | 512 | 32 | 14 |
| 2 | ... | .. | .. |
| .. | ... | .. | .. |
+----------+-------------+------------+----------+
The first column is a foreign key referencing the id of the main table.
To select the data you need, you could have an SQL like this:
select route.*, rw.waypoint_id, rw.bearing_id, rw.distance
from route
inner join route_waypoints rw on rw.route_id = route.id
order by route.id, rw.waypoint_id
Now PHP will receive the triplets (waypoint, bearing, distance) that belong together in the same record. You might need a nested loop while the route.id remains the same, but this is how it is done.
To answer your question, code below will work as long as waypointsIds are unique. That beign said, as other mentioned, fix your database layout. What you have here could really benefit from a separate table.
<?php
$waypointIds = [510, 511, 512];
$bearings = [65, 50, 32];
$distances = [74, 19, 14];
$output = [];
for ($i = 0; $i < count($waypointIds); $i++) {
$output[$waypointIds[$i]] = [
'bearing' => $bearings[$i],
'distance' => $distances[$i]
];
}
print_r($output);
$waypoints = array(510, 511, 512);
$bearings = array(65, 50, 32);
$distances = array(74, 19, 14);
for ($i = 0; $i < count($waypoints); $i++) {
$res[] = array(
'waypoint' => $waypoints[$i],
'bearing' => sprintf("%03d", $bearings[$i]),
'distance' => $distances[$i]/100
);
}
print_r($res);
I want to exchange image order(Data retrieved from angularjs/ui-sortable, so the $newImgOrder array is representing the new order.)
(array) $newImgOrder = ['test.jpeg', 'another.jpeg'];
Images::select('url')->where('project_id', '=', $args['id'])->get()
->each(function($img) use (&$newImgOrder) {
foreach ($newImgOrder as $item) {
$img->url = $item;
$img->save();
}
});
* UPDATED = RIGHT WAY FOR THE PERFORMING THIS ACTION *
Thanks #Devon
Removed foreach + select()
(array) $newImgOrder = ['test.jpeg', 'another.jpeg'];
Images::where('project_id', '=', $args['id'])->get()
->each(function($img) use (&$newImgOrder) {
$img->url = array_shift($item);
$img->save();
}
});
Here is a quick demonstration for what i want to do:
Table state before the action
+---------+-------------+--------------+
| id | project_id | url |
+---------+-------------+--------------+
| 1 | 15 | another.jpeg |
+---------+-------------+--------------+
| 2 | 15 | test.jpeg |
+---------+-------------+--------------+
Expected results =
+---------+-------------+--------------+
| id | project_id | url |
+---------+-------------+--------------+
| 1 | 15 | test.jpeg |
+---------+-------------+--------------+
| 2 | 15 | another.jpeg |
+---------+-------------+--------------+
Actual results =
+---------+-------------+--------------+
| id | project_id | url |
+---------+-------------+--------------+
| 1 | 15 | another.jpeg |
+---------+-------------+--------------+
| 2 | 15 | another.jpeg |
+---------+-------------+--------------+
What's the problem with that iteration. I tried with double foreach but i got the same results... Am i something missing out? Any help would be very appreciated. Thanks.
For each image, your code is looping through $newImageOrder and saving the url for that image. Hence, you're performing two saves for every image, which is clearly not what you want.
There is no reason for the inner foreach loop. If you're certain the number of elements in $newImageOrder will match the number of rows from your query and since you're passing $newImageOrder as a reference to the closure, you could make use of shift:
each(function($img) use (&$newImgOrder) {
$img->url = array_shift($newImgOrder);
$img->save();
});
This will shift the first element off of the array and return it, meaning you'll be removing and using the first element of $newImgOrder for each iteration.
Keep in mind, this is a mutable change. $newImgOrder is mutated by the closure. If this is not what you want, you may want to use a running count for the offset instead.
I have a table of food items. They have a "Position" field that represents the order they should appear in on a list (listID is the list they are on, we don't want to re-order items on another list).
+--id--+--listID--+---name---+--position--+
| 1 | 1 | cheese | 0 |
| 2 | 1 | chips | 1 |
| 3 | 1 | bacon | 2 |
| 4 | 1 | apples | 3 |
| 5 | 1 | pears | 4 |
| 6 | 1 | pie | 5 |
| 7 | 2 | carrots | 0 |
| 8,9+ | 3,4+ | ... | ... |
+------+----------+----------+------------+
I want to be able to say "Move Pears to before Chips" which involves setting the position of Pears to position 1, and then incrementing all the positions inbetween by 1. so that my resulting Table look like this...
+--id--+--listID--+---name---+--position--+
| 1 | 1 | cheese | 0 |
| 2 | 1 | chips | 2 |
| 3 | 1 | bacon | 3 |
| 4 | 1 | apples | 4 |
| 5 | 1 | pears | 1 |
| 6 | 1 | pie | 5 |
| 7 | 2 | carrots | 0 |
| 8,9+ | 3,4+ | ... | ... |
+------+----------+----------+------------+
So that all I need to do is SELECT name FROM mytable WHERE listID = 1 ORDER BY position and I'll get all my food in the right order.
Is it possible to do this with a single query? Keep in mind that a record might be moving up or down in the list, and that the table contains records for multiple lists, so we need to isolate the listID.
My knowledge of SQL is pretty limited so right now the only way I know of to do this is to SELECT id, position FROM mytable WHERE listID = 1 AND position BETWEEN 1 AND 5 then I can use Javascript (node.js) to change position 5 to 1, and increment all others +1. Then UPDATE all the records I just changed.
It's just that anytime I try to read up on SQL stuff everyone keeps saying to avoid multiple queries and avoid doing syncronous coding and stuff like that.
Thanks
This calls for a complex query that updates many records. But a small change to your data can change things so that it can be achieved with a simple query that modifies just one record.
UPDATE my_table set position = position*10;
In the old days, the BASIC programming language on many systems had line numbers, it encouraged spagetti code. Instead of functions many people wrote GOTO line_number. Real trouble arose if you numbered the lines sequentially and had to add or delete a few lines. How did people get around it? By increment lines by 10! That's what we are doing here.
So you want pears to be the second item?
UPDATE my_table set position = 15 WHERE listId=1 AND name = 'Pears'
Worried that eventually gaps between the items will disappear after multiple reordering? No fear just do
UPDATE my_table set position = position*10;
From time to time.
I do not think this can be conveniently done in less than two queries, which is OK, there should be as few queries as possible, but not at any cost. The two queries would be like (based on what you write yourself)
UPDATE mytable SET position = 1 WHERE listID = 1 AND name = 'pears';
UPDATE mytable SET position = position + 1 WHERE listID = 1 AND position BETWEEN 2 AND 4;
I've mostly figured out my problem. So I've decided to put an answer here incase anyone finds it helpful.
I can make use of a CASE statement in SQL. Also by using Javascript beforehand to build my SQL query I can change multiple records.
This builds my SQL query:
var sql;
var incrementDirection = (startPos > endPos)? 1 : -1;
sql = "UPDATE mytable SET position = CASE WHEN position = "+startPos+" THEN "+endPos;
for(var i=endPos; i!=startPos; i+=incrementDirection){
sql += " WHEN position = "+i+" THEN "+(i+incrementDirection);
}
sql += " ELSE position END WHERE listID = "+listID;
If I want to move Pears to before Chips. I can set:
startPos = 4;
endPos = 1;
listID = 1;
My code will produce an SQL statement that looks like:
UPDATE mytable
SET position = CASE
WHEN position = 4 THEN 1
WHEN position = 1 THEN 2
WHEN position = 2 THEN 3
WHEN position = 3 THEN 4
ELSE position
END
WHERE listID = 1
I run that code and my final table will look like:
+--id--+--listID--+---name---+--position--+
| 1 | 1 | cheese | 0 |
| 2 | 1 | chips | 2 |
| 3 | 1 | bacon | 3 |
| 4 | 1 | apples | 4 |
| 5 | 1 | pears | 1 |
| 6 | 1 | pie | 5 |
| 7 | 2 | carrots | 0 |
| 8,9+ | 3,4+ | ... | ... |
+------+----------+----------+------------+
After that, all I have to do is run SELECT name FROM mytable WHERE listID = 1 ORDER BY position and the output will be as follows::
cheese
pears
chips
bacon
apples
pie
For instance, if I have the following table:
+----+---+----------+
| id | a | position |
+----+---+----------+
| 0 | 0 | 0 |
| 1 | 0 | 1 |
| 2 | 1 | 4 |
| 3 | 1 | 9 |
| 4 | 1 | 6 |
| 5 | 1 | 1 |
+----+---+----------+
and I want to get an array that contains the first 100 values from position where a is 1 in ascending order, what would I do?
Im guessing something like this:
$col = mysql_fetch_array( mysql_query('
SELECT `position`
FROM `table`
WHERE `a`="1"
ORDER BY `position` ASC
LIMIT 100
'));
I'd expect to get the following array:
+-------+-------+
| index | value |
+-------+-------+
| 0 | 1 |
| 1 | 4 |
| 2 | 6 |
| 3 | 9 |
+-------+-------+
but it doesn't work.
¿What should I do to make it work?
Thanks
mysql_fetch_array() gets a single row at a time from the result of your query. To access all of the rows you need a loop. Something like...
while ($row = mysql_fetch_array($result, MYSQL_NUM))
{
printf("index: %s value: %s", $row[0], $row[1]);
}
I would take a closer look at: http://php.net/manual/en/function.mysql-fetch-array.php
Okay, couple of things:
Running the mysql_query inside the fetch_array is weird. Mysql_fetch_array works on a query result to put the individual lines of the result(as fetched) into an array. So when you're running it as you've got it, if it runs at all, it's only going to give you the first row, not the first hundred rows.
Second, the quoting looks pretty weird. Depending on the data type in "a", the double quotes might cause. (Haven't used MySQL in a bit, could be wrong.)
If I was going to do it, I'd do it like this:
$result = mysql_query("SELECT index, position FROM table WHERE a = 1 ORDER BY position ASC LIMIT 100");
while($col = mysql_fetch_array($result)){
*do something*
}
*Thanks to JYelton for the Query reformat.
Your query is okay.
The problem is that mysql_fetch_array retrieves only one row. You should loop all the rows and add each value to your $col array.
$result = mysql_query('...');
while($row = mysql_fetch_array($result, MYSQL_NUM))
{
$col[] = $row[0];
}
Now $col contains following:
Array
(
[0] => "1"
[1] => "4"
[2] => "6"
[3] => "9"
)