PHP: <?php /** * Description of ModelGenerator * @author padaboo */ class ModelGenerator { /** * путь до шаблона с моеделью * @var string */ private $modelTpl; /** * папка куда помешаются * созданные классы * @var string */ private $output; /** * объект mysqli * @var object */ private $db; /** * конструктор * @param string $modelTpl * @param string $output * @param object $db */ public function __construct($db, $modelTpl = 'src/class/models/modeltpl.php', $output = 'src/class/models/'){ if(!file_exists($modelTpl)) throw new Exception ("$modelTpl не найден"); if(!is_readable($modelTpl)) throw new Exception ("$modelTpl не читаем"); if(!is_dir($output)) throw new Exception ("$output не директория"); $this->modelTpl = file_get_contents($modelTpl); $this->output = $output; $this->db = $db; } /** * Создает модель и маппер * из таблицы, на основе шаблонов * @param string $table */ public function create($table){ $name = $this->getClassName($table); if(file_exists($this->output .$name. '.php')) throw new Exception ("$name уже существует"); $fields = $this->getFields($table); $this->buildModel($fields, $name, $table); } /** * @param string $table * возвращает массив полей * @return array */ private function getFields($table){ if(!$result = $this->db->query("SHOW CREATE TABLE `$table`")) throw new Exception ("таблица $table не найдена"); $row = $result->fetch_array(); preg_match_all("/`(.*)`/", $row[1], $out);; $fields = array_unique($out[1]); unset($fields[array_search($table, $fields)]); return $fields; } /** * Создает имя для модели * @param string $table * @return string */ private function getClassName($table){ if(substr($table, -1) == 's') $table = substr($table, 0, strlen($table)-1); $table = ucfirst(strtolower($table)); return $table; } /** * Формирует данные для шаблона модели * @param array $fields * @param string $name */ private function buildModel($fields, $name, $table){ $fieldString = ''; unset($fields[array_search('id', $fields)]); foreach ($fields as $field) { $c = (end($fields) == $field)? "":",\r\n"; $fieldString .= "\t '$field' => '' $c"; } $data = str_replace(array('{name}','{fields}', '{table}'), array($name, $fieldString, $table), $this->modelTpl ); $file = $this->output .$name. '.php'; touch($file); chmod($file, 0644); file_put_contents($file, $data); } } ?> шаблон PHP: <?php class {name} implements Iterator{ private $index = 0; private $row = array('id' => NULL, {fields} ); public $table = '{table}'; public function getTable(){ return $this->table; } public function __set($name, $value) { $this->isValid($name); $this->row[$name] = $value; } public function __get($name) { $this->isValid($name); return $this->row[$name]; } protected function isValid($name) { if (!array_key_exists($name, $this->row)) { trigger_error("Атрибута $name не существует", E_USER_ERROR); } } public function rewind(){ $this->index = 0; } public function current(){ $k = array_keys($this->row); $row = $this->row[$k[$this->index]]; return $row; } public function key(){ $k = array_keys($this->row); $row = $k[$this->index]; return $row; } public function next(){ $k = array_keys($this->row); if (isset($k[++$this->index])) { $row = $this->row[$k[$this->index]]; return $row; } else { return false; } } public function valid(){ $k = array_keys($this->row); $row = isset($k[$this->index]); return $row; } public function getLastKey(){ $keys = array_keys($this->row); $lastKey = $keys[sizeof($this->row)-1]; return $lastKey; } public function loadRow($row){ $this->row = $row; } } ?> маппер: PHP: <?php /** * Description of Mapper * @author padaboo */ class Mapper{ /** * Объект бд * @var object */ protected $db; /** * объект модели * @param object $object * возвращает этот же объект * @return object */ public function delete($object){ $query = "DELETE FROM `". $object->getTable() ."` WHERE `id` = $object->id"; return $this->db->query($query); } /** * @param object $db */ public function __construct($db){ $this->db = $db; } /** * Найти по id * @param object $object * @param int $id * @return object */ public function findById($object, $id){ $query = "SELECT * FROM `". $object->getTable() ."` WHERE id = '$id'"; $result = $this->db->query($query); if($result->num_rows > 0){ $row = $result->fetch_assoc(); $object->loadRow($row); return $object; } return false; } /** * объект модели * @param object $object * дополнительные параметны поиска * @param string $q * вернет масси объектов или false * @return array */ public function find($object, $q = ''){ $q = ($q!='')?'WHERE '.$q:''; $query = "SELECT * FROM `". $object->getTable() ."` ".$q; $result = $this->db->query($query); if($result->num_rows > 0){ $arr = array(); while($row = $result->fetch_assoc()){ $object->loadRow($row); $arr[$row['id']] = clone $object; } return $arr; } return false; } /** * сохраняет (UPDATE) * @param object $object */ public function save($object){ $query = "UPDATE `" .$object->getTable()."` SET "; $lastKey = $object->getLastKey(); foreach($object as $property => $val){ $c = ($lastKey == $property)?'':','; if($property != 'id') $query .= "`$property` = '".$this->db->real_escape_string($val)."'".$c; } $query .=" WHERE `id` = $object->id"; return $this->db->query($query); } /** * добавляет новый объект * @param object $object */ public function add($object){ $query = "INSERT INTO `".$object->getTable()."` VALUES ("; $lastKey = $object->getLastKey(); foreach($object as $property => $val){ $c = ($lastKey == $property)?'':','; $query .= "'".$this->db->real_escape_string($val)."'".$c; } $query .=")";; return ($this->db->query($query))?$this->db->insert_id:null; } } ?> таблица: [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: <?php try { $g = new ModelGenerator($db); $g->create('users'); } catch (Exception $e) { trigger_error($e->getMessage(), E_USER_ERROR); } создадим и запишем нового пользователя PHP: <?php $mapper = new Mapper($db); $user = new User(); $user->login = 'Test'; $user->password = 'qwerty'; $mapper->add($user); найдем пользователя по id PHP: <?php $mapper = new Mapper($db); $user = $mapper->findById(new User(), 2); просто select *, можно с дополнительными параметрами PHP: <?php $mapper = new Mapper($db); $users = $mapper->find(new User()); удалить PHP: <?php $mapper->delete($user);
>public function __construct(mysqli $db, нахрена нужны такой код городить ради одного msqli нахрена вообще городить копипаст - когда можно использовать ООП нормально.
pohapecoder ну я mysli пользуюсь что значит городить копипаст? просто генерирует модель и базовые возможности маппера, поля то у моделей все разные и методы тоже свои потом дописываешь и там и там, руками приятнее писать?
а не судьба вот эти save, add, findById, find завернуть в какой-нть AbstractModel и наследоваться от него? Код (Text): public $id; public $login; public $password; зачем? делаешь private $_row - и пихаешь в него результат а конкретные поля достаешь при помощи __get
alBoo судьба, так и сделаю, никогда сразу не получается правильно... вопрос в другом, меня тут за смущали, я вот это делал http://martinfowler.com/eaaCatalog/dataMapper.html
[vs] ну, допустим добавлю abstract но ведь, абстрактные методы нужны... имеет смысл, что бы не таскать ссобой __get __set и isValid
Всё нормально, только вот save и add можно реализовать в абстрактном классе. Но просто тут может быть многое не учтено. Вот как например работать с идентификатрами. У меня add возвращает объект у которого установлен Id. Над своим кодом я договорился, что Id устанавливает только Mapper, поэтому все объекты имеют метод isExists() - так удобно отменять транзакции и вообще много чего проверять, например, залогиненного пользователя и т.п.
Моя вариация на тему ActiveRecord: http://www.php.ru/forum/viewtopic.php?p=244480#244480 И пример конкретного класса: PHP: <?php class AuthorZap extends Zapis { public function __construct($id=null, $db=null){ $this->emptyFields=array("id"=>-1, "FIO"=>""); parent::__construct('avtor', $id, 'id', $db); } }
Mr.M.I.T. переписал еше днем, тестирую, в половину кода меньше теперь и mapper 1 скоро обновлю первый пост к тому же, поля которые есть в таблице все равно будут в модели, не вижу ничего страшно, чтобы просто с генерировать каркас
ну так реализация копипаститься для разных типов, каждый из которых определяет конкретную структуру набора данных. Если твоё приложение оперирует этой структурой зависимо от неё, а в большинстве случае оно так и есть, то при сокрытии сложности часто появляются именно такие решения, поскольку они оптимальны - сгенерировать модель приложения по схеме хранения.
Непонятно тогда зачем такое разделение public function save(${namel}){ public function add(${namel}){ в генерируемом маппере, а delete и find в родительском. sql запросы там вообще однотипные, различен только список полей. тут что якобы экономия на генерации части, где перечислены поля с данными? что мешает тогда генерировать строку запроса один раз и хранить в статических свойствах? сам так делал раньше, но давно вырос уже из этих велосипедов.
я думаю add должен возвратить объект или null если таковой не созвался, причем при add должен установиться id объекта, или возвращать id, который ты установишь если захочешь извне... Методы save и delete должны возвращать true в случае успешного выполнения запроса... А так ничё вроде. Ты хоть пробовал его использовать? ))
[vs] можем найти юзеров например так PHP: <?php $users = $map->find(new User()); header("Content-type:text/plain"); print_r($users); вернет массив объектов PHP: <?php Array ( [0] => User Object ( [index:User:private] => 0 [row:User:private] => Array ( [id] => 1 [login] => Vasia [password] => 123 ) [table] => users ) [1] => User Object ( [index:User:private] => 0 [row:User:private] => Array ( [id] => 8 [login] => Test2 [password] => qwert3y ) [table] => users ) вот так пройтись по ним можем PHP: <?php foreach($users as $user){ echo $user->login; } а можем с дополнительными условиями поиска PHP: <?php $users = $map->find(new User(),"`login` = 'Vasia'"); Костян поправил как ты сказал, add возвращает вставленный id или null, save и delete возвращают true ну на трех таблицах проверил, вроде бы работает, сейчас надо все остальное что есть переписать получше и поудобнее, работу с сессиями и библиотеку для работы с демонами хотел, потом все сразу обкатывать буду