Loading multiple Objects from one Class (php)

Hey everyone,

I run into one thing with OOP programming, which I don’t quite understand since it is defined differently by one person than by another.

Suppose we have the following class.

<?php

class User extends DB {
  public $username;

  public function setUsername($name) {
    $this->username = $name;
  }

  public function getUsername() {
    return $this->username;
  }
}

$user = new User();
$user->setUsername('jan');

echo $user->getUsername();
?>

This will represent a single user and you can set a user by.

$user = new User();
$user->setUsername('jan');

But let’s say we want to get all users that we have set. We can never get this out of this class since it is about a single object.

May contain a class of multiple names? For example cars, bicycles, users, etc.

For example, I have the next class: User

<?php

class User extends DB {
 
public $username;
public $userId;

public function makeNewUser($userName, $userId){

   return  $this->addtoDB('users', 'username', $userName, 'userId', $userId);

}

public function getUser($userId){

    return $this->retrieveFromDb('users', 'userId', $userId);

} }

?>

It extends the DB class. That handles the SQL requests. Now I want to get all Users, no matter what userId they have.

As far as I know, a single object is the "user" Which is created in this class? (a single user, one).
I cannot add the public function getUsers on the User class since it is about one user (not five, ten of thousand).

What I am doing wrong? :joy:

  1. Property should be private!!! (public $username).

  2. Your object User could be called Active Record. For retreive a lot of users you should declare class Recordset (e.g.) that includes collection of User objects. Actually that is quite complex thema…

1 Like

Sort of?

A single user is created when you do this line:

Defining a class is laying out a blueprint. You can build multiple units from that blueprint. It’s called instantiation, and the individual unit is called an instance of that class.

$user1 = new User(); //This is an instance.
$user2 =  new User(); //This is another.

$user1 and $user2 can have different usernames; the class defines that there will BE a username, but it doesnt mean they will be the same thing between different instances.

I can do anything with these objects that I can do with any other; I can use them as parameters in functions, or for your example…

Users only exist once you’ve created their object. If you want to get a list of all users in the database, that’s a separate thing. But once you’ve got your list, you could create an object for every user, and store them in an array.

Yes, you should maybe have a different class for handling multiple users, usergroup for example (or perhaps a more generic class for multiple anythings).
It could fetch a list of user objects and store them in an array.

To give a very basic and somewhat cliche example, consider a deck of cards. Each card is an instance of the Card object; the deck as a whole is an instance of the Deck object.

I can define a Card class, a Deck class, and invoke one with the other:

class Card {
   private $suit;
   private $value;
   public function getSuit() { return $suit }
   public function getValue() { return $value }
   public function __toString() { return $value.$suit; }
   public function __construct($suit,$value) {
      $this->suit = $suit;
      $this->value = $value;
   }
}

class Deck {
   private $cards
   public function draw($x = 1) {
      $out = []
      while($x--) {
        array_push($out,array_pop($cards));
       }
      return $out;
   }
   public function shuffle() {
      shuffle($this->cards);
      echo "Shuffling the Deck";
   }
   public function __construct() {
       $this->cards = [];
       foreach(['Spade','Diamond','Heart','Club'] as $suit) {
          foreach(['1','2','3','4','5','6','7','8','9','10','J','Q','K','A'] as $value) {
              array_push($this->cards, new Card($suit,$value));
           }
         }
         $this->shuffle();
    }

$mydeck = new Deck(); //I now have a Deck full of Cards.
$hand = $mydeck->draw(5); // $hand is now an array of 5 cards.
print_r($hand);

(and yes, this is a very basic set of functions. This is a demonstration of instantiation, not of a complete and robust card deck).

4 Likes

Hey Everyone (again :wave:),

There went a few days since I have written the first post.
After that, I made a new Github repository to practice my OOP skills.

.

I started to make a folder with Classes

  • The database class (handles all request with the database)
  • The mailer class (can send mails)
  • The management class (can handle Users)

https://github.com/dseegers/OOP-for-learning

some functions will not work yet :slight_smile: :rofl:

When you look at the code (and the structure), do you still have feedback (things that I can improve?)

.
|-- App
|   `-- Classes
|       |-- Database
|       |   `-- DB.php
|       |-- Helper
|       |   `-- Helper.php
|       |-- Mailer
|       |   `-- SendMail.php
|       `-- Management
|           `-- User.php
|-- LICENSE
|-- README.md
|-- Settings
|   `-- config.php
|-- Template
|   `-- Header.php
|-- bootstrap.php
|-- composer.json
|-- composer.lock
|-- index.php```

Let’s start with some low-hanging fruit:

  1. Don’t put index.php in the root of your project - create a public/ directory and put in there. That way when you serve the project from a web server (apache, nginx, etc) you can point it to the public/ directory and all other files will not be visible to the public. You could achieve the same thing with an .htaccess file in Apache, but it’s not nearly as nice as completely closing it off by using a public/ directory.

  2. The DB class uses constants from config.php - which makes it non-reusable if you ever wanted to have more than one database connection in a project. Instead the database controller should take constructor arguments with username, password, host, etc and use that instead of the constants. If you want to use constants to instantiate the class then that’s fine, but the class itself shouldn’t know about them.

  3. Do not include the header first thing as you’ll be stuck with it - if you later find there is an error and want to output a completely different page any more you can’t. Most frameworks have a separate set of classes for template rendering and usually call ob_start() before they start rendering to avoid anything being actually output and then ob_get_clean() to get the result at the end. If anything goes wrong in between they just discard the output and start rendering an error page instead.

  4. Why is there an empty Helper class? It doesn’t get much more generic than that and I would strongly advise against it, as such classes attract all kinds of stuff that don’t belong together in a single class.

  5. The DB class uses prepared but there are no placeholders in the queries, so they are still vulnerable to SQL injection. Instead of using values in queries directly use ? and then pass all the parameters at the end when executing the query. See https://phptherightway.com/#pdo_extension

There’s more, but I’ll keep it at this for now :slight_smile:

3 Likes

Thank you for your reply :slight_smile: ,

I will look at your feedback and use it.
After I use your feedback. Will make a new reply :rofl::ok_man::ok_woman:

@rpkamp,

When I look at point 5. You have it about preparing before you actually execute the query.
The reason I did not do that directly. Is because I want to have the possibility to insert an array.
Since you must be able to use it for anything.

I want to use the same function to insert 20 columns as 5

What is the best way to read the complete array and push it in a prepare statement.

$db = new App\Classes\Management\DB();

$db->makeRecord('myTable', [
    "colom1" => "bar",
    "colom2" => "foo",
    "colom3" => "foop",
]);

I would normally do it like so:

$placeholders = substr(str_repeat('?,', count($values)), -1);
$query = 'INSERT INTO table ('.implode(',', array_keys($values)).' VALUES ('.$placeholders.')';

And then you bind and execute exactly like you normally would with PDO.

Okay Let me try to understand :slight_smile:.

Let us say we have the function makeRecord.
First, you add the table name. And after that the values.

public function makeRecord($table , $values)
    {      
        $placeholders = substr(str_repeat('?,', count($values)), -1);
        $query = 'INSERT INTO table ('.implode(',', array_keys($values)).' VALUES ('.$placeholders.')';
        foreach($values as $key => $value){
          $this->sth->bindParam($value, $key);
    }
        $this->sth = $this->DB->prepare($query);
        $this->sth->execute($query);
    }

    • We generate the placeholder
      This is in the placeholder
      string(1) ","
  1. Second, we make the query
    The query looks like this
    string(47) "INSERT INTO table (Username,Password VALUES (,)"

  2. Then I want to loop through the $values and bind them.

Fatal error: Uncaught Error: Call to a member function bindParam() on null in C:\xampp\htdocs\OOP\App\Classes\Database\DB.php:60 Stack trace: #0 C:\xampp\htdocs\OOP\index.php(9): App\Classes\Database\DB->makeRecord('users', Array) #1 {main} thrown in C:\xampp\htdocs\OOP\App\Classes\Database\DB.php on line 60 

Before the execute it script will stop with the error.
So it cannot bind them.

When I look at the
$this->sth->bindParam($value, $key)
function. Then is this not quite correct.

Ah, I see the line for $placeholders is incorrect. It should be:

$placeholders = substr(str_repeat('?,', count($values)), 0, -1);

Thank you,

But I think my bindParam is still wrong :slight_smile:

Here I call the function

echo $db->makeRecord('users', ['UserName' => 'Dss', 'Password' => '123']);

When I look at the code


   public function makeRecord($table, $values)
    {      
        $placeholders = substr(str_repeat('?,', count($values)), 0, -1);
        $query = 'INSERT INTO table ('.implode(',', array_keys($values)).' VALUES ('.$placeholders.')';
        foreach($values as $key => $value){
            $this->sth->bindParam(':'.$key, $value, PDO::PARAM_INT);   
    }
        $this->sth = $this->DB->prepare($query);
        $this->sth->execute($query);
    }

I loop through the data using this loop (documented as $stmt->bindParam(':id', $id, PDO::PARAM_INT))

     foreach($values as $key => $value){
            $this->sth->bindParam(':'.$key, $value, PDO::PARAM_INT);   (this is line 60)
    }

When I dump the $key I get the right value (also when I dump the $value).

For example, using this

     foreach($values as $key => $value){
            echo $key;
            echo '<br>';
            echo $value;
            echo '<br>';
            // $this->sth->bindParam(':'.$key, $value, PDO::PARAM_INT);   
    }

Wil result in this:

UserName
Dss
Password
123

But when I try to bind them. I get this message

**Fatal error** : Uncaught Error: Call to a member function bindParam() on null in C:\xampp\htdocs\OOP\App\Classes\Database\DB.php:60 Stack trace: #0 C:\xampp\htdocs\OOP\index.php(9): App\Classes\Database\DB-&gt;makeRecord('users', Array) #1 {main} thrown in  **C:\xampp\htdocs\OOP\App\Classes\Database\DB.php**  on line  **60**

Why bother with bind param?
Just put the array of values in execute($values)

1 Like

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.