OTUS. Онлайн-образование corporate blog
Programming
Laravel
16 October

Laravel: объясняем основные понятия. Часть вторая: «Практика»

Всем привет! Продолжаем серию авторских публикаций в преддверии старта курса «Framework Laravel». В прошлой статье мы с вами посмотрели на теоретические основы Laravel. Однако теорию любого фреймворка можно изучать достаточно долго и ничего не понять, пока сам не напишешь ничего на практике.



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

+9
4.4k 64
Comments 12
Top of the day