Przeglądaj źródła

hosting updates

Ivan Asmer 6 lat temu
rodzic
commit
9433a3c4af
1 zmienionych plików z 116 dodań i 0 usunięć
  1. 116 0
      MVCHW.md

+ 116 - 0
MVCHW.md

@@ -0,0 +1,116 @@
+# ДЗ по MVC
+
+## Роутер
+
+Сделать роутер, который будет запускать функции (контроллеры), по шаблону адресной строки в регулярках:
+
+```php
+    $routes = [
+        "\/"  => function(){
+            echo 'index controller';
+        },
+        "\/chat\/(\d+)\/"  => function($chatId){
+            echo "chat controller for chat Id " . $chatId;
+        },
+        "\/user\/(\w+)\/(\d+)\/"  => function($username, $someId){
+            echo "user with name " . $username . " and some id: $someId";
+            echo '<pre>';print_r( $_GET );echo '</pre>';
+        },
+        "" => "myController",
+        "asdfasdf" => [$obj, "indexController"]
+    ];
+```
+
+Выше несколько роутов, каждый из которых запускается по тому или другому шаблону адреса. Используйте .htaccess для перенаправления всех запросов на один php файл, в котором
+соотносите `$_SERVER['REQUEST_URI']` с вашими шаблонами адресов используя `preg_match`. Параметры в строке адреса (то, что в скобочках в регулярках) должны попадать в контроллер
+в качестве параметров при вызове.
+
+продолжение ДЗ читайте тут: 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`
+
+
+#### Конструктор
+- Если конструктор без параметров, то предполагается, что объект сконструирован для новой записи. (т. е. при `save` будет `INSERT`)
+- Конструктор так же может быть вызыван из `getByPk`. В таком случае используйте `get_called_class` или `__class__` из **PHP7**, для того, что бы инстанциировать потомка. 
+- Если конструктор вызывается из `getByPk`, то в него передаются данные из СУБД, которые сразу попадают в `$values`. Пусть это будет первым параметром конструктора со значением по умолчанию, которое позволит запускать его без параметров.
+
+
+# Делайте всё поэтапно
+- Stage1
+- Stage2
+- Stage3
+
+
+