title: Notice: Trying to get property of non-object - nor is foreach looping properly
body: <p>For some reason I cannot for the life of me figure out/understand why this is not working properly...All I am trying to do is display comments on a photo page. The photo I am testing this on has 2 comments but all I get is the following:</p>
  
  <pre><code>Notice: Trying to get property of non-object in /Applications/MAMP/htdocs/photo_gallery/public/photo.php on line 47
  NULL @ ::
  </code></pre>
  
  <p>photo.php page is simply calling for the comments by the photo id and then displaying them in a foreach loop:</p>
  
  <pre><code>&lt;?php
  $photo = Photograph::findByID($_GET['id']);
  $comments = $photo-&gt;comments();
  printr($comments);
  ?&gt;
  &lt;div class="comments"&gt;
  &lt;?php if ($comments): ?&gt;
      &lt;?php foreach ($comments as $comment): ?&gt;
      &lt;p style="margin-bottom: 15px;"&gt;
          &lt;?php echo htmlentities($comment-&gt;author); ?&gt; @ &lt;?php echo strip_tags($comment-&gt;created, '&lt;strong&gt;&lt;em&gt;&lt;p&gt;'); ?&gt; :: &lt;?php echo $comment-&gt;body; ?&gt;
      &lt;/p&gt;
      &lt;?php endforeach; ?&gt;
  &lt;?php else: ?&gt;
      &lt;p&gt;No comments found.&lt;/p&gt;
  &lt;?php endif; ?&gt;
  &lt;/div&gt;
  </code></pre>
  
  <p>When I print_r on the $comments results, only the first comment shows up in the object array..</p>
  
  <p><strong>Part of the Photograph class:</strong></p>
  
  <pre><code>class Photograph extends DatabaseObject {
      public $id;
  
       function comments() {
          return Comment::findCommentsOn($this-&gt;id);
      }
  }
  </code></pre>
  
  <p><strong>Part of the MySQLiDatabase class:</strong></p>
  
  <pre><code>public static function findByPrepare($sql = "", $params) {
      global $db;
      $result = $db-&gt;query($sql, $params);
      foreach ($result as $row) {
          printr($result);
          return self::instantiate($row);
      }
  }
  
  private static function instantiate($record) {
      $className = get_called_class();
      $obj = new $className;
  
      foreach ($record as $attribute =&gt; $val) {
          if ($obj-&gt;hasAttribute($attribute)) {
              $obj-&gt;$attribute = $val;
          }
      }
      return $obj;
  }
  private function hasAttribute($key) {
      // get_object_vars returns an assoc array with all attributes as keys and their current values as the value
      $objVars = $this-&gt;attributes();
      return array_key_exists($key, $objVars);
  }
  
  protected function attributes() {
      // return an array of attribute keys and their values
      $attributes = array();
      foreach(static::$dbFields as $field) {
          if (property_exists($this, $field)) {
              $attributes[$field] = $this-&gt;$field;
          }
      }
      return $attributes;
  }
  </code></pre>
  
  <p>Seems like it is stemming from the foreach in the findByPrepare() function...</p>
  
  <p>When I printr($result) it shows both comments in an array fine, however when I printr($row) it displays just the first comment in an array...so somehow I need to get $row to loop through each result. I have tried looping the self::instantiate($row) in an array but then that throws another error: <strong>Fatal error: Call to a member function comments() on a non-object</strong></p>
  
  <p>*<strong>print_r on $comments (this is only coming up with 1 result when there's actually 2 comments)*</strong></p>
  
  <pre><code>Comment Object
  (
      [id] =&gt; 1
      [photo_id] =&gt; 1
      [created] =&gt; 2013-12-20 16:37:02
      [author] =&gt; Nate
      [body] =&gt; This is a cool pic!
  )
  </code></pre>
  
  <p>*<strong>var_dump($comment->author) displays*</strong></p>
  
  <pre><code>NULL @ ::
  </code></pre>
  
  <p>*<strong>var_dump($comments) displays*</strong></p>
  
  <pre><code>object(Comment)#4 (5) { ["id"]=&gt; int(1) ["photo_id"]=&gt; int(1) ["created"]=&gt; string(19) "2013-12-20 16:37:02" ["author"]=&gt; string(4) "Nate" ["body"]=&gt; string(19) "This is a cool pic!" }
  </code></pre>
  
  <p><em>The rest of the code that was requested:</em></p>
  
  <p><strong>Comments class:</strong></p>
  
  <pre><code>class Comment extends DatabaseObject {
      protected static $tableName = "comments";
      protected static $dbFields = array('id', 'photo_id', 'created', 'author', 'body');
      public $id;
      public $photo_id;
      public $created;
      public $author;
      public $body;
  
      public static function findCommentsOn($photo_id = 0) {
          $params = func_get_args();
          $sql = "SELECT * FROM ". self::$tableName ." WHERE photo_id = ? ORDER BY created ASC";
          $results = parent::findByPrepare($sql, $params);
          return $results;
      }
  }
  </code></pre>
  
  <p><strong>MySQLiDatabase class:</strong></p>
  
  <pre><code>class MySQLiDatabase {
          public $conn;
          public $lastQuery;
          public $stmt;
          public $id;
          public $username;
          public $password;
          public $first_name;
          public $last_name;
          private $params;
  
          public function __construct() {
              $this-&gt;connection();
          }
  
          public function connection() {
              $this-&gt;conn = new mysqli(DB_SERVER, DB_USER, DB_PASS, DB_NAME);
              if ($this-&gt;conn-&gt;connect_errno) {
                  return "Failed to connect to MySQL: (" . $this-&gt;conn-&gt;connect_errno . ") " . $this-&gt;conn-&gt;connect_error;
              }
          }
  
          public function query($sql, $params) {
              $this-&gt;lastQuery = $sql;
              $result = $this-&gt;prepareQuery($sql, $params);
              if ($params) { $this-&gt;bindParams($params, $this-&gt;stmt); }
              $this-&gt;stmt-&gt;execute();
              $results = $this-&gt;bindResults($this-&gt;stmt);
  
              return $results;
          }
  
          private function prepareQuery($sql, $params) {
              $this-&gt;stmt = $this-&gt;conn-&gt;prepare($sql);
              $this-&gt;confirmQuery($this-&gt;stmt);
              return $this-&gt;stmt;
          }
  
          public function bindParams($params, $stmt) {
              $types = "";
              for($i = 0; $i &lt; sizeof($params); $i++) {
                  $types .= "s";
              }
  
              $array = array_merge(array($types), $params);
              return call_user_func_array(array($this-&gt;stmt, 'bind_param'), $this-&gt;refValues($array));
          }
  
          public function bindResults($stmt) {
              if ($stmt-&gt;affected_rows === -1) { // SELECT
                  $meta = $this-&gt;stmt-&gt;result_metadata();
                  $params = array();
                  while ($field = $meta-&gt;fetch_field()) {
                      $params[] = &amp;$row[$field-&gt;name];
                  }
                  call_user_func_array(array($this-&gt;stmt, 'bind_result'), $params);
  
                  $results = array();
                  while ($stmt-&gt;fetch()) {
                      $x = array();
                      foreach ($row as $key =&gt; $val) {
                          $x[$key] = $val;
                      }
                      $results[] = $x;
                  }
              } else { // INSERT UPDATE DELETE
                  $results = $stmt-&gt;affected_rows;
              }
              return $results;
          }
  </code></pre>