Статья содержит описание некоторых базовых приемов использования 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/
Добавить комментарий