I understand that preparing a statement and then executing it multiple times is much more efficient than simply repeating the same query.
Correct. Because a server-side prepared statement (what mysqli uses) only sends the bound arguments to the database on subsequent executions instead of the whole query. Also the database server will not have to reparse the query, but can reuse the prepared one. So, this can make a difference in retrieving data. So in theory, this is more efficient.
See http://php.net/manual/en/mysqli.quickstart.prepared-statements.php.
In practice, don't expect that much difference. It certainly depends on the amount and the complexity of queries you intend to call for this to make a significant difference. When in doubt, benchmark.
It also has the added benefit of protecting you from SQL Injection.
The class below fetches a sound object by primary key. If I needed to have several instances of this class in the same script, would I be getting the benefits of a prepared statement?
No. Because mysqli will fire a query to the server each time you create a new instance to prepare that statement then. In addition to the execute call. So you win nothing. It would be more straightforward then to just use something like
$query = sprintf('SELECT name, musickey FROM sounds WHERE id = %d', $id);
and then execute that as a whole query if you really want one instance per ID.
If you want to do this with prepared statements, you want only one instance that prepares once but that you can pass the IDs to multiple times.
Would this be a good candidate for a stored procedure?
If you put application logic into stored procedures, you are making your application less portable. If you want to change your database system at one point, you'd need to recreate the stored procedures. It's up to you to make an informed choice about this. If you don't need to change the database system, then it might be viable. I prefer to keep the logic inside the application.
I'm also pretty new to object oriented PHP.
One aspect of OOP is about encapsulating and hiding information (and complexity) from a client. So having public properties there isn't the best idea. Also, doing work in the constructor is not what the constructor is for. The constructor should only put your object into a valid state. And since preparing does a query you want to defer that until it's needed.
With all that said, consider
interface FindById
{
public function findById($id);
}
class SoundFinder implements FindById
{
private $mysqli;
private $statement;
private $query = "SELECT name, musickey FROM sounds WHERE id = ?";
public function __construct(mysqli $mysqli)
{
$this->mysqli = $mysqli;
}
private function prepare()
{
$this->statement = $mysqli->prepare($this->query)
}
public function findById($id)
{
if (!$this->statement) {
$this->prepare();
}
$this->statement->bind_param("i", $this->id);
$this->statement->execute();
$this->statement->bind_result($name, $musickey);
$this->statement->free_result();
return array($name, $musickey);
}
}
Then you can use it like this:
$connection = new mysqli(/* your connection data */);
$soundFnder = new SoundFinder($connection);
$sound42 = $soundFinder->findById(42);
$sound314 = $soundFinder->findById(314);