Поэтапная разработка сайта на Laravel 4: урок №4

Доброго времени суток 🙂

Настало время для четвертого урока, и в нем мы реализуем следующее:

  • Создадим модель для планет
  • Реализуем форму добавления планет в базу (с валидацией и сообщениями об ошибках)
  • Выведем список последних планет на главной странице

Итак, начнем с создания контроллера, который будет отвечать за обработку и вывод всех страниц, связанных с планетам. Назовем его  PlanetsController и поместим в app/controllers/PlanetsController.php :

Важно, чтобы имя файла совпадало с именем класса, который в этом файле содержится, чтобы автозагрузчик мог без проблем этот класс подключить по мере необходимости.

И сразу можем создать маршрут для этого контроллера в  app/routes.php :

Если кто не до конца усвоил материал второго урока, напомню, что эта запись создаст маршруты для всех методов указанного контроллера, причем маршруты эти будут начинаться с  /planets . Возможно плюс, а возможно и минус этого подхода в том, что маршруты будут генерироваться при каждом запросе, а не единожды. Плюс в том, что нам уже не придется трогать файл  routes.php для новых маршрутов конкретно этого контроллера: по мере добавления новых методов в него маршруты также появятся.

Первым делом предлагаю начать с вывода формы для добавления планеты, которая будет отображаться по URL’у /planets/add . Для этого нужно добавить один незамысловатый метод в контроллер:

Этот метод просто вернет пользователю шаблон  app/views/planets/add.blade.php , который нам и предстоит сейчас создать. Суть этого шаблона заключается в простой генерации формы со всеми необходимы полями для добавления новой планеты.

Как я уже упоминал ранее, используемая в Laravel Eloquent ORM нигде не хранит структуру базы данных и, соответственно, моделей. Поэтому в Laravel, по крайней мере «из коробки», не существует автоматической генерации форм для модели. То есть все поля нужно прописывать «вручную». Но тут на помощь нам придет так называемый FormBuilder, доступный через фасад Form. Он содержит многочисленные методы для генерации различных элементов форм, а также имеет поддержку так называемого model-биндинга, то есть «умного» отображения моделей, о чем я расскажу детальнее, когда мы дойдем до редактирования существующих планет.

Итак, давайте создадим шаблон для создания планет в файле app/views/planets/add.blade.php . Начнем с «каркаса»:

Как можно видеть, форма, пока пустая, создается вызовами  Form::open(...) и  Form::close() . С последним, думаю, все понятно: он просто генерирует закрывающий тэг  </form> . А вот  Form::open(...) рассмотрим подробнее.

Синтаксис этой команды на самом деле прост: в передаваемом массиве мы передаем «настройки» формы. Есть некоторые «зарезервированные» ключи у этого массива, как например  url — он задает  action формы, то есть адрес (см. примечание), куда будет отправлена форма. Остальные же элементы массива просто определяют дополнительные атрибуты тэга  <form ...> . В нашем случае:

  • 'method' => 'post' добавляет атрибут  method , указывающий, что форма должна быть отправлена методом POST
  • 'role' => 'form'  и  'class' => 'form-horizontal' являются чисто украшательными, так мы создаем форму, используя Bootstrap.

Примечание: в данном случае адрес указывается не напрямую  /planets/add , а посредством вспомогательной функции action , которая генерирует этот путь исходя из переданного ей контроллера и метода. В этом есть смысл, если вдруг в определении маршрута вы решите заменить planets  на что-то другое, при этом код создания этой формы менять не придется.

Теперь, чтобы это заработало, нужно создать, пока пустой, метод postAdd  в нашем контроллере, иначе функция action  сгенерирует ошибку:

Опять же, для тех, кто вдруг не полностью усвоил второй урок, напомню: мы имеем дело с двумя разными маршрутами, хоть и имеющих одинаковый URL /planets/add . При запросе адреса http://sbshare.localhost:8000/planets/add  из броузера (это GET  запрос), будет выполнен метод getAdd , в то время как форма будет отправлена на такой же адрес, но уже методом POST , и, следовательно, отрабатывать будет метод postAdd  контроллера.

Теперь я предлагаю сделать следующее: саму форму мы вынесем в отдельный под-шаблон form.blade.php , а в этот лишь добавим кнопку submit  (отправку формы) и инклюд самой формы. Таким образом часть шаблона add.blade.php будет такой:

Обратить внимание стоит на инклюд под-шаблона: @include('planets/form') , который будет содержать саму форму. Делаю я так потому, что для редактирования планеты у нас будет абсолютно идентичная форма. Разница лишь в открывающей инструкции — в случае с редактированием форма будет открываться посредством  Form::model(...) — что позволяет «привязать» модель планеты к форме. Но об этом позже. Сейчас давайте напишем код для генерации само формы в файле app/view/planets/form.blade.php .

Как я уже говорил, для генерации различных элементов форм у фасада Form  есть множество методов. Например, для генерации text-input ‘а в простейшей форме может использоваться такая запись:

Результатом этого вызова будет такой input :

Как и другие методы фасада, метод text может принимать дополнительные параметры:

  • второй необязательный параметр указывает значение value это поля
  • третий необязательный аргумент — это массив, описывающий дополнительные атрибуты input’а. В большинства случае он используется для указания требуемого CSS-класса input’а

Например, вызов Form::text('fieldName', 'some-value', array('class' => 'my-input-class'))  сгенерирует такой input :

Помимо метода text у FormBuilder’а имеются, например, такие методы с аналогичными аргументами: email, password, textarea, select. Метод select немного отличается: к нему добавляется еще один аргумент (после имени поля) — массив, содержащий все опции этого выпадающего списка.

Итого, наша форма может выглядеть так:

Надеюсь Bootstrap-разметка не сильно мешает вникнуть в суть этого кода 🙂 Обратить внимание нужно на следующие две вещи:

  1. В качестве значения полей во всех этих методах указывается null. Это значение по-умолчанию при вызове метода, если этот аргумент не задан, но так как нам надо передавать третий аргумент (для описания класса инпутов), второй параметр все-же нужно указывать.
  2. На данный момент у полей select передается пустой массив опций. Позже, когда у нас будет готова модель для планет, мы исправим это — эти поля будут заполняться допустимыми опциями.

Сохранив изменения, можно уже увидеть нашу форму, которая выглядит примерно так:

Форма добавления планеты

Если нажать сейчас на кнопку «Добавить», то в ответ мы увидим пустую страницу, просто потому, что метод postAdd  в контроллере тоже пустой.

Теперь настало время создать модель для планеты, чтобы упомянутому выше методу postAdd  было с чем работать. Для этого создадим файл app/models/Planet.php .

Уже не раз упоминал, что для работы с базой данных в Laravel используется Eloquent ORM. Чтобы наша модель получила все возможности этой ORM нужно, чтобы наш класс наследовал класс Eloquent :

Вообще говоря, этого уже достаточно, чтобы начать работать с планетами 🙂 Настолько Eloquent прост, и в то же время мощен. Имя всего один такой класс Planet мы уже можем делать выборки из базы, добавлять планеты в базу, вести счетчик всех планет и т.д.

Например, чтобы добавить новую планету в базу, мы можем сделать так:

Или например, чтобы выбрать из базы планету с id, равным 4, мы можем сделать так:

Но сейчас не буду слишком забегать вперед: это были просто примеры, демонстрирующие простоту и удобство Eloquent ORM.

У нас в таблице используются стандартные именования полей для первичного ключа (id), и для временных меток создания и изменения записи (created_at и updated_at). Поэтому данный класс уже является достаточным для работы. Если вас интересует вопрос: «откуда Eloquent знает, в какую таблицу производить запись?», отвечу: по умолчанию, Eloquent приводит имя класса к нижнему регистру (в простейшем случае, то есть если имя класса состоит из одного слова, как у нас) и добавляет окончание «s» или «es» (в зависимости от правил английского языка) и таким образом получает название таблицы «planets» — именно так называется наша таблица, то есть Planet во множественном числе.

Но для полноты картины я опишу, как можно переопределить такие значения по умолчанию. Например, если бы наша таблица в базе называлась my_planets , а первичный ключ назывался planet_id , то тогда к классу добавились бы следующие protected  переменные:

В этом случае Eloquent будет брать именно эти значения, а не значения по умолчанию.

Но тем не менее, модель планеты нам еще нужно немного довести до нужного нам состояния, а именно:

  • поместить в нее правила валидации
  • определить в ней допустимые значения для некоторых полей (сектор, биом, и т.д.)
  • описать поля, используемые в так называемом mass-assignment’е (массовом заполнении полей; что это такое будет видно позже)

Вспомним наш шаблон формы — сейчас в нем в select’ы подставляются пустые массивы для списка опций, просто потому, что эти массивы нами еще нигде не определены и брать их неоткуда. Самым логичным образом было бы определить их в самой модели, и сделать их статическими, чтобы можно было воспользоваться ими «сразу», то есть без инстанциирования модели. Итак, добавим следующие определения в наш класс Planet :

Надеюсь формат массивов понятен. Например, для первого сектора, определенного в таком формате, сгенерируется такая опция:

Осталось подставить эти массивы в форме. К примеру, select для секторов будет выглядеть так:

Надеюсь проставить оставшиеся массивы у вас не составит труда 🙂 Как только вы это сделаете, снова зайдите на страницу с формой и проверьте, появились ли в выпадающих списках опции.

Немного выше я упомянул такой термин, как mass-assignment. Что это такое? Еще раньше, приводя примеры работы с Eloqueny, я привел код заполнения модели посредством установки каждого отдельного поля. Но это долго и неудобно. Вместо этого можно выполнить одну простую инструкцию:

В этом случае будет выполнено тоже самое: модель заполнится данными из $data и будет сохранена в базу. $data  — это ассоциированный массив, где ключ — заполняемое поле, а значение — соответственно значение этого поля. Учитывая, что у нас в форме все поля именуются как раз в соответствии с полями в базе, процесс создания новой записи в базе еще больше упрощается: мы можем просто взять все входящие данные и заполнить ими модель. В нашем случае это будет выглядеть так, с использованием фасада Input :

Примечание: фасад Input предназначен для получения входящих данных, полученных будь то GET или POST методом. Input::all() возвращает все полученные в запросе данные в виде ассоциированного массива. Немного более подробно мы с ним поработаем, когда будем реализовывать поиск по планетам в одном из следующих уроков.

Но у опытных разработчиков возникнет вопрос: а как быть с безопасностью? Ведь таким образом злонамеренный пользователь может добавить в запрос произвольное поле? Вот для избежания этого в модели и нужно прописать правила mass-assignment’а, чтобы ограничить допустимые поля.

Есть два способа это сделать. Первый: перечислить поля, которые допускаются к такому массовому заполнению, то есть установлены будут только поля, перечисленные в этом списке. Второй, наоборот, перечислить поля, которые не могут быть массово заполнены.

Для реализации первого способа нужно задать protected массив $fillable  в модели, в котором будут перечислены допустимые для заполнения поля. В нашем случае он будет такой:

Для реализации второго способа нужно объявить protected массив $guarded , содержащий поля, которые не могут быть заполнены. Например, в нашем случае он мог бы выглядеть так:

Таким образом мы защитим перечисленные поля, то есть даже если пользователь передаст их в запросе, они не будет установлены в модели.

Хоть я и не могу точно сказать, в каком случае какой способ использовать «правильнее», я больше предпочитаю первый способ ( $fillable ). Стоит также отметить, что до версии Laravel 4.1.29  была серьезная уязвимость во втором способе, когда переданные в mass-assignment’е данные не экранировались должным образом.

Итак, на данном этапе уже можно попробовать добавить планету. Временно запишем в метод postAdd  контроллера такой код:

Теперь заполните форму данными и попробуйте добавить. В результате вы должны увидеть сообщение «Добавлена планета, id: 1». И чтобы удостовериться — загляните в базу используемым вами менеджером, о котором я говорил в одном из прошлых уроков:

 

Что же, могу поздравить с первой записью в базе 🙂 Но на этом останавливаться нельзя. Еще одним важным моментом является проверка, или валидация, данных, чтобы не дать пользователю возможность внести заведомо некорректные данные. Также мы должны сообщать пользователю о том, какие поля были заполнены неверно. Речь идет в первую очередь о серверной валидации, так как какой бы умной не была валидация на клиенте, ее пользователь с легкостью может обойти, а проверять данные все равно нужно. На сайте sbshare.ru нету клиентской валидации, просто потому, что не считаю ее нужной, да и признаться не сильно люблю возиться с JavaScript’ом. А вот серверной мы сейчас и займемся.

Валидация в Laravel производится при помощи фасада Validator . В общем случае, делается это так:

В большинстве случаев в качестве $data  выступает Input::all() . А вот правила представляют из себя следующее …

Это ассоциированный массив, где каждый элемент определяет правила для одного конкретного значения из $data . Рассмотрим на примере. Нам нужно, чтобы значение поля level  из проверяемых данных было:

  1. обязательным для заполнения
  2. было числом
  3. было не меньше 1 и не больше 10

В этом случае массив $rules должен содержать такой элемент:

required , integer , min , max  — это стандартные правила валидации, определенные в самом Laravel ( required  означает, что поле обязательно для заполнения, integer  — что это число, min  — минимальное значение, а max , соответственно, — максимальное).

Как можно видеть, в данной форме записи всё правило валидации является просто строкой, в которой за счет разделителя |  (вертикальная черта) перечислены различные правила. У некоторых правил возможны аргументы ( min  и max ): в этом случае они указываются после двоеточия. Если аргументов несколько, они перечисляются через запятую.

Помимо строковой записи правила существует еще запись в виде массива, но она более громоздка. Те же самые правила можно описать так:

Помимо перечисленных правил валидации нам на данном этапе потребуется еще правило in , указывающее, что проверяемое поля должно являться одним из перечисленных значений. Например, так мы можем ограничить значение передаваемого поля sector :

С полным списком имеющихся в Laravel правил валидации можно ознакомиться по ссылке: http://laravel.com/docs/validation#available-validation-rules.

Теперь осталось определиться, где хранить эти правила валидации. В контроллере? Неразумно, потому как вдруг они понадобятся в другом контроллере … Можно сделать для валидации отдельный класс-валидатор, но это немного усложнит нам задачу. Поэтому предлагаю пойти по оптимальному пути — определить правила валидации модели в самой модели. Сделать это можно либо статическим массивом, либо, что будет в нашем случае удобнее — статическим методом, возвращающим массив. Дело в том, что если просто определить правила в виде статического массива, у нас будет повторение данных: у нас уже есть определенные допустимые значения для выпадающих списков, зачем нам дублировать их еще и в правилах валидации? Поэтому наши правила валидации будут динамическими и генерироваться на лету, исходя из имеющихся списков.

Итого, у модели Planet появляется такой статический метод:

Теперь мы можем изменить наш метод postAdd с учетом валидации:

Обратите внимание на 6ю строчку: в ней, в случае неудачной валидации, мы возвращаем пользователя на страницу добавления планеты, но не просто так, а добавляя в шаблон данные об ошибках ( withErrors($validation) ) и, что тоже интересно, с его же введенными данными ( withInput() ). Тут проявляется польза от использования фасада Form  для генерации элементов форм: он достаточно умен, чтобы увидеть, что был произведен редирект с ранее введенными данными, и подставит их обратно во все поля. Если бы мы писали все инпуты вручную, нам бы пришлось очень сильно заморочиться, чтобы реализовать такую логику. Можете проверить: введите какое-нибудь заведомо неверное значение в поле «Уровень» (например строку) и попробуйте добавить планету: вы будете возвращены обратно, а поле «Уровень» будет содержать ранее введенное значение.

Теперь об ошибках. Благодаря вызову withErrors() шаблон получает в свое распоряжение переменную $errors, содержащую сообщения об ошибках. Работать с этой переменной можно по-разному: можно «по-умному» подсветить ошибочные поля и отобразить сообщения об ошибке где-нибудь справа каждого поля. Но мы поступим проще: просто выведем список ошибок.

Добавьте следующий код в шаблон add.blade.php  перед заголовком h2 :

Здесь мы видим новые инструкции шаблонизатора Blade: @if  и @foreach , но так как их синтаксис абсолютно аналогичен таким же конструкциям PHP, останавливаться на них не буду. Важно только правильно закрывать их соответствующими @endif  и @endforeach .

Переменная $errors  представляет собой экземпляр класса MessageBag  (буквально — мешок сообщений). Метод all  возвращает все сообщения, содержащиеся в мешке. Можно также получить сообщение об ошибке для конкретного поля, например, так:

В нашем же случае идет просто обход всего массива ошибок и вывод их. Можете проверить, оставив незаполненными некоторые поля в форме и отправив ее. Вы вернетесь снова к форме и увидите сообщения об ошибках.

Что вы сразу заметите — сообщения об ошибках будут на английском языке. Но это легко поправимо. Этим мы сейчас и займемся.

Все языковые файлы содержатся в папке app/lang , точнее в под-папках для каждого языка. По умолчанию, понятное дело, имеется только английская локаль ( en ). Подробное описание работы с локализацией выходит за рамки это урока, да и вообще у нас сайт не многоязычный, поэтому это тема вообще говоря стоит отдельной статьи.

Используемый Laravel’ом язык прописан в конфиге app/config/app.php  (параметр locale ). Как можно догадаться, Laravel просто ищет переводы строк в папке, соответствующей значению этого параметра.

Таким образом, первое, что нам нужно сделать — это установить язык приложения в  ru . Далее нам нужна папка app/lang/ru , которая будет содержать те же самые файлы, что и в папке en , но с русским переводом. Я не предлагаю заниматься сейчас переводом, так как Laravel-сообщество уже сделало это (вспоминается фраза из известного фильма: «все уже украдено до нас :(«). Просто перейдите по ссылке на репозиторий GitHub: https://github.com/caouecs/Laravel4-lang и скачайте zip-архив с переводами (кнопка справа) куда-нибудь, распакуйте, а затем просто перенесите папку ru  внутрь app/lang .

Если вы решите проверить локализацию, то увидите, что сообщения об ошибках на русском, но выглядят они примерно так: «Поле level не может быть более 10.». Мы конечно могли бы на этом и остановиться, но душа требует доводить все до конца: нужно как-то перевести еще и названия полей.

Для этого предназначено отдельное поле в массиве переводов сообщений валидатора (файл app/lang/ru/validation.php ): attributes . Это — тоже массив, содержащий переопределения названий полей. В нашем случае он должен быть таким:

Я немного забежал вперед и добавил поля для почты, имени пользователя и пароля — просто чтобы больше не возвращаться к этому массиву, хотя к самому файлу validation.php  мы еще разок вернемся, когда будем делать капчу.

На этом добавление планеты можно считать почти законченным: планета добавляется, а если нет, то об этом красиво сообщается пользователю. Осталось для полноты картины при успешном добавлении сделать редирект на страницу, отображающую всю информацию об этой планете. Точнее сам то редирект — дело простое, а вот саму страницу еще нужно сделать. Я приведу лишь код контроллера под это дело, а написание шаблона оставлю вам как домашнее задание 🙂 Знаний для его выполнения у вас уже достаточно.

Итак, нужно добавить метод getView в наш контроллер со следующим содержимым:

Здесь все просто: сначала из базы извлекается планета с указанным id . Если такой планеты нет — выдается 404 ошибка. Далее увеличивается счетчик просмотров планеты и производится обновление записи в базе. Хочу заметить, что Eloquent достаточно умен, чтобы производить запись только обновленного поля, то есть в результирующий SQL-запрос попадет только поле views  (ну и еще обновится автоматически поле updated_at ). Остается только красиво вывести содержимое объекта $planet  в шаблоне planets/view.blade.php .

На что только хочу обратить внимание, когда будете делать шаблон: выводить поля объекта стоит посредством тройных скобок: {{{ $planet->comment }}} . Они экранируют вывод, что предотвращает попытки пользователей встроить свой HTML-код в страницу.

Теперь нам остается вывести список уже добавленных планет. На сайте он имеется в двух «версиях»: последние N добавленных планет отображаются на главной странице, и полный список с постраничным выводом в разделе «Планеты». Так как функционал в целом одинаковый, здесь я приведу только код главной страницы, точнее контроллера, шаблон как и прошлом случае предоставлю сделать вам самостоятельно. К тому же список планет в разделе «Планеты» напрямую завязан на функционал поиска, поэтому его я рассмотрю позднее, вместе с пагинацией.

Заглушка для главной страницы у нас уже есть, осталось дополнить ее логикой и непосредственно выводом самих планет. Напомню, это метод getIndex  контроллера IndexController . Пока он примет такой вид:

Обратите внимание, как получается список последних шести планет. Вообще подобные интерфейсы называются fluent-интерфейсами, то есть когда каждый метод объекта возвращает этот же объект, что позволяет вызывать его методы «по-цепочке». Я иногда называю такие вызовы каскадными. Что же происходит в данном случае? Вызывая один из статических методов класса Planet  (а точнее Eloquent ), мы получаем в ответ некоторый объект, предназначенный для формирования запроса (но это не то же самое, что Query Builder, хотя по сути его можно так и назвать). Далее мы по цепочке можем вызывать дополнительные его методы, уточняющие запрос, это может быть и where , причем не один, или как в данном случае take  (или его полный аналог limit ) и так далее. Завершают цепочку вызов одного из, как ни странно, «завершающих» методов, например get , first , paginate  и другие, возвращающие либо коллекцию объектов  Planet , либо единичный экземпляр этого же класса. Коллекцию можно рассматривать как обычный массив, то есть ее можно обходить как и обычные массивы, например при помощи foreach .

Вообще тема Eloquent довольно обширна, и в данном уроке я лишь рассмотрел некоторый минимум, нужный нам для реализации наших задач. Я не претендую на полноту описания этой ORM в своих уроках, поэтому рекомендую параллельно ознакомиться с официальной документацией: http://laravel.com/docs/eloquent.

На этом четвертый урок подходит к концу. Но мы еще несколько раз вернемся к тому, что реализовали в нем, например, когда реализуем регистрацию пользователей (добавим капчу к форме), или когда будем говорить о кэшировании (как минимум будем кэшировать счетчик планет). Также немного дополним метод postAdd , когда затронем тему событий и отправки писем. Но не буду сейчас сильно забегать вперед: всему свое время 🙂

А пока желаю всем удачи в освоении Laravel и поменьше багов 🙂 До скорых встреч!

Поделиться в соц. сетях

39 thoughts on “Поэтапная разработка сайта на Laravel 4: урок №4

  1. Прикольно, ё. Интересно как пагинация реализуется. Посмотрю сейчас сам.

    Многое в Laravel напоминает Zend Framework 2, но упрощено. А валидация по допустимым опциям select’а в ZF2 автоматом есть в соответствующем элементе. И при динамическом заполнении опций эту валидацию необходимо отключать.

    Спасибо вам огромное за ваши статьи! Читать их перед сном одно удовольствие. А на следующий день я на практике пробую. 🙂

  2. Zhanat:

    можно в общей модели, наследуемой от Eloquent или в каком-нибудь хелпере, или прям в этом классе добавить метод:
    public function getEnumAndRequiredRule($v)
    {
    return ‘required|in:’ . implode(‘,’, array_keys($v));
    }

  3. adeyka:

    Спасибо. Все четко и с разъяснениями. Уже хоть что-то мне стало понятно по eloquent (очень крутая штука, по ней бы больше инфы). Надеюсь таких уроков будет больше 🙂

  4. Здравствуйте, Александр.

    Спасибо за ваши замечательные уроки, сейчас осваиваю по ним Laravel. Возник такой вопрос.

    Заполнители для select’ов класс Planet каким-то чудесным образом перекочевали в SQLyog, т.е. я могу выбирать их и там при заполнении соответствующих полей (sectors и др.). Как это вообще произошло? Ведь поля определены в классе. Или Eloquent ORM как-то их туда экспортирует.
    Я попробовал добавить новый вариант выбора для select’а, но ничего получается, в базе такого варианта нет. Если его выбирать в форме, то в базу попадает пустое поле.

      1. А если теперь захочется расширить список выбора для некоторого поля (типа enum), то нужно сначала удалить это поле, а затем создать его заново?

        1. Вопрос снимается. Проще ручками в базе поправить. И в уже примененную миграцию вписать соответствующие значения, чтобы потом при новом развертывании все сразу было ок.

  5. Александр, а можно в модели явно описать атрибуты? То есть


    class PlanetController extends BaseController
    {
    // Координаты x и y. Тип поля: INT
    public $x;
    public $y;

    Мне это нужно для того, чтобы вся информация о модели была в классе модели. Я помню, что эта информация есть в миграции, но это не очень удобно, чтобы разобраться в модели нужно лезть или в файл миграции или в БД. А так все в одном месте и потом легче будет разобраться что к чему.

    Такой подход не поломает работу ORM? Я проверил, вроде бы работает, но может быть, есть какие-то нюансы?

    1. Пардон, небольшая ошибка. Атрибуты должны быть в классе модели:

      class Planet extends Eloquent {
      // Координаты x и y. Тип поля: INT
      public $x;
      public $y;

      1. К сожалению, с явным определением полей в классе будут проблемы, когда придется устанавливать отдельные свойства модели — в этом случае не будет срабатывать магический сэттер Eloquent, который выполняет довольно много работы «по учету» изменившихся полей. При этом с mass-assignment’ом проблем вроде быть не должно.

        В качестве альтернативы могу предложить прописывать все свойства модели в php-doc’е к классу вида

        /**
        * ...
        * @property int $x
        * @property int $y
        * и т.д.
        */
        class Planet extends Eloquent {
        // ...

        В этом случае будет автокомплит в IDE’шках, включая шторм.

        Можно также воспользоваться laravel_ide_helper’ом, который проанализирует таблицы в базе и сам пропишет phpDoc’и в моделях.

        1. Спасибо, Александр! Это хороший вариант.

          Воспользовался laravel_ide_helper’ом сгенерировал phpDoc для Planet. Все замечательно, только шторм не реагирует на него, т.е. эта информация не попадет в автокомплит.

          Не знаете, в чем дело? Можете быть, в шторме нужно еще какие-то опции включить (у меня phpStorm 8.0.1)?

          P.S. Кэш чистил.

          1. Вопрос снимается, разобрался. Пытался увидеть свойства после:

            $planet = Planet::find($planetId);
            $planet-> ничего из phpDoc

            но find ведь не возвращает класс Planet.

            А так все ок:

            $planet = new Planet();
            $planet-> все, что определено в phpDoc

            1. > но find ведь не возвращает класс Planet.

              Может быть, это не совсем корректное высказывание. Я имел в виду тип Planet. После C# с его строгой типизацией типы из PHP непривычны.

              ***

              Если нужен автокомплит в случае с find, то для IDE можно указать тип переменной:

              /** @var Planet $planet */
              $planet = Planet::find($planetId);

              1. Все верно, find формально возвращает класс Eloquent. Как вариант (я сам еще не пробовал, поэтому гарантировать не могу) — это прописать в том же phpDoc’е к классу Planet что-то вроде

                * @method Planet|null find($id)

  6. AGS:

    Если получаете ошибку Class ‘Planet’ not found
    Возможно, необходимо оптимизировать загрузчик классов.
    Выполните в командной строке php artisan dump-auto Мне помогло.

  7. …»по-меньше» — ошибка, правильно «поменьше». Не пользуйтесь spellchecker’ом Word’а. Нельзя впихнуть Великий Русский Язык в убогую майкрософтовскую программу, как нельзя вылить бочку в бутылку.

  8. Иван:

    Уперся на этапе добавления записи с БД. А именно все написал как в статье, даже воспользовался копипастом (пытаясь найти ошибку), но в итогде не могу ее найти. По нажатии «добавить» получаю ошибку

    Illuminate \ Database \ Eloquent \ MassAssignmentException
    _token

    Не могли бы к статье выкладывать еще написанные исходники, чтоб можно было сверить и найти эту ошибку?

    1. Ошибка скорее всего в том, что в модели у Вас не прописаны правила mass-assignmenta:

      Но у опытных разработчиков возникнет вопрос: а как быть с безопасностью? Ведь таким образом злонамеренный пользователь может добавить в запрос произвольное поле? Вот для избежания этого в модели и нужно прописать правила mass-assignment’а, чтобы ограничить допустимые поля.

      Есть два способа это сделать. Первый: перечислить поля, которые допускаются к такому массовому заполнению, то есть установлены будут только поля, перечисленные в этом списке. Второй, наоборот, перечислить поля, которые не могут быть массово заполнены.

      Исходники я тоже стал добавлять в пятом уроке, на котором пока и остановился:

      А вот ссылка на репозиторий GitHub: . Для каждого урока я буду заводить отдельную ветку в нем. Таким образом, у вас будет возможность просто склонировать конечный результат урока себе, чтобы посмотреть его вживую у себя, или просто сравнить со своими вариантами решений. К примеру, конечный результат четвертого урока можно увидеть по ссылке .

  9. Спасибо за уроки! Всех с 5 версией!

    Пытаюсь повторить Ваши уроки в 5 версии. И сразу есть вещи, у которых пятерка «бьет по рукам» и заставляет призадуматься. Во вьюхе есть поля sector, biome, version, os, которые обращаются к модели Planet. По теории MVC вид ничего не должен знать о модели. В 5 версии этот код выдает ошибку(«Не найден класс Planet»). Поэтому приходится данные обработать в контроллере и передать виду:

    public function getAdd() {

    return view('planets/add', ['sectors'=>Planet::$sectors, 'bioms'=>Planet::$bioms, 'versions'=>Planet::$versions,'oses'=>Planet::$oses]);

    }

    Та же проблема с видом


    {{ date_create($planet->created_at)->format('d.m.Y H:i:s') }}
    OS: {{ Planet::$oses[$planet->os] }}, Уровень: {{ $planet->level }}
    Координаты: X {{ $planet->x }}, Y {{ $planet->y }}

    Вывод OS не совсем понятен, обращаемся к модели, хотя вся информация есть в $planet->os.

    1. Насчет пятой версии пока сказать затрудняюсь. Но хочу отметить, что «вид ничего не должен знать о модели» — это не верно. Не знаю, о какой теории идет речь, но мы же на практике во вьюхах выводим поля модели, соотв-но вьюха не может не знать ничего о модели. Соотв-но вьюха знает как минимум поля, которые есть у модели. Другое дело, что спорный вопрос, разрешать ли вьюхе обращаться к методам извлечения данных при помощи модели из БД, но в данном случае это тоже «не в тему», так как списки биомов и прочее — это всего лишь статические поля модели.
      Насчет OS: в $planet->os содержится ключ OS, тогда как отображаемое значение для конкретной OS содержится в массиве Planet::$oses. Просто они (ключи и значения) внешне очень похожи.

      1. Я не сторонник давать так много полномочий виду. Ну это мое видение. Сегодня статические поля модели, завтра нам захочется хранить их базе и т.д.

        У меня другое предложение: присмотритесь к 5 версии. Там уже «из коробки» идет реализация аутентификации точь-в-точь как у вас в 5 уроке. Хотелось бы услышать ваше мнение. Разобрать как оно реализовано. В целом, спасибо за ваши уроки, они для меня имеют большую ценность.

  10. Спасибо.
    На сегодня один из самых простых, понятных и удобных мануалов по интеграции в Laravel.
    Прочитал 4 урока на одном дыхании.

    Рекомендовал прочитать все уроки своим сотрудникам…
    Успехов и ещё раз спасибо.

    Соглашусь с некоторыми, что хотелось бы ещё изучать срез с Laravel5.
    Чтобы лучше понимать разницу и суть обновлений.

  11. Ребята можно и под 5 самостоятельно портировать. Например проблема с недоступностью заполнения
    полей формы из методов класса решается путем создании папки Models по пути app/Models/ ваша модель. После чего надо прописать в файле composer.json
    «autoload»: {
    «classmap»: [
    «database»,
    «app/Models»
    ],
    Далее у кого выскакивает сообщение о том что класс Planet не доступен надо просто добавить слэш перед вызовом класса. Тоесть
    public function postAdd(Planets $planets) {
    $planet = \Planet::create(Request::all($this));
    return ‘Добавлена планета, id: ‘ . $planet->id;
    }

  12. Евгений:

    Замечательные туториалы! Спасибо! Laravel — отличный фреймворк, Yii 2, с которого я, собственно, перехожу на Laravel не очень понравился — замутов там много, усложняют жизнь разработчику.

  13. Максим:

    О редактировании формы где-то есть? В уроке два раза звучит, что та же форма будет использоваться. Как в эту форму данные из базы вставлять?
    На том месте, где null
    isset($planet) ? $planet->name : null
    Или есть что—то поизящнее?
    Потому что если вставлять просто $planet->name, в форме add ошибка кричит, что нет такого объекта.

  14. Повторил ваши уроки на 5-ом (были проблемы с пространством имен). Споткнулся с выводом ошибки по не валидным данным из формы добавления планет:
    переменная $errors не определена …
    Куда копать не соображу …
    Спасибо!

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Лимит времени истёк. Пожалуйста, перезагрузите CAPTCHA.