|
@@ -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
|
|
|
+
|
|
|
+
|
|
|
|