Организация работы с базой данных - PullRequest
0 голосов
/ 17 августа 2011

У меня есть опыт работы с PHP, но нет никого в архитектуре приложения
Теперь я хочу создать свой собственный «велосипед». Это что-то бесполезное, может быть, мини-фреймворк или мини-приложение, я хочу получить здесь немного опыта.

Теперь мне нужно написать классы для работы с базой данных и classese для сущностей (один из них User)
У меня есть следующий код для базы данных (некоторые чеки и методы опущены, чтобы минимизировать этот вопрос):

namespace DataBase;
class DataBase {
    /**
     *
     * @var \PDO $pdo
     */
    public $pdo;
    public function __construct($host, $dbname, $username, $password=''){

        $this->pdo = new \PDO('mysql:host='.$host.';dbname='.$dbname, $username, $password,
            array(\PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'UTF8'"));
        $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);

    }
    /**
     *
     * @param string $statement
     * @return Statement
     */
    public function prepare($statement){
        return new Statement($this->pdo->prepare($statement));
    }
}


namespace DataBase;

class Statement {
    private $stmt;

    public function __construct(\PDOStatement $stmt) {
        $this->stmt = $stmt;
    }

    public function query() {
    try {
        $this->stmt->execute();
        return $this; //for chaining
    }

    public function bind($key, $value) {
        $this->stmt->bindValue($key, $value, $this->typeof($value));
        return $this; //for chaining
    }
        //some methods for fetching data(works with fetch,fetchAll, fetchColumn and different PDO::FETCH_ methods

    public function fetchUpdate($obj) {
        $this->stmt->setFetchMode(\PDO::FETCH_INTO, $obj);
        $this->stmt->fetch();
    }

    public function fetchRow() {
        return $this->stmt->fetch(\PDO::FETCH_OBJ);
    }
    public function fetchRowClass($class) {
        return $this->stmt->fetchObject($class);
    }
}

И немного пустышки для класса пользователя

<?php

/**
 * Description of User
 *
 * @author riad
 */
class User {
    private $id;

    private $name = null;

    private $status = null;

    private $hasInfo = false;

    private static $cache=array();
    public function __construct() {

    }
    public function getId() {
        return $this->id;
    }
    public function getName() {
        if(!$this->hasInfo)
            $this->getInfo ();
        return $this->name;
    }
    public function isAuthorized(){
        return $this->status!="noauth";
    }
    public static function createById($id) {
        // I want this method to cerate user from id, then get info only I will use it after that
        if(array_key_exists($id,self::$cache)){
            return self::$cache[$id];
        }
        $user = new User;
        $user->id = $id;
        return $user;
    }
    private function getInfo(){
        try{
            \FrontController::getInstance()->getDB()->
                prepare('SELECT * FROM `users` WHERE `id`=:id')->
                bind('id', $this->id)->query()->fetchUpdate($this);
            $this->hasInfo = true;
        }
        catch(\DataBase\NotFoundException $dbe){
            $this->status = "deleted";
        }
    }
    public static function createFromRequest($request){
        $user = new User;
        try{
            //try get data from cookie
            \FrontController::getInstance()->getDB()->
                prepare('SELECT * FROM `users` WHERE `session` = :session AND `id`= :id')->
                bind('id', $id)->bind('session',$session)->query()->
                fetchUpdate($user);
        }
        catch(... $e){
            $user->status = "noauth";
            $user->id = 0;
            // make it unregged
        }
        return $user;
    }
}

?>

У меня есть некоторые проблемы с этим.

  • Я не хочу задавать свойства из базы данных, которые не перечислены в подпунктах списка классов (это, конечно, не так важно). Я знаю, что могу использовать
    public function __call($name,$value){
    //do nothing;
    }
  • Я хочу сделать этот реквизит приватным, но хочу также использовать $stmt->fetchUpdate($obj) Я знаю, что могу использовать
    public function __call($name,$value){
    $this->$name=$value;
    }
    , но это так же, как объявить реквизит общедоступным, и он находится в дороге с первым пунктом Я также могу использовать
    public function __call($name,$value){
    if($name=='id'){
        $this->id=$value;
    }
    else if($name=='status'){
        $this->status=$value;
    }
    }
    Но не удобно писать его для каждого класса сущностей и не сохранять, как из-за гласности этих методов
  • Я хочу установить $this->hasInfo на true, когда получу этот класс из базы данных. Я знаю, что могу изменить свой класс Database, чтобы всегда устанавливать некоторую переменную на true, когда по умолчанию это false. Но, похоже, это не элегантно.
  • Я хочу обновить кеш, когда я установил id (возможно, он используется как предыдущая точка)
  • Можно ли избежать fetchRowClass прямой записи в props и использовать setter как с fetchUpdate? Или, может быть, разрешить fetchUpdate прямой доступ?

Я знаю, что пишу много кода самостоятельно, но мне нужно ваше мнение:

  • Что мне улучшить?
  • Каковы другие / наилучшие возможные решения проблем из предыдущего списка?

Надеюсь, это не так сложно читать и понимать.
Рад видеть любые предложения
С уважением, Алекс

1 Ответ

1 голос
/ 18 августа 2011

Несколько советов: [основываясь на моем собственном опыте и каркасах, которые я уже использовал]

В основном, что вы должны / хотите / могли бы сделать, - это создать суперкласс для всех предложений в вашей модели.Этот класс будет содержать ссылку на экземпляр базы данных, и он будет иметь все общие методы для вашей модели, например getById($id), getAll(), getPaginated() и т. Д.

Другая цель этого суперклассаэто отобразить результаты из базы данных в экземпляры классов вашей модели.Таким образом, в конце ваш пользовательский класс будет иметь только те свойства, методы доступа и методы, которые специфичны для этого класса, например, специальные запросы или что-то в этом роде.

Вот пример того, как это может выглядеть:

Class Model{
    protected function getById($_id){
        $_table = get_class($this);
        $anonymous = $this->_getObjectById($_table,$_id); //this method will execute a query (unsing PDO) and return a StdClass object with the results
        $this->mapTable($anonymous,$_table); //this method will take the StdClass instance and will transform it into a $_table Instance
    }

    private function mapTable($stdInstance,$model_name){
        foreach($stdInstance as $key => $value){
            try{
                if(property_exists($this,$key)){
                    $this->$key = $value; //you could declare the model's properties     as protected... or you could create accessors and call them here
                }
            } catch(Exception $ex) {
                /* something went wrong o_O */
            }
     }

Class User extends Model{
     protected $id;
     protected $name;
     .....
}

Class Controller{
    public function index(){
         $user = new User();
         $user->getById($_GET['id']);
         print_r($user);
         //now you can pass the $user object to the View to display it
    }
}

в нескольких словах ... Класс Model очень маленький ORM .Вы можете попытаться создать свой собственный ORM (как я это сделал), но вы столкнетесь с множеством проблем при попытке сопоставить отношения между объектами: Nx1,1xN, NxN, 1x1, наследование, «более глубокие отношения» и n + 1 проблема .Вам также необходимо каким-то образом определить структуру модели, чтобы ваш ORM мог ее понять, возможно, используя файлы YAML / XML или считывая структуру непосредственно из структуры таблицы вашей базы данных, или имея соглашение об именовании в ваших свойствах ...

это действительно интересное поле:)

Надеюсь, это поможет и удачи

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...