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

Всем доброго времени суток!

Настало время продолжить нашу серию о поэтапной разработке сайта на Laravel 4! В предыдущем уроке мы разобрали, как можно настроить окружение для разработки, а также несколько способов создания пустого Laravel-приложения. Надеюсь он не вызвал сильных затруднений 🙂

Сегодня я, как и обещал, расскажу о следующем:

  • Структура обычного Laravel-приложения
  • Маршрутизация в Laravel (routing)
  • Ответы пользователю: фасад View, шаблоны и редиректы (перенаправления)

Данный список тем выбран не случайно, потому как мы должны двигаться по порядку. Равно как нет смысла приводить куски кода на Laravel, если читатель не знает, как это начать «щупать» (тема первого урока), так и нет смысла писать какую-то логику в приложении, если не знать, как выдать ответ пользователю. То есть если вы ждали, что уже сейчас мы начнем создавать сайт, то вынужден пока разочаровать. Думаю, что непосредственно над сайтом мы начнем работу в третьем уроке. Я делаю так потому, что мне важно, чтобы читатель понимал основы работы с Laravel. В конце концов, кому нужна просто копия сайта sbshare.ru ? Мне важно, чтобы читатель после прочтениях этих уроков смог самостоятельно реализовать свои задачи посредством Laravel, а не рассчитывал на готовые решения со стороны.

Но прежде чем перейти к более практичным вещам, надо понимать, из чего состоит Laravel-приложение, чтобы знать, куда помещать свои классы или даже функции, а особо любопытным — куда смотреть, чтобы понять, как работает тот или иной функционал на Laravel. Так что продолжу статью описанием структуры Laravel-приложения.

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

  • /app
    В этой папке и ее подпапках будет сосредоточена вся логика конкретно вашего приложения.
  • /bootstrap
    Здесь содержатся бутстрап (bootstrap)-файлы приложения. Под бутстрапом обычно подразумевается какая-либо стартовая инициализация приложение. В подавляющем большинстве случаев трогать файлы в этой папке нет необходимости.
  • /public
    Как говорит само название — это «публичная» папка вашего проекта. Она содержит единственную точку входа в ваше приложение (index.php) а также все дополнительные файлы, которые должны быть доступны из-вне любому посетителю вашего сайта, будь то стили, JavaScript-файлы и прочее. Веб-сервер «смотрит» именно в эту папку, что исключает возможности какому-то хакеру получить содержимое какого-либо файла допустим из папки app, чтобы увидеть серверную логику.
  • /vendor
    «Сердце» вашего приложения. Эта папка содержит все файлы самого фреймворка, а также каких-либо дополнительных компонентов (называемых также пакетами), устанавливаемых при помощи Composer. Папка vendor является стандартной для любого приложения, созданного при помощи Composer, и, в частности, Laravel. Это то, на что опирается все ваше приложение. Файлы в этой папке никогда не стоит править — это считается очень плохим и грязным тоном, не говоря о непрактичности таких правок — они будут затерты при ближайшем обновлении фреймворка или конкретного компонента..
  • Файлы в корне. Наибольший интерес представляют artisan и composer.json:
    • artisan
      Artisan — это, если можно так выразится, консольная версия Laravel-приложения. Позволяет запускать различные обслуживающие команды (создание миграций, просмотр маршрутов приложения и так далее) из консоли, но помимо этого является и расширяемым средством: можно написать свои произвольные обслуживающие команды, что большинству разработчиков больше известно как cron-скрипты. Мы постепенно будем знакомиться с некоторыми стандартными командами artisan’а, а ближе к концу серии даже напишем свою собственную.
    • composer.json
      Файл настроек Composer. Наибольший интерес в нем представляет секция «require», при помощи которой мы можем добавлять любые дополнительные компоненты в приложение, которые нам потребуется. Позже мы добавим два дополнительных компонента в него для реализации CAPTCHA и BBCodes.

Директория app

Остановимся поподробнее на директории app, в которой будет содержаться весь специфичный для конкретного приложения код. Здесь, помимо самой логики приложения, также находятся: файлы конфигурации, миграции и сиды, кэш приложения и многое другое.

В папке config находится конфигурация приложения. Она содержит разные конфигурационные файлы, которые могут использоваться как самим фреймворком, так и нашим приложением. Расписывать здесь назначение каждого файла и тем более его значений смысла особого нет, но могу просто порекомендовать просмотреть эти файлы и их содержимое. Имя каждого файла вполне однозначно говорит о конфигурируемой им «области» приложения, а каждый параметр внутри подробно прокомментирован. Так что будет полезно иметь представление о возможных опциях приложения, будь то драйвер базы данных или сессии и так далее. Некоторые файлы правятся один раз, и больше никогда не трогаются (например настройки соединения с базой данных), другие же могут периодически подвергаться изменением по ходу разработки приложения. Чаще всего приходится править файл app/config/app.php, что мы будем делать немного позже.

Папка controllers подразумевает хранение в ней всех контроллеров приложения. Все классы в этой папке доступны автозагрузчику.

В папке lang содержатся локализационные файлы приложения, разделенные по «категориям». Например в скором времени нам придется создать подпапку ru для русской локализации и заменить в ней, например, файл validation.php, в котором содержатся переводы сообщений об ошибках валидации.

Папка models подразумевает хранение в ней всех моделей приложения. Начиная с третьего урока, мы начнем писать свои модели.

В папке start находятся файлы, выполняемые при запуске приложения. Речь идет, в первую очередь, о файле global.php. В нем можно определить некоторую логику поведения приложения (например, переопределить стандартное поведение приложения при 404 ошибке сервера, и так далее). В этот файл также можно добавлять какие-то свои маленькие вспомогательные функции.

Папка storage, как ясно из названия, является хранилищем приложения. Здесь содержится кэш (если, конечно, в качестве драйвера для кэша указаны файлы), логи, сессии (опять же, если драйвер — файлы). Если вы работаете над проектом используя какую-либо VCS (version control system), эту папку следует добавлять в игнор.

И, наконец, папка views. Она будет содержать все наши шаблоны для вывода данных пользователю.

Рекомендую посмотреть небольшой видео-урок от Егора Громыко на эту тему.

Маршрутизация

При написании своего приложения, будь то на Laravel, другом фреймворке, или же на чистом PHP, важно иметь четкое представление о таком понятии, как маршрутизация запросов.

Если упростить, то машрутизация — это процесс определения, какую логику приложения стоит выполнить при том или ином запросе от пользователя.

Первоначальную маршрутизацию выполняет сам веб-сервер: на основе запроса (например GET-запроса) он определяет, какой файл выдать пользователю, или же в случае использования PHP — какой скрипт выполнить. Изначально, еще до появления серверных языков, таких как PHP, роль веб-сервера была проста: просто выдавать запрошенный файл. К примеру, если пользователь запросил http://domain.com/index.html — выдать файл index.html, находящийся в корневой папке веб-сервера. Аналогично с другими файлами.

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

По-началу, для каждого «маршрута» создавался отдельный скрипт, выполняющий свою логику. Это ситуация нескольких точек входа в приложение. Я думаю многие начинающие программисты на PHP начинали именно с такого подхода: для каждого отдельного раздела сайта — свой скрипт. У такого подхода есть один большой минус: даже если есть какой-то центральный функционал приложения, инициализация этого приложения повторялась в каждом скрипте. Понятное дело, что если приходилось изменять инициализацию, это приходилось делать в каждой точке входа.

Поэтому, в результате эволюции и усложнения веб-приложений, многие пришли к использованию единой точки входа в приложение. Создается один файл, в большинстве случаев index.php, обрабатывающий все запросы. Ну точнее запросы обрабатываются конечно же не в самом этом файле — это просто точка входа. Здесь происходит инициализация приложения, будь то какой-то отдельный класс Application, или же отдельные функции, если не используется ООП-подход. И уже в этом приложении происходит дальнейшая маршрутизация запроса: приложение анализирует сделанный пользователем запрос и выполняет соответствующую логику, распределенную, если это MVC, на контроллеры.

В результате все той же эволюции, как и во всех языках программирования, на PHP стали появляться различные фреймворки, или, если переводить это слово (framework), — каркасы для написания приложений, содержащие в себе готовый для использования функционал для реализации самых различных задач. И пожалуй каждый фреймворк предоставляет возможность настроить маршрутизацию различными способами. Некоторые фреймворки в этом плане являются «навязчивыми», буквально навязывая свои маршруты, другие же предоставляют большую гибкость в настройке. Одним из многих преимуществ Laravel является полная свобода в выборе предпочитаемых маршрутов и удобство настройки.

Маршрутизация в Laravel

Как я уже сказал, Laravel предоставляет полную свободу выбора в плане маршрутов. Помимо этого, делается это очень удобно, и более того, поддерживается несколько способов создавать маршруты.

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

Все маршруты приложения описываются, как правило, в файле app/routes.php. Если вы откроете этот файл, то увидите в нем маршрут по умолчанию для пути (url) «/»:

Пока можно не обращать внимание на конструкцию View::make(‘hello’); — это выдача шаблона в ответ на запрос пользователя. Шаблоны и фасад View мы рассмотрим позже в этой статье. Пока внимание следует обратить на само определение маршрута:

Это — простейшая форма определения маршрута, используя замыкание. Как можно увидеть, для определения маршрута используется фасад Route (пусть слово фасад вас не пугает, для простоты воспринимайте пока это как статический класс; подробно о том, что такое фасады, я постараюсь рассказать в одной из следующих статей).

Фасад Route содержит множество методов для различного определения маршрутов, но прежде, чем мы их рассмотрим, снова немного теории.

Формально, любой HTTP запрос состоит из нескольких частей:

  • URL
    Это запрашиваемый «путь». В большинстве случаев многими под маршрутом подразумевается именно этот URL. Примеры url’ов: «/», «/news» и т.д.
  • Метод запроса
    Каждый запрос к веб-серверу делается с использованием одного из методов. Наиболее распространены методы GET и POST, они поддерживаются всеми броузерами. Однако существуют и более специфичные методы, такие, как например PUT или DELETE. Таким образом важно понимать, что метод запроса является частью маршрута. Ведь в зависимости от него, по одному и тому же URL’у может выполняться совершенно разная логика. К примеру, запрос «GET HTTP/1.0 /news/1» служит для получение новости №1. В то время как запрос «DELETE HTTP/1.0 /news/1» служит для удаления той же новости. URL один, а действия разные. Laravel позволяется создавать маршруты, учитывая метод запроса.
  • Необязательное тело запроса.
    Используется не со всеми методами. Как правило тело запроса присутствует при использовании методов POST и PUT. Частью маршрута не является, и это тело используется уже внутри обработчика маршрута.

Но вернемся к Laravel’у. В примере выше создается маршрут для запроса «GET /» (Route::get(«/», …)). Помимо метода get, фасад Route имеет также методы для всех других методов запроса:

Иногда можно объединять методы в один маршрут. К примеру, если поведение приложение по определенному URL’у не зависит от используемого метода. Для создания подобных маршрутов у фасада Route есть два метода:

Во всех примерах я упускал последний параметр, заменяя многоточием. Этот параметр определяет поведение приложения по указанным маршрутам. Для начала, чтобы просто поиграться с маршрутами или простейшей логикой, удобнее всего будет использовать замыкания, как в самом первом примере. Но этим возможности маршрутизации Laravel’а конечно же не ограничиваются. Фасад Route поддерживает не только замыкания в последнем параметре. В нем также можно указывать методы контроллеров. Более того, у Route даже есть метод controller, который может сгенерировать все маршруты для указанного контроллера, освобождая разработчика от ручного создания маршрута для каждого метода контроллера, но об этом позже. Сейчас я хочу еще немного рассказать о создании самих маршрутов.

Выше были рассмотрены примеры статических маршрутов. Но на практике очень часто маршруты являются параметризованными. Например, необходимо создать маршрут для редактирования какой-то новости на сайте. Для этого логично было бы использовать URL’ы вида «/news/edit/1», где 1 — это id новости, и, например, метод POST. В этом случае мы имеем дело с параметризованным маршрутом, последний сегмент URL’а которого и является параметром. Создать такой маршрут можно следующим образом:

Пока я буду использовать только замыкания. В данном случае мы имеем параметр {id}. То есть POST запросы вида «/news/edit/1» или «/news/edit/5» попадут под один и тот же маршрут. Но для полноты картины, было бы неплохо еще как-то определить диапазон значений для этого параметра, ведь пока под этот маршрут попадет и запрос POST «/news/edit/abc», а это не то, что нам нужно, ведь предполагается, что id новости — это число. Конечно проверку на значение $id можно сделать и внутри самого обработчика, и в каких-то сложных случаях так и придется сделать, но для простого ограничения значений по регулярному выражению можно возложить эту задачу на Laravel. Сделать это в нашем случае можно так:

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

В случае не соответствия одного из параметров маршрута шаблону, Laravel вернет 404 ошибку (не найдено), и указанный обработчик шаблона соответственно выполнен не будет. Забегая немного вперед, поведение Laravel для 404 ошибки можно переопределить так, как нам заблагорассудится.

Следует также отметить, что Laravel поддерживает необязательные параметры маршрута. Например:

Маршрутам в Laravel также можно присваивать названия. Это удобно, когда нужно в каком-то месте приложения сгенерировать ссылку на маршрут. Имена присваиваются следующим образом:

В данном случае маршруту GET «user/profile» присваивается имя «profile». Впоследствии на этот маршрут можно сгенерировать ссылку, вызвав, например, $url = URL::route(‘profile’);

До сих пор в примерах в качестве обработчиков использовались только замыкания. Но на практике, вся логика обработки запроса распределена по контроллерам. И в Laravel есть несколько способов указания контроллера в качестве обработчика маршрута.

В простейшем случае это будет выглядеть так:

В данном случае для обработки маршрута GET «/users/profile/{name}» будет инстанциирован контроллер UsersController, у которого соответственно предполагается, что реализован метод «getProfile($name)».

Если в контроллере много методов, то описание каждого маршрута для такого контроллера может быть утомительным. Поэтому в Route для решения этой задачи есть специальный метод Route::controller, который сгенерирует маршруты для всех методов указанного контроллера. Допустим, у нас имеется такой контроллер:

Сгенерировать для него маршруты можно соответственно так:

В этом случае, будут сгенерированы такие маршруты:

На что тут следует обратить внимание:

  1. URL’ы всех маршрутов начинаются с указанной в первом параметре строки, в данном случае «planets»
  2. Генератор анализирует имя функции для определения метода и URL’а маршрута. К примеру метод контроллера postAdd «транслируется» в маршрут POST «/planets/add».
  3. Для действий по умолчанию используется метод контроллера с именем Index, будь то getIndex или postIndex и т.д.
  4. Генератор не анализирует кол-во параметров в методах контроллера. Вместо этого, как можно видеть, он генерирует маршрут с 5ю необязательными параметрами. Поэтому, если метод контроллера предполагает получать более 5 параметров, чего лично в моей практике еще ни разу не было, Route::controller не подойдет и придется описывать маршрут вручную. Возможно в этом случае стоит задуматься, стоит ли передавать столько параметров в маршруте? Может будет резонно вынести их в GET-переменные?

Заранее скажу, что на SBShare используются практически только Route::controller для создания маршрутов, за одним исключением для тэгов.

Вообще, тема маршрутизации в Laravel довольно обширна, здесь я лишь постарался описать основы, которые будут необходимы нам для создания сайта. Laravel также позволяет группировать маршруты, задавать им префиксы, обрабатывать поддомены… Для заинтересованных могу только привести ссылку на официальную документацию: http://laravel.com/docs/routing, где это все довольно подробно описано.

Пока же предлагаю читателю поиграться с созданием маршрутов. В качестве ответа из обработчиков пока можно просто делать return «какая то строка»; дальше я расскажу о том, что еще можно возвращать из этих обработчиков помимо простых строк. Это и шаблоны, и перенаправления, и произвольный объект Response, дающий полный контроль над возвращаемым сервером ответом.

Экспериментируя с маршрутами, будет полезно пользоваться командой artisan’а «route». В командной строке, или же из PhpStrom (правда тут требуется небольшая предварительная настройка), можно выполнить:

Эта команда выведет на экран все сгенерированные маршруты. Для выполнения этой команды нужно находится в корневой папке приложения. Предполагается также, что путь к исполняющему файлу php прописан в переменной окружения PATH (под Homestead об этом беспокоиться не нужно).

Ответы из обработчиков

До сих пор, в экспериментальных целях, наши обработчики возвращали простые строки, которые в результате отображались в броузере. Но, как я уже сказал, возвращать из обработчиков можно много чего, и Laravel умело распознает это и отдает соответствующий ответ клиенту. В подавляющем большинстве случаев из обработчиков будут возвращаться либо шаблоны (они же представления (views)) (посредством фасада View), либо перенаправления (посредством фасада Redirect). В более редких случаях, если требуется гибкость формирования ответа, можно прибегнуть к непосредственному формированию ответа посредством фасада Response.

Представления (views)

Представления, они же — шаблоны, как правило содержат HTML код вашего приложения и являются удобным средством разделения логики самого приложения, и логики отображения.

Как было сказано в начале статьи, шаблоны находятся в папке app/views. Допустим, у нас есть следующий шаблон, расположенный в файле app/views/hello.blade.php (здесь blade используется для указания того, что шаблон должен обрабатываться шаблонизатором Blade Laravel’а):

Это представление можно вернуть броузеру так:

Вторым параметром методу make передаются данные, которые необходимо отобразить в шаблоне (в данном случае «{{ $name }}» будет заменено на «Amegatron»).

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

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

Перенаправления (редиректы)

Иногда в качестве ответа нужно вернуть не какое-то представление, а сказать броузеру перенаправить пользователя на другой URL.

Для этого нужно формировать ответ при помощи фасада Redirect. Вот несколько примеров редиректов:

По мере создания сайта мы не раз будет сталкиваться с необходимостью вернуть редирект и рассмотрим эту тему детальнее.

Гибкие ответы

Для формирования более специфичных ответов предназначен фасад Response. У нас на сайте он не будет использоваться, поэтому просто приведу ссылку на документацию, описывающего его, и вышеперечисленные ответы: http://laravel.com/docs/responses.

Заключение

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

Все ваши комментарии / предложения / пожелания / благодарности / вопросы можно по-прежнему оставлять в обсуждении в группе ВКонтакте, то есть здесь.

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

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

  1. Aleksandr:

    Отличнейший пост! «уроки» в стиле «создадим блог, копи-пэйстите за мной, не врубаясь, что делаете» изрядно надоели. Сам сейчас работаю на друпал, но чем дальше, тем больше понимаю, что пора переходить на более взрослые инструменты. Laravel мой первый фрэймворк, описанное в этой статье я знаю, но каким тернистым путём… Желаю Вам успехов в дальнейшем написании уроков, у Вас это великолепно получается!

  2. Игорь:

    В чем приниципиальная разница между роутами контролеррами?
    Вроде выполняют одинаковые задачи…

    1. Не понял вопрос 🙂 «Между роутами И контроллерами» ? Роут — это путь и метод, по которому должна выполняться какая-то логика сайта. Контроллер лишь место, где эта логика определена. Для простых роутов можно определять логику в замыканиях, но на практике все же лучше «расфасовывать» всю логику по контроллерам.

  3. Вера:

    Спасибо, «поподробнее» глаз режет ))) ru.wiktionary.org/wiki/%D0%BF%D0%BE%D0%BF%D0%BE%D0%B4%D1%80%D0%BE%D0%B1%D0%BD%D0%B5%D0%B5

  4. Rusl:

    Здравствуйте. Отличная статья, спасибо!! Я только начал изучать Laravel и у меня есть вопрос: как передать параметр из строки запроса в контроллер из маршрута? Напр.: есть route — Route::get(‘test{name?}’, ‘MyFirstController@test’); Вот как параметр ‘name’ передать в контроллер, где определён метод подстановки этого name в строку? Спасибо.

    1. Для того, чтобы сформировать URL к этому маршруту средствами Laravel, маршрут должен быть именованным:

      Route::get('test{name?}', array('as' => 'test', 'uses' => 'MyFirstController@test'));

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

      URL::action('test', array('my_param'));

  5. Стас:

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

  6. Igor:

    Александр, здравствуйте!
    Подскажите пожалуйста, как правильно обрабатывать «несущесвтующие» маршруты в Laravel 4.2? Например, если в адресной строке набрать несуществующий адрес, например что-то типа , то выскочит вот такое сообщение об ошибке: Whoops, looks like something went wrong. А хотелось бы, чтобы происходил редирект на главную страницу сайта или какую-нибудь другую страничку, подготовленную специально для таких случаев.

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

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

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