Data mapper php реализация

Discussion in 'PHP' started by extjs, 4 Jul 2017.

  1. extjs

    extjs Member

    Joined:
    23 Jun 2013
    Messages:
    31
    Likes Received:
    6
    Reputations:
    0
    Это класс БД:

    PHP:
    class DB {

      protected 
    $dbname;
      protected 
    $user;
      protected 
    $password;
      protected 
    $host;
      protected 
    $port;
      protected 
    $charset;
      protected 
    $connection;
      protected 
    $statement;
      protected 
    $queries = [];

      public function 
    __construct($dbname$user 'root'$password ''$host 'localhost'$port 3306$charset 'utf8') {
        
    $this->dbname $dbname;
        
    $this->user $user;
        
    $this->password $password;
        
    $this->host $host;
        
    $this->port $port;
        
    $this->charset $charset;
      }

      public function 
    connect() {
        if (
    $this->connection) {
          return;
        }
        
    $dsn sprintf(
          
    'mysql:dbname=%s;host=%s;port=%d;charset=%s',
          
    $this->dbname,
          
    $this->host,
          
    $this->port,
          
    $this->charset
        
    );
        try {
          
    $this->connection = new PDO($dsn$this->user$this->password, [
            
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
          
    ]);
        } catch (
    PDOException $e) {
          throw new 
    RuntimeException($e->getMessage());
        }
      }

      public function 
    disconnect() {
        
    $this->connection null;
      }

      public function 
    query($sql$bind = []) {
        
    // Подключаемся при первом запросе
        
    $this->connect();
        try {
          
    $t = -microtime(1);
          
    $this->statement $this->connection->prepare($sql);
          
    $this->statement->execute($bind);
          
    $t += microtime(1);
          
    $this->queries[$sql] = $t;
          return 
    $this;
        } catch (
    PDOException $e) {
          throw new 
    RuntimeException($e->getMessage());
        }
      }

      function 
    select($table$conditions = [], $sort = [], $limit 0$offset 0) {
        
    $bind array_values($conditions);
        
    $where = [];
        foreach (
    array_keys($conditions) as $column) {
          
    $where[] = $this->quoteIdentifier($column) . ' = ?';
        }
        
    $order_by = [];
        foreach (
    $sort as $column => $order) {
          
    $order trim($order);
          
    $order $order === 'ASC' ?: 'DESC';
          
    $order_by[] = sprintf('%s %s'$this->quoteIdentifier($column), $order);
        }
        
    $sql sprintf(
          
    'SELECT * FROM %s%s%s%s;',
          
    $this->quoteIdentifier($table),
          
    $where ' WHERE ' implode(' AND '$where) : '',
          
    $order_by ' ORDER BY ' implode(', '$order_by) : '',
          
    $limit sprintf(' LIMIT %d, %d'$offset$limit) : ''
        
    );
        
    $this->query($sql$bind);
        return 
    $this;
      }

      public function 
    insert($table$data) {
        
    $sql sprintf(
          
    'INSERT INTO %s (%s) VALUES (%s);',
          
    $this->quoteIdentifier($table),
          
    implode(', 'array_map([$this'quoteIdentifier'], array_keys($data))),
          
    substr(str_repeat('?, 'count($data)), 0, -2)
        );
        
    $this->query($sqlarray_values($data));
        return 
    $this->lastInsertId();
      }

      public function 
    update($table$data$conditions) {
        
    $bind array_values($data);
        
    $set = [];
        foreach (
    array_keys($data) as $column) {
          
    $set[] = $this->quoteIdentifier($column) . ' = ?';
        }
        
    $where = [];
        foreach (
    $conditions as $column => $value) {
          
    $where[] = $this->quoteIdentifier($column) . ' = ?';
          
    $bind[] = $value;
        }
        
    $sql sprintf(
          
    'UPDATE %s SET %s WHERE %s;',
          
    $this->quoteIdentifier($table),
          
    implode(', '$set),
          
    implode(' AND '$where)
        );
        return 
    $this->query($sql$bind)->affectedRows();
      }

      public function 
    delete($table$conditions) {
        
    $bind array_values($conditions);
        
    $where = [];
        foreach (
    array_keys($conditions) as $column) {
          
    $where[] = $this->quoteIdentifier($column) . ' = ?';
        }
        
    $sql sprintf(
          
    'DELETE FROM %s WHERE %s;',
          
    $this->quoteIdentifier($table),
          
    implode(' AND '$where)
        );
        return 
    $this->query($sql$bind)->affectedRows();
      }

      public function 
    fetch() {
        try {
          return 
    $this->statement->fetch();
        } catch (
    PDOException $e) {
          throw new 
    RuntimeException($e->getMessage());
        }
      }

      public function 
    fetchOne() {
        try {
          return 
    $this->statement->fetchColumn();
        } catch (
    PDOException $e) {
          throw new 
    RuntimeException($e->getMessage());
        }
      }

      public function 
    fetchAll() {
        try {
          return 
    $this->statement->fetchAll();
        } catch (
    PDOException $e) {
          throw new 
    RuntimeException($e->getMessage());
        }
      }

      public function 
    affectedRows() {
        try {
          return 
    $this->statement->rowCount();
         } catch (
    PDOException $e) {
          throw new 
    RuntimeException($e->getMessage());
        }
      }

      public function 
    lastInsertId() {
        try {
          return 
    $this->connection->lastInsertId();
        } catch (
    PDOException $e) {
          throw new 
    RuntimeException($e->getMessage());
        }
      }

      public function 
    beginTransaction() {
        try {
          return 
    $this->connection->beginTransaction();
        } catch (
    PDOException $e) {
          throw new 
    RuntimeException($e->getMessage());
        }
      }

      public function 
    commit() {
        try {
          return 
    $this->connection->commit();
        } catch (
    PDOException $e) {
          throw new 
    RuntimeException($e->getMessage());
        }
      }

      public function 
    rollBack() {
        try {
          return 
    $this->connection->rollBack();
        } catch (
    PDOException $e) {
          throw new 
    RuntimeException($e->getMessage());
        }
      }

      public function 
    quoteIdentifier($name) {
        return 
    '`' str_replace('`''``'$name) . '`';
      }

      public function 
    getQueries() {
        return 
    $this->queries;
      }

      public function 
    __destruct() {
        
    $this->disconnect();
      }
    }
    В нем много копипасты.

    Это уже реализация:

    PHP:
    abstract class Entity implements IteratorAggregate {

      protected 
    $data;

      public function 
    __construct(array $data = []) {
        
    $this->data $data;
      }

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

      public function 
    __get($property) {
        return 
    $this->data[$property] ?? null;
      }

      public function 
    __isset($property) {
        return isset(
    $this->data[$property]);
      }

      public function 
    __unset($property) {
        unset(
    $this->data[$property]);
      }

      public function 
    getIterator() {
        
    $obj = new ArrayObject($this->data);
        return 
    $obj->getIterator();
      }

      public function 
    toArray() {
        return 
    $this->data;
      }
    }


    // Код ужасен
    abstract class EntityMapper {

      protected 
    $db;
      protected 
    $fields = [];
      protected 
    $primaryKey;
      
    // Эти свойства переопределяются в классах наследниках
      
    protected $table;
      protected 
    $entitylClass;

      public function 
    __construct($db) {
        
    $this->db $db;
        
    $this->describeFields();
      }

      public function 
    find($id) {
        
    $row $this->db->select($this->table, [$this->primaryKey => $id])->fetch();
        return 
    $row
          
    $this->createEntity($row)
          : 
    null;
      }
      public function 
    findAll(array $conditions = [], array $sort = [], int $limit 0int $offset 0): array {
        
    $rows $this->db->select($this->table$conditions$sort$limit$offset)->fetchAll();
        return 
    $this->createEntities($rows);
      }

      public function 
    save(Entity $entry): bool {
        
    $data = [];
        foreach (
    $this->fields as $field) {
          
    $data[$field] = $entry->$field;
        }
        if (
    $entry->{$this->primaryKey}) {
          unset(
    $data[$this->primaryKey]);
          return (bool) 
    $this->db->update($this->table$data, [
            
    $this->primaryKey => $entry->{$this->primaryKey}
          ]);
        }
        
    $id $this->db->insert($this->table$data);
        if (
    $id) {
          
    $entry->{$this->primaryKey} = $id;
          return 
    true;
        }
        return 
    false;
      }

      public function 
    delete(Entity $entry): bool {
        
    $affected $this->db->delete($this->table, [
          
    $this->primaryKey => $entry->{$this->primaryKey}
        ]);
        if (
    $affected) {
          
    $entry->{$this->primaryKey} = null;
          return 
    true;
        }
        return 
    false;
      }

      protected function 
    describeFields() {
        
    $sql sprintf('DESCRIBE %s;'$this->db->quoteIdentifier($this->table));
        
    $rows $this->db->query($sql)->fetchAll();
        foreach (
    $rows as $row) {
          
    $this->fields[] = $row['Field'];
          if (
    $row['Key'] === 'PRI') {
            
    $this->primaryKey $row['Field'];
          }
        }
      }

      protected function 
    createEntity(array $row) {
        return new 
    $this->entityClass($row);
      }

      protected function 
    createEntities(array $rows) {
        return 
    array_map([$this'createEntity'], $rows);
      }
    }


    class 
    MapperRepository {

      protected 
    $db;
      protected 
    $mappers = [];

      public function 
    __construct(DB $db) {
        
    $this->db $db;
      }

      public function 
    getDB() {
        return 
    $this->db;
      }

      public function 
    getMapper($name) {
        if (!isset(
    $this->mappers[$name])) {
          
    $this->mappers[$name] = new $name($this->db);
        }
        return 
    $this->mappers[$name];
      }
    }
    Тест:

    PHP:
    class User extends Entity {
      
    // Add some methods
    }


    class 
    UserMapper extends EntityMapper {
      protected 
    $table 'users';
      protected 
    $entityClass 'User';
    }

    /*

    CREATE TABLE `users` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `username` varchar(20) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
    `email` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
    `password` varchar(20) NOT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `username` (`username`),
    UNIQUE KEY `email` (`email`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8

    */


    function dump($v) {
      echo 
    '<pre>' print_r($v1) . '</pre>';
    }


    $db = new DB('test');
    $repo = new MapperRepository($db);

    $user = new User([
      
    'username' => 'u' uniqid(),
      
    'email' => uniqid() . '@example.com',
      
    'password' => substr(md5(uniqid()), 06)
    ]);

    $map $repo->getMapper('UserMapper');
    $map->save($user);

    $users $map->findAll([], ['id' => null], 10);

    dump($users);

    dump($db->getQueries());
    Вывод:

    Code:
    Array
    (
        [0] => User Object
            (
                [data:protected] => Array
                    (
                        [id] => 35
                        [username] => u595bbe5e129fd
                        [email] => [email protected]
                        [password] => 1ecfee
                    )
    
            )
    
        [1] => User Object
            (
                [data:protected] => Array
                    (
                        [id] => 34
                        [username] => u595bbe5dd20eb
                        [email] => [email protected]
                        [password] => 134a6a
                    )
    
            )
    
        [2] => User Object
            (
                [data:protected] => Array
                    (
                        [id] => 33
                        [username] => u595bbe5d777d6
                        [email] => [email protected]
                        [password] => 01ffec
                    )
    
            )
    
        [3] => User Object
            (
                [data:protected] => Array
                    (
                        [id] => 32
                        [username] => u595bbe5d130ac
                        [email] => [email protected]
                        [password] => 2a64bc
                    )
    
            )
    
        [4] => User Object
            (
                [data:protected] => Array
                    (
                        [id] => 31
                        [username] => u595bbe5cd9b8d
                        [email] => [email protected]
                        [password] => 3ef9e6
                    )
    
            )
    
        [5] => User Object
            (
                [data:protected] => Array
                    (
                        [id] => 30
                        [username] => u595bbe5c84d33
                        [email] => [email protected]
                        [password] => 1f0196
                    )
    
            )
    
        [6] => User Object
            (
                [data:protected] => Array
                    (
                        [id] => 29
                        [username] => u595bbe5c56495
                        [email] => [email protected]
                        [password] => 6624e1
                    )
    
            )
    
        [7] => User Object
            (
                [data:protected] => Array
                    (
                        [id] => 28
                        [username] => u595bbe5c0bafb
                        [email] => [email protected]
                        [password] => c08747
                    )
    
            )
    
        [8] => User Object
            (
                [data:protected] => Array
                    (
                        [id] => 27
                        [username] => u595bbe5b98df5
                        [email] => [email protected]
                        [password] => f171dd
                    )
    
            )
    
        [9] => User Object
            (
                [data:protected] => Array
                    (
                        [id] => 26
                        [username] => u595bbe4dde2a1
                        [email] => [email protected]
                        [password] => 8305f7
                    )
    
            )
    
    )
    Array
    (
        [DESCRIBE `users`;] => 0.0033631324768066
        [INSERT INTO `users` (`id`, `username`, `email`, `password`) VALUES (?, ?, ?, ?);] => 0.048413991928101
        [SELECT * FROM `users` ORDER BY `id` DESC LIMIT 0, 10;] => 0.00035905838012695
    )
    Скажите где у меня ошибки в реализации? код на 100% рабочий.