Высокоуровневые методы (например, find****) должны быть обёрткой над кодом, который использует билдер запросов. У тебя дубляж низкоуровневого кода работы с PDO повсеместно.
Когда я свою ORM писал, у меня это выглядело так:
// высокоуровневый метод делающий ТОЛЬКО выборку по id
public function findModelById($id): Model
{
return $this->findModelByParams(
array('where' => array('`id` = ?i' => array($id)))
);
}
// УНИВЕРСАЛЬНЫЙ низкоуровневый метод, находящий объект модели по любым параметрам
public final function findModelByParams(array $params=array()): Model
{
$res = parent::createQuerySelect($params);
$object = $this->createModelFromDatabaseResult( is_object($res) && $res->getNumRows() ? $res->fetch_assoc() : array() );
return $object;
}
// построитель любого sql-запроса на SELECT, его исполнение и возвращение объекта типа РЕЗУЛЬТАТ
protected final function createQuerySelect(array $params)
{
$params = self::makeSqlFromParams($params); // это сам билдер!!!!!!!!!!!!!!
$sql = 'SELECT' . $params['what'] . 'FROM ?f' . $params['join'] . $params['where'] . $params['order'] . $params['limit'];
array_unshift($params['args'], $this->getTableName());
array_unshift($params['args'], $sql);
$result = call_user_func_array(array($this->getDb(), 'query'), $params['args']);
return $result;
}
Аналогично был метод на удаление:
protected final function createQueryDelete(array $params)
{
$params = self::makeSqlFromParams($params);
$sql = 'DELETE FROM ?f ' . $params['where'] . $params['limit'];
array_unshift($params['args'], $this->getTableName());
array_unshift($params['args'], $sql);
return call_user_func_array(array($this->getDb(), 'query'), $params['args']);
}
insert/update в подобном билдере не нуждались, они были частью метода save, в точности как у тебя.
Твоя обертка будет работать, но дубляжа кода много, это прям бросается в глаза. Ты можешь не писать билдер запросов, но тогда ты будешь описывать каждый новый метод и в каждом методе у тебя везде будет написано одно и тоже - prepare/execute/fetch
Это всё будет давить, особенно когда тебе понадобится сделать методы типа findUserByIdOrNameAndSurname($id, $name, $surname)
где нужно будет исполнить запрос вида select * from users where id = ? OR name = ? AND surname = ?
Тут уже не спасет твой метод findByAttribute.
Тебе захочется иметь "красивый" метод высокого уровня, в итоге ты либо напишешь его в привычной стилистике
public static function findUserByIdOrNameAndSurname($id, $name, $surname)
{
$stmt = self::$db->prepare('SELECT * FROM `' . static::getTableName() . '` WHERE id = ? or name = ? and surname = ?');
$stmt->execute([$id, $name, $surname]); // или как тут правильно, не знаю..
$r = $stmt->fetch();
return $r ? new static($r) : null;
}
либо сам придешь к необходимости создать метод findModelBySql($sql).
Но и то и то будет нагромождением повторяющихся кусков кода.
spoilerPS дзен AR ты познал, крайне советую остановиться и не повторять моих ошибок. Я тоже писал свою ОРМ, только это никому нахер не нужно. И твоя ОРМ тоже не нужна, даже тебе. Есть проверенные решения, тот же eloquent. Не занимайся ерундой.