Ostatnie szlify na modelu – startsWith i endsWith oraz stosowanie operatora LIKE w SQL. Do dzieła.

Metoda startsWith:

abstract class Model {
    
    public $id;
    private $data = array();
    protected static $conn;
    const OPERATORS = ['=', '>=', '>', '<=', '<', '<>'];

   //(...)

    public static function startsWith($col, $starts, $type=PDO::FETCH_OBJ){
        $table = static::$tablename;
        if(!in_array($col, static::$keys))
            return;
        $query = "SELECT * FROM $table WHERE {$col} LIKE '{$starts}%'";
        $stmt = static::$conn->prepare($query);
        $stmt->execute();
        return $stmt->fetchAll($type);
    }
}

Dzięki nadużywaniu static nasza klasa pochodna może wyglądać tak:

class PersonModel extends Model {

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

Zaś użycie (po ustawieniu conn dla Model) jest takie proste:

print_r(PersonModel::startsWith("name", "J"));
//wszystko na litere J

Udało się osiągnąć fajną sytuację, w której logika jest na górze, proste ustawienia na dole, działające API na końcowej klasie i jej obiekcie.

To teraz endsWith:

abstract class Model {
    
   //(...)

    public static function endsWith($col, $ends, $type=PDO::FETCH_OBJ){
        $table = static::$tablename;
        if(!in_array($col, static::$keys))
            return;
        $query = "SELECT * FROM $table WHERE {$col} LIKE '%{$ends}'";
        $stmt = static::$conn->prepare($query);
        $stmt->execute();
        return $stmt->fetchAll($type);
    }
}

Sprawdzamy, czy kolumna istnieje na „dolnym” modelu (który tego będzie używać), jeśli tak, wyszukujemy rekordy, które tak się kończą:

print_r(PersonModel::endsWith("name", "ne"));
//Array ( [0] => stdClass Object ( [ID] => 6 [name] => Jane [age] => 22 ) )

Warto by było jeszcze popracować nad bezpieczeństwem, bindparams i tak dalej.