Ivan Asmer 7 年之前
父節點
當前提交
e740c52f8b
共有 1 個文件被更改,包括 88 次插入1 次删除
  1. 88 1
      MVCHW.md

+ 88 - 1
MVCHW.md

@@ -25,5 +25,92 @@
 соотносите `$_SERVER['REQUEST_URI']` с вашими шаблонами адресов используя `preg_match`. Параметры в строке адреса (то, что в скобочках в регулярках) должны попадать в контроллер
 в качестве параметров при вызове.
 
-продолжение ДЗ читайте тут: http://route.asmer.php.a-level.com.ua/
+продолжение ДЗ читайте тут: http://route.asmer.php.a-level.com.ua/. Тут же можно посмотреть как работают контроллеры из кода выше.
+
+## ActiveRecord
+
+Разработайте класс `Model`, который будет обеспечивать следующую функциональность:
+
+```php
+class Posts extends Model{
+    public static $__table__ = 'posts';
+}
+
+class Comments extends Model{
+    public static $__table__ = 'comments';
+}
+
+$post = new Posts();
+$post->title = 'new post';
+$post->text = 'new post text';
+$post->save();
+
+$comment = Comments::getByPk(5);
+echo ($comment->text)
+$comment->text = 'update comment';
+$comment->save();
+```
+
+Класс должен обеспечивать возможность работы с СУБД исходя из парадигмы **Active Record**, т. е.
+
+### Класс
+Класс наследник ('Posts' или 'Comments' в примере выше) является сущностью, связанной с таблицей в СУБД.
+Класс должен уметь:
+- узнать структуру таблицы (`DESC TABLE`), используя позднее статическое связывание (имя таблицы в поле `$__table__`)
+- исходя из структуры таблицы, узнать имя первичного ключа (может быть `id`, `post_id` и так далее) 
+- статический метод `getByPk` должен исходя из информации о структуры таблицы сформировать правильный запрос `SELECT * FROM <имя таблицы> WHERE <имя первичного ключа> == <параметр функции getByPk>`
+
+
+### Объект
+Каждый объект класса является сущностью, связанной с конкретной записью в таблице
+Объект должен уметь:
+- используя магические `__set` и `__get` считывать и задавать поля записи из СУБД (`$post->title = 'POST TITLE'`) 
+- уметь сохраняться с помощью метода `save`. Метод `save` должен уметь исходя из известной структуры таблицы сформировать корректный `UPDATE` или `INSERT` для сохранения записи.
+
+
+### Stage1
+Считайте что первичный ключ у вас всегда называется `id`.
+
+### Stage2
+Сделайте так, что бы имя первичного ключа у вас извлекался из СУБД с помощью `DESC TABLE` и в дальнейшем используйте его как статическую переменную, каждую для своего класса-наследника `Model`.
+
+### Stage3
+Добавьте флаг измененности записи (`dirty`) и не сохраняйте запись если она неизменна
+Добавьте **Identity Map**, для того, что бы объекты, полученные по одному и тому же первичному ключу не дублировались в памяти.
+
+### Пожелания и советы по реализации
+#### Структура таблицы и имя первичного ключа:
+- Заведите *статический метод* который *лениво* вычитывает структуру таблицы в какое-то статическое поле и возвращает эту структуру (массив с набором колонок). Например `getFields` и `public static $fields`
+- Заведите *статический метод* который *лениво* находит название поля с первичным ключем. Этот метод скорее всего будет связан с первым, так как нет смысла дважды делать `DESC TABLE`. Например `getPkName` и `public static $pkName`
+*Ленивость* значит, что при первой вычитке происходит реальное обращение к СУБД, а в дальнейшем методы просто возвращают статическое свойство.
+
+#### `getByPk`
+- Используте статический метод для получения названия первичного ключа. Сформируйте запрос `SELECT * FROM <имя таблицы> WHERE <имя первичного ключа> == <параметр функции getByPk>`
+- Если у вас есть **Identity Map**, проверяйте наличие объекта в ней, и возвращайте его если он уже там есть. Иначе вычитывайте из базы и сохраняйте объект в **Identity Map** на будущее.
+
+#### `__get` и `__set`
+- Проверяйте что имя магического свойства присутствует в колонках СУБД. (проверка наличия в вашем `public static $fields`)
+- заведите приватное поле (`$values`, например), которое будет ассоциативным массивом со значениями конкретной записи и ключами - именами колонок. `__get` будет доставать данные оттуда, `__set` будет туда их записывать, `save` - сохранять этот массив в СУБД.
+- при реальном изменени меняйте флаг изменения `$dirty`, если вы до него дошли
+
+
+#### save
+- Создайте статический метод (`buildSetValues`) например, который из массива ваших колонок (`getFields()`) будет создавать строку вида `title = :title, text = :text` и так далее для каждой конкретной таблицы.
+- используя этот метод, в зависимости от того, запись новая или получена из базы, создавайте запросы `INSERT` или `UPDATE` с тем или иным набором полей. Набор полей должен варьироваться - при первом добавлении первичный ключ обычно отсутствует. В таком случае
+  после добавления новой записи его нужно вычитать используя `lastInsertId`
+- проверяйте флаг `dirty`
+
+
+#### Конструктор
+- Если конструктор без параметров, то предполагается, что объект сконструирован для новой записи.
+- Конструктор так же может быть вызыван из `getByPk`. В таком случае используйте `get_called_class` или `__class__` из **PHP7**, для того, что бы инстанциировать потомка. 
+- Если конструктор вызывается из `getByPk`, то в него передаются данные из СУБД, которые сразу попадают в `$values`. Пусть это будет первым параметров конструктора.
+
+
+# Делайте всё поэтапно
+- Stage1
+- Stage2
+- Stage3
+
+