-1

I created photo upload form. Different users are able to upload multiple photos at once and the photos, the user's details and input get stored in the database as shown in picture below:

Database table storing filenames of photos uploaded and other inputs

I want to be able to display the users and their photos in my index.php page according to the date the photos were uploaded. I would like the index.php page to look something like this

Final output page - How to display multiple photos from database

I tried to use GROUP_CONCAT() and GROUP BY in my SQL statement hoping to achieve something like the table below which might allow me to loop through the table data in order to display the photos alongside the users

Example of how GROUP_CONTAT() and GROUP BY look like in MYSQL

This is my code which did not work:

$sql = "SELECT id, unique_id, GROUP_CONCAT(fileName) as fileName,  title,date GROUP BY unique_id FROM images"; 
$result = $conn->query($sql); 
if ($result->num_rows > 0) { // This is the line 22 in my code.
    while($row = $result->fetch_assoc()) { 
        echo $row['fileName']; //I wanted to know if echo $row['fileName']; would print something like cow1.jpg, cow2,jpg etc.
    } 
} 

But I am getting error as follows:

Notice: Trying to get property of non-object in C:\xampp\htdocs\display.php on line 22

Someone suggested the GROUP_CONCAT() to me. I have done some search but all tutorials give example of how the table will look after using GROUP_CONCAT() but not how to get data from the final table to use in e.g index.php. I am new to GROUP_CONCAT() and I don't really know how to work with that.

Gleb Kemarsky
  • 10,160
  • 7
  • 43
  • 68
Elizabeth Kof
  • 65
  • 1
  • 2
  • 8
  • 1
    Don't have an answer for you at the moment but I have a comment for you to think about. You only show one date per user in your example image but each photo can have different upload dates. So your design can't work. You should show a date for each photo. Also,if you group by uniqueID, the date could belong to any one of the photos under that uniqueID. You won't know which date you are showing. – Cave Johnson Dec 08 '17 at 21:23
  • What is line 22? – Paul Spiegel Dec 08 '17 at 21:25
  • 1
    @PaulSpiegel Says it in the code :) – The Codesee Dec 08 '17 at 21:26
  • 1
    @KodosJohnson Actually each photo can only have one date based on his table. And his wanted output would show the same user multiple times if they have other uploaded photos on different dates. His theory here is correct, but theres a problem with grouping it up cleanly. – IncredibleHat Dec 08 '17 at 21:31
  • @Kudos Johnson, thanks for the feedback . but I am looking for both design and code that will work. I am a beginner, I can understand better with examples – Elizabeth Kof Dec 08 '17 at 21:32
  • 1
    As for the error at 22, its because your SQL is invalid. You can display mysqli errors to show the full extent of it, but in a nutshell, `GROUP BY` goes after `FROM`. – IncredibleHat Dec 08 '17 at 21:34
  • @Paul Spiegel, I have commented line 22 in the code: if ($result->num_rows > 0) { // This is the line 22 in my code. – Elizabeth Kof Dec 08 '17 at 21:34
  • Easy on the formatting, try not to jam in as many images. Stay focused on the problem in terms of code. Don't beg or plead with things like "please!!!!!" We'll help you if we can. – tadman Dec 08 '17 at 21:38
  • 3
    A lot of problems can be detected and resolved by [enabling exceptions in `mysqli`](https://stackoverflow.com/questions/14578243/turning-query-errors-to-exceptions-in-mysqli) so mistakes aren't easily ignored. This helps find simple syntax and connection errors. – tadman Dec 08 '17 at 21:38
  • 1
    -1 for deleting your previous question about the same topic. People already took the time to comment on there to try and steer you in the right direction. Not only did you devalue those efforts by deleting the question, now with the _Trying to get property of non-object_ thingie you are asking about something that has been discussed countless times before already - this is where you are supposed to make an effort, do proper research, and solve that part of the problem yourself! This is not a teach me the very basics of debugging workshop. – CBroe Dec 08 '17 at 21:58
  • Possible duplicate of [Reference - What does this error mean in PHP?](https://stackoverflow.com/questions/12769982/reference-what-does-this-error-mean-in-php) – CBroe Dec 08 '17 at 21:59
  • @Randall thanks, putting GROUP BY after table name solved my problem – Elizabeth Kof Dec 08 '17 at 22:19
  • @CBroe you are right about me deleting the first post. I needed to make my question clean so people would be encouraged to help. After like 9 hours, the first question was barely getting response and being down voted and it seemed ignored which is why i deleted it. I appreciate that you and few others commented on the question – Elizabeth Kof Dec 08 '17 at 22:25
  • @Randall That's what I meant. I made a mistake when I said that each photo can have different dates. I meant each photo has a date. But he is grouping by uniqueID which could have multiple photos. And he only does group_concat on the file name. He is only showing one date per group, but each group could actually have multiple dates. So the dates he gets in his query is undefined. – Cave Johnson Dec 08 '17 at 22:32

2 Answers2

2

After giving you a bit of a rebuke in comments for deleting the previous question, now let's try some "teaching" ;-)

As I said in a comment on the earlier question, this is something where I would not try and get fancy with GROUP_CONCAT. Foremost because it has the disadvantage that a) it is MySQL-specific, so using it kinda hinders portability of your code, and b) more severely, the length of the resulting string you can assemble is limited. File names (plus paths) could easily get longer, if you have different upload directories for different users or additional parameters to include in the URLs, so that would limit the amount of photos a group could contain, before this "explodes" ...


The control break I mentioned is a pretty basic programming principle. You loop over a sorted(!) set of data, and compare one or more criteria to see whether they have changed between the previous row and the current one.

Your example requires a simple one-level control break - the date is the only grouping criterion here. But this same principle can be extended to cover multiple levels - say if you had a list of your credit card purchases covering several years, and you want to output them grouped by year first, and then by month second.

The following example loops over an array more or less matching your example data; but looping over a database query result this works pretty much the same.

But remember, in any case, the input data must already be sorted by the break criterion (or criteria, for a multi-level control break; and in that case, also "ordered in the right order", meaning for above example with your CC purchases by year first and then by month second.)

Example with comments should be pretty self-explanatory from here on, so:

<?php

// example data - I left out a few of your columns; the important part is that
// it is sorted by the control group break criterion, in this case the date
$data = [
 ['id' => '1', 'name' => 'John', 'file' => 'cow1.jpg', 'date' => '07-12-2017 01:05'],
 ['id' => '2', 'name' => 'John', 'file' => 'cow2.jpg', 'date' => '07-12-2017 01:05'],
 ['id' => '3', 'name' => 'Dan', 'file' => 'cat1.jpg', 'date' => '08-12-2107 02:09'],
 ['id' => '4', 'name' => 'Dan', 'file' => 'cat2.jpg', 'date' => '08-12-2107 02:09'],
 ['id' => '5', 'name' => 'John', 'file' => 'dog.jpg', 'date' => '09-12-2107 03:10'],
];

echo '<ul>'; // open unordered list
echo '<li>'; // opening list item tag for first grouped set of data

$previousDate = null;
foreach($data as $row) {
  // check if group break has occurred - is the date of this row different
  // from that of the previous one? (since we initialized $previousDate as null,
  // this will be true for the first row in any case)
  if($row['date'] != $previousDate) {
    // break has occured
    if(null != $previousDate) {
      // if this is not the first row, then we have created output before, so
      // now we need to close the previous list item, and open an new one.
      echo '</li>';
      echo '<li>';
    }

    // now we output whatever is needed at the start of a new group - in this case,
    // user name and date, since we want those only once
    echo 'User: ' . $row['name'] . '<br>';
    echo 'Date: ' . $row['date'] . '<br>';
  }

  // now, handle the output of the actual "per-row" data - in this case, the individual image
  echo '[img src="' . $row['file'] . '"]<br>'; // pseudo image tag in lack of real image files

  // remember current row's date as the new previous one, so that we can compare against
  // that in the next loop iteration
  $previousDate = $row['date']; 
}

echo '</li>'; // closing list item tag for last grouped set of data
echo '</ul>'; // close unordered list

If you want something different than an unordered list (a bit ironic not using an ordered list here, I know, but I did not want to spoil the example output with the default item numbering that causes, that might be confusing), you can output the data in other structures as well of course - say, a table instead of a list. Just needs a little thought put into what to output and where/when exactly in each case. Say you wanted a table with three columns name, date and image, and leave name and date blank in each row where they still match the previous one - then you still have to output empty table cells for name and date, so as not to mess up the basic HTML table structure required. So the logic might need slight tweaks, depending on the specific output desired.

CBroe
  • 91,630
  • 14
  • 92
  • 150
  • thank you for the response, I will try it and give you feedback. – Elizabeth Kof Dec 09 '17 at 00:26
  • I have checked on your code again. However, the date will not be reliable way to identify the owner of a photo. This is because, for example, if 1million users are using the same upload form, chances are that different users may have the same date. But your code is a good example if I use unique ID instead. The unique ID can be `$UserID. $Date` where user ID is different for each user. – Elizabeth Kof Dec 09 '17 at 07:39
  • If one user logs into his/her account from two computers and uploads photos on both computers at the same time, the `$UserID . $Date` may also not be a reliable unique identifier so I think `ID . UserID . Date` will be the most reliable way to identify the owners' posts. – Elizabeth Kof Dec 09 '17 at 07:51
  • how will the SQL queries be in order to produce the $data array you have constructed. I am having problem here. – Elizabeth Kof Dec 09 '17 at 13:38
  • Well I have figured it out. This is the query that will produce your $data array above. `$sql = "SELECT * FROM images ORDER BY date ASC"; $result = $conn->query($sql); // output data of each row while($row = $result->fetch_assoc()) { $data[] = $row; }` However, like I mentioned earlier above, it will be more reliable to use uniqueID instead of just the date to ORDER the rows and also to identify the owner of the photos. Thank you everyone, who helped. Hope this will help another person too – Elizabeth Kof Dec 10 '17 at 01:28
1

Try this:

$sql = "SELECT id, unique_id, GROUP_CONCAT(fileName) AS filenames, title, date FROM images GROUP BY unique_id"; 
$result = $conn->query($sql); 
if ($result->num_rows > 0) {
    while($row = $result->fetch_assoc()) { 
        echo $row['filenames'];
    }
}
iazaran
  • 196
  • 1
  • 6
  • 17
  • @PaulSpiegel GROUP BY used after TABLE NAME and also: echo $row['GROUP_CONCAT(fileName)']; – iazaran Dec 08 '17 at 21:44
  • Ah.. I didn't even see that GROUP BY was before FROM. You should write your comment into the answer. However - The query still doesn't make sence, because columns are selected, which are not in the GROUP BY clause. With ONLY_FULL_GROUP_BY mode enabled this will produce an error. – Paul Spiegel Dec 08 '17 at 21:49
  • @PaulSpiegel OK. Just add "AS fileNames" to prevent error for ONLY_FULL_GROUP_BY. Now we do not need to whole of columns in the GROUP BY. You can use it just for single column. I edit my answer. – iazaran Dec 08 '17 at 22:02
  • The column alias `filenames` has nothing to do with ONLY_FULL_GROUP_BY mode. You can not select `id` if you GROUP BY `unique_id`. At least it doesn't make sence. What `id` should be selected for the group with `unique_id = 'id_cow'`? – Paul Spiegel Dec 08 '17 at 22:17
  • @PaulSpiegel Yes this is strange that IDs for one unique_id. But we do not echo ID. We can remove it. I found this for current issue: https://stackoverflow.com/questions/13566562/add-id-column-by-group – iazaran Dec 08 '17 at 22:26
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/160826/discussion-between-giliapps-and-paul-spiegel). – iazaran Dec 08 '17 at 22:30
  • 2
    Hi @Giliapps, thanks for the solution. As Randall pointed out earlier in the comment, the error is solved after putting GROUP BY after the table name. images – Elizabeth Kof Dec 08 '17 at 22:33