Статья содержит описание некоторых базовых приемов использования Laravel при разработки сайтов и будет полезна тем, кто начинает осваивать данный фреймворк. Для примера использую Ubuntu 12.04, PostgreSQL 9.3, Nginx 1.1.19, PHP 5.5.7, Composer и свежий проект, созданный с использованием Laravel 4.1. Под управление PostgreSQL крутится база данных examples, к которой имеет доступ пользователь examples c одноименным паролем. Nginx же настроен таким образом, что при обращении по адресу http://examples.loc
в браузере открывается главная страницу-заглушка, которая идет с Laravel в комплекте, с надписью «You have arrived.»
Все пути к редактируемым файлам указаны относительно директории проекта.
Окружение
Сначала настраиваю локальное окружение в Laravel. Для этого создаю директорию app/config/local
и добавляю в нее файл database.php
:
<?php /** * app/config/local/database.php */ return array( 'default' => 'pgsql', 'connections' => array( 'pgsql' => array( 'driver' => 'pgsql', 'host' => 'localhost', 'database' => 'examples', 'username' => 'examples', 'password' => 'examples', 'charset' => 'utf8', 'prefix' => '', 'schema' => 'public', ), ), );
Прошу Laravel использовать окружение local
по умолчанию. Для этого редактирую файл bootstrap/start.php и заменяю строку 'your-machine-name'
на '*'
:
// Фрагмент из bootstrap/start.php $env = $app->detectEnvironment(array( 'local' => array('*'), ));
Подключение Sentry
Ссылку на инструкцию по подключению Sentry можно найти в конце статьи. Кратко, делаю следующее.
Добавляю в composer.json
строку "cartalyst/sentry": "2.0.*"
в блок require
.
// Фрагмент composer.json { "name": "laravel/laravel", "description": "The Laravel Framework.", "keywords": ["framework", "laravel"], "license": "MIT", "require": { "laravel/framework": "4.1.*", "cartalyst/sentry": "2.0.*" }, ...
Выполняю команду:
$ composer update
composer update
Добавляю в список сервис-провайдеров в файле app/config/app.php:
'Cartalyst\Sentry\SentryServiceProvider',
Добавляю в список псевдонимов в файле app/config/app.php:
'Sentry' => 'Cartalyst\Sentry\Facades\Laravel\Sentry',
Выполняю миграции Sentry:
$ php artisan migrate --package=cartalyst/sentry
Публикую конфигурационный файл Sentry:
$ php artisan config:publish cartalyst/sentry
Открываю на редактирование app/config/packages/cartalyst/sentry/config.php
. Нахожу в нем строку 'login_attribute' => 'email'
и заменяю на 'login_attribute' => 'username'
, для того чтобы Sentry выполнял аутентификацию пользователя по его логину, а не e-mail.
При выполнении миграций Sentry была создана таблица users
, в которой есть поле email
, но нет username
. Поэтому, чтобы Sentry работал, надо добавить недостающее поле. Для этого создаю миграцию:
$ php artisan migrate:make alter_users_add_username
В директории app/database/migration
появится файл имя, которого будет состоять из текущей даты и времени и заканчивается на alter_users_add_username.php
. Редактирую его следующим образом:
<?php /** * app/database/migration/0000_00_00_000000_alter_users_add_username.php */ use Illuminate\Database\Migrations\Migration; class AlterUsersAddUsername extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::table('users', function($table) { $table->string('username'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('users', function($table) { $table->dropColumn('username'); }); } }
Для проверки миграции выполняю:
$ php artisan migrate
Для проверки, что миграция успешно откатывается, выполняю:
$ php artisan migrate:rollback
Создаю еще одну миграцию, которая добавляет в таблицу users
запись о суперпользователе:
$ php artisan migrate:make add_user_admin
Нахожу в директории app/database/migrate
файл, который заканчивается на add_user_admin.php
и редактирую его:
<?php /** * app/database/migration/0000_00_00_000001_add_user_admin.php */ use Illuminate\Database\Migrations\Migration; class AddUserAdmin extends Migration { /** * Run the migrations. * * @return void */ public function up() { $user = Sentry::createUser(array( 'username' => 'admin', 'email' => 'admin@examples.loc', 'password' => 'password', 'activated' => 1, 'permissions' => array( 'superuser' => 1, ), )); } /** * Reverse the migrations. * * @return void */ public function down() { User::where('username', '=', 'admin')->firstOrFail()->delete(); } }
Проверяю, как миграции накатываются и откатываются. И перехожу к созданию контроллера, который будет отвечать за аутентификацию пользователей.
Форма входа
В директории app/controllers
создаю файл AuthController.php
:
<?php /** * app/controllers/AuthController.php */ class AuthController extends BaseController { /** * Отображает страницу входа * * @return Illuminate\View\View */ public function getLogin() { $title = 'Вход'; return View::make('auth.login', compact('title')); } }
Метод getLogin()
передает заголовок страницы через переменную $title
в представление auth.login
, которое служит для отображения страницы входа в админку.
Создаю представление auth.login
. Для этого в директорию app/views
добавляю директорию auth
и создаю в ней файл login.blade.php
:
/** * app/views/auth/login.blade.php */ @extends('layout') @section('main') <div class="container"> {{ Form::open(array('class' => 'form-signin')) }} @if (!$errors->isEmpty()) <div class="alert alert-danger"> @foreach ($errors->all() as $error) <p>{{ $error }}</p> @endforeach </div> @endif <h2 class="form-signin-heading">{{ $title }}</h2> {{ Form::text('username', null, array('class' => 'form-control', 'placeholder' => 'Логин')) }} {{ Form::password('password', array('class' => 'form-control', 'placeholde' => 'Пароль')) }} <label class="checkbox"> {{ Form::checkbox('remember-me', 1) }} Запомни меня </label> {{ Form::submit('Войти', array('class' => 'btn btn-lg btn-primary btn-block')) }} {{ Form::close() }} </div> @stop
Шаблон login.blade.php
расширяет шаблон layout.blade.php
. Поэтому создаю его в директории app/views
:
/** * app/views/layout.blade.php */ <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>{{ $title }}</title> @section('styles') {{ HTML::style('//netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css') }} {{ HTML::style(URL::asset('styles/base.css')) }} @show </head> <body> @yield('main') @section('scripts') {{ HTML::script('//netdna.bootstrapcdn.com/bootstrap/3.0.3/js/bootstrap.min.js') }} @show <body> </html>
После того, как есть метод контроллера и возвращаемое им представление, необходимо настроить роутинг, чтобы GET запросы, отправляемые по адресу http://examples.loc/login
, обрабатывались методом getLogin()
. Для этого добавляю в файл app/routes.php
следующий код:
// Фрагмент app/routes.php ... Route::group(array('before' => 'guest'), function () { Route::get('login', array( 'as' => 'auth.login', 'uses' => 'AuthController@getLogin' )); });
С помощью Route::get()
определяю роут с именем auth.login
, который направляет GET запросы по адресу /login
методу getLogin()
контроллера AuthController
.
Также поместил роут auth.login
в группы роутов. Перед любым роутом, входящим в данную группу будет выполнятся фильтр guest
. Данный фильтр означает, что обработка GET запросов по адресу /login
будет происходить только в том случает, если пользователя является гостем, т.е. не авторизован.
Перепишу фильтр guest
так, чтобы он использовал Sentry. Для этого в файле app/filters.php
изменю код фильтра guest
:
// Фрагмент app/filters.php ... Route::filter('guest', function() { if (Sentry::check()) return Redirect::to('/'); }); ...
Теперь можно посмотреть, как выглядит форма входа в браузере. Для этого перехожу по адресу http://examples.loc/login
и…
Вижу сообщение об ошибке Call to undefined method Illuminate\Cookie\CookieJar::get()
.
Легким движением Google выясняю, что Sentry 2.0 совместим с Laravel 4.0, но не совместим 4.1. Хорошо, что уже зарелизился Sentry 2.1. Чтобы избавиться от ошибки, изменяю версию Sentry в composer.json
на 2.1:
// Фрагмент composer.json { "name": "laravel/laravel", "description": "The Laravel Framework.", "keywords": ["framework", "laravel"], "license": "MIT", "require": { "laravel/framework": "4.1.*", "cartalyst/sentry": "2.1.*" }, ...
Выполняю команду:
$ composer update
Еще раз пытаюсь открыть http://examples.loc/login
и вижу форму ввода логина и пароля. Для того чтобы форма выглядела элегантно, надо добавить немного CSS. Для этого в директории public
создаю директорию styles
и добавляю туда файл base.css
:
/** * public/style/base.css */ body { padding-top: 40px; padding-bottom: 40px; background-color: #eee; } .form-signin { max-width: 330px; padding: 15px; margin: 0 auto; } .form-signin .form-signin-heading, .form-signin .checkbox { margin-bottom: 10px; } .form-signin .form-signin-heading { text-align: center; } .form-signin .checkbox { font-weight: normal; } .form-signin .form-control { position: relative; font-size: 16px; height: auto; padding: 10px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } .form-signin .form-control:focus { z-index: 2; } .form-signin input[type="text"] { margin-bottom: -1px; border-bottom-left-radius: 0; border-bottom-right-radius: 0; } .form-signin input[type="password"] { margin-bottom: 10px; border-top-left-radius: 0; border-top-right-radius: 0; }
Контролеры
Теперь форма отображается красиво, но при нажатии на кнопку «Войти», возвращается ошибка 404. Значит надо добавить в AuthController
обработчик POST запросов, отправляемых на адрес /login
.
<?php /** * app/controllers/AuthController.php */ class AuthController extends BaseController { /** * Отображает страницу входа * * @return Illuminate\View\View */ public function getLogin() { $title = 'Вход'; return View::make('auth.login', compact('title')); } /** * Аутентифицирует и редиректит в админку * * @return Illuminate\Http\RedirectResponse */ public function postLogin() { Input::flash(); try { $credentials = array( 'username' => Input::get('username'), 'password' => Input::get('password') ); $user = Sentry::authenticate($credentials, Input::get('remember-me')); } catch (Exception $e) { return Redirect::to(route('auth.login')) ->withErrors(array($e->getMessage())); } return Redirect::intended(route('admin')); } /** * Обрабатывает выход * * @return Illuminate\Http\RedirectResponse */ public function getLogout() { Sentry::logout(); return Redirect::route('auth.login'); } }
В методе postLogin()
данные, переданные через форму сохраняются в сессии с помощью Input::flash()
. В блоке try
Sentry пытается аутентифицировать пользователя. Если в форме ввода логина и пароля пользователь установит галочку «Запомни меня», то вторым параметром в Sentry::authenticate()
будет передано значение true
и Sentry запомнит пользователя в случае успешной аутентификации. Если аутентификация по какой-либо причине не прошла успешно, то в блоке catch
метод Redirect::to()
отправит нас на страницу ввода логина и пароля в месте с сообщением об ошибке. В случае успешной аутентификации метод Redirect::intended()
отправит пользователя на ту страницу, на которую он намеревался зайти, когда был перенаправлен на форму ввода логина и пароля. Если такая страница не задана, то откроется страница по адресу /admin
, с которой связан роут с именем admin
.
Метод getLogin()
самая проста реализация как можно разлогинить пользователя. После выхода пользователь будет перенаправление на страницу ввода логина и пароля.
Прежде чем переходить к настройке роутинга, надо добавить метод, который будет обрабатывать запросы, отправляемые по адресу /admin
. Для этого отредактирую файл app/controllers/HomeController.php
следующим образом:
<?php /** * app/controllers/HomeController.php */ class HomeController extends BaseController { public function showWelcome() { return View::make('hello'); } public function getAdmin() { return link_to(route('auth.logout'), 'Выход'); } }
Метод getAdmin()
просто отображает на страницу ссылку «Выход», при нажатии на которою пользователь будет разлогиниваться.
Роутинг
Теперь, чтобы новые методы могли обрабатывать запрос, отредактирую в файл app/routes.php
:
/** * app/routes.php */ Route::get('/', function() { return View::make('hello'); }); Route::group(array('before' => 'guest'), function () { Route::get('login', array( 'as' => 'auth.login', 'uses' => 'AuthController@getLogin' )); Route::post('login', array( 'before' => 'csrf', 'uses' => 'AuthController@postLogin' )); }); Route::group(array('before' => 'auth'), function () { Route::get('admin', array( 'as' => 'admin', 'uses' => 'HomeController@getAdmin', )); Route::get('logout', array( 'as' => 'auth.logout', 'uses' => 'AuthController@getLogout' )); });
Первая группа роутов, перед которой выполняется фильтр guest
, содержи роутинг для адреса /login
для GET и POST запросов. Фильтр guest
означает, что по адресу /login
будут обрабатываться запросы только от гостей, т.е. неавторизованных пользователей.
Вторая группа роутов, перед которой выполняется фильтр auth
, содержит роутинг для адресов /admin
и /logout
. Фильтр auth
означает, что по данным адресам будут обрабатываться запросы только от авторизованных пользователей.
Также необходимо отредактировать фильтр auth
, так чтобы он использовал Sentry. Для этого открываю файл app/filters.php
изменяю фильтр auth
следующим образом:
// Фрагмент app/filters.php ... Route::filter('auth', function() { if (!Sentry::check()) return Redirect::guest(route('auth.login')); }); ...
Теперь можно пробовать логиниться в админку использую логин admin и пароль password. Неавторизованному пользователю не удастся зайти на страницу /admin
, она будет доступна, только после успешной аутентификации. Также после аутентификации будет невозможно зайти на страницу /login
, так как будет происходить редирект на главную страницу.
Надеюсь данный пример поможет начинающим разработчикам лучше понять некоторые элементы Laravel.
Ссылки по теме
getcomposer.org/doc/00-intro.md#installation-nix
laravel.com/docs/installation#install-laravel
cartalyst.com/manual/sentry/installation/laravel-4
ссылка на оригинал статьи http://habrahabr.ru/post/206908/
Добавить комментарий