Your function code can return a zero if no matching data was found, an array of fetched data, or a false if there is a query error. Your code that calls the function must handle each of those values. Since it currently just tries to loop over the returned value like it is always a non-empty array, you are getting a follow-on php error, having nothing to do with the actual problem, for two of those three values.
Start by simplifying the error handling, so that your main code only has to deal with error free execution. For a database dependent web page, most database statement errors are fatal, i.e. the web page doesn’t work and there’s nothing the visitor to the page can do about the problem (and you certainly don’t want to tell hackers that something they did caused a specific type of error, as that will just encourage them to do more of the same.) The exception to this rule is for inserting/updating user supplied data. In this case, a duplicate or out of range value might be something that the visitor can correct. This is the only case where having database statement error handling logic in your code ‘adds value’ to what you are doing. You would catch the exception, detect if the error number is for something that your code can handle, and set up a message telling the visitor what was wrong with the data that they submitted. In all other cases, just let php catch and handle the database exceptions, where it will use its error related settings to control what happens with the actual error information (database errors will ‘automatically’ get displayed or logged the same as php errors.) When learning, developing, and debugging code/queries, you would display all php errors (which now includes database statement errors.) When on a live/public web server, you would log all php errors (which now includes database statement errors.)
This will reduce the values that your code must deal with to just two.
Next, the fetchAll() method returns an empty array if there is no matching data, so there’s no need for any of the rowCount() logic. Just return the value from the fetchAll() method. This will give you either an empty array or an array containing data. Your main code should then test the returned value and output an appropriate message if there is nothing to display.
BTW - if you set the default fetch mode to assoc when you make the database connection, you won’t have to specify it in each fetch… statement.
There’s also no good reason to use explicit binding (the bindParam() statement), and in fact because it works by using a reference to the variable, doesn’t produce a php error if the variable doesn’t exist. Use the much simpler implicit binding, by supplying an array of the input data to the ->execute([…]) method call.
Your get_all_couples() function, which doesn’t seem to be well named, should accept all inputs as call-time parameters. This will make the function general purpose. Your current question/problem seems to be about using the session variable to supply the username for the query. If you hard-code this inside the function, the function can only find data that matches the currently logged in user. If you instead supply this input as a call-time parameter, the function can now find data that matches the user given to it when it is called.
Lastly, the database table you have shown should NOT use the username to relate data back to the user it belongs with. You should use the user’s id (auto-increment integer primary index.) This will result in the least amount of data storage, the fastest queries, and allow the username to be edited without breaking the relationship in the data.