Laravel: explain the basic concepts. Part Two: Practice

Hello! We continue the series of copyright publications on the eve of the start of the “Framework Laravel” course. In a previous article, we looked at the theoretical foundations of Laravel. However, the theory of any framework can be studied for a long time and you won’t understand anything until you write anything in practice.







Therefore, let us write an application in which each user can log in, create new albums and upload photos there. If you do not know yet what MVC is, read this , soon you will have to use it in practice .



Guides of this kind begin with an explanation of how to set up your environment, etc. The problem is that on each platform it is different, but I want the article to be interesting to everyone. I will just give links to the necessary, and you will try to install and configure it yourself.



So we need:







Composer For those in the tank, Composer is a package manager for PHP (yes, like npm for JavaScript). You can download it here .



Laravel himself. Here are some of the most popular options: Homestead and Valet (and in fact, many others). However, the latter is only suitable for Mac OS. Technically, the reader can use any favorite assembly, if only it meets these requirements.



And a subtle detail, but Node.js will also be needed for us to install npm packages.



Run the command: composer global require laravel/installer



to install Laravel.



Remember to put the $Home/.composer/vendor/bin



(or its equivalent on your OS) into your PATH



variable so that the Laravel command works on your system.



So, a good project starts with an understandable TK. Let's try to formulate the basic requirements for our simple application:







The user has the opportunity to upload photos to the site in albums created by him. He can register, and can log in. The database is MySQL. The framework that is used to simplify the layout process is Bootstrap, as the most common layout framework.



The first action that you take immediately after creation is connecting the database. If you still do not understand how to install and run your database, use ready-made desktop solutions from Evil Corporation: here and here . This will be enough at first.



So, everything is ready. We go to your favorite directory and start the project with



 laravel new gallery
      
      





Next you need to connect our database. To do this, go through the MySQL Branch and create a new gallery database (do not forget to set the Character Set to utf-8, and ollation to utf_general_ci



to avoid problems with the Russian language).







Next, go to the .ENV



file and configure a similar connection, remembering to change the variables to your username and password:



 DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=gallery DB_USERNAME=pavel DB_PASSWORD=
      
      





After that, we will be able to migrate our databases.



 php artisan migrate
      
      





If you configured your connection in .ENV



correctly, then everything should work for you.



As we agreed on t / z, we will have a process of authorization, registration, and also the user will have the opportunity to create albums and upload photos there. To implement all we need the appropriate tables in the database.



Create the albums table


So where does the backend start? From creating a table in our database. This command in the terminal will create an opportunity for us to migrate to the database:



 php artisan make:migration CreateAlbumsTable
      
      





So, we need to determine which columns we should have in our table. This must be done in the file that you created due to the last migration. It is located in your project in the gallery/database/migrations



folder and its name is associated with the time of its creation. It must be edited as follows:



 <?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'); } }
      
      





Don't forget to do



  php artisan migrate
      
      





Now the tab of your database in MySQL Branch should take the following form:







So, we created our database, now we need to connect our database with the Eloquen model, which will interact with our database.



Create an Album Model







Now we need to create an Eloquent ORM model. If you don’t understand how we moved so quickly from one to the other, you should read this. We will use the same Artisan helper to generate the model template:





php artisan make: model Album



Next, we go to gallery/app/Album.php



and edit it:



 <?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 table


Wow! Our album model is ready, now you can do our images. Here the process will be repeated again:



 php artisan make:migration CreateImagesTable
      
      





We are going to edit our migration:



 <?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'); } }
      
      





And again we do the migration:



 php artisan migrate
      
      





I hope your migration is a success! However, we need to explain one more thing that would have been too long to write down in the comments: we used the foreign



key to join the two tables Albums and Images . Each album has its own images, and if you want to delete some kind of album, then you probably believe that your photos will also be deleted.



Image Model


It's time to create a model that will work with the images table. I hope you already guess what to do:



 php artisan make:model Image
      
      





The template is created, go to gallery/app/Image.php



to edit it.

Here, too, everything should be clear:



 class Image extends Model { protected $table = 'images'; // ,      protected $fillable = array('album_id','description','image'); // }
      
      





Our Image model is ready. Now we need a controller to create albums in our database.



Album creation


To save, show and delete in our album, we need some functions in our controller. But first you need the controller itself:



 php artisan make:controller AlbumsController
      
      





Go to 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'); } }
      
      





Next, we have to redefine our paths. For those who do not know, routing is the setting of the paths that are displayed to the user in the browser, and the actions that our application should perform. We go to the web.php



file:



 <?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'));
      
      





Wow. In order for our application to make money, we need to make sure that we have the necessary links to the alias of the classes we need at the end of the gallery/config/app.php



. These are only technical settings:



 '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     )
      
      



Fine! Now we need to define our ideas! Go to the gallery/resources/views



folder. You can delete welcome.blade.php



, you can leave, it doesn’t matter. Create index.blade.php



and includes



subfolder to add the repeating parts of the view



. So, our index.blade.php



should take the following form:



 @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 FY",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>
      
      





Content 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>
      
      



Content 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>
      
      





That's all for today! This, of course, is only the first part of our application. Thank you all for your attention, in the next part we will supplement our view



and discuss the issue of image validation.



Useful links:



Documentation in Russian

A useful English guide that better than the documentation explains how inheritance and extension of Blade templates work

A lot of useful information and practical examples of creating applications



All Articles