- Обязательно
- ознакомиться с хелперами
- почитать про collections
- почитать про фасады
- почитать про middleware
- почитать про request
- почитать про validation
- почитать про seeders
- почитать про relationships !!
- почитать про laravel collective
- почитать про faker и пример
- полюбить tinker
- хоть немного разобраться с markdown
- Создаем laravel проект
composer create-project laravel/laravel news-app
Инициализируем git репозиторий и комитим
git init
git add -A
git commit -m "initial commit"
По мере разработки, не забываем КОМИТИТЬ изменения, придерживаемся таких правил:
- лучше закомитить 1 измененный файл и описать изменения, чем закомитить 10 файлов с коротеньким сообщением
- закончили работать с файлом — комитим
- сообщение в коммите должно соответствовать изменениям в файлах
И не забываем про коменты в коде, они должны быть написаны обсолютно к каждому методу (или переменной класса), которые Вы написали!!!
- Создаем БД
news_app
с кодировкойutf8mb4_general_ci
и в файлеconfig/database.php
ставим такие вот значения:
// ..
'mysql' => [
// ..
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_general_ci',
// ..
],
// ..
И комитим
git add config/database.php
git commit -m "Поправил кодировку с utf8_unicode_ci на utf8mb4_general_ci"
Дальше напоминать не буду, просто не забываем об этом.
- Перенесем модель User в папку
App\Models
- в файлах
app\Http\Controllers\Auth\RegisterController.php
,config/auth.php
,database/factories/ModelFactory.php
заменимApp\User
наApp\Models\User
, вUser.php
заменим namespaceApp
наApp\Models
- выполним команду
composer dump-autoload
- Добавим в проект debugbar laravel-tracy, выполним
composer require recca0120/laravel-tracy
и в файлеconfig/app.php
в массивproviders
в конце допишем:
'providers' => [
// ..
Recca0120\LaravelTracy\LaravelTracyServiceProvider::class,
// ..
];
Затем выполним команду
php artisan vendor:publish --provider="Recca0120\LaravelTracy\LaravelTracyServiceProvider"
и в появившемся файле config/tracy.php
отключаем параметр panels.terminal
(ставим false), это опасная для безопасности штука и лучше ее не трогать
Про нее можно почитать тут 5.0 и тут 5.4
- Создадим модели и миграции для новостей и ролей:
php artisan make:auth
php artisan make:model Models/News -m
php artisan make:model Models/Role -m
Кто не знает, ключ -m
создаем миграцию для модели.
Команда php artisan make:auth
создает миграции (и views) для модели User, кому интересно, гуглите)
Структура таблиц должна быть примерно такой:
| news
|------
| id
| user_id - автор
| title название статьи
| slug - str_slug(title) (должно быть уникально)
| content - markdown контент новости
| created_at
| updated_at
| roles
|------
| id
| slug - unique
| role_user
|------
| role_id
| user_id
- str_slug
- Пример миграции, не копипастим, думаем
- чтобы не создавать в таблицах поля
created_at
иupdated_at
(если они конечно там не нужны), в миграции удаляем$table->timestamps();
, а в модели добавимpublic $timestamps = false;
Так же, добавим сидеры для ролей:
php artisan make:seeder RolesTableSeeder
, и в появившемся файле (database/seeds/RoleSeeder.php
) добавим данные в бд:
// ..
public function run()
{
$roles = collect(['admin', 'user', 'moderator']);
$roles->each(function($role) {
DB::table('roles')->insert([
'slug' => $role
]);
});
}
// ..
- метод each
И аналогично сделаем для модели User (UsersTableSeeder
), создадим одного пользователя (Посмотрите файл database/factories/ModelFactory.php
).
Выполняем
php artisan migrate
php artisan db:seed --class=RolesTableSeeder
php artisan db:seed --class=UsersTableSeeder
Запустим tinker (php artisan tinker
), выполним в нем App\Models\Role::all()
и нам должно вернуть список наших ролей
- Добавим отношения к моделям
Role
,User
иNews
(тут сами, не трудно же), сделайте так, чтобы работал код (tinker):
$user = App\Models\User::first();
$admin = App\Models\Role::where('slug', 'admin')->first();
$user->roles->count(); //вернет 0
$user->roles()->save($admin);
$user = $user->fresh();
$admin = $admin->fresh();
$admin->users; // тут будет User к которому добавили роль
$user->news()->create([
'title' => 'Hello world!',
'content' => '# Hello world content!!!'
]);
App\Models\News::first()->user // тут будет User к которому добавили новость
Чтобы у нас поле slug генерировалось автоматически, в laravel есть такая штука, как mutators, в модели News напишем метод:
public function setTitleAttribute($value) {
$this->attributes['title'] = $value;
$this->attributes['slug'] = str_slug($value);
}
и теперь достаточно указать только поле title, slug сгенерируется автоматически.
- Добавим в регистрацию поле для выбора роли пользователя, в
app\Http\Controllers\Auth\RegisterController.php
исправим валидацию и создание модели User:
// ..
protected function validator(array $data)
{
return Validator::make($data, [
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:6|confirmed',
'role' => 'required|exists:roles,id' // добавили валидацию роли
]);
}
// ..
protected function create(array $data)
{
$user = User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
$user->roles()->sync([$data['role']]); // добавили роль к модели
return $user;
}
// ..
И на странице регистрации не забываем добавить поле в форму, для этого нам, нужно получить все доступные роли и передать их в view.
Для начала, выполним команду php artisan route:list
и получим что-то вроде этого:
+--------+----------+------------------------+------------------+------------------------------------------------------------------------+--------------+
| Domain | Method | URI | Name | Action | Middleware |
+--------+----------+------------------------+------------------+------------------------------------------------------------------------+--------------+
| | GET|HEAD | / | | Closure | web |
| | GET|HEAD | api/user | | Closure | api,auth:api |
| | GET|HEAD | home | home | App\Http\Controllers\HomeController@index | web,auth |
| | GET|HEAD | login | login | App\Http\Controllers\Auth\LoginController@showLoginForm | web,guest |
| | POST | login | | App\Http\Controllers\Auth\LoginController@login | web,guest |
| | POST | logout | logout | App\Http\Controllers\Auth\LoginController@logout | web |
| | POST | password/email | password.email | App\Http\Controllers\Auth\ForgotPasswordController@sendResetLinkEmail | web,guest |
| | GET|HEAD | password/reset | password.request | App\Http\Controllers\Auth\ForgotPasswordController@showLinkRequestForm | web,guest |
| | POST | password/reset | | App\Http\Controllers\Auth\ResetPasswordController@reset | web,guest |
| | GET|HEAD | password/reset/{token} | password.reset | App\Http\Controllers\Auth\ResetPasswordController@showResetForm | web,guest |
| | GET|HEAD | register | register | App\Http\Controllers\Auth\RegisterController@showRegistrationForm | web,guest |
| | POST | register | | App\Http\Controllers\Auth\RegisterController@register | web,guest |
+--------+----------+------------------------+------------------+------------------------------------------------------------------------+--------------+
Это список всех наших маршрутов, нам нужен всего 1, это register, видим что он у нас ссылается на RegisterController@showRegistrationForm
, откроем этот файл,
но в нем не будет метода showRegistrationForm
, потому что он находится в трейте
Illuminate\Foundation\Auth\RegistersUsers
(лежит в папке vendor) и примерно такой:
// ..
public function showRegistrationForm()
{
return view('auth.register');
}
// ..
Все что мы сделаем, это переопределим его в RegisterController
:
use App\Models\Role;
// ..
public function showRegistrationForm()
{
$roles = Role::all()->pluck('slug', 'id');
return view('auth.register', compact('roles'));
}
// ..
И теперь в файле resources/views/auth/register.blade.php
добавим поле с ролями:
<!-- email -->
<!-- role -->
<div class="form-group{{ $errors->has('role') ? ' has-error' : '' }}">
<label for="role" class="col-md-4 control-label">Role</label>
<div class="col-md-6">
<select id="role" class="form-control" name="role" required>
@foreach($roles as $id => $slug)
<option value="{{ $id }}" {{ (int)$id === (int) old('role') ? 'selected' : '' }}>{{ $slug }}</option>
@endforeach
</select>
@if ($errors->has('role'))
<span class="help-block">
<strong>{{ $errors->first('role') }}</strong>
</span>
@endif
</div>
</div>
<!-- password -->
Чтобы у нас не было таких громоздких конструкций как:
@foreach($roles as $id => $slug)
<option value="{{ $id }}" {{ (int)$id === (int) old('role') ? 'selected' : '' }}>{{ $slug }}</option>
@endforeach
Придумали LaravelCollective, его мы подключим и настроим позже, и при желании можете переписать авторизацию c его помощью.
Теперь у нас полностью готова авторизация.
- Добавим новости на сайт.
Выполним команду php artisan make:controller NewsController --model=Models/News
, у нас появится файл App\Controllers\NewsController
,
в нем будут методы:
- index — отвечает за отображение всех новостей
- create — показываем форму создания новости
- store — отвечает за сохранение созданной новости (form.action = 'news/store', form.method = post)
- show — отвечает за отображение какой-либо новости на сайте
- edit — показываем форму редактирования новости
- update — сохраняем новость (form.action = 'news/{news}/update', form.method = post)
- destroy — отвечает за удаление новости
Откроем файл routes/web.php
и добавим:
Route::middleware('auth')->resource('news','NewsController');
Выполним php artisan route:list
и увидим, что добавились примерно такие маршруты:
+--------+-----------+------------------------+------------------+------------------------------------------------------------------------+--------------+
| Domain | Method | URI | Name | Action | Middleware |
+--------+-----------+------------------------+------------------+------------------------------------------------------------------------+--------------+
| ------------------------------------------------------------------------------------ |
| | POST | news | news.store | App\Http\Controllers\NewsController@store | web,auth |
| | GET|HEAD | news | news.index | App\Http\Controllers\NewsController@index | web,auth |
| | GET|HEAD | news/create | news.create | App\Http\Controllers\NewsController@create | web,auth |
| | PUT|PATCH | news/{news} | news.update | App\Http\Controllers\NewsController@update | web,auth |
| | GET|HEAD | news/{news} | news.show | App\Http\Controllers\NewsController@show | web,auth |
| | DELETE | news/{news} | news.destroy | App\Http\Controllers\NewsController@destroy | web,auth |
| | GET|HEAD | news/{news}/edit | news.edit | App\Http\Controllers\NewsController@edit | web,auth |
| ------------------------------------------------------------------------------------ |
+--------+-----------+------------------------+------------------+------------------------------------------------------------------------+--------------+
- middleware('auth') — доступ к маршрутам будет только у авторизированных пользователей.
Создайте в папке resources/views
папки news
, news/forms
и внутри news
файлы index.blade.php
, show.blade.php
, forms/create.blade.php
, forms/edit.blade.php
, forms/form.blade.php
.
Это все что нам нужно будет для работы.
Самостоятельно подключим Laravel Collective и HTMLPurifier, почитаем про parsedown
В модели News
напишем еще два мутатора для поля content, так как это у нас markdown-текст будет, туда могут напихать много плохих вещей, таких как XSS (гуглите):
use Parsedown;
// ..
// удаляем опасный код
public function setContentAttribute($value){
$this->attributes['content'] = clean($value);
}
// преобразуем markdown в html
public function getMarkdownContentAttribute() {
return (new Parsedown)->text($this->attributes['content']);
}
// в news.show выводить новость так: {!! $news->markdownContent !!}
В config/purufier.php
отключим опцию AutoFormat.AutoParagraph
И в tinker можно потренироваться:
$news = new App\Models\News(['title' => 'Hello', 'content' => '#Hello from <script>alert("XSS")</script>']);
$news->content; //вернет html
Самостоятельно напишем логику для NewsController:
- не забываем о валидации, можно сделать по анологии с RegisterController, а можно почитать
- только модератор может создать или отредактировать новость
- только админ может новость удалить
- только зарегистрированные пользователи могут смотреть новости (уже реализовали, middleware('auth'))
Если не сверстали еще нужные странички (что создали выше), верстаем, посмотрите на страницу home
и сделайте по анологии (если хотите).
Вот и все, потом потренеруйтесь писать markdown посты на своем сайте
Думаю, у Вас будем много коммитов ;)