Создаём сайт, используя Laravel и Recurly. Часть 1

Original author: Lukas White
  • Translation
  • Tutorial
Планирование и обработка платежей это легко, но не так легок прием регулярных платежей. Сложности могут возникнуть очень быстро. Вы должны решить, как обрабатывать отказы, данные о платежах должны быть постоянно актуальны, и поддерживать актуальность данных на сайте. И пожалуй, самый важный вопрос — держать своих пользователей в курсе платежной информации на сайте.
К счастью, Recurly — это сервис, который выполняет большую часть задач, связанных с обработкой регулярных платежей. Используя JavaScript-библиотеки, вы сможете создавать защищенные формы для отправки и обновления платежной информации, включающие в себя все важные данные платежной карты.
После того, как вы определили сумму и частоту платежей, Recurly заботится о расписании и принятии платежей, до тех пор, пока клиент не будет отписан. Recurly, также учитывает изменения тарифных планов, расчетов, внесении и вычетов дополнительных платежей.
В первой статье из двух, я покажу Вам как создать платную подписку используя сайт, созданный с использование php-фреймворка Laravel и сервиса приема платежей Recurly. Мы начнём с создания сайта с простой регистрацией, авторизацией и правами пользователя. Затем мы добавим обработку платежей, свяжем её с процессом регистрации, что позволит пользователям покупать различные тарифные планы.

Установка приложения.


Начнём создания приложения с создания новой папки для проекта и запуском следующей команды:
composer create-project laravel/laravel recurly --prefer-dist

Эта команда создаст новое приложение Laravel, использую Composer, скачает фреймворк и все необходимые зависимости. Позже нам понадобятся несколько библиотек, поэтому добавьте следующие строки в composer.json и запустите composer.phar update
"machuga/authority": "dev-develop",
"machuga/authority-l4" : "dev-master",
"recurly/recurly-client": "2.1.*@dev"

Будет скачана библиотека Authority, которая будет использована для создания ролей пользователей и прав доступа, и библиотека Recurly.
Затем, необходимо настроить соединение с БД указав значение схемы, пользователя, пароля и адресс к БД в файле app/config/database.php. Laravel работает «из коробки» со следующими базами данных: MySql, Posrges, SQLite и SQL Server.
Нам так же необходима будет таблица с пользователями. Вместо того, что бы делать всё «руками», используем механизм миграций, предоставляемый Laravel. Выполните следующую команду:
php artisan migrate:make create_users_table

Эта команда создаст файл миграции в папке app/database/migrations
public function up()
{
    Schema::create('users', function($table) {
        $table->increments('id');
        $table->string('email')->unique();
        $table->string('name');
        $table->string('password');
        $table->timestamps();
    });
}
 
public function down()
{
    Schema::drop('users');
}

Вернемся к командной строке и выполним:
php artisan migrate

Посмотрим в базе данных и увидим что Laravel создал таблицу с пользователями.

Создадим роли пользователей и права.


Для определения типа пользователя и прав для этого пользователя, мы установим пользователям роли. Мы уже скачали библиотеку Authoriry с помощью composer, и сейчас мы должны выполнить ещё несколько шагов для настройки ролей полностью.
В файле app/config/app.php добавьте следующую строку:
'Authority\AuthorityL4\AuthorityL4ServiceProvider',

Добавьте следующую строку к алиасам:
'Authority' => 'Authority\AuthorityL4\Facades\Authority',

и опубликуйте файл настроек Authority следующей командой:
php artisan config:publish machuga/authority-l4

Вернемся к файлу настроек позже. А сейчас, мы должны создать несколько дополнительных таблиц в БД. К счастью, Laravel содержит собственные миграции для них. Запустим их следующей командой:
php artisan migrate --package="machuga/authority-l4"

Вы увидите что создалось 3 дополнительных таблицы: permissions, roles, role_user.
Теперь нам необходимо создать модель для работы с ролями и правами доступа. Пока мы их не будем изменять и оставим очень простыми. В app/models/Role.php:
<?php
class Role extends Eloquent
{
}

В файле: app/models/Permission.php:
<?php
class Permission extends Eloquent 
{
}

Сейчас мы должны изменить модель User, она уже была создана, а мы добавим связь пользователей с их ролями и правами доступа. Добавьте следующие строки в app/models/User.php:
public function roles() {
    return $this->belongsToMany('Role');
}
 
public function permissions() {
    return $this->hasMany('Permission');
}
 
public function hasRole($key) {
    foreach($this->roles as $role){
        if ($role->name === $key) {
            return true;
        }
    }
    return false;
}

А теперь заполним базу данных некоторым данными. Откройте файл app/database/seeds/DatabaseSeeder.php и вставьте следующее:
<?php
class DatabaseSeeder extends Seeder
{
    public function run() {
        Eloquent::unguard();
        $this->call('UserTableSeeder');
        $this->command->info('User table seeded!');
        $this->call('RoleTableSeeder');
        $this->command->info('Role table seeded!');
    }
}
 
class UserTableSeeder extends Seeder
{
    public function run() {
        DB::table('users')->delete();
        User::create(array(
            'email'    => 'joe.bloggs@example.com',
            'name'     => 'Joe Bloggs',
            'password' => Hash::make('password')
        ));
    }
 
}
 
class RoleTableSeeder extends Seeder
{
    public function run() {
        DB::table('roles')->delete();
        Role::create(array('name' => 'admin'));
        Role::create(array('name' => 'pending'));
        Role::create(array('name' => 'member'));
        Role::create(array('name' => 'bronze'));
        Role::create(array('name' => 'silver'));
        Role::create(array('name' => 'gold'));
    }
}

Далее заполним базу данных, выполнив следующую команду:
php artisan db:seed


Создаём шаблон


Сейчас мы создадим общий шаблон для страниц. Скачаем для этого Twitter Bootstrap и положим исходники в папку public, а js-файлы в папку — public/js/libs/
Создадим файл app/views/layouts/default.blade.php со следующим кодом:
<!DOCTYPE html>
<html lang="en">
 <head>
  <meta charset="utf-8">
  <title>Subscription Site Tutorial</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="description" content="">
  <meta name="author" content="">
 
  <!-- Le styles -->
  <link href="/css/bootstrap.css" rel="stylesheet">  
  <link href="/css/style.css" rel="stylesheet"> 
 </head>
 
 <body>
  <div class="navbar navbar-inverse navbar-fixed-top">
   <div class="navbar-inner">
    <div class="container">
     <button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
      <span class="icon-bar"></span>
      <span class="icon-bar"></span>
      <span class="icon-bar"></span>
     </button>
     <a class="brand" href="#">Subscription Site Tutorial</a>
     <div class="nav-collapse collapse">
      <ul class="nav">
       <li class="active"><a href="#">Home</a></li>
       <li><a href="#about">About</a></li>
       <li><a href="#contact">Contact</a></li>
      </ul>
     </div><!--/.nav-collapse -->
    </div>
   </div>
  </div>
 
  <div class="container">
 
    @if(Session::has('success'))
    <div class="alert alert-success">
      <button type="button" class="close" data-dismiss="alert">×</button>
      {{ Session::get('success') }}
    </div>    
    @endif
 
    @if(Session::has('error'))
    <div class="alert alert-error">
      <button type="button" class="close" data-dismiss="alert">×</button>
      {{ Session::get('error') }}
    </div>    
    @endif
 
    @yield('content')
 
  </div> <!-- /container -->
 
  <!-- Le javascript
  ================================================== -->
  <!-- Placed at the end of the document so the pages load faster -->
  <script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
  <script src="/js/libs/bootstrap.min.js"></script>
 </body>
</html>

Это базовый шаблон для нашего сайта, контент страницы будет выводиться в строке @yield('content').
Далее, создадим файл public/css/style.css:
body {
    padding-top: 50px;
}
 
p.logged-in {
    color: white;
    margin-top: 0.5em;
}

И наконец, создадим главную страницу нашего сайта. Создайте файл app/views/home/index.blade.php:
@extends('layouts.default')
@section('content')
  <h1>Subscription Site Tutorial</h1>
@stop

@extends говорит Laravel, что надо использовать шаблон по-умолчанию, который мы только что создали.
Не забудем изменить роутинг нашего сайта в файле app/routes.php:
Route::get('/', function() {
    return View::make('home/index');
});

Создаём механизм авторизации


У нас есть пользователи, и теперь мы должны дать им возможность авторизации. В файле app/routse.php, добавим путь к странице входа:
Route::get('/auth/login', function() {
    return View::make('auth/login');
});

Сейчас мы создадим отображение в файле app/views/auth/login.blade.php. .blacde означает что мы используем шаблонизатор Blade, идущий вместе с Laravel.
@extends('layouts.default')
@section('content')
  <h1>Please Log in</h1>
  {{ Form::open(array('url' => 'auth/login')) }}
  {{ Form::label('email', 'E-Mail Address') }}
  {{ Form::text('email') }}
  {{ Form::label('password', 'Password') }}
  {{ Form::password('password') }}
  <div class="form-actions">
  {{ Form::submit('Login', array('class' => 'btn btn-primary')) }}
  </div>
  {{ Form::close() }}
  <p>Not a member?  <a href="/user/register">Register here</a>.</p>
@stop

Если вы перейдете по адрес /auth/logn вы увидите простую форму входа. Для обработки входа, мы должны будем обработать POST данные. Авторизация в Laravel проста:
Route::post('/auth/login', function() {
    $email = Input::get('email');
    $password = Input::get('password');
 
    if (Auth::attempt(array('email' => $email, 'password' => $password))) {
        return Redirect::to('/')->with('success', 'You have been logged in');
    }
    else {
        return Redirect::to('auth/login')->with('error', 'Login Failed');
    }
 
    return View::make('auth/login');
});

Вся магия находится в методе Auth::attempt(), если авторизация успешна создаётся сессия и экземпляр объекта User, доступны через статичный метод Auth::user()
Метод выхода из аккаунта:
Route::get('/auth/logout', function() {
    Auth::logout();
    return Redirect::to('/')->with('success', 'You have successfully logged out');
});


Базовая регистрация


Нашей последней задачей, в этой части, является создание процесса регистрации. Создадим путь для регистрации в файле app/routse/php:
Route::get('/user/register', function() {
    return View::make('user/register/index');
});

Создадим представление в файле /app/views/user/register/index.blade.php:
@extends('layouts.default')
@section('content')
  {{ Form::open(array('url' => 'user/register')) }}
         
  {{ Form::label('email', 'E-Mail Address') }}
  {{ Form::text('email') }}
  {{ $errors->first('email') }}
 
  {{ Form::label('name', 'Your name') }}
  {{ Form::text('name') }}
  {{ $errors->first('name') }}
 
  {{ Form::label('password', 'Password') }}
  {{ Form::password('password') }}
  {{ $errors->first('password') }}
 
  {{ Form::label('password_confirmation', 'Repeat') }}
  {{ Form::password('password_confirmation') }}
 
  <div class="form-actions">
  {{ Form::submit('Register', array('class' => 'btn btn-primary')) }}
  </div>
  
  {{ Form::close() }}
@stop

Отмечу несколько моментов:
  • Объект $error передаётся в представлении автоматически, и если нет ошибок валидации, то соответствующее поле будет пустым
  • Суффикс _confirmation означает что поле должно соответствовать полю без суффикса, т.е. для проверки, что пароль оба раза был введен корректно

Теперь перейдём к обработке POST данных:
Route::post('/user/register', function() {
    $validator = Validator::make(
        Input::all(),
        array(
            'name' => array('required', 'min:5'),
            'email' => array('required', 'email', 'unique:users'),
            'password' => array('required', 'confirmed')
        )
    );
 
    if ($validator->passes()) {
        $user = new User();
        $user->name     = Input::get('name');
        $user->email    = Input::get('email');
        $user->password = Hash::make(Input::get('password'));
        $user->save();
 
        $role_pending = Role::where('name', '=', 'pending')->first();
        $user->roles()->attach($role_pending);
 
        Auth::login($user);
        return Redirect::to('/')->with(
            'success',
            'Welcome to the site, . Auth::user()->name . '!'
        );
    }
    else {
        return Redirect::to('user/register')->with(
            'error',
            'Please correct the following errors:'
        )->withErrors($validator);
    }
});

Это достаточно простой способ. Мы создаём валидатор и передаём ему POST данные (Input::all()) и определяем правила для валидации. Если валидация прошла, ты мы создаём нового пользователя, назначаем ему роли, авторизуем, и перенаправляем его на главную страницу.
Если валидация завершилась ошибкой, то мы редиректим пользователя обратно на форму, создаём сообщение об ошибке, и передаём сообщения об ошибках от валидатора, которые будут доступны из представления в переменной $errors.

Заключение


В этой части мы шаг за шагом создавали «скелет» приложения, для работы с платными подписками. В следующей части мы будем интегрировать Recurly для использования подписок.
Share post
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 21

    +1
    И не одной ссылки в статье.
      0
      Добавил недостающие ссылки
      +1
      Laravel начинает набирать обороты в России. Это радует
        0
        Мне вот тоже очень понравился Laravel. Жду выхода в продакшн первого моего приложения на нём
        0
        Кто-нибудь пробовал использовать Eloquent вне Laravel?
          0
          А разве Laravel использует не doctrine/dbal?
            0
            Использует внутри Eloquent ORM.
              0
              Судя по исходникам, нет.
                0
                Ошибся, используется для нескольких частей, но надстройка над ним слишком велика.
                  0
                  laravel$ grep -r 'use Doctrine' *
                  framework/src/Illuminate/Database/Schema/Grammars/Grammar.php:use Doctrine\DBAL\Schema\Column;
                  framework/src/Illuminate/Database/Schema/Grammars/Grammar.php:use Doctrine\DBAL\Schema\TableDiff;
                  framework/src/Illuminate/Database/Schema/Grammars/Grammar.php:use Doctrine\DBAL\Schema\AbstractSchemaManager as SchemaManager;
                  framework/src/Illuminate/Routing/ControllerServiceProvider.php:use Doctrine\Common\Annotations\SimpleAnnotationReader;

                  ?
                    0
                    Doctrine\DBAL используется только для схемы и драйверов.
                0
                Просто подключаем в composer.json
                {
                    "require": {
                        "illuminate/database": "*"
                    }
                }
                

                А дальше по инструкции: github.com/illuminate/database
                +1
                Второй день читаю документацию и статьи про laravel и я немного в шоке от его доступности, чтоли. До этого имел дело с ZF1 только.
                  –2
                  А как вам habrahabr.ru/post/178899/
                    0
                    Я думал о нем в самом начале, но читая эту статью раньше, я, по каким-то неизвестным мне причинам, не слишком восхищенно воспринял pixie.
                    Когда выбирал laravel, то я в первую очередь гуглил движки, где нет поддерки 5.2 и в идеале бы вообще использовался 5.5 (такие есть вообще?), но почитав документацию laravel, я понял, что это просто шикарная и простая штука, которую я могу использовать не только в опытах над самим собой, но и в реальных проектах.

                    В laravel очень привлек его подход к коду. Он очень близок, как мне кажется, к обычному английскому языку. Как те самые регулярки с естественным синтаксисом, которые никому, в том числе и мне, не понравились :)
                    Ну и чисто визуально в laravel, есть ощущение, что пишется очень мало кода, документация легка (по сравнению с ZF) и просто очень много новых для меня вещей, о которых я знал, но никогда толком не вникал.
                  0
                  «machuga/authority»: «dev-develop»,
                  «machuga/authority-l4»: «dev-master»,

                  Судя по описаниям на Github, это очень похожие библиотеки. В чем разница между ними и почему нужно обе подключать?

                  Только вчера делал ACL на Lavarel, за основу брал https://medium.com/on-coding/a7f2fa1f9791. Похоже что ваше решение выглядит красивее, буду ждать вторую часть ))
                    0
                    Ну никак не могу запомнить название этого фреймворка. Laravel, laravel, laravel, laravel, laravel, laravel…
                      0
                      А вы не рассматривали Сonfide как вариант?
                        0
                        Я только на этих выходных с Laravel познакомился, так что еще не всё успел посмотреть. Confide вроде за authentication отвечает, а мне именно ACL нужно было реализовать.

                        Нашел в нем ссылку на Entrust, буду смотреть.

                        Уважаемые коллеги, поделитесь опытом. С помощью каких библиотек лучше Auth + ACL реализовать? Ох уж это разнообразие…
                          0
                          Я вижу 2 основных варианта, Сonfide + Entrust или sentry-2.
                        0
                        «machuga/authority» — это сама библиотека, а «machuga/authority-l4» — адаптер библиотеки для Laravel 4

                      Only users with full accounts can post comments. Log in, please.