In This tutorial;, we will Build A photo sharing website with laravel 5.2 from scratch . Laravel is a perfect choice for amateur projects as well as enterprise solutions. There are continuous improvement & enhancements in Laravel framework by the community developers. The recent announcement of Laravel 5.2 update contains few excellent new features. It continues the improvements made in Laravel 5.1 by adding multiple authentication driver support, simplified eloquent global scopes, implicit model binding, middleware groups, opt-in authentication scaffolding, array validation improvements, rate limiting middleware, and more. First, we will create an images table. Then we’ll cover methods to resize and share images.
some important notes on larvel 5.2 before diving The tutorial
Implicit model binding
Implicit model binding is the latest feature which automatically binds a model to a route. Below is an example in code:
What this does is behind the scenes call
Array Validation
This is very excellent feature. For example: you have a form with an array of input fields like this:
While in Laravel 5.1 to add validation rules, it required looping through and adding the rules separately. Instead of having to do all that it’s been “Laravelized” into this:
Middleware Groups
Middleware groups enable to group many route middleware below a single, convenient key, allowing to assign several middleware to a route at once. For eg, this can be useful when building a web UI and an API within the same application. The user may group the session and CSRF routes into a web group, and possibly the rate limiter in the api group.
Rate Limiting
A latest rate limiter middleware is presently included with the framework, facilitating to easily restrict the number of requests that a given IP address can make to a route over a particular number of minutes. For eg, to limit a route to 60 requests every minute from a single IP address, user may do the following:
Collections Wildcards
When utilizing a collection and wanting to pull out data, user can now pass a * as a wildcard:
Eloquent Global Scope
In earlier versions of Laravel, global Eloquent scopes were complex and error-prone to implement; though, in Laravel 5.2, global query scopes only require to implement a single, simple method: apply.
Database Session Driver
The database session driver now covers user_id and ip_address so user can easily clear all sessions for a given user.
Hence, Laravel helps the application to stay alive and relevant utilizing established software development patterns.
note – please comment below if you were facing any problem or if you are new to laravel checkout this article
The following topics are covered in this tutorial:
After successfully installing Laravel and defining database credentials from config/database.php
, create a database called images
. For this, you can either create a new database from your hosting provider’s panel, or if you are the server administrator, you can simply run the following SQL command:
CREATE DATABASE images
After successfully creating the database for the application, we need to create a images
table and install it to the database. To do this, open up your terminal, navigate to your project folder and run the following command:
php artisan make:migration create_images_table --table=images –create
This command will generate a new MySQL database migration for us to create a table named images.
Now we need to define what sections should be in our database table. For our example, I thought id column
, image titles
, image file names
, and timestamps
should be sufficient. So for this, open the migration file just created with the preceding command and change its contents as shown in the following code:
<?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateimagesTable extends Migration { /** * Run the migrations. * @return void */ public function up() { Schema::create('images', function(Blueprint $table) { $table->increments('id'); $table->string('title',400)->default('');//the column that holds the image's name $table->string('image',400)->default('');//the column that holds the image's filename $table->timestamps(); }); } /** * Reverse the migrations. * @return void */ public function down() { Schema::drop('images'); } }
After saving the file, run the following command to execute migrations:
php artsian migrate
If no error has occurred, you are ready for the next step of the project.
As you know, for anything related to database operations on Laravel, using models is the best practice. We will take advantage of the Eloquent ORM.
Save the following code as images.php
in the app
directory:
<?php class image extends Eloquent { //the variable that sets the table name protected $table = 'images'; //the variable that sets which columns can be edited protected $fillable = array('title','image'); //The variable which enables or disables the Laravel'stimestamps option. Default is true. We're leaving this hereanyways public $timestamps = true; }
We have set the table name with the protected $table
variable. The content of which columns of the table can be updated/inserted will be decided with the protected $fillable
variable. Finally, whether the model can add/update timestamps or not will be decided by the value of the public $timestamps
variable. Just by setting this model (even without setting anything), we can easily use all the advantages of Eloquent ORM.
Our model is ready, now we can proceed to the next step and start to create our controller along with the upload forms. But before this, we are missing one simple thing. Where should the images be uploaded? What will be the maximum width and height of the thumbnails? To set these configuration values (think of it like constants of raw PHP), we should create a new configuration file.
With Laravel, setting configuration values is quite easy. All config
values are within an array and will be defined as a key=>value
pair.
Now let’s make a new configuration file. Save this file as image.php
in config
:
We should make an upload form and then a controller for our image site. But before doing that, we will install a third-party library for image processing as we will be benefiting from its methods. Laravel 4 uses Composer , so it’s quite easy to install packages, update packages, and even update Laravel. For our project, we will be using a library called Intervention
. The following steps must be followed to install the library:
composer.phar
file by running php composer.phar self-update
in your terminal.composer.json
and add a new value to the require
section. The value for our library is intervention/image: "dev-master"
.Currently, our composer.json
file’s require
section looks as follows: "require": { "laravel/framework": "5.1.*", "intervention/image": "dev-master" }
You can find more packages for Composer at www.packagist.org.
root
folder, and type the following command: php composer.phar update
This command will check composer.json
and update all the dependencies (including Laravel itself) and if new requirements are added, it will download and install them.
Intervention
class. Now open your app/config/app.php
, and add the following value to the providers
key: Intervention\image\imageServiceProvider
'image' => 'Intervention\image\Facades\image',
image::make(Input::file('image')->getRealPath())->resize(300, 200)->save('foo.jpg');
For more information about the Intervention
class, go to the following web address:
http://intervention.olivervogel.net
Now, everything for the views and the form processing is ready; we can go on to the next step of our project.
please note – csrf protection comes out of box in laravel 5
Now we should make an upload form for our image site. We must make a view file, which will be loaded over a controller.
app/http/routes.php
, remove the line starting with Route::get()
that comes with Laravel, and add the following lines: //This is for the get event of the index page Route::get('/',array('as'=>'index_page','uses'=>'imageController@getIndex')); //This is for the post event of the index.page Route::post('/',array('as'=>'index_page_post','before' =>'csrf', 'uses'=>'imageController@postIndex'));
The key 'as'
defines the name of the route (like a shortcut). So if you make links to the routes, even if the URL changes for the route, your links to the application won’t be broken. The before
key defines what filters will be used before the action starts. You can define your own filters, or use the built-in ones. We set csrf
, so the CSRF (Cross-site Request Forgery) checking will be done before the action starts. This way, you can prevent attackers from injecting unauthorized requests into your application. You can use multiple filters with the separator; for example,filter1|filter2
.
You can also define the CSRF protection from controllers directly.
imageController.php
in app/http/controllers/
: <?php class imageController extends BaseController { public function getIndex() { //Let's load the form view return View::make('tpl.index'); } }
Our controller is RESTful; that’s why our method index is named getIndex()
. In this method, we are simply loading a view.
frontend_master.blade.php
in app/views/
: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html lang="en"> <head> <meta http-equiv="content-type"content="text/html; charset=utf-8"> <title>Laravel image Sharing</title> {{HTML::style('css/styles.css')}} </head> <body> {{--Your title of the image (and yeah, blade enginehas its own commenting, cool, isn't it?)--}} <h2>Your Awesome image Sharing Website</h2> {{--If there is an error flashdata in session(from form validation), we show the first one--}} @if(Session::has('errors')) <h3 class="error">{{$errors->first()}}</h3> @endif {{--If there is an error flashdata in session whichis set manually, we will show it--}} @if(Session::has('error')) <h3 class="error">{{Session::get('error')}}</h3> @endif {{--If we have a success message to show, we printit--}} @if(Session::has('success')) <h3 class="error">{{Session::get('success')}}</h3> @endif {{--We yield (get the contents of) the section named'content' from the view files--}} @yield('content') </body> </html>
To add a CSS
file (which we will create in the next steps), we use the style()
method of the HTML
class. And our masterpage yields a section named content
, which will be filled with the view files
sections.
view file
section by using the following code. Save this file asindex.blade.php
in the resources/views/tpl/
directory: @extends('frontend_master') @section('content') {{Form::open(array('url' => '/', 'files' => true))}} {{Form::text('title','',array('placeholder'=>'Please insert your title here'))}} {{Form::file('image')}} {{Form::submit('save!',array('name'=>'send'))}} {{Form::close()}} @stop
In the first line of the preceding code, we told the Blade Engine that we will be usingfrontend_master.blade.php
as the layout. This is done using the @extends()
method in Laravel 4.
Benefiting from the Form
class of Laravel, we generated an upload form with the title
field andupload
field. , to make a new upload form, we are not usingForm::open_for_files()
anymore. It’s merged with the open()
method, which accepts either a string or an array if you want to pass more than one parameter. We will be passing the action URL as well as telling it that it’s an upload form, so we passed two parameters. The url
key is to define where the form will be submitted. The files
parameter is Boolean, and if it’s set to true
, it’ll make the form an upload form, so we can work with files.
To secure the form and to prevent unwanted form submission attempts, we will be needing a CSRF key hidden
in our form. Thanks to Laravel’s Form
class, it’s generated in the form automatically, right after the form opening tag. You can check it by looking at the source code of the generated form.
The hidden autogenerated CSRF form element looks as follows:
<input name="_token" type="hidden" value="SnRocsQQlOnqEDH45ewP2GLxPFUy5eH4RyLzeKm3">
styles.css
file in public/css/
(the path we defined on the master page): /*Body adjustment*/body{width:60%; margin:auto; background:#dedede} /*The title*/h2{font-size:40px; text-align:center; font-family:Tahoma,Arial,sans-serif} /*Sub title (success and error messages)*/h3{font-size:25px; border-radius:4px; font-family:Tahoma,Arial,sans-serif; text-align:center;width:100%} h3.error{border:3px solid #d00; background-color:#f66; color:#d00 } h3.success{border:3px solid #0d0; background-color:#0f0; color:#0d0}p{font-size:25px; font-weight: bold; color: black;font-family: Tahoma,Arial,sans-serif}ul{float:left;width:100%;list-style:none}li{float:left;margin-right:10px} /*For the input files of the form*/input{float:left; width:100%; border-radius:13px;font-size:20px; height:30px; border:10px 0 10px 0;margin-bottom:20px}
We’ve styled the body by giving it 60 percent width, making it center-aligned, and giving it a grayish background. We also formatted h2
and h3
messages with success
and error
classes, andforms
.
Now that our form is ready, we are ready to progress to the next step of the project.
In this section, we are going to validate the submitted form and make sure that the required fields are present and the submitted file is an image. Then we will upload the image to our server, process the image, create the thumbnail, and save the image information to the database as follows:
image.php
file in the app/models/
directory (the model that we generated earlier in this tutorial) inside the class definition before the last curly bracket(}
): //rules of the image upload form public static $upload_rules = array( 'title'=> 'required|min:3', 'image'=> 'required|image' );
We set the variable as public
, so it can be used outside the model file, and we set it to static, so we can directly access the variable.
We want both title
and image
to be mandatory, and title
should have at least three characters. Also, we want to check MIME types of the image
column and make sure that it’s an image.
Laravel’s MIME-type checking requires the Fileinfo
extension to be installed. So make sure it’s enabled in your PHP configuration.
post
method to process the form. Add this method in the imageController.php
file in app/http/controllers/
before the last curly bracket(}
): public function postIndex() { //Let's validate the form first with the rules which areset at the model $validation = Validator::make(Input::all(),image::$upload_rules); //If the validation fails, we redirect the user to theindex page, with the error messages if($validation->fails()) { return Redirect::to('/')->withInput()->withErrors($validation); } else { //If the validation passes, we upload the image to thedatabase and process it $image = Input::file('image'); //This is the original uploaded client name of theimage $filename = $image->getClientOriginalName(); //Because Symfony API does not provide filename//without extension, we will be using raw PHP here $filename = pathinfo($filename, PATHINFO_FILENAME); //We should salt and make an url-friendly version of//the filename //(In ideal application, you should check the filename//to be unique) $fullname = Str::slug(Str::random(8).$filename).'.'.$image->getClientOriginalExtension(); //We upload the image first to the upload folder, thenget make a thumbnail from the uploaded image $upload = $image->move(Config::get( 'image.upload_folder'),$fullname); //Our model that we've created is named image, thislibrary has an alias named image, don't mix them two! //These parameters are related to the image processingclass that we've included, not really related toLaravel image::make(Config::get( 'image.upload_folder').'/'.$fullname)->resize(Config::get( 'image.thumb_width'),null, true)->save(Config::get( 'image.thumb_folder').'/'.$fullname); //If the file is now uploaded, we show an error messageto the user, else we add a new column to the databaseand show the success message if($upload) { //image is now uploaded, we first need to add columnto the database $insert_id = DB::table('images')->insertGetId( array( 'title' => Input::get('title'), 'image' => $fullname ) ); //Now we redirect to the image's permalink return Redirect::to(URL::to('snatch/'.$insert_id))->with('success','Your image is uploadedsuccessfully!'); } else { //image cannot be uploaded return Redirect::to('/')->withInput()->with('error','Sorry, the image could not beuploaded, please try again later'); } } }
Let’s dig the code one by one.
image::$upload_rules
.insert_id
, we redirect the user to a new page that will show thumbnails, full-image links, and a forum thumbnail BBCode, which we will generate in the next sections.
Now, we need to make a new view and method from the controller to show the information of the image uploaded. This can be done as follows:
GET
route for the controller. For this, open your file routes.php
in theapp
folder and add the following codes: //This is to show the image's permalink on our website Route::get('snatch/{id}', array('as'=>'get_image_information', 'uses'=>'imageController@getSnatch')) ->where('id', '[0-9]+');
We defined an id
variable on the route, and with the where()
method, using regular expression, we filtered it first hand. So we don’t need to worry about filtering the ID field, whether it’s a natural number or not.
imageController.php
in app/http/controllers/
before the last curly bracket (}
): public function getSnatch($id) { //Let's try to find the image from database first $image = image::find($id); //If found, we load the view and pass the image info asparameter, else we redirect to main page with errormessage if($image) { return View::make('tpl.permalink')->with('image',$image); } else { return Redirect::to('/')->with('error','image not found'); } }
First, we looked for the image with the find()
method of Eloquent ORM. If it returns the value as false, that means there is a row found. So we can simply check whether there is a result or not with a simple if
clause. If there is a result, we will load our view with the found image info as a variable named $image
, using the with()
method. If no values are found, we return to the index page with an error message.
permalink.blade.php
in resources/views/tpl/
: @extends('frontend_master') @section('content') <table cellpadding="0" cellspacing="0" border="0"width="100percent"> <tr> <td width="450" valign="top"> <p>Title: {{$image->title}}</p> {{HTML::image(Config::get('image.thumb_folder').'/'.$image->image)}} </td> <td valign="top"> <p>Direct image URL</p> <input >You should be familiar with most methods used in this template by now. There is a new method called
entities()
of theHTML
class, which actually ishtmlentities()
of raw PHP, but with some pre-checks and as Laravel’s way.Also, because we’ve returned the
$image
variable to the view (which is the database row object that we’ve gained using Eloquent), we can use it directly as$image->columnName
in the view.We have added a permalink feature for our project, but what if we want to show all the images? For that, we need an
'all pages'
section in our system.
In this section, we are going to create an 'all images'
section in our system, which will have a page navigation (pagination) system. There are a few steps to be followed as shown:
route.php
file. For this, open app/http/routes.php
and add the following lines: //This route is to show all images. Route::get('all',array('as'=>'all_images','uses'=>'imageController@getAll'));
getAll()
(there is a get
method at the start because it will be a RESTful controller) to get values and load the view. To do this, open yourapp/controllers/imageController.php
and add these codes before the last curly bracket (}): public function getAll(){ //Let's first take all images with a pagination feature $all_images = DB::table('images')->orderBy('id','desc')->paginate(6); //Then let's load the view with found data and pass thevariable to the view return View::make('tpl.all_images')->with('images',$all_images); }
Here, we first got all the images from the database using the paginate()
method, which will allow us to get the pagination links easily. After that, we loaded the view for the user with the images data with pagination.
all_image.blade.php
in the resources/views/tpl/
directory: @extends('frontend_master') @section('content') @if(count($images)) <ul> @foreach($images as $each) <li> <a href="{{URL::to('snatch/'$each->id)}}">{{HTML::image(Config::get('image.thumb_folder')'/'.$each->image)}}</a> </li> @endforeach </ul> <p>{{$images->links()}}</p> @else {{--If no images are found on the database, we will showa no image found error message--}} <p>No images uploaded yet, {{HTML::link('/','care to upload one?')}}</p> @endif @stop
We first extend the frontend_master.blade.php
file with our content section. As for the content section, we first check whether any rows are returned. If so, then we loop them all in list item tags (<li>
) with their permalinks. The links()
method that came with the paginate
class will create the pagination for us.
You can switch the pagination template from config/view.php
.
If no rows have returned, that means there are no images (yet), so we show a warning message with a link to the new upload page (which is the index page in our case).
What if a person uploads an image that is not allowed or not safe for work? You would not like to have them on your website, right? So there should be an image deleting feature on your website.
We would like to have a delete feature in our script, using which we will delete the image both from the database and from its uploaded folder. This process is quite easy with Laravel.
app/routes.php
and add the following lines: //This route is to delete the image with given ID Route::get('delete/{id}', array ('as'=>'delete_image','uses'=> 'imageController@getDelete')) ->where('id', '[0-9]+');
getDelete($id)
inside imageController
. To do this, open app/http/controllers/imageController.php
and add the following code above the last curly bracket (}
): public function getDelete($id) { //Let's first find the image $image = image::find($id); //If there's an image, we will continue to the deletingprocess if($image) { //First, let's delete the images from FTP
File::delete(Config::get('image.upload_folder').'/'.$image->image);
File::delete(Config::get('image.thumb_folder').'/'.$image->image);
//Now let's delete the value from database $image->delete(); //Let's return to the main page with a success message return Redirect::to('/')->with('success','image deleted successfully'); } else { //image not found, so we will redirect to the indexpage with an error message flash data. return Redirect::to('/')->with('error','No image with given ID found'); } }
Let’s understand the code:
find()
method of Eloquent ORM, we will store it with a variable called $image
.$image
is not false, there is an image matching the image in our database. Then, we delete the file with the delete()
method of the File class. Alternatively, you can also use the unlink() method of raw PHP.delete()
method of Eloquent ORM.In practical application, you should have a backend interface for such actions.
In this tutorial , we’ve created a simple image sharing website with Laravel’s built-in functions. We’ve learned how to validate our forms, how to work with files and check their MIME types, and set custom configuration values. We’ve learned more about database methods both with Fluent and Eloquent ORM. Also, for image processing, we’ve installed a third-party library from packagist.org using Composer and learned how to update them. We’ve also listed images with the page navigation feature and learned to delete files from the server.
hope you will like it :)
IT is more important than ever in the world of higher education, and yet with…
If you’re here for the top tips, we assume you’re ahead of the “how to…
The world is progressing at unprecedented rates at the current moment, especially in terms of…
This article will highlight the Top 20 Opensource Python Tkinter Projects which we believe will…
With their numerous applications in streamlining the data flow, securing both the servers and the…
In this article, We will be looking at some of the top Node.js dashboard templates.…