Pull to refresh
831.71
OTUS
Цифровые навыки от ведущих экспертов

Создаем todo для удаленной команды на Laravel

Reading time8 min
Views5K
Салют, хабровчане. Следующая статья была написана одним из наших постоянных читателей и определенно не претендует на звание хардкорного материала, но при этом вполне может послужить туториалом для новичка. Ждем в комментариях ваше мнение по статье, а за более хардкорными знаниями приглашаем на наш курс «Framework Laravel».




Всем привет! Сегодня, в столь «удаленное» для всех время работы, я бы хотел разобрать создание несложной todo, в которой можно создавать свои задачи. Звучит как то, что написано в официальной документации Laravel, и так оно и есть — я использую их todo в качестве базовой основы, немного его трансформирую, а основная часть моего рассказа будет про то, как же создавать роль администратора, и создать очередной нелепый клон Trello.

Создаем основание




Как я уже сказал, за основу мы возьмем следующий гайд. Он очень простой и больше концентрируется на работе с миграциями и с роутингом, чем на программировании контроллеров и моделей. К большому сожалению, его не обновляли с версии 5.1 и, если вы только начинаете программировать в Laravel на последней 7 версии (только 3 марта вышла, свеженькая)), роутинг вам придется писать не в app/Http/routes.php, а в routes/web.php (это правда уже с 6 версии поменялось), и в общем на этом вся разница заканчивается. В итоге, у вас должно получится что-то такое:



Так же я поменял фреймворк для верстки с bootstrap на bulma. Во-первых, мне он больше нравится, а вот вторых у меня уже была кодовая база blade шаблонов регистраций (хотя, с другой стороны, нарастить страницы регистраций и авторизаций очень просто, я покажу дальше, как).

Эту версию проекта можно скачать здесь в ветке мастер (а в ветке new вы можете скачать готовое приложение). Активировать вы её сможете с помощью следующих команд, при условии, что у вас уже установлен composer и laravel:

composer install
// создаете файл .env, копируете в него .env example, настраиваетесь под вашу базу данных
php artisan key:generate //устанавливаете ключ безопасности
php artisan serve //запускаете приложение

Создаем авторизацию и регистрацию

Авторизации «из коробки» поменялись с 6 версии. Теперь для быстрого создания нужно поставить пакет, который может вам быстро сгенерировать контроллеры и blade — шаблоны:

composer install laravel/ui
php artisan ui vue --auth
 

В вашем проекте должно появиться несколько новых файлов:

в папке resourses появится подпапка auth, в которой будут файлы blade шаблонов, посвященные авторизации, паролям. Изначально blade-шаблоны сверстаны в bootstrap, однако я их немного трансформировал в bulma, чтобы соблюсти общность css-фрейворка в проекте. Однако, чтобы удобно было пользоваться веб-сайтом, нам потребуется панель навигации. В resources я создал папку includes, в которой разместил вспомогательные файлы — header и nav. Чтобы не занимать много места, скажу, что в header у меня была только head с подключением bulma, а в nav.blade.php было следующее:

<nav class="navbar has-background-black-ter" role="navigation" aria-label="main navigation">
  <div class="navbar-brand">
    <a class="navbar-item has-text-white is-size-4" href="/">
      TODO
    </a>
    <a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false">
      <span aria-hidden="true"></span>
      <span aria-hidden="true"></span>
      <span aria-hidden="true"></span>
    </a>
  </div>
  <div class="navbar-menu">
    <div class="navbar-start">
      @if (Auth::check())
      <button type="button" class="button is-primary">
        <!-- если пользователь зарегистрирован, тогда ->отобразить его ник -->
        {{{ Auth::user()->name}}}
      </button>
      @else
      <!-- в другом случае -->
      <a class="navbar-item has-text-danger" href="{{route('register')}}">Регистрация</a>
      <a class="navbar-item has-text-danger" href="{{route('login')}}">Войти</a>
      @endif
      @if (Auth::check())
      <!-- если зарегистрирован, тогда нам нужно показать как выйти -->
      <!-- это можно сделать через форму -->

      <a class="navbar-item has-text-danger" href="{{url('/logout') }}" onclick="event.preventDefault();
                  document.getElementById('logout-form').submit();">
        Выйти </a>
      <form id="logout-form" action="{{ url('/logout') }}" method="POST" style="display: none;">
        {{ csrf_field() }}
      </form>

      @endif
      </ul>
    </div>

    <div class="navbar-end">
      <!-- navbar items -->
    </div>
  </div>
</nav>
 

Начинаем создавать нашу архитектуру


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

Окей, значит у нас будет две группы юзеров и условимся, что увидеть нашу доску задач смогут только авторизованные юзеры, которым дали доступ, а добавлять задачи — некоторые суперадмины. Давайте приступим.

Для небольшого упрощения я решил разбить приложение на две страницы: одну, на которой у нас будет только задачи без возможности редактирования, и вторую, которая будет доступна только суперадмину. Доступную команде страницу я решил назвать welcome.blade.php:

@include('includes.header')

<body>
    @include('includes.nav')
    <div class="columns is-centered">
        <div class="column is-half">
            <div class="panel">
                <div class="panel-heading">
                    Текущие задачи
                </div>

                <div class="panel-body">
                    @foreach ($tasks as $task)
                    <a class="panel-block">
                        <button class="button is-rounded">
                            <span>{{ $task->name }}</span>
                        </button>
                        @endforeach
                </div>
            </div>
        </div>
    </div>
<body>
</html>

Эта страница выводит только список задач. Для ее отображения я добавлю новый путь в web.php:

Route::get('/', function () {
    return view('welcome', [
        'tasks' => Task::orderBy('created_at', 'asc')->get(),
    ]);
});

Шаблон, в котором у нас тот же список, но с возможностью редактирования и добавления, называется task.blade.php. Он слишком велик, поэтому я размещу его под спойлером:

task.blade.php
@extends('layouts.app')

@section('content')
<div class="columns is-centered">
    <div class="column is-half">
        <div class="panel">
            <div class="panel-heading">
                Новое дело
            </div>
            <div class="panel-block">
                @include('common.errors')
                <!-- Форма для создания тасков -->
                <form action="{{ url('task')}}" method="POST">
                    {{ csrf_field() }}

                    <div class="field">
                        <label for="task-name" class="label is-medium">Дело</label>
                        <input type=" text" name="name" id="task-name" class="input is-medium"
                            value="{{ old('task') }}">
                    </div>

                    <div class="field">
                        <button type="submit" class="button is-success">
                            <span class="icon">
                                <i class="fa fa-btn fa-plus">
                            </span></i>
                            <span>Добавить дело</span>
                        </button>
                    </div>
                </form>
            </div>
        </div>

        <!-- Current Tasks -->
        @if (count($tasks) > 0)
        <div class="panel">
            <div class="panel-heading">
                Текущие задачи
            </div>

            <div class="panel-body">
                {{-- <table class="table table-striped task-table">
                    <thead>
                        <th>Дело</th>
                        <th> </th>
                    </thead>
                    <tbody> --}}
                @foreach ($tasks as $task)
                <a class="panel-block">
                    <button class="button is-rounded">
                    <span>{{ $task->name }}</span>
                </button>
                    <form action="{{ url('task/'.$task->id) }}" method="POST">
                        {{ csrf_field() }}
                        {{ method_field('DELETE') }}
                        <button type="submit" class="button is-danger">
                            <span class="icon is-small"> <i class="fa fa-btn fa-trash"></i></span>
                            <span> Удалить </span>
                        </button>
                    </form>
                </a>
                @endforeach
            </div>
        </div>
        @endif
    </div>
</div>
@endsection



И этот шаблон, как и оригинальном туториале, расширяется в app/layouts.blade.php:

@include('includes.header')

<body>
    @include('includes.nav')

    @yield('content')

</body>

</html>

Отлично! С внешним видом мы определились, теперь можно переходить к роутингу и созданию наших middlewares.

Для начала зайдем в модель Users и добавим новые типы пользователей:

const ADMIN_TYPE = 'admin';
const TEAM_TYPE = 'team';
#и добавляем в самый конец класса функцию проверки:

public function isAdmin(){        
    return $this->type === self::ADMIN_TYPE;
    // требуем доложить тип пользователя    
}

Далее заходим в последнюю миграцию таблицы users (папка database/migrations) и добавляем колонку с нашим новым типом данных:

$table->string('type')->default('team');
// пока предполагаем, что кроме команды у нас сервисе больше никого нету

Дальше проходим в app/Http/Controllers/Auth/RegisterController.php и немного редактируем функцию create (она тоже в версии 5 выглядела немного по другому):

protected function create(array $data)
    {
        return User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => Hash::make($data['password']),
            'type' => User::TEAM_TYPE,  
        ]);
    }


Окей, дальше нам нужно создать нашу middleware для админа. Проще всего это осуществить с помощью следующей команды:

php artisan make:middleware IsAdmin

Надеюсь, у вас все получилось. В папке HTTP/middleware у вас должен был появится isAdmin.php, в которой функцию handle нужно отредактировать следующим образом:

public function handle($request, Closure $next)
    {
        if(auth()->user()->isAdmin()) {
            return $next($request);
        // если пользовать админа, пропускаем. Если нет - тогда отправляем домой
    }
        return redirect('/');
    }

Дальше нам нужно зарегистрировать наш middleware в app/HTTP/Kernel, чтобы им можно было воспользоваться:

protected $routeMiddleware = [
    #множество других мидлевеиров, не так важных для этой истории
        is_admin' => \App\Http\Middleware\IsAdmin::class,
];

Трансформируем путь dash в web.php, который должен иметь возможность видеть только суперадмин:

Route::get('/dash', function () {
    return view('tasks', [
        'tasks' => Task::orderBy('created_at', 'asc')->get(),
    ]); 
})
->middleware('is_admin')    
->name('admin');

# не забываем добавить авторизацию и возможность разлогироваться

Auth::routes();
Route::post('/logout', 'Auth\LoginController@logout')->name('logout');

Осталось создать кастомный контролер для нашего администратора:

php artisan make:controller AdminController

Редактируем:

public function __construct()
    {
        $this->middleware('auth');
    }
    public function admin()
    {
        return view('admin');
    }
 

Чтобы у вас все с регистрацией заработало конкретно, нужно не забыть поправить редирект в RouteServiceProvider.php в app/HTTP/Providers, потому он изначально рассчитан на шаблон home:

public const HOME = '/';

Теперь администратор у нас может появится только с помощью нашего помощника в терминале tinker:

php artisan tinker
use App\User;
User::where('email', 'admin@mail.com')->update(['type' => 'admin']);
//даем нашем пользователю права админа

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

Возможно, у вас есть корпоративная почта, и тогда вы сможете ограничивать доступ в команду с помощью регулярок в валидизации почты. Или вы сможете добавить проверку на членство в команде точно так же, как админа, проделав схожие манипуляции. По традиции, приведу несколько полезный ссылок, для тех, кто только собирается начать осваивать Laravel:

Чуть более подробная статья, чем в документации, о миграциях в Laravel
Неплохая статья на Medium про роли в Laravel
Что у нас свеженького в Laravel 7



Разворот приложений и непрерывный деплой «без боли» с помощью Forge/Envoyer.


Tags:
Hubs:
+3
Comments9

Articles

Information

Website
otus.ru
Registered
Founded
Employees
101–200 employees
Location
Россия
Representative
OTUS