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

в 15:05, , рубрики: laravel, php, recurly

Планирование и обработка платеже это легко, но не так легок прием регулярных платежей. Сложности могут возникнуть очень быстро. Вы должны решить, как обрабатывать отказы, данные о платежах должны быть постоянно актуальны, и поддерживать актуальность данных на сайте. И пожалуй, самый важный вопрос — держать своих пользователей в курсе платежной информации на сайте.
К счастью, 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 добавьте следующую строку:

'AuthorityAuthorityL4AuthorityL4ServiceProvider',

Добавьте следующую строку к алиасам:

'Authority' => 'AuthorityAuthorityL4FacadesAuthority',

и опубликуйте файл настроек 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 для использования подписок.

Автор: WildZero

Источник

  1. falur:

    Спасибо! Долго искал как пользоваться стандартным шаблонизатором

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js