In the last article, we started writing a gallery in which the user can log in and register, create albums with a description, cover and name. In the created application there is the primary value of our
view
and now it is necessary to expand it a little.
Also, the existing code is worth a little improvement:
We already have two controllers -
AlbumController
and
ImageController
. Add the import of an additional class that is responsible for working with strings:
use Illuminate\Support\Str;
And change the line that is responsible for generating a random image name in our database:
$random_name = Str::random(8);
In addition, now it’s worth changing the migration code a bit to increase the reliability of our application, because the user must have the right to leave our
description
field empty (otherwise Laravel with dynamite will notify us of an error). Let’s go into the migration associated with the image table, and correct there the line related to the image description to this one:
$table->string('description')->nullable();
Well, with the "refactoring" of the application is finished. Next, you need to finish the look of our application. Create a form in which the user will be able to create their albums:
addimage.blade.php
file
@include('includes.header') <body> @include('includes.nav') <div class="container" style="text-align: center;"> <div class="span4" style="display: inline-block; margin-top:100px;"> @if (isset($errors) && $errors->has('')) <div class="alert alert-block alert-error fade in"id="error-block"> <?php $messages = $errors->all('<li>:message</li>'); ?> <button type="button" class="close"data-dismiss="alert">×</button> <h4>Warning!</h4> <ul> @foreach($messages as $message) {{$message}} @endforeach </ul> </div> @endif <form name="createnewalbum" method="POST"action="{{route('create_album')}}" enctype="multipart/form-data"> {{ csrf_field() }} <fieldset> <legend> </legend> <div class="form-group"> <label for="name"> </label> <input name="name" type="text" class="form-control"placeholder=" " value="{{old('name')}}"> </div> <div class="form-group"> <label for="description"> </label> <textarea name="description" type="text"class="form-control" placeholder=" ">{{old('descrption')}}</textarea> </div> <div class="form-group"> <label for="cover_image"> </label> {{Form::file('cover_image')}} </div> <button type="submit" class=«btn btn-default">!</button> </fieldset> </form> </div> </div> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script> <script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0-rc1/js/bootstrap.min.js"></script> </body> </html>
Let me remind you that in
includes
at the top of the page we have the navigation and libraries used, which were described in the last part.
In addition, we need to add the look of
album.blade.php
@include('includes.header') <body> @include('includes.nav') <div class="container"> <div class="starter-template"> <div class="media"> <img class="media-object pull-left"alt="{{$album->name}}" src="/albums/{{$album->cover_image}}" width="350px"> <div class="media-body"> <h2 class="media-heading" style="font-size: 26px;"> :</h2> <p>{{$album->name}}</p> <div class="media"> <h2 class="media-heading" style="font-size: 26px;"> :</h2> <p>{{$album->description}}<p> <a href="{{route('add_image',array('id'=>$album->id))}}"><button type="button"class="btn btn-primary btn-large"> </button></a> <a href="{{route('delete_album',array('id'=>$album->id))}}" onclick="return confirm(' ?')"><button type="button"class="btn btn-danger btn-large"> </button></a> </div> </div> </div> </div> <div class="row"> @foreach($album->Photos as $photo) <div class="col-lg-3"> <div class="thumbnail" style="max-height: 350px;min-height: 350px;"> <img alt="{{$album->name}}" src="/albums/{{$photo->image}}"> <div class="caption"> <p>{{$photo->description}}</p> <p> : {{ date("d FY",strtotime($photo->created_at)) }}at {{ date("g:ha",strtotime($photo->created_at)) }}</p> <a href="{{route('delete_image',array('id'=>$photo->id))}}" onclick="returnconfirm(' ?')"><button type="button"class="btn btn-danger btn-small"> </button></a> <p> :</p> <form name="movephoto" method="POST"action="{{route('move_image')}}"> {{ csrf_field() }} <select name="new_album"> @foreach($albums as $others) <option value="{{$others->id}}">{{$others->name}}</option> @endforeach </select> <input type="hidden" name="photo"value="{{$photo->id}}" /> <button type="submit" class="btn btn-smallbtn-info" onclick="return confirm(' ?')"> </button> </form> </div> </div> </div> @endforeach </div> </div> <script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0-rc1/js/bootstrap.min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script> </body> </html>
In addition, we can slightly update our routing, for example, in
nav.blade.php
:
<li><a href="{{URL::route('create_album_form')}}"> </a></li>
To a newer and more fashionable option:
<li><a href="{{route('create_album_form')}}"> </a></li>
On the other hand, the previous version worked on 6 Laravel. So, we already have a form in which the user can add photos to the album, and there is the main
view
our album. Next, we need to add a controller who will be responsible for processing a single photo.
So, we create a controller responsible for working with individual photos:
php artisan make:controller ImageController
So, what should be inside of us:
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Support\MessageBag; use Validator; // use App\Album; use App\Image; use Illuminate\Support\Str; class ImageController extends Controller { public function getForm($id) { $album = Album::find($id); return view('addimage') ->with('album',$album); // , } public function postAdd(Request $request) { // , $rules = [ 'album_id' => 'required|numeric|exists:albums,id', 'image'=>'required|image' ]; $input = ['album_id' => null]; $validator = Validator::make($request->all(), $rules); if($validator->fails()){ return redirect()->route('add_image', ['id' => $request->get('album_id')])->withErrors($validator)->withInput(); } $file = $request->file('image'); // $random_name = Str::random(8); // $destinationPath = 'albums/'; $extension = $file->getClientOriginalExtension(); $filename=$random_name.'_album_image.'.$extension; $uploadSuccess = $request->file('image')->move($destinationPath, $filename); Image::create(array( 'description' => $request->get('description'), // 'image' => $filename, // 'album_id'=> $request->get('album_id') // )); return redirect()->route('show_album',['id'=>$request->get('album_id')]); } public function getDelete($id) // { $image = Image::find($id); $image->delete(); return redirect()->route('show_album',['id'=>$image->album_id]); } // public function postMove(Request $request) { $rules = array( 'new_album' => 'required|numeric|exists:albums,id', 'photo'=>'required|numeric|exists:images,id' ); // $validator = Validator::make($request->all(), $rules); if($validator->fails()){ return redirect()->route('index'); } $image = Image::find($request->get('photo')); $image->album_id = $request->get('new_album'); $image->save(); return redirect()->route('show_album', ['id'=>$request->get('new_album')]); } }
Registration and authorization
At the moment, our application is almost ready. However, we still need to tighten the ability to register in our application, log out, and log into an existing account.
In the last part, we have already shown how our
includes/nav.blade.php
looks like. Because we want the Login and
buttons to appear when the user has not registered yet, and the Log out button when he is registered with us.
So, this is how we now look at the list in
includes/nav.blade.php
:
<ul class="nav navbar-nav"> @if (Auth::check()) <li> <button type="button" class="btn btn-primary"> <!-- , -> --> {{{ Auth::user()->name}}} </button> </li> <li><a href="{{route('create_album_form')}}"> </a></li> @else <!-- --> <li><a href="{{route('register')}}"></a></li> <li><a href="{{route('login')}}"></a></li> @endif @if (Auth::check()) <!-- , --> <!-- --> <li> <a 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> </li> @endif </ul>
Using the
Auth::check
construction and elementary
if else
, we can build changes in our
view
, depending on whether our user is registered or not.
However, with changes to the
view
before creating the model and controller, we run a little ahead of the engine. In order to make our authentication, we will use all the ready-made and quick solutions that are in Laravel 6.
So, about the User model - we will use the one that comes in the box. On the one hand, it is clearly not enough for a full-fledged application, on the other - I do not want to limit myself to the scope of one specific application. What kind of gallery do you want? Where can a user create his own private albums that are not accessible to anyone? Or where does everyone share their photos? We will not have restrictions in our application - I think the reader will be able to finish them without any problems to his taste and color.
So, in Laravel 6, creating authentication has become even easier. We go to the command line of our application:
composer require laravel/ui —dev
Which gives us the necessary packages. Now create:
php artisan ui:auth
What does this team give us? Firstly, we have a new
auth
folder in
resources/views
, in which we have all the
views
that may be needed to authorize the user and register him:
login
,
register
and
verify
. There is also a
passwords
subfolder dedicated to resetting and recovering passwords. In the
Http/Controllers
folder, we have
HomeController
, which is needed to redirect to the home page after user registration. We will also have a subfolder
layouts
, which will contain
app.blade.php
, which we will not parse today, but it can be an excellent basis for an application created from scratch.
Generated blade templates already have an initial layout tailored for
Bootstrap
. We already have it connected in
header.blade.php
.
In addition, we have updated a bit of routing in
routes/web.php
. The following lines were added:
Auth::routes(); Route::get('/home', 'HomeController@index')->name('home'); Route::post('/logout', 'Auth\LoginController@logout')->name('logout');
The path to
home
automatically generated, and I did the redirect to
logout
myself.
In order for your layout to work on the generated templates, we’ll connect our
nav
and
header
to them:
@include('includes.header') <body> @include('includes.nav') @extends('layouts.app')
Great, now everything is ready. Try to register. Password confirmation should also work:
In the future, the user can choose which page of the service he wants to go to (or the reader can make a redirect by himself, it’s not enough complicated). We just have to fix a small detail: to make it so that only registered and authorized users can see our precious albums.
To do this, we add a condition to our routing so that the user logs in to access the albums.
Updating our
web.php
:
Route::get('/', array('as' => 'index','uses' => 'AlbumsController@getList')) ->middleware('auth');; //middleware http Route::get('/createalbum', array('as' => 'create_album_form','uses' => 'AlbumsController@getForm')) ->middleware('auth');; // Route::post('/createalbum', array('as' => 'create_album','uses' => 'AlbumsController@postCreate')) ->middleware('auth');; // Route::get('/deletealbum/{id}', array('as' => 'delete_album','uses' => 'AlbumsController@getDelete')) ->middleware('auth');; // Route::get('/album/{id}', array('as' => 'show_album','uses' => 'AlbumsController@getAlbum')) ->middleware('auth');; //
Basically, at this stage we can already finish. I will leave it up to the reader to decide which application he wants to make: with private albums or with public ones. Purely theoretically, the application can already be used if you saw off the possibility of registration and create a user separately directly in the database - for example, if you and your colleagues are going to organize your private albums and for some reason do not want to use other services.
Thank you all for your attention! As always, useful links: