Nie bawimy się w Repository Pattern – zamiast tego tworzymy model przypominający te z Laravela tak, jak to tylko możliwe. Do dzieła.

Nasz model wygląda tak:

class PersonModel extends Model {

    protected static $tablename = 'people';
    protected static $keys = ['name', 'age'];
 
}

I tyle konfiguracji ma wystarczyć. Reszta w klasie bazowej. Chcemy osiągnąć coś takiego:

$person = new PersonModel();
$person->setConn($conn);
$person->name = "Jim";
$person->age = 22;
echo $person->name . " " . $person->age;
$person->save(); //INSERT INTO DB
$person->age = 29;
$person->save(); //UPDATE

Dodajemy nowe pola w klasie bazowej:

abstract class Model {
    
    public $id;
    private $data = array();

    protected static $conn;
    const OPERATORS = ['=', '>=', '>', '<=', '<', '<>'];

    //(...)

}

Teraz konstruktor:

abstract class Model {
    
    //(...)

   public function __construct()
    {
        $this->id = null;
    }

}

Metody get i set:

abstract class Model {

//(...)

public function __set($name, $value) {
        $this->data[$name] = $value;
    }

public function __get($name){
       if(array_key_exists($name, $this->data)) 
            return $this->data[$name];
}
}

Już teraz możemy wykonać tę część:

$person = new PersonModel();
$person->setConn($conn);
$person->name = "Jim";
$person->age = 22;
echo $person->name . " " . $person->age;

Metoda save wygląda tak:

abstract class Model {
    
    //(...)

    public function save(){
        $data = $this->data;
        if(is_null($this->id)){
            //INSERT INTO table ....
        } else {
            //UPDATE table SET key = 'val', key2 = 'val2' WHERE id = $this->id
        }
    }
        
}

Piszemy część robiącą insert podobnie jak w statycznej metodzie insert:

abstract class Model {
    
    //(...)

    public function save(){
        $data = $this->data;
        if(is_null($this->id)){
            $table = static::$tablename;
            $keys = static::keysString();
            $vals = static::valuesString();
            $query = "INSERT INTO {$table} {$keys} VALUES {$vals}";
            $stmt = static::$conn->prepare($query);
            $stmt->execute($data);
            $this->id = static::$conn->lastInsertId();
        } else {
            //UPDATE table SET key = 'val', key2 = 'val2' WHERE id = $this->id
        }
    }
        
}

Podobnie z UPDATE – logika może i trochę pokręcona, ale możemy to sobie zdebugować:

abstract class Model {
    
    //(...)

    public function save(){
        $data = $this->data;
        if(is_null($this->id)){
            //insert to id
            //assign $this->id to lastInsertedId
        } else {
            $table = static::$tablename;
            $setSTR = "";
            foreach($data as $key => $value){
                $setSTR .= $key;
                $setSTR .= " = ";
                $setSTR .= '"'.$value.'"';
                if($key === array_key_last($data))
                    break;
                $setSTR .= ", ";
            }

            $query = "UPDATE {$table} SET {$setSTR} WHERE id = {$this->id}";
            $stmt = static::$conn->prepare($query);
            $stmt->execute();
        }
    }
        
}

Teraz bez problemu możemy zapisywać robiąc zarówno insert jak i update:

$person->name = "Jim";
$person->age = 22;
echo $person->name . " " . $person->age;
$person->save();
$person->age = 29;
$person->save();