За последние 24 часа нас посетили 18053 программиста и 1679 роботов. Сейчас ищет 1131 программист ...

Model, Mapper, Generator

Тема в разделе "Решения, алгоритмы", создана пользователем Padaboo, 10 ноя 2010.

  1. Padaboo

    Padaboo Старожил
    Команда форума Модератор

    С нами с:
    26 окт 2009
    Сообщения:
    5.242
    Симпатии:
    1
    PHP:
    1. <?php
    2. /**
    3.  * Description of ModelGenerator
    4.  * @author padaboo
    5.  */
    6. class ModelGenerator {
    7.     /**
    8.      * путь до шаблона с моеделью
    9.      * @var string
    10.      */
    11.     private $modelTpl;
    12.     /**
    13.      * папка куда помешаются
    14.      * созданные классы
    15.      * @var string
    16.      */
    17.     private $output;
    18.      /**
    19.      * объект mysqli
    20.      * @var object
    21.      */
    22.     private $db;
    23.     /**
    24.      * конструктор
    25.      * @param string $modelTpl
    26.      * @param string $output
    27.      * @param object $db
    28.      */
    29.     public function  __construct($db,
    30.                                  $modelTpl  = 'src/class/models/modeltpl.php',
    31.                                  $output    = 'src/class/models/'){
    32.      if(!file_exists($modelTpl)) throw new Exception ("$modelTpl не найден");
    33.      if(!is_readable($modelTpl)) throw new Exception ("$modelTpl не читаем");
    34.      if(!is_dir($output)) throw new Exception ("$output не директория");
    35.  
    36.      $this->modelTpl = file_get_contents($modelTpl);
    37.      $this->output  = $output;
    38.      $this->db      = $db;
    39.     }
    40.     /**
    41.      * Создает модель и маппер
    42.      * из таблицы, на основе шаблонов
    43.      * @param string $table
    44.      */
    45.     public function create($table){
    46.         $name   = $this->getClassName($table);
    47.         if(file_exists($this->output .$name. '.php'))
    48.                  throw new Exception ("$name уже существует");
    49.  
    50.          $fields = $this->getFields($table);
    51.          $this->buildModel($fields, $name, $table);
    52.     }
    53.     /**
    54.      * @param string $table
    55.      * возвращает массив полей
    56.      * @return array
    57.      */
    58.     private function getFields($table){
    59.             if(!$result = $this->db->query("SHOW CREATE TABLE  `$table`"))
    60.                     throw new Exception ("таблица $table не найдена");
    61.  
    62.             $row = $result->fetch_array();
    63.             preg_match_all("/`(.*)`/", $row[1], $out);;
    64.             $fields = array_unique($out[1]);
    65.             unset($fields[array_search($table, $fields)]);
    66.             return $fields;
    67.     }
    68.     /**
    69.      * Создает имя для модели
    70.      * @param string $table
    71.      * @return string
    72.      */
    73.     private function getClassName($table){
    74.         if(substr($table, -1) == 's')
    75.             $table = substr($table, 0, strlen($table)-1);
    76.  
    77.         $table = ucfirst(strtolower($table));
    78.         return $table;
    79.     }
    80.     /**
    81.      * Формирует данные для шаблона модели
    82.      * @param array $fields
    83.      * @param string $name
    84.      */
    85.     private function buildModel($fields, $name, $table){
    86.         $fieldString = '';
    87.         unset($fields[array_search('id', $fields)]);
    88.         foreach ($fields as $field) {
    89.             $c = (end($fields) == $field)? "":",\r\n";
    90.             $fieldString .= "\t '$field' => '' $c";
    91.         }
    92.         $data = str_replace(array('{name}','{fields}',  '{table}'),
    93.                             array($name,   $fieldString, $table),
    94.                             $this->modelTpl            );
    95.        
    96.         $file = $this->output .$name. '.php';
    97.         touch($file);
    98.         chmod($file, 0644);
    99.         file_put_contents($file, $data);
    100.     }
    101. }
    102. ?>
    103.  
    шаблон
    PHP:
    1. <?php
    2. class {name} implements Iterator{
    3.     private  $index = 0;
    4.     private  $row = array('id' => NULL,
    5.     {fields}
    6.         );
    7.  
    8.     public   $table = '{table}';
    9.  
    10.     public function getTable(){
    11.         return $this->table;
    12.     }
    13.  
    14.     public function __set($name, $value) {
    15.         $this->isValid($name);
    16.         $this->row[$name] = $value;
    17.     }
    18.  
    19.     public function  __get($name) {
    20.         $this->isValid($name);
    21.         return $this->row[$name];
    22.     }
    23.  
    24.     protected function isValid($name) {
    25.         if (!array_key_exists($name, $this->row)) {
    26.             trigger_error("Атрибута $name не существует", E_USER_ERROR);
    27.         }
    28.     }
    29.  
    30.     public function rewind(){
    31.         $this->index = 0;
    32.     }
    33.  
    34.     public function current(){
    35.         $k = array_keys($this->row);
    36.         $row = $this->row[$k[$this->index]];
    37.         return $row;
    38.     }
    39.  
    40.     public function key(){
    41.         $k = array_keys($this->row);
    42.         $row = $k[$this->index];
    43.         return $row;
    44.     }
    45.  
    46.     public function next(){
    47.         $k = array_keys($this->row);
    48.         if (isset($k[++$this->index])) {
    49.             $row = $this->row[$k[$this->index]];
    50.             return $row;
    51.         } else {
    52.             return false;
    53.         }
    54.     }
    55.  
    56.     public function valid(){
    57.         $k = array_keys($this->row);
    58.         $row = isset($k[$this->index]);
    59.         return $row;
    60.     }
    61.    
    62.     public function getLastKey(){
    63.         $keys = array_keys($this->row);
    64.         $lastKey = $keys[sizeof($this->row)-1];
    65.         return  $lastKey;
    66.     }
    67.  
    68.     public function loadRow($row){
    69.         $this->row = $row;
    70.     }
    71. }
    72. ?>
    маппер:
    PHP:
    1. <?php
    2. /**
    3.  * Description of Mapper
    4.  * @author padaboo
    5.  */
    6. class Mapper{
    7.     /**
    8.      * Объект бд
    9.      * @var object
    10.      */
    11.     protected $db;
    12.     /**
    13.      * объект модели
    14.      * @param object $object
    15.      * возвращает этот же объект
    16.      * @return object
    17.      */
    18.     public function delete($object){
    19.         $query  = "DELETE FROM `". $object->getTable() ."`
    20.                   WHERE `id` = $object->id";
    21.         return $this->db->query($query);
    22.     }
    23.     /**
    24.      * @param object $db
    25.      */
    26.     public function __construct($db){
    27.         $this->db = $db;
    28.     }
    29.     /**
    30.      * Найти по id
    31.      * @param object $object
    32.      * @param int $id
    33.      * @return object
    34.      */
    35.     public function findById($object, $id){
    36.         $query  = "SELECT * FROM `". $object->getTable() ."`
    37.                   WHERE id = '$id'";
    38.         $result = $this->db->query($query);
    39.         if($result->num_rows > 0){
    40.             $row = $result->fetch_assoc();
    41.             $object->loadRow($row);
    42.             return $object;
    43.         }
    44.         return false;
    45.     }
    46.     /**
    47.      * объект модели
    48.      * @param object $object
    49.      * дополнительные параметны поиска
    50.      * @param string $q
    51.      * вернет масси объектов или false
    52.      * @return array
    53.      */
    54.     public function find($object, $q = ''){
    55.         $q = ($q!='')?'WHERE '.$q:'';
    56.         $query  = "SELECT * FROM `". $object->getTable() ."` ".$q;
    57.         $result = $this->db->query($query);
    58.         if($result->num_rows > 0){
    59.             $arr = array();
    60.             while($row = $result->fetch_assoc()){
    61.                 $object->loadRow($row);
    62.                 $arr[$row['id']] = clone $object;
    63.             }
    64.             return $arr;
    65.         }
    66.         return false;
    67.     }
    68.     /**
    69.      * сохраняет (UPDATE)
    70.      * @param object $object
    71.      */
    72.     public function save($object){
    73.         $query  = "UPDATE `" .$object->getTable()."` SET ";
    74.         $lastKey =  $object->getLastKey();
    75.         foreach($object as $property => $val){
    76.             $c = ($lastKey == $property)?'':',';
    77.             if($property != 'id')
    78.                 $query .= "`$property` = '".$this->db->real_escape_string($val)."'".$c;
    79.         }
    80.         $query .=" WHERE `id` = $object->id";
    81.         return $this->db->query($query);
    82.     }
    83.     /**
    84.      * добавляет новый объект
    85.      * @param object $object
    86.      */
    87.     public function add($object){
    88.         $query  = "INSERT INTO `".$object->getTable()."` VALUES (";
    89.         $lastKey =  $object->getLastKey();
    90.         foreach($object as $property => $val){
    91.             $c = ($lastKey == $property)?'':',';
    92.             $query .= "'".$this->db->real_escape_string($val)."'".$c;
    93.         }
    94.         $query .=")";;
    95.         return ($this->db->query($query))?$this->db->insert_id:null;
    96.     }
    97. }
    98. ?>
    99.  
    таблица:
    [sql]CREATE TABLE `users` ( `id` int(1) NOT NULL AUTO_INCREMENT, `login` varchar(40) NOT NULL, `password` varchar(40) NOT NULL, PRIMARY KEY (`id`)[/sql]
    создаем модель:
    PHP:
    1. <?php
    2. try {
    3.         $g = new ModelGenerator($db);
    4.         $g->create('users');
    5.      } catch (Exception $e) {
    6.          trigger_error($e->getMessage(), E_USER_ERROR);
    7.      }
    создадим и запишем нового пользователя
    PHP:
    1. <?php
    2. $mapper  = new Mapper($db);
    3. $user = new User();
    4. $user->login    = 'Test';
    5. $user->password = 'qwerty';
    6. $mapper->add($user);
    найдем пользователя по id
    PHP:
    1. <?php
    2. $mapper  = new Mapper($db);
    3. $user = $mapper->findById(new User(), 2);
    просто select *, можно с дополнительными параметрами
    PHP:
    1. <?php
    2. $mapper  = new Mapper($db);
    3. $users = $mapper->find(new User());
    удалить
    PHP:
    1. <?php
    2. $mapper->delete($user);
     
  2. pohapecoder

    pohapecoder Активный пользователь

    С нами с:
    16 окт 2010
    Сообщения:
    63
    Симпатии:
    0
    >public function __construct(mysqli $db,

    нахрена нужны такой код городить ради одного msqli

    нахрена вообще городить копипаст - когда можно использовать ООП нормально.
     
  3. pohapecoder

    pohapecoder Активный пользователь

    С нами с:
    16 окт 2010
    Сообщения:
    63
    Симпатии:
    0
    Еще улыбнуло использование trigger_error(..., E_USER_ERROR); вместо исключений
     
  4. Padaboo

    Padaboo Старожил
    Команда форума Модератор

    С нами с:
    26 окт 2009
    Сообщения:
    5.242
    Симпатии:
    1
    pohapecoder
    ну я mysli пользуюсь
    что значит городить копипаст? просто генерирует модель и базовые возможности маппера, поля то у моделей все разные и методы тоже свои потом дописываешь и там и там, руками приятнее писать?
     
  5. iliavlad

    iliavlad Активный пользователь

    С нами с:
    24 янв 2009
    Сообщения:
    1.689
    Симпатии:
    4
  6. Padaboo

    Padaboo Старожил
    Команда форума Модератор

    С нами с:
    26 окт 2009
    Сообщения:
    5.242
    Симпатии:
    1
    там Active Record и Table Data Gateway
     
  7. alBoo

    alBoo Активный пользователь

    С нами с:
    27 мар 2008
    Сообщения:
    63
    Симпатии:
    0
    а не судьба вот эти
    save, add, findById, find
    завернуть в какой-нть AbstractModel и наследоваться от него?

    Код (Text):
    1.  
    2.      public $id;
    3.      public $login;
    4.      public $password;
    зачем? делаешь private $_row - и пихаешь в него результат а конкретные поля достаешь при помощи __get
     
  8. Padaboo

    Padaboo Старожил
    Команда форума Модератор

    С нами с:
    26 окт 2009
    Сообщения:
    5.242
    Симпатии:
    1
    alBoo
    судьба, так и сделаю, никогда сразу не получается правильно...
    вопрос в другом, меня тут за смущали, я вот это делал
    http://martinfowler.com/eaaCatalog/dataMapper.html
     
  9. [vs]

    [vs] Суперстар
    Команда форума Модератор

    С нами с:
    27 сен 2007
    Сообщения:
    10.559
    Симпатии:
    632
    есть смысл сделать абстрактным (потому что сам по себе смысла не имеет).
     
  10. Padaboo

    Padaboo Старожил
    Команда форума Модератор

    С нами с:
    26 окт 2009
    Сообщения:
    5.242
    Симпатии:
    1
    [vs]
    ну, допустим добавлю abstract но ведь, абстрактные методы нужны... имеет смысл, что бы не таскать ссобой __get __set и isValid
     
  11. Костян

    Костян Активный пользователь

    С нами с:
    12 ноя 2009
    Сообщения:
    1.724
    Симпатии:
    1
    Адрес:
    адуктО
    Всё нормально, только вот save и add можно реализовать в абстрактном классе. Но просто тут может быть многое не учтено. Вот как например работать с идентификатрами. У меня add возвращает объект у которого установлен Id. Над своим кодом я договорился, что Id устанавливает только Mapper, поэтому все объекты имеют метод isExists() - так удобно отменять транзакции и вообще много чего проверять, например, залогиненного пользователя и т.п.
     
  12. Volt(220)

    Volt(220) Активный пользователь

    С нами с:
    11 июн 2009
    Сообщения:
    1.640
    Симпатии:
    1
    Моя вариация на тему ActiveRecord:
    http://www.php.ru/forum/viewtopic.php?p=244480#244480

    И пример конкретного класса:

    PHP:
    1. <?php
    2.     class AuthorZap extends Zapis {
    3.        
    4.         public function __construct($id=null, $db=null){
    5.             $this->emptyFields=array("id"=>-1, "FIO"=>"");
    6.             parent::__construct('avtor', $id, 'id', $db);
    7.         }
    8. }
     
  13. Dagdamor

    Dagdamor Активный пользователь

    С нами с:
    4 фев 2006
    Сообщения:
    2.095
    Симпатии:
    1
    Адрес:
    Барнаул
    Volt(220)
    Надеюсь, хоть там генерирования автокода нет? ;)
     
  14. Koc

    Koc Активный пользователь

    С нами с:
    3 мар 2008
    Сообщения:
    2.253
    Симпатии:
    0
    Адрес:
    \Ukraine\Dnepropetrovsk
    кодогенераторы имеют место быть
     
  15. Mr.M.I.T.

    Mr.M.I.T. Старожил

    С нами с:
    28 янв 2008
    Сообщения:
    4.586
    Симпатии:
    1
    Адрес:
    у тебя канфетка?
    дожили.
     
  16. Mr.M.I.T.

    Mr.M.I.T. Старожил

    С нами с:
    28 янв 2008
    Сообщения:
    4.586
    Симпатии:
    1
    Адрес:
    у тебя канфетка?
    зачем ООП абстракция, реализующиеся, копипастом?
    абсурд понятен?
     
  17. Padaboo

    Padaboo Старожил
    Команда форума Модератор

    С нами с:
    26 окт 2009
    Сообщения:
    5.242
    Симпатии:
    1
    Mr.M.I.T.
    переписал еше днем, тестирую, в половину кода меньше теперь и mapper 1
    скоро обновлю первый пост
    к тому же, поля которые есть в таблице все равно будут в модели, не вижу ничего страшно, чтобы просто с генерировать каркас
     
  18. Костян

    Костян Активный пользователь

    С нами с:
    12 ноя 2009
    Сообщения:
    1.724
    Симпатии:
    1
    Адрес:
    адуктО
    ну так реализация копипаститься для разных типов, каждый из которых определяет конкретную структуру набора данных. Если твоё приложение оперирует этой структурой зависимо от неё, а в большинстве случае оно так и есть, то при сокрытии сложности часто появляются именно такие решения, поскольку они оптимальны - сгенерировать модель приложения по схеме хранения.
     
  19. Volt(220)

    Volt(220) Активный пользователь

    С нами с:
    11 июн 2009
    Сообщения:
    1.640
    Симпатии:
    1
    Dagdamor
    Да вроде нету. =))
     
  20. pohapecoder

    pohapecoder Активный пользователь

    С нами с:
    16 окт 2010
    Сообщения:
    63
    Симпатии:
    0
    Непонятно тогда зачем такое разделение

    public function save(${namel}){
    public function add(${namel}){

    в генерируемом маппере, а delete и find в родительском.

    sql запросы там вообще однотипные, различен только список полей. тут что якобы экономия на генерации части, где перечислены поля с данными?
    что мешает тогда генерировать строку запроса один раз и хранить в статических свойствах? сам так делал раньше, но давно вырос уже из этих велосипедов.
     
  21. Костян

    Костян Активный пользователь

    С нами с:
    12 ноя 2009
    Сообщения:
    1.724
    Симпатии:
    1
    Адрес:
    адуктО
    pohapecoder
    а хрена ты меня цитируешь в данном посте? я вроде то же самое написал выше чуть...
     
  22. Padaboo

    Padaboo Старожил
    Команда форума Модератор

    С нами с:
    26 окт 2009
    Сообщения:
    5.242
    Симпатии:
    1
    обновил первый пост, гляньте
     
  23. [vs]

    [vs] Суперстар
    Команда форума Модератор

    С нами с:
    27 сен 2007
    Сообщения:
    10.559
    Симпатии:
    632
    Padaboo
    сильно лучше ))))
    а в чем суть этого метода:
    ?
     
  24. Костян

    Костян Активный пользователь

    С нами с:
    12 ноя 2009
    Сообщения:
    1.724
    Симпатии:
    1
    Адрес:
    адуктО
    я думаю add должен возвратить объект или null если таковой не созвался, причем при add должен установиться id объекта, или возвращать id, который ты установишь если захочешь извне... Методы save и delete должны возвращать true в случае успешного выполнения запроса... А так ничё вроде. Ты хоть пробовал его использовать? ))
     
  25. Padaboo

    Padaboo Старожил
    Команда форума Модератор

    С нами с:
    26 окт 2009
    Сообщения:
    5.242
    Симпатии:
    1
    [vs]
    можем найти юзеров например так
    PHP:
    1. <?php
    2. $users = $map->find(new User());
    3. header("Content-type:text/plain");
    4. print_r($users);
    вернет массив объектов
    PHP:
    1. <?php
    2. (
    3.     [0] => User Object
    4.         (
    5.             [index:User:private] => 0
    6.             [row:User:private] => Array
    7.                 (
    8.                     [id] => 1
    9.                     [login] => Vasia
    10.                     [password] => 123
    11.                 )
    12.  
    13.             [table] => users
    14.         )
    15.  
    16.     [1] => User Object
    17.         (
    18.             [index:User:private] => 0
    19.             [row:User:private] => Array
    20.                 (
    21.                     [id] => 8
    22.                     [login] => Test2
    23.                     [password] => qwert3y
    24.                 )
    25.  
    26.             [table] => users
    27.         )
    вот так пройтись по ним можем
    PHP:
    1. <?php
    2. foreach($users as $user){
    3.     echo $user->login;
    4. }
    а можем с дополнительными условиями поиска
    PHP:
    1. <?php
    2. $users = $map->find(new User(),"`login` = 'Vasia'");
    3.  
    Костян
    поправил как ты сказал, add возвращает вставленный id или null, save и delete возвращают true
    ну на трех таблицах проверил, вроде бы работает, сейчас надо все остальное что есть переписать получше и поудобнее, работу с сессиями и библиотеку для работы с демонами хотел, потом все сразу обкатывать буду