Laravel: we analyze the basic concepts. Part Three: The Final

A few days remain until the start of a new course from OTUS - “Framework Laravel” . In anticipation of the start of the course, we share the final part of the author’s publication on the basic concepts in Laravel. Important: this series of publications is not related to the educational program of the course and is a little useful material for beginners . The course program can be found here .








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:






All Articles