Поэтому давайте мы с вами напишем приложение, в котором каждому пользователю можно будет авторизовываться, создавать новые альбомы и загружать туда фотографии. Если вы не знаете пока, что такое MVC, почитайте это , скоро вам предстоит воспользоваться этим на практике.
Подобного рода гайды начинаются с объяснения, как настроить свое окружение и т.д. Проблема в том, что на каждой платформе оно разное, но хочется, чтобы статья была интересна всем. Я просто дам ссылки на необходимое, а вы постараетесь поставить и настроить это самостоятельно.
Итак, нам потребуется:
Composer. Для тех, кто в танке, Composer это менеджер пакетов для PHP (да, как npm для JavaScript). Скачать его можно здесь .
Сам Laravel. Здесь есть несколько самых популярных вариантов: Homestead и Valet (и на самом деле множество других). Однако, последний подходит только для Mac OS. Технически, читатель может воспользоваться любой любимой сборкой, лишь бы она соответствовала этим требованиям.
И малозаметная деталь, но Node.js нам тоже понадобится для установки npm пакетов.
Выполняем команду:
composer global require laravel/installer
для установки Laravel.Не забудьте поместить каталог
$Home/.composer/vendor/bin
(или его эквивалент в вашей ОС) в вашу переменную PATH
, чтобы команда Laravel заработала в вашей системе.Итак, хороший проект начинается с понятного ТЗ. Давайте попробуем сформулировать основные требования к нашему несложному приложению:
У пользователя есть возможность загружать фотографии на сайт в созданные им самими альбомы. Он может зарегистрироваться, и может авторизоваться. База данных — MySQL. Фреймворк, который используется для упрощения процесса верстки — Bootstrap, как самый распространённый фреймворк для верстки.
Первое действие, которое вы совершаете сразу после создания — это подключение базы данных. Если вы пока плохо понимаете, как установить и запустить свою базу данных, воспользуйтесь готовыми desktop-решениями от корпорации Зла: здесь и здесь. Этого поначалу будет достаточно.
Итак, все готово. Проходим в свою любимую директорию и начинаем проект с
laravel new gallery
Далее нужно подключить нашу базу данных. Для этого проходим в MySQL Branch создаем новую базу данных gallery (не забудьте настроить Сharacter Set на utf-8, и Сollation на
utf_general_ci
для избежания проблем с русским языком). Далее проходим в файл
.ENV
и настраиваем подобное подключение, не забыв поменять переменные на свое имя пользователя и пароль:DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=gallery
DB_USERNAME=pavel
DB_PASSWORD=
После этого мы сможем сделать миграцию наших баз данных.
php artisan migrate
Если вы правильно настроили ваше подключение в
.ENV
, то у вас все должно сработать.Как мы с вами договорились на т/з, у нас будет процесс авторизации, регистрации, а также у пользователя будет возможность создавать альбомы и загружать туда фотографии. Для реализации всего нам потребуются соответствующие таблицы в базе данных.
Создаем таблицу albums
Итак, с чего начинается бэкэнд? С создания таблицы в нашей базе данных. Эта команда в терминале создаст нам возможность совершить миграцию в базу данных:
php artisan make:migration CreateAlbumsTable
Итак, нам нужно определить, какие у нас должны быть колонки в нашей таблице. Это нужно сделать в файле, который создался у вас за счет последней миграции. Он находится в вашем проекте в папке
gallery/database/migrations
и название у него связано со временем его создания. Его нужно отредактировать следующим образом:<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateAlbumsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('albums', function(Blueprint $table)
{
$table->increments('id')->unsigned();
$table->string('name'); //имя нашего альбома
$table->text('description'); // описание нашего альбома
$table->string('cover_image'); //картинка на превью
$table->timestamps(); // дата создания. Необязательно ее показывать, но может быть полезна
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('albums');
}
}
Не забудьте сделать
php artisan migrate
Теперь вкладка вашей базы данных в MySQL Branch должна принять следующий вид:
Итак, мы создали нашу базу данных, теперь нам нужно связать нашу базу данных с моделью Eloquen, которая и будет взаимодействовать с нашей базой данных.
Создаем модель Album
Теперь нам нужно создать модель Eloquent ORM. Если вы не понимаете, как мы так быстро перешли от одного к другому, вам стоит почитать вот это. Воспользуемся для генерации шаблона модели все тем же помощником Artisan:
php artisan make:model Album
Далее мы проходим в
gallery/app/Album.php
и редактируем его:<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Album extends Model
{
protected $table = 'albums';
protected $fillable = array('name','description','cover_image');
public function Photos(){
return $this->hasMany('App\Image');
}
}
Таблица Images
Здорово! Наша модель альбома готова, теперь можно заняться нашими изображениями. Здесь процесс повторится вновь:
php artisan make:migration CreateImagesTable
Идем редактировать нашу миграцию:
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateImagesTable extends Migration
{
public function up()
{
Schema::create('images', function (Blueprint $table) {
$table->increments('id');
$table->integer('album_id')->unsigned();
$table->string('image');
$table->string('description');
$table->foreign('album_id')->references('id')->on('albums')->onDelete('CASCADE')->onUpdate('CASCADE');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('images');
}
}
И снова делаем миграцию:
php artisan migrate
Надеюсь, ваша миграция удалась! Однако нам нужно объяснить ещё одну вещь, которую слишком долго было бы расписывать в комментариях: мы использовали ключ
foreign
, чтобы связать две таблицы Albums и Images. У каждого альбома есть свои изображения, и если вы хотите удалить какой-то альбом, тогда вы наверное полагаете что и фотографии у вас тоже удалятся.Модель Image
Пора создать и модель, которая будет работать с таблицей images. Надеюсь, вы уже догадываетесь что делать:
php artisan make:model Image
Шаблон создан, идем в
gallery/app/Image.php
его редактировать. Здесь тоже все должно быть понятно:
class Image extends Model
{
protected $table = 'images'; //сама таблица, с которой мы собираемся связываться
protected $fillable = array('album_id','description','image');
//свойства
}
Наша модель Image готова. Теперь нам потребуется контроллер для создания альбомов в нашей базе данных.
Создание альбома
Что бы сохранять, показывать и удалять в нашем альбоме, нам нужны некоторые функции в нашем контроллере. Но для начала нужен сам контроллер:
php artisan make:controller AlbumsController
Заходим в
gallery/app/Http/Controllers/AlbumsController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\MessageBag;
use Validator;
use App\Album;
class AlbumsController extends Controller
{
public function getList() //получение списка альбомов
{
$albums = Album::with('Photos')->get();
return view('index')->with('albums',$albums);
}
public function getAlbum($id)
{
$album = Album::with('Photos')->find($id);
$albums = Album::with('Photos')->get();
return view('album', ['album'=>$album, 'albums'=>$albums]);
}
public function getForm()
{
return view('createalbum'); //форма создания альбома
}
public function postCreate(Request $request)
{
$rules = ['name' => 'required', 'cover_image'=>'required|image']; // нельзя создать альбом без имени и обложки
$input = ['name' => null];
//валидатор мы обсудим в следующей части
$validator = Validator::make($request->all(), $rules);
if($validator->fails()){
return redirect()->route('create_album_form')->withErrors($validator)->withInput();
}
$file = $request->file('cover_image');
$random_name = str_random(8);
$destinationPath = 'albums/';
$extension = $file->getClientOriginalExtension();
$filename=$random_name.'_cover.'.$extension;
$uploadSuccess = $request->file('cover_image')->move($destinationPath, $filename);
$album = Album::create(array(
'name' => $request->get('name'),
'description' => $request->get('description'),
'cover_image' => $filename,
));
return redirect()->route('show_album',['id'=>$album->id]);
}
public function getDelete($id) // возможность удаления альбома
{
$album = Album::find($id);
$album->delete();
return Redirect::route('index');
}
}
Далее нам предстоит заняться переопределением наших путей. Для тех, кто не знает, роутинг — это настройка путей, которые показываются пользователю в браузере, и действий, которое должно совершать наше приложение. Мы заходим в файл
web.php
:<?php
//роутинг альбомов
Route::get('/', array('as' => 'index','uses' => 'AlbumsController@getList'));
//получение списка альбомов на главной странице
Route::get('/createalbum', array('as' => 'create_album_form','uses' => 'AlbumsController@getForm'));
//создание альбома из формы
Route::post('/createalbum', array('as' => 'create_album','uses' => 'AlbumsController@postCreate'));
//само создание альбома
Route::get('/deletealbum/{id}', array('as' => 'delete_album','uses' => 'AlbumsController@getDelete'));
// удаление альбома
Route::get('/album/{id}', array('as' => 'show_album','uses' => 'AlbumsController@getAlbum'));
// удаление альбома
//Эта часть связана с обработкой самих фотографий
Route::get('/addimage/{id}', array('as' => 'add_image','uses' => 'ImageController@getForm'));
//добавление изображений в форме
Route::post('/addimage', array('as' => 'add_image_to_album','uses' => 'ImageController@postAdd'));
//добавление изображений в сам альбом
Route::get('/deleteimage/{id}', array('as' => 'delete_image','uses' => 'ImageController@getDelete'));
//удаление
Route::post('/moveimage', array('as' => 'move_image', 'uses' => 'ImageController@postMove'));
Здорово. Что бы наше приложение точно могло заработать, перед настройками представления вида нам нужно убедиться, что у нас есть необходимые ссылки на ярлыки (alias) необходимых нам классов в конце файлаОтлично! Теперь нам нужно определить наши представления! Проходим в папкуgallery/config/app.php
. Это только технические настройки:
'aliases' => [ 'App' => Illuminate\Support\Facades\App::class, 'Artisan' => Illuminate\Support\Facades\Artisan::class, 'Auth' => Illuminate\Support\Facades\Auth::class, 'Blade' => Illuminate\Support\Facades\Blade::class, 'Broadcast' => Illuminate\Support\Facades\Broadcast::class, 'Bus' => Illuminate\Support\Facades\Bus::class, 'Cache' => Illuminate\Support\Facades\Cache::class, 'Config' => Illuminate\Support\Facades\Config::class, 'Cookie' => Illuminate\Support\Facades\Cookie::class, 'Crypt' => Illuminate\Support\Facades\Crypt::class, 'DB' => Illuminate\Support\Facades\DB::class, 'Eloquent' => Illuminate\Database\Eloquent\Model::class, 'Event' => Illuminate\Support\Facades\Event::class, 'File' => Illuminate\Support\Facades\File::class, 'Gate' => Illuminate\Support\Facades\Gate::class, 'Hash' => Illuminate\Support\Facades\Hash::class, 'Lang' => Illuminate\Support\Facades\Lang::class, 'Log' => Illuminate\Support\Facades\Log::class, 'Mail' => Illuminate\Support\Facades\Mail::class, 'Notification' => Illuminate\Support\Facades\Notification::class, 'Password' => Illuminate\Support\Facades\Password::class, 'Queue' => Illuminate\Support\Facades\Queue::class, 'Redirect' => Illuminate\Support\Facades\Redirect::class, 'Redis' => Illuminate\Support\Facades\Redis::class, 'Request' => Illuminate\Support\Facades\Request::class, 'Response' => Illuminate\Support\Facades\Response::class, 'Route' => Illuminate\Support\Facades\Route::class, 'Schema' => Illuminate\Support\Facades\Schema::class, 'Session' => Illuminate\Support\Facades\Session::class, 'Storage' => Illuminate\Support\Facades\Storage::class, 'URL' => Illuminate\Support\Facades\URL::class, 'Validator' => Illuminate\Support\Facades\Validator::class, 'View' => Illuminate\Support\Facades\View::class, 'Form' => Collective\Html\FormFacade::class, 'Html' => Collective\Html\HtmlFacade::class, ], //потому что класса FORM у вас скорее всего нет)
gallery/resources/views
. Можете удалить welcome.blade.php
, можете оставить, неважно. Создаем index.blade.php
и подпапку includes
, чтобы сложить туда повторяющиеся части view
. Итак, наш index.blade.php
должен принять следующий вид:@include('includes.header')
<body>
@include('includes.nav')
<div class="container">
<div class="starter-template">
<div class="row">
@foreach($albums as $album)
<div class="col-lg-3">
<div class="thumbnail" style="min-height: 514px;">
<img alt="{{$album->name}}" src="/albums/{{$album->cover_image}}">
<div class="caption">
<h3>{{$album->name}}</h3>
<p>{{$album->description}}</p>
<p>{{count($album->Photos)}} image(s).</p>
<p>Created date: {{ date("d F Y",strtotime($album->created_at)) }} at {{date("g:ha",strtotime($album->created_at)) }}</p>
<p><a href="{{route('show_album', ['id'=>$album->id])}}" class="btn btn-big btn-default">Показать галерею</a></p>
</div>
</div>
</div>
@endforeach
</div>
</div><!-- /.container -->
</div>
</body>
</html>
Содержимоеincludes/header.blade.php
:
<!doctype html> <html lang="ru"> <head> <meta charset="UTF-8"> <title>Мои альбомы</title> <link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0-rc1/css/bootstrap.min.css" rel="stylesheet"> <script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0-rc1/js/bootstrap.min.js"></script> <style> body { padding-top: 50px; } .starter-template { padding: 40px 15px; text-align: center; } </style> </head>
Содержимоеincludes/nav.blade.php
:
<div class="navbar navbar-inverse navbar-fixed-top"> <div class="container"> <button type="button" class="navbar-toggle"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="navbar-brand" href="/">Мои альбомы</a> <div class="nav-collapse collapse"> <ul class="nav navbar-nav"> <li><a href="{{URL::route('create_album_form')}}">Создание нового альбома</a></li> </ul> </div><!--/.nav-collapse --> </div> </div>
На этом на сегодня все! Это, понятное дело, только первая часть нашего приложения. Всем спасибо за внимание, в следующей части дополним наши
view
и обсудим вопрос валидизации изображений. Полезные ссылки:
Документация на русском языке
Полезный гайд на английском, который лучше, чем в документации объясняет как работает наследование и расширение Blade-шаблонов
Множество полезной информации и практических примеров создания приложений
Комментарии (11)
wscms
16.10.2019 18:36+1Статья о том, как не нужно писать на Laravel, да и вообще на PHP
В миграции стоило бы добавить nullable
$table->text('description'); // описание нашего альбома
Потому что при создании альбома поле description не обязательное по валидатору и этот код вызовет ошибку при пустом значении description
$album = Album::create(array(
'name' => $request->get('name'),
'description' => $request->get('description'),
'cover_image' => $filename,
));
Про CodeStyle, похоже, никто не слышал
Про CRUD через php artisan make:resource и соответствующие роуты написали выше. И еще куча всего
Статья пойдет как для ну очень новичка, ИМХО
UPD
Зачем во вьюхах@include('includes.header')
?
Можно же наследовать,
@extends('site.layouts.main')
@section('content')
@endsectionterantul
17.10.2019 03:13Для новичков больше вреда чем полезности. Навалено в кучу ИМХО автора.
Начиная работу с Ларавелем перерыл кучу подобных мануалов. Пока тим лид не обьяснил на пальцах структуру где, что лежит и за что отвечает тот или иной кусок кода — в голове была жуткая каша.
WebMonet
17.10.2019 12:04Собираюсь к вам на курс, а тут такое… Я хотел-бы научиться так не делать.
MaxRokatansky Автор
17.10.2019 12:54Статья никак не связана с программой курса. Она написана автором не имеющим отношения к процессу обучения.
MaxRokatansky Автор
17.10.2019 13:22Благодарю всех за комментарии. Все замечания переданы автору, для того, чтобы в последующих публикациях такого не повторялось.
Sky4eg
и будет вам и миграции и контроллер
Роуты жуть, почему не ресурсы?
И в целом материал какой-то устаревший, например:
сейчас просто
asvechkar
Даже так:
А почему Bootstrap 3? Уже давно 4.3
Судя по примерам кода автор не до конца знает фреймворк, пишет какой то самопал.