{{Laracast Laravel 5 Fundamentals, 11, "Dates, Mutators, and Scopes", 26.01.2015, 26.06.2016, https://laracasts.com/series/laravel-5-fundamentals/episodes/11}} %%(hvlraw) %% (0:00) Итак, мы хотим разобраться с валидацией, но для начала, давайте чуть-чуть поговорим о паре других моментов, чтобы наш код стал чище. Посмотрим на несколько хороших примеров, чтобы немного больше поработать с Carbon. Мы можем поговорить об областях запросов (query scopes) и ещё о ряде мелких моментов типа этого. OK, назад к %%ArticlesController%%, где мы остановились в прошлый раз. И мы можем на самом деле улучшить этот код. Сейчас мы просто настроили поле %%published_at%% на текущее время. И это просто потому, что мы не хотели разбираться с датами. (0:30) Однако в жизни всё конечно не так. Так почему бы нам не исправить это? А в процессе вы увидите некоторые классные штуки, связанные с Eloquent. ОК, так что я тут всё полностью удалю и вставлю напрямую в виде параметра. Намного лучше, не так ли? Далее, нам нужно перейти в наше представление, потому что если помните, (если мы перейдём обратно в браузер), у нас тут в форме нет поля %%published_at%%. В действительности, вы вероятно будете использовать какой-то специальный JavaScript календарь, так? (1:00) Но в нашем случае, давайте используем простой ввод даты с помощью HTML5. Итак, давайте сделаем это. Мы создадим текстовое поле и назовём его %%published_at%%. Но мы изменим тут текст на "Дата публикации" (Publish On): %% {!! Form::label('published_at', 'Publish On:') !!} %% И далее, тут должно быть не текстовое поле, а дата, но я не думаю, что в Laravel есть тип %%(t)date%%. Вместо этого мы используем обычный метод ввода. И таким образом я могу указать тип входных данных, в этом случае - date: %% {!! Form::input('date', 'published_at', null, ['class' => 'form-control']) !!} %% (1:30) OK, давайте просто оставим это как есть и взглянем что мы получили в браузере. Запустим, и готово. И к счастью, поскольку мы используем современный браузер, он принудительно потребует от нас нужный формат. OK, далее, я думаю было бы хорошо ставить по умолчанию сегодняшний день. Таким образом, мне не нужно будет постоянно вводить дату вручную. Это надоедает. Так что давайте сделаем это. Я переключусь обратно. И вот здесь, третий аргумент - это то, что всегда будет по умолчанию. Но помните что тут нельзя просто ввести что-угодно. Вводимые данные должны быть в правильном формате. Поэтому здесь должна быть дата. (2:00) Таким образом мы переключаемся обратно и да, если вы хотите можно использовать Carbon. Например вот так: %% format('Y-m-d') %% Но в данном случае, нам действительно это не требуется. Мы можем просто использовать простую функцию PHP: %%date('Y-m-d')%%. Вот так. ОК, давайте переключимся обратно, обновим страницу, и теперь у нас есть значение по умолчанию, которое вы можете настроить, когда вам нужно. Прекрасно. А теперь давайте попробуем кое-что ещё. Давайте просто впишем тут белиберду, в тело - белиберду и дата публикации будет 30 января. (2:30) Так что если я это запущу, то, да, кажется, что всё сработало, но давайте посмотрим внимательно. %%(sh) sqlite3 storage/database.sqlite %% И там: %%(sql) select * from articles; %% И вот смотрите. Да, дата была установлена на 30 января, но было бы неплохо, если бы мы на самом деле здесь также сохранили и текущее время. Таким образом, внутри в БД, мы могли бы знать, что эта статья была опубликована 30 января в 4:23. Так? На данный момент мы этого не знаем. (3:00) Так почему бы нам это не исправить? И по ходу я покажу вам некоторые классные вещи. Сейчас я переключусь к моей модели %%Article%% в Eloquent, чтобы вы познакомились с методами доступа и модифицирующими методами (мутаторами). Они дают нам возможность манипулировать данными перед добавлением в БД или после их извлечения из неё. Так например, с помощью мутатора, я мог бы допустим создать метод с названием %%setPublishedAtAttribute%%. Обратите внимание на принятое соглашение, это очень важно: %% public function setPublishedAtAttribute($date) %% (3:30) У нас есть слово %%(t)set%%, за ним название поля, так что если бы мы пытались манипулировать со столбцом %%(t)name%%, то мы бы написали: %% setNameAttribute %% Вот такое здесь соглашение. Так, например, если мы хотим задать адрес: %% setAddressAttribute %% И обратите внимание, в случаях, где у вас есть подчеркивания, вы просто можете использовать стиль CamelCase. ОК, так что теперь, когда вы устанавливаете атрибут %%published_at%%, я хочу убедиться что он в правильном формате. Поэтому мы можем задать атрибуты напрямую: %% $this->attributes['published_at'] = Carbon::createFromFormat('Y-m-d', $date); %% (4:00) Почему бы нам не использовать здесь Carbon? Так что мы импортируем его. И передадим строку %%$date%% в качестве второго аргумента. Вот и всё. Итак, мы корректно разбили строку, и время также будет добавлено если это имеет смысл для вашего проекта. Хорошо, давайте попробуем ещё раз. Мы создадим новую статью, ещё немного белиберды тут, и опубликуем 28 января. (4:30) ОК, так что теперь, если мы снова сделаем %%(t)select *%%, то вы увидите, что мы установили и время тоже. Однако вот кое-что интересное. Если мы установим дату в будущем, то вы просто можете решить, допустим, что 28 января в полночь, статья будет опубликована. И если это ваш случай, то мы вряд ли захотим так делать. Вместо этого вы могли бы написать что-то вроде: %% Carbon::parse($date) %% чтобы попытаться разобраться с датой. Теперь если вернёмся назад. (5:00) Третья попытка. И мы снова запустим. Посмотрите на разницу. Сейчас мы запустили и дата будет установлена на 28 января, но у нас здесь нет времени, так что по сути она настроена на полночь. Итак, это начинает хорошо выглядеть. Однако сейчас у нас появилась проблема. Если я обновлю страницу, я вижу статьи, которые будут опубликованы в будущем. Например любые из вот этих, мы не должны их здесь отображать, потому что все они настроены на публикацию в течение следующих пары дней. (5:30) Поэтому мы должны убедиться, что у нас есть некая область действия запроса (query scope), чтобы ограничить вывод. Вот как мы это делаем. Сначала я покажу вам непосредственно в контроллере, а затем мы немного подчистим тут всё, перейдя к модели Eloquent. Так что прямо здесь наверху, мы говорим: %% $articles = Article::latest('published_at') ->get(); %% (упорядочим их все по убыванию по %%published_at%%, и затем сделаем %%get()%% на весь набор) На самом деле мы не хотим этого. В действительности мы хотим ограничить их: %% ->where ('published_at' , '<=' , Carbon::now())->get(); %% (6:00) OK, так что теперь, если мы переключимся обратно, и мы обновим страницу, обратите внимание, что теперь мы их не отображаем, поскольку мы скорректировали запрос. Получить все статьи, упорядочить их по %%published_at%% в убывающем порядке, но только получить статьи где в этом столбце, их время меньше или равно настоящему моменту. Поэтому всё, что находится в будущем, т.е. больше чем сейчас, конечно же будет исключено. И тогда мы получим результаты этого запроса. (6:30) Но теперь вы можете видеть, что это начинает выглядеть несколько путано, особенно когда вы можете представить себе ситуации использования подобных запросов в нескольких частях приложения. Досадно, что мы должны писать такие довольно-таки длинные строки кода для чего-то, что является своего рода неотъемлемой частью для нашего приложения. И вот здесь-то области запросов (или "границы выборки" - scope) и могут очень пригодиться. Мы можем взять запрос и добавить к нему условие непосредственно в модели Eloquent. Или другими словами, какой бы здесь был хороший способ, чтобы изобразить это? Мы хотим последние статьи, но я также хочу те, которые были опубликованы. (7:00) Это имеет смысл, правда? Так что, возможно, я мог бы написать здесь, %%published()%% и теперь это выглядит почище, не так ли? ОК, давайте сделаем это. Я хочу область под названием %%published()%%. Так что перейдем к нашей модели и добавим новую область запроса прямо здесь. Соглашение для этого случая - слово %%(t)scope%% и за ним имя. Мы назвали наш %%published()%% и он будет принимать наш конструктор запросов: %% public function scopePublished($query) %% Теперь мы можем просто вставить то что у нас там было раньше, так: %% $query->where('published_at', '<=', Carbon::now()); %% (7:30) Итак, вы добавили свою первую область. Если мы вернёмся и обновим, всё должно по-прежнему работать, и точно, всё работает. Но теперь, преимущество в том, что вы можете использовать это в любом месте в вашем приложении. Или что делать, если вы хотите сделать обратное - выбрать все остальные статьи? Допустим вам необходим некий способ в вашем приложении, чтобы получить все статьи, которые ещё только будут опубликованы. Это могло бы быть полезно для панели администратора, не так ли? Давайте напишем: %% scopeUnpublished($query) %% (8:00) И единственное отличие будет в том, чтобы получить статьи, где дата %%published_at%% будет в будущем (т.е. больше чем сейчас): %% $query->where('published_at', '>', Carbon::now()); %% ОК, давайте это проверим. Теперь мы хотим выбрать только неопубликованные статьи. Итак, если мы обновим, всё работает. Проще простого. ОК, мы почти закончили этот урок. Я лишь хочу вам тут показать еще одну штуку. Представьте, что когда мы смотрим статью, нам нужно что-то сделать с датой в некоторых случаях. (8:30) Так что давайте сделаем так: %% dd($article->published_at); %% И посмотрим как это выглядит. OK, обновим страницу. Давайте-ка взглянем. И да, у нас тут временной штамп (timestamp) в виде строки: %%(t)2015-01-23 21:21:10%%. Но было бы неплохо, если бы это был экземпляр Carbon. Потому что вот кое-что прикольное. Взгляните на поле %%(t)created_at%%. Это специальное поле, о которых Laravel уже знает. И обратите внимание, что он достаточно умён, чтобы сконвертировать его в экземпляры Carbon. (9:00) Опять же, если Carbon для вас - нечто абсолютно новое, у нас есть посвящённое ему видео на Laracasts.com, и там вы можете узнать о нем всё более подробно. Но я покажу вам пару вещей, которые вы можете с ним сделать. Вы можете делать некоторые простые вещи, так например, у нас есть хорошие методы доступа к свойствам. Так что если я хочу выбрать год, то это очень легко. Просто напишите %%->year%%: %% dd($article->created_at->year); %% месяц: %% dd($article->created_at->month); %% Вернёмся, обновим. Или мы даже можем манипулировать свойствами очень простым и читаемым образом. Например, если я хочу добавить 8 дней к дате: %% dd($article->created_at->addDays(8)); %% OK. Теперь если мы обновим страницу, мы изменили дату на 8 дней с момента %%published_at%%. (9:30) Или вы могли бы даже задать новый формат. Например, вы можете сказать: %% dd($article->created_at->addDays(8)->format('Y-m')); %% Давайте посмотрим. Обновите и теперь это работает. Или мы могли бы даже отформатировать дату в более читаемый вид с помощью метода %%diffForHumans()%%. И это действительно полезно, например для лент новостей. Представьте себе Twitter, где у вас есть статус, а ниже говорится: «Этот статус был опубликован 12 минут назад». Вы могли бы использовать что-то подобное, используя стандартный способ: %% dd($article->created_at->addDays(8)->diffForHumans()); %% (10:00) Обновим, и похоже что мы опубликуем эту статью через 5 дней. Так что, как вы можете себе представить, это очень удобно, если дата и время могут быть автоматически преобразованы в экземпляры Carbon. Но давайте вернёмся тут к %%published_at%% и кстати, всё то же самое было бы верно и для %%updated_at%%. Но в любом случае, давайте напишем снова %%published_at%%: %% dd($article->published_at); %% Как видите, ничего из этого мы использовать не можем, так как у нас просто строка. Поэтому было бы здорово, если бы мы могли сказать Laravel: (10:30) "У меня тут был дополнительный временной штамп, и я хочу чтобы ты работал с ним как с экземпляром Carbon". Вот как мы это делаем. Мы вернёмся к статье, и добавим такое свойство: %% protected $dates = ['published_at']; %% Здесь я задам имя столбца. И вот и всё. Так что теперь если мы вернёмся и обновим, мы видим, что это экземпляр Carbon. Что означает, что у нас есть доступ ко всем этим методам и методам %%get()%%, как мы и делали со столбцами %%created_at%%, %%updated_at%% и %%deleted_at%%. (11:00) И если вам интересно, как это работает, то вы всегда можете покопаться с моделями. Теперь, если мы посмотрим на метод %%getDates()%% вот здесь, вы увидите, что он принимает наши значения по умолчанию, поэтому он по-прежнему будет обрабатывать %%created_at%% и %%updated%% как временные штампы. Но он объединит (merge) те значения по умолчанию с тем, что мы установили в качестве свойства %%->dates%%. Так что вот как здесь это делается. Хорошо, мы делаем большие успехи, не так ли? В этом уроке вы узнали немного больше о Carbon, вы узнали об областях запросов, вы познакомились с модифицирующими методами-мутаторами. (11:30) Мы также научились принимать временные штампы любого типа и работать с ними как с экземплярами Carbon, и если мы переключимся обратно в %%ArticlesController%%, мы основательно почистили наш контроллер. Итак, в нашем следующем видео, мы уже достаточно долго ждали, и нам действительно нужно разобраться с валидацией. Там и увидимся!