(0:00)
Итак, мы хотим разобраться с валидацией, но для начала, давайте чуть-чуть поговорим о паре других моментов, чтобы наш код стал чище. Посмотрим на несколько хороших примеров, чтобы немного больше поработать с Carbon. Мы можем поговорить об областях запросов (query scopes) и ещё о ряде мелких моментов типа этого. OK, назад к PHPArticlesController
, где мы остановились в прошлый раз. И мы можем на самом деле улучшить этот код. Сейчас мы просто настроили поле PHPpublished_at
на текущее время. И это просто потому, что мы не хотели разбираться с датами.
(0:30)
Однако в жизни всё конечно не так. Так почему бы нам не исправить это? А в процессе вы увидите некоторые классные штуки, связанные с Eloquent. ОК, так что я тут всё полностью удалю и вставлю напрямую в виде параметра. Намного лучше, не так ли? Далее, нам нужно перейти в наше представление, потому что если помните, (если мы перейдём обратно в браузер), у нас тут в форме нет поля PHPpublished_at
. В действительности, вы вероятно будете использовать какой-то специальный JavaScript календарь, так?
(1:00)
Но в нашем случае, давайте используем простой ввод даты с помощью HTML5. Итак, давайте сделаем это. Мы создадим текстовое поле и назовём его PHPpublished_at
. Но мы изменим тут текст на «Дата публикации» (Publish On):
{!! Form::label('published_at', 'Publish On:') !!}
И далее, тут должно быть не текстовое поле, а дата, но я не думаю, что в Laravel есть тип date. Вместо этого мы используем обычный метод ввода. И таким образом я могу указать тип входных данных, в этом случае — date:
{!! Form::input('date', 'published_at', null, ['class' => 'form-control']) !!}
(1:30)
OK, давайте просто оставим это как есть и взглянем что мы получили в браузере. Запустим, и готово. И к счастью, поскольку мы используем современный браузер, он принудительно потребует от нас нужный формат. OK, далее, я думаю было бы хорошо ставить по умолчанию сегодняшний день. Таким образом, мне не нужно будет постоянно вводить дату вручную. Это надоедает. Так что давайте сделаем это. Я переключусь обратно. И вот здесь, третий аргумент — это то, что всегда будет по умолчанию. Но помните что тут нельзя просто ввести что-угодно. Вводимые данные должны быть в правильном формате. Поэтому здесь должна быть дата.
(2:00)
Таким образом мы переключаемся обратно и да, если вы хотите можно использовать Carbon. Например вот так:
format('Y-m-d')
Но в данном случае, нам действительно это не требуется. Мы можем просто использовать простую функцию PHP: PHPdate('Y-m-d')
. Вот так.
ОК, давайте переключимся обратно, обновим страницу, и теперь у нас есть значение по умолчанию, которое вы можете настроить, когда вам нужно. Прекрасно. А теперь давайте попробуем кое-что ещё. Давайте просто впишем тут белиберду, в тело — белиберду и дата публикации будет 30 января.
(2:30)
Так что если я это запущу, то, да, кажется, что всё сработало, но давайте посмотрим внимательно.
shsqlite3 storage/database.sqlite
sqlselect * from articles;
И вот смотрите. Да, дата была установлена на 30 января, но было бы неплохо, если бы мы на самом деле здесь также сохранили и текущее время. Таким образом, внутри в БД, мы могли бы знать, что эта статья была опубликована 30 января в 4:23. Так? На данный момент мы этого не знаем.
(3:00)
Так почему бы нам это не исправить? И по ходу я покажу вам некоторые классные вещи. Сейчас я переключусь к моей модели PHPArticle
в Eloquent, чтобы вы познакомились с методами доступа и модифицирующими методами (мутаторами). Они дают нам возможность манипулировать данными перед добавлением в БД или после их извлечения из неё. Так например, с помощью мутатора, я мог бы допустим создать метод с названием PHPsetPublishedAtAttribute
.
Обратите внимание на принятое соглашение, это очень важно:
public function setPublishedAtAttribute($date)
(3:30)
У нас есть слово set, за ним название поля, так что если бы мы пытались манипулировать со столбцом name, то мы бы написали:
setNameAttribute
Вот такое здесь соглашение. Так, например, если мы хотим задать адрес:
setAddressAttribute
И обратите внимание, в случаях, где у вас есть подчеркивания, вы просто можете использовать стиль CamelCase. ОК, так что теперь, когда вы устанавливаете атрибут PHPpublished_at
, я хочу убедиться что он в правильном формате. Поэтому мы можем задать атрибуты напрямую:
$this->attributes['published_at'] = Carbon::createFromFormat('Y-m-d', $date);
(4:00)
Почему бы нам не использовать здесь Carbon? Так что мы импортируем его. И передадим строку PHP$date
в качестве второго аргумента.
Вот и всё. Итак, мы корректно разбили строку, и время также будет добавлено если это имеет смысл для вашего проекта. Хорошо, давайте попробуем ещё раз. Мы создадим новую статью, ещё немного белиберды тут, и опубликуем 28 января.
(4:30)
ОК, так что теперь, если мы снова сделаем select *, то вы увидите, что мы установили и время тоже. Однако вот кое-что интересное. Если мы установим дату в будущем, то вы просто можете решить, допустим, что 28 января в полночь, статья будет опубликована. И если это ваш случай, то мы вряд ли захотим так делать. Вместо этого вы могли бы написать что-то вроде:
Carbon::parse($date)
чтобы попытаться разобраться с датой. Теперь если вернёмся назад.
(5:00)
Третья попытка. И мы снова запустим. Посмотрите на разницу. Сейчас мы запустили и дата будет установлена на 28 января, но у нас здесь нет времени, так что по сути она настроена на полночь. Итак, это начинает хорошо выглядеть. Однако сейчас у нас появилась проблема. Если я обновлю страницу, я вижу статьи, которые будут опубликованы в будущем. Например любые из вот этих, мы не должны их здесь отображать, потому что все они настроены на публикацию в течение следующих пары дней.
(5:30)
Поэтому мы должны убедиться, что у нас есть некая область действия запроса (query scope), чтобы ограничить вывод. Вот как мы это делаем. Сначала я покажу вам непосредственно в контроллере, а затем мы немного подчистим тут всё, перейдя к модели Eloquent. Так что прямо здесь наверху, мы говорим:
$articles = Article::latest('published_at') ->get();
(упорядочим их все по убыванию по PHPpublished_at
, и затем сделаем PHPget()
на весь набор)
На самом деле мы не хотим этого. В действительности мы хотим ограничить их:
->where ('published_at' , '<=' , Carbon::now())->get();
(6:00)
OK, так что теперь, если мы переключимся обратно, и мы обновим страницу, обратите внимание, что теперь мы их не отображаем, поскольку мы скорректировали запрос. Получить все статьи, упорядочить их по PHPpublished_at
в убывающем порядке, но только получить статьи где в этом столбце, их время меньше или равно настоящему моменту. Поэтому всё, что находится в будущем, т.е. больше чем сейчас, конечно же будет исключено. И тогда мы получим результаты этого запроса.
(6:30)
Но теперь вы можете видеть, что это начинает выглядеть несколько путано, особенно когда вы можете представить себе ситуации использования подобных запросов в нескольких частях приложения. Досадно, что мы должны писать такие довольно-таки длинные строки кода для чего-то, что является своего рода неотъемлемой частью для нашего приложения. И вот здесь-то области запросов (или «границы выборки» — scope) и могут очень пригодиться. Мы можем взять запрос и добавить к нему условие непосредственно в модели Eloquent. Или другими словами, какой бы здесь был хороший способ, чтобы изобразить это? Мы хотим последние статьи, но я также хочу те, которые были опубликованы.
(7:00)
Это имеет смысл, правда? Так что, возможно, я мог бы написать здесь, PHPpublished()
и теперь это выглядит почище, не так ли? ОК, давайте сделаем это. Я хочу область под названием PHPpublished()
. Так что перейдем к нашей модели и добавим новую область запроса прямо здесь. Соглашение для этого случая — слово scope и за ним имя. Мы назвали наш PHPpublished()
и он будет принимать наш конструктор запросов:
public function scopePublished($query)
Теперь мы можем просто вставить то что у нас там было раньше, так:
$query->where('published_at', '<=', Carbon::now());
(7:30)
Итак, вы добавили свою первую область. Если мы вернёмся и обновим, всё должно по-прежнему работать, и точно, всё работает. Но теперь, преимущество в том, что вы можете использовать это в любом месте в вашем приложении. Или что делать, если вы хотите сделать обратное — выбрать все остальные статьи? Допустим вам необходим некий способ в вашем приложении, чтобы получить все статьи, которые ещё только будут опубликованы. Это могло бы быть полезно для панели администратора, не так ли? Давайте напишем:
scopeUnpublished($query)
(8:00)
И единственное отличие будет в том, чтобы получить статьи, где дата PHPpublished_at
будет в будущем (т.е. больше чем сейчас):
$query->where('published_at', '>', Carbon::now());
ОК, давайте это проверим. Теперь мы хотим выбрать только неопубликованные статьи. Итак, если мы обновим, всё работает. Проще простого. ОК, мы почти закончили этот урок. Я лишь хочу вам тут показать еще одну штуку. Представьте, что когда мы смотрим статью, нам нужно что-то сделать с датой в некоторых случаях.
(8:30)
Так что давайте сделаем так:
dd($article->published_at);
И посмотрим как это выглядит. OK, обновим страницу. Давайте-ка взглянем. И да, у нас тут временной штамп (timestamp) в виде строки: 2015-01-23 21:21:10. Но было бы неплохо, если бы это был экземпляр Carbon. Потому что вот кое-что прикольное. Взгляните на поле created_at. Это специальное поле, о которых Laravel уже знает. И обратите внимание, что он достаточно умён, чтобы сконвертировать его в экземпляры Carbon.
(9:00)
Опять же, если Carbon для вас — нечто абсолютно новое, у нас есть посвящённое ему видео на Laracasts.com, и там вы можете узнать о нем всё более подробно. Но я покажу вам пару вещей, которые вы можете с ним сделать. Вы можете делать некоторые простые вещи, так например, у нас есть хорошие методы доступа к свойствам. Так что если я хочу выбрать год, то это очень легко. Просто напишите PHP->year
:
dd($article->created_at->year);
dd($article->created_at->month);
Вернёмся, обновим. Или мы даже можем манипулировать свойствами очень простым и читаемым образом. Например, если я хочу добавить 8 дней к дате:
dd($article->created_at->addDays(8));
OK. Теперь если мы обновим страницу, мы изменили дату на 8 дней с момента PHPpublished_at
.
(9:30)
Или вы могли бы даже задать новый формат. Например, вы можете сказать:
dd($article->created_at->addDays(8)->format('Y-m'));
Давайте посмотрим. Обновите и теперь это работает. Или мы могли бы даже отформатировать дату в более читаемый вид с помощью метода PHPdiffForHumans()
. И это действительно полезно, например для лент новостей. Представьте себе Twitter, где у вас есть статус, а ниже говорится: «Этот статус был опубликован 12 минут назад». Вы могли бы использовать что-то подобное, используя стандартный способ:
dd($article->created_at->addDays(8)->diffForHumans());
(10:00)
Обновим, и похоже что мы опубликуем эту статью через 5 дней. Так что, как вы можете себе представить, это очень удобно, если дата и время могут быть автоматически преобразованы в экземпляры Carbon. Но давайте вернёмся тут к PHPpublished_at
и кстати, всё то же самое было бы верно и для PHPupdated_at
. Но в любом случае, давайте напишем снова PHPpublished_at
:
dd($article->published_at);
Как видите, ничего из этого мы использовать не можем, так как у нас просто строка. Поэтому было бы здорово, если бы мы могли сказать Laravel:
(10:30)
«У меня тут был дополнительный временной штамп, и я хочу чтобы ты работал с ним как с экземпляром Carbon». Вот как мы это делаем. Мы вернёмся к статье, и добавим такое свойство:
protected $dates = ['published_at'];
Здесь я задам имя столбца. И вот и всё. Так что теперь если мы вернёмся и обновим, мы видим, что это экземпляр Carbon. Что означает, что у нас есть доступ ко всем этим методам и методам PHPget()
, как мы и делали со столбцами PHPcreated_at
, PHPupdated_at
и PHPdeleted_at
.
(11:00)
И если вам интересно, как это работает, то вы всегда можете покопаться с моделями. Теперь, если мы посмотрим на метод PHPgetDates()
вот здесь, вы увидите, что он принимает наши значения по умолчанию, поэтому он по-прежнему будет обрабатывать PHPcreated_at
и PHPupdated
как временные штампы. Но он объединит (merge) те значения по умолчанию с тем, что мы установили в качестве свойства PHP->dates
. Так что вот как здесь это делается.
Хорошо, мы делаем большие успехи, не так ли?
В этом уроке вы узнали немного больше о Carbon, вы узнали об областях запросов, вы познакомились с модифицирующими методами-мутаторами.
(11:30)
Мы также научились принимать временные штампы любого типа и работать с ними как с экземплярами Carbon, и если мы переключимся обратно в PHPArticlesController
, мы основательно почистили наш контроллер. Итак, в нашем следующем видео, мы уже достаточно долго ждали, и нам действительно нужно разобраться с валидацией. Там и увидимся!