0

I'm trying to learn, understand and implement object-oriented PHP. Something's happening which I don't understand, and I would appreciate any guidance or help.

I have the following class with the following properties and method:

class Film
{

    public $film_ids;
    public $numfilms;

    public function getFilms() {

        global $dbh;
        global $film_ids;
        global $numfilms;

        $film_ids = array();

        new DBConnection;

        $sql = "SELECT * FROM films";

        $stmt = $dbh->prepare($sql);            
        if ($stmt->execute()) {
            while ($row = $stmt->fetch()) {
                $film_id = $row['film_id'];
                $film_ids[] = $film_id;
            }
        }

        $numfilms = count($film_ids);

        $dbh = NULL;

    }

}

"DBConnection" is a class that allows me to access a MySQL database with the PDO extension.

I also have a view file (view.php) where I instantiate this class and call the method:

$film = new Film;
$film->getFilms();

After all this, I expect to be able to access the property $film_ids (a simple array) in my view.php file. But when I do a print_r($film_ids) in view.php, nothing shows up!

What's strange is that when I add print_r($film_ids) inside the getFilms() method, the array prints in my browser just fine!

Array ( [0] => 1 [1] => 2 )

So it's obvious that I'm having no trouble accessing the database and calling the getFilms() method in view.php -- that's not where the problem lies.

The problem seems to have something to do with variable scope -- accessing the method properties outside of the class. The weird thing is, I have all my properties and methods declared as public. Nothing has been declared private or even protected, so this shouldn't be a problem, should it? I even have the properties I want declared as global inside the getFilms() method.

Can anybody tell me what I may be doing wrong? Thank you so much!

4
Contributors
6
Replies
32
Views
4 Years
Discussion Span
Last Post by johnnycho
0

It should be this:

class Film
{
    public $film_ids;
    public $numfilms;

    public function getFilms() {
        $this->film_ids = array(); // change to use the class property

        $dbh = new DBConnection(); // this should be in variable 

        $sql = "SELECT * FROM films";

        $stmt = $dbh->prepare($sql);            
        if ($stmt->execute()) {
            while ($row = $stmt->fetch()) {
                $this->film_ids[] = $row['film_id']; // change to use the class property
            }
        }

        $this->numfilms = count($this->film_ids);

        $dbh = NULL;
    }
}

Somehow you are mixing properties and globals, which is bad anyway. Perhaps my OOP introduction may help.

1

Hm that is strange indeed. Usually you should be able to modify globally defined variables from within a function. What's particulary strange is that the $film_ids variable does get filled within the function, but then appears to be empty in your global scope. I assume you are checking the value of $film_ids AFTER running $film->getFilms()? ;).

The common way to do this using an object, however, is slightly different from what you're doing now. Below I'll post an example of how most people would do this using an object (for as far as I know).

<?php
// Optionally include your database class if this is a standalone file.
include 'dbconnection.php';

class Film
{
    private $film_ids = array(); // We declare these variables as private if they are only accessible through this class's functions.
    private $numfilms;

    // The setter method. Usually in object oriented programming you use setter 
    // and getter methods, to keep a clear distinction in what happens where.
    // The getter method is below this setter method.
    public function setFilms() 
    {
        $dbh = new DBConnection();
        $sql = "SELECT * FROM films";
        $stmt = $dbh->prepare($sql);

        // Could the query be executed?
        if($stmt->execute()) 
        {
            while($row = $stmt->fetch())
            {
                $film_ids[] = $row['film_id'];
            }
        }

        // We have now filled $film_ids WITHIN the function. Let's save this information.
        $this->film_ids = $film_ids; 

        // We have now stored the film id's in the class's properties.

        // Count the number of films and store it.
        $this->numfilms = count($film_ids);

        return true;
    }

    // The getter method for getting the film ids.
    public function getFilms()
    {
        if($this->film_ids)
        {
            return $this->film_ids;
        }
        else
        {
            return false;
        }
    }

    // The getter method for getting the number of films.
    public function getNumberOfFilms()
    {
        if($this->numfilms)
        {
            return $this->numfilms;
        }
        else
        {
            return false;
        }
    }
}

Edited by minitauros

Votes + Comments
Nice explanation
0

Thank you both so much for taking the time to respond. I implemented some of your suggestions and I got the getFilms() method to work, although I still had to declare $dbh as a global property to make it work, because this is the method that's being called in my DBConnection class:

public function open_PDO_connection($db_hostname, $db_name, $db_user, $db_pass) {

    global $dbh;

    try {

        $dbh = new PDO("mysql:host=$db_hostname;dbname=$db_name", $db_user, $db_pass);

        $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    } catch(PDOException $db_error) {

        echo $db_error->getMessage();

    }

}       

I recognize that using global variables in OOP isn't kosher, so if anyone can suggest a better way to do it, I'd love to know.

Anyway, here's what ended up working in my Film class:

public function getFilms() {

    global $dbh;

    $dbconnection = new DBConnection();

    $sql = "SELECT * FROM films";

    $stmt = $dbh->prepare($sql);            
    if ($stmt->execute()) {
        while ($row = $stmt->fetch()) {
            $this->film_ids[] = $row['film_id'];
        }
    }

    $this->numfilms = count($this->film_ids);

    $dbh = NULL;

}

And in my view.php file:

$film = new Film;
$film->getFilms();

print_r($film->film_ids);

This gave me what I was expecting: an array of values from the database. Again, thanks so much!

1

You can pass the $dbh to the Film class as a parameter (dependency injection [DI]).

class Film{
    public $film_ids;
    public $numfilms;
    private $dbh;

    public function __construct($dbh){
        $this->dbh = $dbh;
    }
    //...
}

Then in your client code:

$dbh = new extPDO; //assuming your using a wrapper class to create a custom PDO object
$film = new Film($dbh);

This may be easier than creating multiple instances of the PDO object if it needs to be used for multiple classes or further in procedural code. hence the DI.

What you don't want to do is place a 'create DB Object' in a method that could get run many times, hence the constructor.

Edited by diafol

0

To be able to use the database class in your class, you could also do as follows (although what diafol says is a pretty common way to do it):

<?php
include 'database_file.php';

class Films
{
    private $database;

    public function __construct()
    {
        $this->database = new Database(); // Instantiate your database class.
    }

    public function getFilms()
    {
        $this->database->function(); // Use a function in your database class.
    }
}

Instead of setting the vars you're talking about globally, you could have your function return them. Then your film id's in view.php would be retrieved like this:

<?php
$film_ids = $Films->getFilms();

print_r($film_ids);

Assuming your getFilms() function returns the film id's, for example:

<?php
public function getFilms()
{
    // Rest of your function...

    return $film_ids;
}

What would be prettiest is probably:

<?php
class Films
{
    private $film_ids = array();

    public function __construct()
    {
        // The __construct() function is always called upon class creation.

        // Make sure film id's are set upon class creation.
        $this->setFilmIds();
    }

    private function setFilmIds()
    {
        // Set the film id's. Store them in a class variable.

        // (Retrieve film id's)

        $this->film_ids = $retrieved_film_ids;
    }

    public function getFilmIds()
    {
        return $this->film_ids;
    }
}

Edited by minitauros

0

Thanks diafol, and thanks again minitauros. I tried diafol's solution and I got this error:

Fatal error: Call to a member function prepare() on a non-object

This is the line it was getting stuck on:

$stmt = $dbh->prepare($sql);            

Which meant that it wasn't recognizing $dbh as the PDO object.

After a LOT of experimenting, I finally got it to work! Turns out all I needed was one line of code that was missing in my DBConnection class (i.e. the PDO wrapper):

public function __construct() {

    $this->open_PDO_connection($db_hostname, $db_name, $db_user, $db_pass);

}

public function open_PDO_connection($db_hostname, $db_name, $db_user, $db_pass) {

    try {

        $dbh = new PDO("mysql:host=$db_hostname;dbname=$db_name", $db_user, $db_pass);

        $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

        $this->dbh = $dbh;    /* **** THIS IS THE LINE THAT WAS MISSING!!  **** */

    } catch(PDOException $db_error) {

        echo $db_error->getMessage();

    }

}

Whew!! Thanks again for pointing the way!!

This topic has been dead for over six months. Start a new discussion instead.
Have something to contribute to this discussion? Please be thoughtful, detailed and courteous, and be sure to adhere to our posting rules.