The fact that HTTP is a stateless protocol can cause a number of problems for anyone creating a web application. One common technique when viewing data is known as CRUD, this is simply Create Read Update and Delete. Imagine you have a system that allows you to edit and update invoices, when viewing the details of an invoice you'll want to Create new items for the invoice, Read all the existing items, Update (edit) existing items and lastly Delete items from the invoice.
Typically you might have a table of invoices and another table of invoice details. Each record in the invoice will have a primary key, and coupled with a foreign key in the invoice details, this allow a relationship between the invoice and its details. The tempting solution would be to have the invoices primary key in a "hidden" input of the form. I use quotes around "hidden" for good reason, there is little hidden about it, and indeed when the form is posted, the server has no way to know the form hasn't been modified in some nefarious way, something that is easier to do than many people realise.
There are a number of things we can do to tighten things up, the first is to use a session, ideally coupled with some form of user login. I won't go into lots of detail about securing sessions (its a large enough subject on its own), but briefly there are a number of issues you need to look out for, at the very least you MUST be using HTTPS, you should also regenerate the session id regularly and use some kind of token for each form. Using a token for each form gives some mitigation from some security issues, but also avoids a legitimate users from inadvertently reposting forms.
The next trick it to change how you think about forms, its all to easy to think of the form as part of your application, however this really isn't the way to go, rather you should think of it as an untrusted element of your user interface. With this in mind what can we do about the need to identify (for example) the invoice with its primary key? Likely with a paginated list of invoices on each row you'll have a button to view the invoice details, while it's tempting just to put the invoice id in a hidden input (of a form), this gives a road in to anyone who want to abuse your application.
You really do not want to be transmitting database keys back and forth between server and client!
This is where sessions come in, with php you can handily store arrays in each individual session, the serialisation and de-serialisation is handled transparently. This means we can have an array of pages containing an array of forms, with an array of data pertinent to that form. While we do need to use a hidden field in the form, this time rather than using some potentially sensitive information like a primary key, we simply use the hidden input on each line of the form to identify which line of the form has been selected. This line number can then be used to look up the required database key in the session data. Assuming a pagination of say 10 lines this means at worst all some nefarious person can do is limited to that small subset of invoices, and as they have no access to the actual key values this also limits the scope for mayhem. Couple this with using PDO and parametrised queries (to mitigate SQL injections) and you have already improved the robustness of your application by a significant margin.
I hope this simple technique has given you some insight and allowed you to start thinking about how a web application can be made more robust.
Enjoy!